用 Emacs Muse 来制作测试结果报告

news/2024/7/5 13:43:50
 

用 Emacs Muse 来制作测试结果报告

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

<script language="JavaScript" type="text/javascript"> </script>
将打印机的版面设置成横向打印模式

打印本页

<script language="JavaScript" type="text/javascript"> </script>
将此页作为电子邮件发送

将此页作为电子邮件发送

样例代码


级别: 初级

颜 林 (yanl@cn.ibm.com), 软件工程师, IBM

2008 年 6 月 26 日

Emacs Muse 是一个可以在 Emacs 中写 Wiki 文档的插件,通过 Emacs Muse,我们可以很容易地编写 Wiki 文档,并生成各种格式的文件。本文介绍了如何扩展使用 Emacs Muse —— 一个 Emacs 编辑器插件来生成精美的测试结果报告。

前言

Emacs 是一个开放源代码的编辑器。由于它使用效率高,可扩展性强,自20世纪70年代诞生以来就一直经久不衰,受到广大开发人员的热烈追捧。关于 Emacs 的各种介绍和使用技巧教程屡见不鲜。而本文所介绍的 Emacs Muse 是 Emacs 的一个扩展插件,它的前身就是 Emacs Wiki。由于 Emacs Wiki Mode 原作者 Michael Olson 需要重新架构代码,才另外创立了一个新的 Emacs Muse 项目。通过该插件,我们可以在 Emacs 中写 Wiki 文档,生成各种格式,包括网页,pdf ,DocBook ,LaTex 等等,并可以直接发布到网络中。

我们在工作过程中,往往需要制作一些结果报告,例如测试结果报告。在一份测试 报告中,需要统计测试结果,并对结果进行一定的分析和总结。如果我们想制作一份精美的报告,包括能对不同的结果着色,自动统计结果,对结果分析能有按照不 同的格式来突出显示,这样的工作完全可以通过 Emacs Muse 来实现。而且 Emacs Muse 可能按照 Wiki 网页的要求来生成 HTML 格式的网页,发布到网络中供人浏览。





回页首


Emacs Muse 的安装与配置

从 Emacs Muse 的官方网站上(参考资源)可以下载最新的 Muse 安装包,解压后修改Makefile.defs 文件来设置 Emacs 的安装路径,如清单1所示,该路径是 Mac OS X 平台下 Emacs 的设置,其他平台也可以类似修改。


清单 1 Makefile.defs示例
                
#设置EMACS的路径
EMACS    = emacs
SITEFLAG = --no-site-file
#设置Muse安装路径
DESTDIR  =
PREFIX   = /Applications/Emacs.app/Contents/Resources
ELISPDIR = $(DESTDIR)$(PREFIX)/site-lisp/muse
INFODIR  = $(DESTDIR)$(PREFIX)/info

安装路径设置完成后,用 GNU Make 编译安装 Muse ,只需要运行 make install 命令即可。

安装完成后,我们需要修改 Emacs 的配置文件 .emacs 来加载 Muse 包,添加如清单2所示的代码到 .Emacs 文件中。


清单2 .emacs文件配置
                
(require 'muse-mode)

(require 'muse-html)         ;添加html格式的支持
(require 'muse-latex)        ; 添加latex格式的支持
(require 'muse-texinfo)      ; 添加texinfo格式的支持
(require 'muse-docbook)      ; 添加docbook格式的支持
(require 'muse-wiki nil t)   
(require 'muse-project)      ; 添加wiki project的支持

;设置编码方式为utf-8
(setq muse-html-meta-content-type (concat "text/html; charset=utf-8"))

;新建一个wiki工程
(setq muse-project-alist
      '(("MyWiki"
         ("~/Documents/wiki" :default "index")
         (:base "html" :path "~/Document/wiki/publish"))))

这样就完成了配置,在这个配置文件里创建了我们第一个 wiki 工程 MyWiki。





回页首


Emacs Muse的基本操作

配 置完成以后重新启动 Emacs,就已经加载了 Muse。这时所有的以 muse 为扩展名的文件都是 Emacs Muse 的源文件,我们只需要用一些简单的标记来编写 muse 文件,然后用 Emacs 来生成各种格式的输出,下面我们就以生成一个简单的 Wiki 网页为例介绍下 Emacs Muse 的基本操作。

在 Emacs 中按快捷键 Ctrl+x Ctrl+f ,创建文件 ~/Documents/wiki/FirstPage.muse ,输入如清单3所示的内容:


清单3 FirstPage.muse
                
#title 第一个Wiki页面

* 一级标题需要一个星号开头
** 二级标题需要两个星号开头
*** 三级标题需要三个星号开头


段落需要两个以上空行

          居中一行文字需要以6个以上的空白开头
----
示一个横线只需要输入4个以上的“-”标记

这是 *着重* 的文字,这是 **进一步着重** 的文字,这是 ***更进一步的着重*** 的文字

这是 _下划线_ 文字,这是 =等宽verbatim and monospace= 的文字

*** 列表:

  - 无序列表需要以空格和“-”开头
  1. 有序列表需要以空格和数字序号开头
  字典 :: 名词定义需要以“::”分隔名词和所定义文字
  - 列表也可以嵌套
     1. 列表嵌套深度按照开头空格的多少来控制
     2. 可以继续嵌套不同类型的列表
        - 比如这样

*** 表格:
表格标题 || 用“||”分割
表格内容	| 用“|”分割
表格结尾	||| 用“|||”分割

在 FirstPage.muse 文件输入过程中,Emacs 的 Muse 模式会根据用户的输入,生成不同的显示预览,如图1所示,这样大大方便了我们写 wiki 的效率,在 Emacs 中实现了所见即所得的用户体验。


图 1. Emacs 的 Muse 模式
Emacs 的 Muse 模式

输入完成后,按快捷键 Ctrl+c Ctrl+p ,该快捷键用来在当前工程目录下生成 wiki 网页。生成成功后, wiki 网页位于 ~/Documents/wiki/publish/FirstPage.html 。按快捷键 Ctrl+c Ctrl+v 来预览该网页,如图2所示:


图 2. 第一份 wiki 网页
第一份 wiki 网页

这 样我们就生成了一份 wiki 网页。在 firstPage.muse 文件中,我们用各种标记符号(*, -, =)来表示网页中的各种元素(标题,加粗,列表),这样就非常简单地生成了一张 wiki 网页,它可以发布到网站中直接供人阅读,非常适用于记录技术文档和笔记。

更为重要的是,Emacs Muse 不仅可以由 wiki 记号生成 HTML 格式,还可以生成 Latex, texinfo, docbook 等等多种格式,这也意味着我们完全可以用 Emacs Muse 来制作各种格式的文档。Emacs Muse 对多种文档格式的支持非常丰富,这在它的官方网站上可以找到完整的支持文档列表。





回页首


自定义单元测试报告的样式和自动分析结果

上面内容简单介绍了 Emacs Muse 的基本用法和基本快捷键。由于 Emacs 天生的易于扩展的特点,Emacs Muse 也提供了大量的函数接口来扩展它的功能。扩展 Emacs 所用的 Elisp 语言简洁优美,非常容易学习和编程。

Muse 默认生成的 HTML 格式是非常普通的网页样式,我们可以通过自定义其样式来美化其输出。我们可以根据项目,工作和学习的具体需要,来定义不同需求的格式,使其成为一个多样式 的网页生成工具。下面本文就介绍如何自定义一种简单的单元测试报告的格式,该格式基于 HTML 的输出样式,但自定义了表单样式,同时根据测试结果来生成统计数据。

将 Muse 扩展代码放在 emacs-muse.el 文件中,在 .emacs 文件最后一样加入代码来包括这个文件,如清单4所示,把加载 muse 包的代码以及自定义样式的代码放入到 emacs-muse.el 文件,这样可以方便编写调试,而且还能方便在命令行调用。


清单4 在.emacs文件中包括Muse扩展包加载代码
                
;; Emacs Muse
    (load-file "~/emacs-muse.el")

自定义单元测试样式

Muse 提供了函数 muse-define-style 来自定义格式,还有函数 muse-derive-style 来继承已定义的格式。本文所需输出的测试结果是一种自定义的 HTML 格式,所以只需要函数 muse-derive-style 来继承自 HTML 格式即可,代码如清单5所示,这里我们定义了一个名为 UT 的新格式,它继承自 HTML 格式。


清单5 定义UT样式
                
(muse-derive-style "ut" "html"
				   :header 'ut-html-header
				   :style-sheet 'ut-style-sheet)

在该格式中,HTML 文件头由函数 ut-html-header 定义,该函数代码如清单6所示:


清单6 ut-html-header函数定义HTML文件头
                
(setq ut-html-header
"<!DOCTYPE HTML PUBLIC /"-//W3C//DTD HTML 4.0 Transitional//EN/">
<html>
  <head>
    <title><lisp>
  (concat (muse-publishing-directive /"title/")
          (let ((author (muse-publishing-directive /"author/")))
            (if (not (string= author (user-full-name)))
                (concat /" (by /" author /")/"))))
</lisp></title>
    <meta name=/"generator/" content=/"muse.el/">
    <meta http-equiv=/"<lisp>muse-html-meta-http-equiv</lisp>/"
          content=/"<lisp>muse-html-meta-content-type</lisp>/">
    <lisp>
      (let ((maintainer (muse-style-element :maintainer)))
        (when maintainer
          (concat /"<link rev=///"made///" href=///"/" maintainer /"///">/")))
</lisp>
<lisp>(muse-style-element :style-sheet muse-publishing-current-style) </lisp>
  </head>
  <body>
    <h1><lisp>(muse-publishing-directive /"title/")</lisp></h1>
	<p><big><em><lisp>(let ((author (muse-publishing-directive /"author/")))
            (if (not (string= author (user-full-name)))
                (concat /"by /" author )))</lisp></em></big></p>
	<p><big><em><lisp>(let ((date (muse-publishing-directive /"date/")))
                 (concat /"Last Modified: /" date ))</lisp></em></big></p>
    <h2><lisp>(let ((package (muse-publishing-directive /"package/")))
                 (concat /"Test script Package &mdash/; /" package )) </lisp> </h2>
   <table class=/"muse-table/" border=/"2/" cellpadding=/"5/">
   <tbody><tr><td><span class=/"pass/">PASS</span></td>
<td>Passed all test</td></tr><tr><td><span class=/"fail/">FAIL</span></td>
<td>Failed for new issue</td></tr><tr><td><span class=/"attempt/">ATTEMPTED</span></td>
<td>Tried but there is known issue</td></tr>
<tr><td><span class=/"nattempt/">NOT ATTEMPTED</span></td> 
<td>Not tried</td></tr></tbody></table>
<!-- Page published by Emacs Muse begins here -->/n")

清单6中,定义了 UT 样式中所输出的网页文件的开头部分代码。作为一个简单的单元测试结果报告样式,在该网页的开始部分,说明了报告的标题,测试人员名称,单元测试的代码包。 定义了四种测试结果: PASS, FAIL, ATTEMPTED, NOT ATTEMPTED ,分别表示通过,失败,有已知错误但已运行了单元测试,未测试。这段报告的开头部分所对应的输出网页位置如图4 所示。

为了表示不同样式元素,我们需要有个自定义的样式表嵌入到输出网页中,清单5 的代码中,样式表格式由函数 ut-style-sheet 定义,该函数定义如清单7所示。


清单7 ut-style-sheet函数定义样式表
                
(setq ut-style-sheet
	  "<style type=/"text/css/">
body {
  FONT: 14px/1.4 'Trebuchet MS',Verdana, Arial, Helvetica, sans-serif;
 background:#fff;
 width: 60em;
 margin: 0 auto 0;
 padding: 2em 0 6em 0;
 text-align: left;
}
a {
  font-family: Verdana;
 text-decoration:none;
 font-weight:bold;
 color:#c00;
}
a:hover {
 background:#000000;
 color:#FFFFFF;
}
h1 a { color:#666;}
h2 a { color:#666;}
h3 a { color:#666;}
h4 a { color:#666;}
h1 {  font-size: 40px;
 color:#666;
 border-bottom: 5px solid #000;
 padding: 2px;
 margin: 0px;
 margin-bottom: 8px;
}
h2 { 
 color:#666;
 font-size: 22px;
 padding: 2px;
 margin-top: 15px;
 border-bottom: 2px solid #000000;
}
h3 { 
 color:#666;
 font-size: 18px;
 padding: 2px;
 margin-top: 5px;
}
h4 { 
 color:#666;
 font-size: 18px;
 padding: 2px;
 margin-top: 5px;
}
img {
    float: right;
    margin: 10px;
    border-style:solid; border-width:2px;
}
#im {     clear: right;}
pre {
    border: #777777 1px solid;
    padding: 0.5em;
    margin-left: 1em;
    margin-right: 2em;
    white-space: pre;
    background-color: #e6e6e6;
    color: black;
}
.pass {  color:Green;  }
.fail {  color:Red;  font-weight: bold; }
.attempt {  color:Maroon;  font-weight: bold; }
.nattempt {  color:Silver;   }
.verse {
    white-space: pre;
    margin-left: 1em;
}
dt {      font-weight: bold;}
li {     margin-bottom: 0.9ex;}
blockquote {
     margin-left: 2em;
     color: #4444ff;
}
td {  font-size: 13px;}
th {
  background: #d6d6d6;
  font-size: 14px;
} 
</style>")

在清单7所示的样式表中,我们定义了网页 报告中的各种元素(标题,章节,表格等)的格式。而且还专门针对于单元测试结果报告,定义了四种样式: .pass, .fail, .attempt, .nattempt 。定义了它们的颜色和字体,读者可以根据自己的需要,修改它们的样式。

这样我们做好了一份测试报告的准备工作,定义了不同测试结果的 html 样式,定义了报告开始部分的格式。下面将介绍如何生成报告的主体部分。

自定义单元测试报告内容格式

在 Muse 中我们可以使用一种格式类似于 XML 的标签来表示特定的内容,例如如果想输入一段包括了 Muse 的关键字字符的文本,而又不想被 Muse 所解释这些关键字字符,则可以在 Muse 文件中输入如清单8所示的文本:


清单8 标签示例
                
<verbatim>
这是 *着重* 的文字,这是 **进一步着重** 的文字,这是 ***更进一步的着重*** 的文字
</verbatim>

输出则如图3所示。


图 3. verbatim 标签输出示例
verbatim 标签输出示例

Muse 提供了多种标签可以在 Muse 文件中使用,包括 lisp(动态嵌入 lisp 代码的运行结果),python (动态嵌入 python 代码的运算结果), src (对标签内的代码文本进行着色)等等。通过 Muse 函数 muse-publish-markup-tags ,我们可以自定义标签。本测试报告自定义了 result 标签,通过在该标签范围内输入测试结果, 那么Muse 在生成 HTML 文件时,会根据我们的扩展代码解释 result 标签内的内容,生成我们所期望的格式。result 标签的定义代码如清单9所示。


清单9 result标签定义代码
                
(defvar muse-pass-tag '("pass" t nil nil muse-ut-pass-tag))
(defun muse-ut-pass-tag (beg end)
  (delete-region beg end)
  (goto-char beg)
  (muse-insert-markup "<span class=/"pass/">PASS</span>"))

(defvar muse-fail-tag '("fail" t nil nil muse-ut-fail-tag))
(defun muse-ut-fail-tag (beg end)
  (delete-region beg end)
  (goto-char beg)
  (muse-insert-markup "<span class=/"fail/">FAIL</span>"))

(defvar muse-attempt-tag '("attempt" t nil nil muse-ut-attempt-tag))
(defun muse-ut-attempt-tag (beg end)
  (delete-region beg end)
  (goto-char beg)
  (muse-insert-markup "<span class=/"attempt/">ATTEMPTED</span>"))

(defvar muse-nattempt-tag '("nattempt" t nil nil muse-ut-nattempt-tag))
(defun muse-ut-nattempt-tag (beg end)
  (delete-region beg end)
  (goto-char beg)
  (muse-insert-markup "<span class=/"nattempt/">NOT ATTEMPTED</span>"))

(defvar muse-result-tag '("result" t t nil muse-ut-result-tag))
(defun muse-ut-result-tag (beg end attrs)
  "Insert unit test result
   <result> ...  </result>."
  (muse-publish-markup-attribute beg end attrs nil
	(save-excursion
      (save-restriction    
		(setq passed 0)
		(setq failed 0)
		(setq attempted 0)
		(setq nattempted 0)
		(goto-char (point-min))
		(while (re-search-forward "<pass/>" nil t)
			  (setq passed (1+ passed)))
		(goto-char (point-min))
		(while (re-search-forward "<fail/>" nil t)
			  (setq failed (1+ failed)))
		(goto-char (point-min))
		(while (re-search-forward "<attempt/>" nil t)
			  (setq attempted (1+ attempted)))
		(goto-char (point-min))
		(while (re-search-forward "<nattempt/>" nil t)
			  (setq nattempted (1+ nattempted)))
		(goto-char (point-min))
		(setq count (+ passed failed attempted nattempted))
		
		(goto-char (point-min))
		(muse-insert-markup (concat "<h3>Result:</h3><p><strong><em>" 
				"Totally " (number-to-string count) " cases," 
				(number-to-string passed) " passed, " 
				(number-to-string failed) " failed, " 
				(number-to-string attempted) " attempted, " 
				(number-to-string nattempted) " not attempted." 
				"</em></strong></p>"))
))))
(add-to-list 'muse-publish-markup-tags muse-result-tag)
(add-to-list 'muse-publish-markup-tags muse-pass-tag)
(add-to-list 'muse-publish-markup-tags muse-fail-tag)
(add-to-list 'muse-publish-markup-tags muse-attempt-tag)
(add-to-list 'muse-publish-markup-tags muse-nattempt-tag)

在清单9中,首先我们定义了四个单元测试 结果标签,这四个标签分别代表了四种单元测试结果,并对每种结果应用对用的样式表样式,进行着色和设置字体。然后定义了 result 标签,该标签统计在该标签内容中所包含的每种测试结果标签的个数。最后一句代码将 result 标签和四种单元测试结果标签发布到 muse 的标签列表中。

解释 result 标签的代码位于 muse-ut-result-tag 函数中,它首先定义了4个变量用于存储每种测试结果的次数,然后不断遍历标签中的文本,统计测试结果标签的个数来获知测试结果。最后,该函数输出统计结果,将其列在报告中的 Result 小节的开始处,如图4 所示。

生成第一份单元测试结果报告

上面做好了所有的准备工作,保存好 emacs-muse.el 文件。重启 Emacs 或者运行命令 M-x eval-buffer ,我们自定义的单元测试报告格式就生效了。

现在新建一个 Muse 工程,如清单10所示:


清单10 第一个单元测试报告工程
                
;添加一个wiki工程
(setq muse-project-alist
      '(("MyWiki"
         ("~/Documents/wiki" :default "index")
         (:base "html" :path "~/Document/wiki/publish")) 
	("MyUTReport"
         ("~/Documents/wiki2" :default "index")
         (:base "ut" :path "~/Document/wiki2/publish"))))

新建 muse 源文件 ~/Documents/wiki2/firstReport.muse ,输入如清单11所示的文本。


清单11 第一份单元测试报告的Muse文件
                
#author sky
#package org.example

<result>
Script Name || Result || Description
Script_a || <fail/> || resaon...reason...reason...reason...
Scrpit_b | <pass/> | 
Script_c | <nattempt/> | why...why...why...why...why...why...why...
Script_d | <attempt/> | result...result...result...result
</result>

* Analysis
  1. Below methods need refactor:
<src lang="c">
1. code ...
2. code2...
</src>

按快捷键 Ctrl+c Ctrl+p 发布工程,发布成功后,按快捷键 Ctrl+c Ctrl+v 来预览结果,结果图4 所示。


图 4. 第一份测试报告输出结果
第一份测试报告输出结果




回页首


同其他工具的结合

通 过 Emacs Muse ,我们可以通过简单的标记来生成精美的测试结果报告。生成报告所需要的muse文件既可以是手动输入,也可以是由自动化测试工具来自动生成。通过 Emacs 的 batch 模式,我们可以从命令行运行 Emacs ,让它以命令行的形式执行 emacs-muse.el 文件中的 Elisp 代码,生成 HTML 格式的网页报告。这一切只需要执行如下的命令:

skys-imac:~ sky$ emacs –q -batch  -l ~/emacs-muse.el –f muse-project-batch-publish MyWiki

最后的 MyWiki 是 muse 工程的名字。这样,通过自动化的测试工具来生成 muse 文件,再从命令行运行生成 HTML 报告。这样就将 Emacs Muse 同其他工具完美结合在一起。而且可以根据自己需求来自定义样式,将艺术和技术完美地结合到了一起,达到了很好的效果。





回页首


结束语

Emacs Muse 扩展性强,并在发布 Wiki 方面有着独到的强大功能。它支持格式多,支持源代码标记,可嵌套列表等等。有兴趣的读者可以直接访问附录中 Muse 的官方网站 http://mwolson.org/ ,这个网站完全使用 Emacs Muse 生成并发布到网络中的,并且所有的 Muse 文件都是开放代码,可以直接下载学习的。正是由于它的强大扩展性,也使得我们可以自定义样式来生成符合我们要求的文档,这也正是 Emacs 编辑器的迷人之处。






回页首


下载

描述名字大小下载方法
样例代码muse.zip3KBHTTP
关于下载方法的信息


参考资料

  • Emacs Muse 的官方主页 http://mwolson.org/ ,上面有全部的文档和最新的代码下载。

  • 王银的个人主页,Emacs 部分 :这是我的第一份 Emacs 入门教程。

  • “Emacs 编辑环境” :值得一看。

  • 生活在 Emacs 中 :developerWorks 中的一系列 Emacs 入门教程。很多人的 Emacs 入门教程。

  • Emacs Wiki :汇聚大量 Emacs 爱好者贡献的 Emacs 扩展代码的维基百科。

  • 让 Emacs 为您工作,第 1 部分 :是这篇文章启发了本文的写作。


关于作者

颜林,目前是 IBM 中国软件开发实验室的软件工程师,从事 Mac OS X 平台下的自动化测试工作。技术兴趣包括 Eclipse ,Mac OS X 开发,Linux 开发,Open Source。


http://www.niftyadmin.cn/n/4820382.html

相关文章

day正则表达式补充

# 2.正则 # 方法&#xff1a;findall | match | split | sub# a 10# print(a.__hash__())# def fn():# pass# print(fn.__name__)# import json# print(json.dumps([1,2,3]))import re # 全文匹配&#xff0c;返回值是列表res re.findall(\d*?, d1) # [, , ] | [, , 1, ]pri…

网络、HTTP相关学习总结

引言&#xff1a;上图是《图解 HTTP》这本书的目录&#xff0c;最近又重新读了一下这本书&#xff0c;然后打算写篇总结&#xff0c;来加深自己的理解&#xff0c;然而在做总结的时候觉得还是全面总结下网络相关的知识吧&#xff0c;所以又在网上找了大量的资料&#xff0c;最终…

sawfish-lisp-source_1.3.1-1_all.deb

sawfish-lisp-source_1.3.1-1_all.deb 的下载页面 如果您正在运行 Ubuntu&#xff0c;请尽量使用像 aptitude 或者 synaptic 一样的软件包管理器&#xff0c;代替人工手动操作的方式从这个网页下载并安装软件包。 您可以使用以下列表中的任何一个源镜像只要往您的 /etc/apt/sou…

TCL解释器与C++代码交互过程?

工 具 心 情 休 闲 博 客 论 坛 游客: 注册 | 登录 | 搜索 | 繁體中文 百思论坛 网络仿真软件 NS TCL解释器与C代码交互过程&#xff1f;上一主题下一主题 可打印版本 | 订阅主题 | 收藏主题 | 开通个人空间 <script type"text/javascript"> fun…

leetcode_654. Maximum Binary Tree

https://leetcode.com/problems/maximum-binary-tree/ 给定数组A&#xff0c;假设A[i]为数组最大值&#xff0c;创建根节点将其值赋为A[i]&#xff0c;然后递归地用A[0,i-1]创建左子树&#xff0c;用A[i1&#xff0c;n]创建右子树。 使用vector的assign函数,该函数的特性&#…

【vueJs源码】阅读之vm.$watch函数

我们经常使用watch肯定知道它&#xff0c;他和computer一样都是数据发生变化都会触发它。今天我们就来了解一下它的原理。 他的用法 Vue.prototype.$watch function (expOrFn: string | (() > any),cb: any,options?: Record<string, any> ): Function这是vuejs源…

Common Lisp

Common Lisp 来自 维客 Jump to: navigation, searchCommon Lisp&#xff0c;一般缩写为 CL&#xff08;不要和缩写同为CL的組合邏輯混淆&#xff09;&#xff0c;是Lisp的方言&#xff0c;标准由ANSI X3.226-1994定义。它是为了标准化此前众多的Lisp分支而开发的&#xff0c;它…

qa qc qm的区别

稍后更新。。。转载于:https://www.cnblogs.com/Chamberlain/p/10586562.html