当代信息社会中,软件的功能日趋强大,而项目却日趋复杂,如何保证软件项目在发布之前进行充分的测试,找出潜在问题,成为了一项重要课题。代码覆盖率是在产品开发,测试过程中,衡量产品质量的一项非常重要的技术指标。它可以帮助工程师准确的判断哪些源代码经过了测试,是否还需要补充测试代码,从而在产品的开发、测试阶段帮助工程师及时发现潜在问题,更好的提升产品质量。
Rational Test RealTime 是 IBM 公司开发的一套强大的、适用于嵌入式系统,实时系统以及网络系统的、跨平台的自动化实时测试分析工具集。它拥有代码覆盖率分析,内存泄露检查,以及性能检测等功能;能够进行单元测试,集成测试,系统测试,实时的嵌入式系统测试,分布式应用测试等各种自动化测试;支持 AIX、HP Unix、Linux、Sun Solaris、Windows 等多种操作平台。Rational Test RealTime 几乎可以满足开发团队建立复杂交互系统测试平台的所有需求。
本文主要介绍在 Linux 环境下如何利用 Rational Test RealTime 的命令模式进行 C/C++ 的代码覆盖率分析,针对不同的覆盖率需求如何部署和配置 Rational Test RealTime,以及在真实的测试环境中如何收集、整合程序覆盖率的测试结果,最终达到自动化分析和测试的目的。
目前针对代码覆盖率有很多测试的角度。Rational Test RealTime 主要提供下列 4 种:
- 功能测试覆盖:检测程序中的每个函数是否被调用。特别是在开始的测试中,可以用来检测软件的哪些地方被覆盖。这种测试往往可以迅速有效的找出测试漏洞。
- 调用代码覆盖:这个标准检测是否执行了每次函数调用。这是基于缺陷通常产生于模块间的调用而提出的一个方法。
- 块代码覆盖:检测是否每一行可执行的代码都被执行过。
- 条件代码覆盖:检测是否有足够的测试用例测试过每一条可能导致不同结果的条件语句。
基于 Rational Test RealTime 提供的上述代码分析方法,接下来本文将会介绍如何使用 Rational Test RealTime 来做代码覆盖分析。其中,所有的实例都是基于本文供下载的例子代码,在 Linux 环境中运行所得。
Rational Test RealTime 的下载,安装方法可以在它的帮助手册中找到。
在使用 Rational Test RealTime 开始编译项目之前,首先需要正确配置环境变量。下面给出了一些比较重要且常见的环境变量的配置方法:
-
TESTRTDIR: 强制赋值的变量。该变量指出 Rational Test RealTime 安装的路径。只有当此变量被分配之后,所有的 Rational Test RealTime 可执行文件和库才能被找到。比如,当 Rational Test RealTime 被安装在 /build/RTRT/releases/TestRealTime.7.5.0.0下,运行命令‘
echo $TESTRTDIR’应该打印出上面的路径信息。 - ATLTGT: 同样是一个强制赋值的变量。它表示当用户使用该工具的命令行时的目标部署端口(Target Deployment Port)的路径。
- ATTOLOBJ: 可选择的赋值变量。该变量指向 project.h 文件和目标部署端口文件 TP.o 或 TDP.obj 生成的路径。缺省条件下,这些文件生成在当前路径下。如果需要在项目中生成多次 project.h 或 TP.o 文件,可以使用该变量指定生成位置,只生成一次,以后用的时候,系统会自动在该变量指定的路径下去寻找这些文件。
Rational Test RealTime 的目标部署技术(Target Deployment Port,TDP)是一种灵活的,高效率的技术,它可以使测试基于目标或者实时分析独立地进行分析,与编译器、链接器、调试器及目标结构无关,从而实现跨多开发环境,多目标结构。作为 Rational Test RealTime 的一个关键组件,目标部署技术允许用户的测试用例和实时分析可以直接运用到目标系统上。
构建 TDP 以使它可以和用户的编译器,链接器,调试器和目标结构相协调,使得测试可以独立于 TDP。当环境改变的时候不需要另外去更新 TDP。测试脚本的配置,执行和报告仍然可以使用。
在每个 TDP 配置中包含四个部分。
- 基本设置:这个部分制定了缺省的文件扩展名,编译链接标志,环境变量和用户自定义变量,允许用户设置所有 Rational Test RealTime 常见的设置和变量以及 TDP 的部分。比如说,用户可以设置在编译,预处理和链接过程中使用的编译器的名字和位置。如果稍后要改变编译器,只需要更新这个部分就可以了。
-
构建(Build)设置:这个部分主要配置 Rational Test RealTime 图形接口集成 build 过程所需要的一些功能。它定义了编译,链接,执行 perl 脚本以及用户所需的自定义的脚本。该部分是 TDP 的核心部分,所有的需要编译和执行的部分都是靠它来驱动的。所有和 build 设置相关的文件都被存储在 TDP
cmd这个下级目录下。 -
库设置:这个部分描述了在目标平台下使用 TDP 的一系列源码文件和用户定制文件。它定义了特定平台下的 TDP 的最常用的部分定制设置。这些文件被存储在 TDP
.lib目录下。 -
解析设置:这个部分可以为了强调非标准的编译后缀名而改变解析器的行为。例如,
non-ANSI后缀。该部分使得用户可以使用 Rational Test RealTime 正确适当的解析自己的源码,插入标志或者生成代码。对应的配置文件放在 TDPana这个下级目录下。
安装完 Rational Test RealTime 在 $TESTRTDIR/targets 这个目录下发现已经有一些常用的 TDP 被安装了。在笔者的机器上,安装了下列 TDP(清单 1).
清单 1. 已安装 TDP
#ls adagnat adaunxapex clinuxgnu cunxdiabsgstep gcc_act jdk1.4.0 stAgent xml |
用户可以通过设置 ATLTGT 这个环境变量来决定使用哪一个 TDP。例如,在本例中使用了 clinuxgnu。
目录 ana 下的文件 atl.opp 包括了解析设置的一部分,也是比较常用的一部分。清单 2 是它的一个实例。
清单 2. 文件 atl.opp 示例
--dollar--no_restrict --special_subscript_cost --no_alternative_tokens --variadic_macros --extended_variadic_macros --void_star_null_pointer --guiding_decls --old_specialization --gnu_mode --microsoft_union_with_array --treat_template_classes_as_static --simulate_virtual_methods --simulate_called_routines --diag_suppress 14,34,46,47,161,174,177,305,375,427,549,550,737,795,830,837,940,997 --diag_suppress 1,30,114,289,330,349,397 --diag_suppress 100,137,280,350,381,416,541,815 --diag_suppress 175,186,307,321,347,485,603,756 --diag_suppress 191,265,603,691,802,1053,1205 --diag_suppress 68,847,1097 --diag_suppress 83, 94, 111, 180, 513 --old_style_template_instantiation --vla --sys_include=/usr/X11R6 --gnu_version=40300 --gen_new_specialization --sys_include=/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0 --sys_include=/usr/include/c++/4.3.0 --sys_include=/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/i386- redhat-linux --sys_include=/usr/include/c++/4.3.0/i386-redhat-linux --sys_include=/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/backward --sys_include=/usr/include/c++/4.3.0/backward --sys_include=/usr/local/include --sys_include=/usr/lib/gcc/i386-redhat-linux/4.3.0/include --sys_include=/usr/include --sys_include=[project path]/CodeCoverage/include |
在该文件中,有几个参数常常会用到,需要引起注意。其中之一是 Diag_suppress。当用户在使用 RT 来编译工程时如果想忽略某些警告甚至错误时,可以通过设置这个参数来实现。例如,如果想忽略错误或者警告 83,94,111,180,153,可以把这样一行 --diag_suppress 83, 94, 111, 180, 513 加入该文件。
另一个需要注意的参数是 sys_include。通常情况下,RT 会对所有的原文件来做代码覆盖分析,包括 .c, .cpp 和 .h 文件。如果用户不想对位于某些特定位置的文件作覆盖分析,可以把对应的目录路径赋值给该变量。例如,该实例中不想对 include 目录下的文件做代码分析,可以加入这样一行 --sys_include=[the example’s folder]/include。
集成 Rational Test RealTime 生成 Build
安装完 Rational Test RealTime,配置好所需的环境参数后,需要检查 Rational Test RealTime 所需要的环境配置是否都已经在当前系统中运行成功。方法如下:
-
运行命令行
$TESTRTDIR/license_check来确认所需的 Rational Server 已经运行,并已经具备有效的注册码。 -
运行命令
$TESTRTDIR/testrtinit.sh设置 Rational Test RealTime 的环境变量,包括 PATH 等等。
Rational Test RealTime 还提供了命令 attolcc 来编译以 C/C++ 为源代码的程序。它的使用方法如下所示。
#attolcc [{<-options>}] [{<-settings>}] -- <compilation_command> |
以上参数分别代表:
-
<options>指一系列可选择的参数项。 -
<settings>是一系列可选择项的对应值。 -
<compilation_command>是指在没有使用 Rational Test RealTime 情况下,用户所用的编译项目的标准命令行。
所有上面描述的参数细节都可以在 Rational Test RealTime 帮助文档中查到。
完成环境配置后,就可以开始利用 Rational Test RealTime 来生成项目的新 Build。生成方法只需在命令行中加入 Rational Test RealTime 的相应指令。接下来将从全代码覆盖率和指定模块代码覆盖率两种不同覆盖方法,举例说明如何利用 Rational Test RealTime 生成新 build,以及如何进行覆盖率分析。
全代码覆盖率分析,就是对整个项目的所有文件进行覆盖率分析。这种方法适用于用户关心整体项目的测试质量,而非某个模块。使用 Rational Test RealTime 进行全代码覆盖率分析十分方便,只需在整个项目顶层的编译参数前加上 Rational Test RealTime 的参数。以上面的例子为例,进行全代码覆盖率分析。我们在项目文件夹 CodeCoverage 下面运行命令 make,并且在其前面加上 Rational Test RealTime 的参数 attolcc,还可以在编译指令(如 gcc)前面加上适当的选择参数。
比如我们有一个包括 3 个模块的项目,每个模块都有他们自己的 .c 文件和 makefiles(如清单 3 所示)。在最顶层的文件夹中,另外有一个总的 main.c 文件,它会链接所有的模块中的目标文件,然后生成一个可执行文件。
清单 3. 用例文件夹结构
#ls component1 component2 component3 include main.c makefile |
进行全代码覆盖率分析我们只需要在顶层文件夹编译时,将编译命令改为:
# make CC="attolcc – gcc” |
在这里,我们为顶层编译的“gcc”加了一个前缀“attolcc -- ”。在打印出的日志信息里面,我们可以看到所有的模块在编译时都会自动加上测试代码覆盖率的前缀“attolcc -- ”,如列表 4 所示
清单 4. 日志示例
attolcc -proc -- gcc -g -I"<project-dir>/include" -o main.o -c main.c #>#Perl Script Command Line (9 args): # attolcc [-proc] [--] [gcc] [-g] [-I<project-dir>/include] [-o] [main.o] [-c] [main.c] # ATLTGT = /RTRT/Execute/releases/TestRealTime.7.5.0.0/targets/clinuxgnu # TESTRTDIR = /RTRT/Execute/releases/TestRealTime.7.5.0.0 TestRT-I-STARTEXEC, Rational(R) Test RealTime instrumentation driver 7.5.0.0 TestRT-I-COPYRIGHT, Copyright(C) 1996-2008 Rational Software Corporation. >preprocessing of main.c to main.i >instrumentation of main.i to main_aug.c >compilation of main_aug.c to main.o … make[1]: Entering directory `<project-dir>/component1' attolcc -proc -- gcc -I"<project-dir>/include" -c file1.c … >preprocessing of file1.c to file1.i >instrumentation of file1.i to file1_aug.c >compilation of file1_aug.c to file1.o … attolcc -proc -- gcc -I"<project-dir>/include" -c file2.c … attolcc -proc -- gcc -I"<project-dir>/include" -c file3.c … attolcc -proc -- gcc -g -I"<project-dir>/include" -o calculator main.o <project-dir>/component1/file1.o <project-dir>/component2/file2.o <project-dir>/component3/file3.o TestRT-I-STARTEXEC, Rational(R) Test RealTime instrumentation driver 7.5.0.0 TestRT-I-COPYRIGHT, Copyright(C) 1996-2008 Rational Software Corporation. >TDP generation... >Link with ./TP.o |
在编译成功后,在对应的文件夹下面会产生一些新的文件,比如 products.h, TP.o 等等,如列表 5 所示。
清单 5. 生成文件
#ls attolccReport.xtp component1 component2 component3 main.c main.o products.h calculator include main.c.fdc makefile TP.o |
其中,.fdc 文件就是代码覆盖率的报告文件。在每个模块中对每个 .c 文件都会生成一个这样对应的文件。
在有些情况下,我们加入“attolcc -- ”进行编译,会增加出错的概率,这可能是因为 Rational Test RealTime 会进行二次编译的原因造成的。这时我们可以加入参量 –verbose 来增加打印错误信息,来判断是何种原因造成的出错以及如何解决。
根据经验,如果遇到“unlink ./products.h failed”的错误,往往是因为系统在链接过程中会因在众多模块中产生多个 TP.o 文件而产生链接错误,可以通过以下方式解决:
- 通过以下命令在某个目录下手动的生成 TP.o 文件
#gcc TP.c |
-
定义参数
ATTOLOBJ。将其值设定为步骤 1 中 TP.o 文件的路径。
#export ATTOLOBJ=[the path in step 1] |
-
在编译命令中加入参数
- noTDP。这会使得系统在链接过程中使用步骤 1 中生成的 TP.o,而不是由系统自己生成的 TP.o。
#make CC=” attolcc –noTDP -- gcc” |
在很多测试情况下,不需要对整个项目进行代码覆盖率分析,而只是对项目中某些特定的模块感兴趣,只需要对这些部分模块进行代码修改或者测试。Rational Test RealTime 不仅支持整个项目的全代码覆盖率分析,而且还支持只分析某些特定的项目模块,为程序员提供了更大的灵活性。
同样以上例为例,在示例中我们有三个模块,分别是:component1, component2 和 component3。假如仅仅需要分析 component1 的代码覆盖率,也就是指定模块的覆盖率分析。可以按以下步骤进行:
- 仅在编译 component1 时使用 Rational Test RealTime。
# make CC="attolcc --atl_runtime_format=NONE -- gcc" |
- 而在其他模块的编译中不使用 Rational Test RealTime。
# make |
- 在顶层的项目文件夹下,先编译出目标文件。
# gcc –c main.c –o main.o |
- 利用 Rational Test RealTime 链接所有的目标文件,最后生成可执行文件。
# attolcc -- gcc -o main.exe main.o component1/file1.a component2/file2.o component3/file3.o |
这个可以通过修改顶层文件夹下的 makefile 来实现(清单 6)。
清单 6. Makefile 示例
CC = gcc CFLAGS = -g $(OPTION64) -I$(PDIR) RT = attolcc -- $(CC) calculator l: main.o $(DIRCOMP1)/file1.o $( DIRCOMP2)/file2.o $( DIRCOMP3)/file3.o $(RT) $(CFLAGS) -o $@ $? main.o: ./main.c $(CC) $(CFLAGS) -o $@ -c $? $(DIRCOMP1)/file1.o: cd $(DIRCOMP1);\ make CC="attolcc --atl_runtime_format=NONE -- gcc";\ cd ..; $(DIRCOMP2)/file2.o: cd $(DIRCOMP2); make; cd ..; $(DIRCOMP3)/file3.o: cd $(DIRCOMP3); make; cd ..; |
通过这种方法,代码覆盖率的报告文件(.fdc 文件)就只会在 component1 下面生成,而不是整个项目下都有。
在使用 Rational Test RealTime 进行指定模块覆盖率分析的时候,有两点我们特别要注意:
- 如果编译过程中遇到以下错误:/build/RTRT/releases/TestRealTime.7.5.0.0/targets/clinuxgnu/lib/priv.c:119: multiple definition of `priv_exit',这可能是由于 Rational Test RealTime 在链接库文件时重复链接造成的,可以尝试加入参数 --atl_runtime_format=NONE 来避免重复链接。当链接时生成的文件是目标文件(obj)或者库文件时必须使用该参数设置。
-
在第 1 步和第 4 步使用的参数除了
--atl_runtime_format=NONE以外必须相同。
在利用 Rational Test RealTime 对项目编译链接完成以后,下一步就需要对代码进行覆盖率数据收集。我们通过运行生产的目标程序来获取源代码插入分析信息文件(Source Code Insertion -SCI),这个文件的默认名字是 atlout.spt(如清单 7 所示)。
清单 7. 生成 spt 文件
#ls atlout.spt calculator component1 component2 component3 main.c.fdc makefile TP.c attolccReport.xtp main.c main.o products.h |
在这之后,利用 dump file splitter 工具—atlsplit 来分离源代码插入分析信息文件。这里假设 .spt 文件使用的就是默认名字,我们可以运行清单 8 中的命令。在其结果中,会新产生一个以 tio 为后缀的文件,默认名为 attolcov.tio,这个就是代码覆盖分析所需的动态追踪文件 (Dynamic Trace File)。
清单 8. Split 结果
#atlsplit atlout.spt Splitting ‘atlout.spt’ traces file… Traces file successfully split. |
现在,所有检查代码覆盖率所需的文件都准备好了。
收集完代码覆盖率数据以后,可以通过 Rational Test RealTime 中的图形界面(GUI)来查看覆盖率分析结果。Rational Test RealTime 的图形查看工具支持 Windows、Linux、AIX 等多种操作系统,能够对代码覆盖率进行柱状图分析,源文件覆盖分析和统计结果分析。这里仍以 Linux 环境下进行结果查看为例。
在 Rational Test RealTime 的安装目录下运行以下代码:
#studio <file>.c.fdc attolcov.tio
图形界面将会自动打开。其中 <file>.c.fdc 是我们所想要查看的代码覆盖率报告文件。我可以指定一个或者同时指定多个 fdc 文件。比如,我们需要查看 component1 下的 file1.c 以及 component2 下的 file2.c 的代码覆盖率情况,可以使用一下命令:
#studio component1/file1.c.fdc component2/file2.c.fdc attolcov.tio
可以得到分析结果,见图 1,图 2,图 3。
图 1. 柱状图
在图表中(图 1),显示了三种不同覆盖率计算方式的覆盖百分比柱状图,他们分别是 Functions(如蓝色所示,约 100%),Statement blocks(红色所示,约 40%)和 Decisions(黄色所示,约 40%)。这是从整体上进行覆盖率分析的结果。
通过选择左边区域中的分层列表 int compare(int),我们看到针对每个函数的更加细节的信息,如图 2 所示。绿色所显示的是已经被测试代码测试过的代码,红色的是还没有被测试过的代码,这部分就是我们在 review test case 时,所需要考虑是否需要设计新的 test case 的地方。
图 2. 源文件覆盖率
在图 3 中,是从每一个 .c 文件的角度查看代码覆盖率。我们可以看到,他清楚的显示出每一个 .c 文件中,从三种不同的覆盖角度:Functions,statement blocks 和 Decisions 的覆盖情况。
图 3. 测试统计结果
在自动化测试中,我们通常需要运行多个 test case,然后统一进行代码覆盖率分析。这就需要把多个 test case 所得到的代码覆盖率融合起来。Rational Test RealTime 在每次运行完 test case 之后,会对每个测试代码进行源代码插入分析(SCI),所得结果都记录在 spt 文件中,只需要在每次执行完测试程序之后,及时将 spt 文件 split 出来,多个测试结果的代码就可以实现整合。为了实现这种要求,我们可以另外编写一个整合脚本,比如叫 test_merge,如清单 9 所示:
清单 9. Merge 文件 test_merge
#!/bin/bash ./[executable] $@ #-----after executing the script, split spt file-----# atlsplit <src-dir>/atlout.spt #----------END---------# |
然后我们可以这样一个一个的运行测试代码:
#./test_merge [parameter_list1] #./test_merge [parameter_list2] |
当所有的测试程序都运行完以后,再查看总结果。如图 4 所示,由于运行了两条测试代码,代码覆盖率(Statements 和 Decisions)从以前的 40% 上升到了 80%,测试程序的覆盖率结果已经融合到了一起。
图 4. 合并后的结果
学习
- 访问 IBM developerWorks 中国网站 Rational 专区,获得关于 IBM Rational 软件交付平台(Rational Software Delivery Platform)产品的技术资源和最佳实践。
- 阅读 Rational Edge 中文版,获取软件开发领域的最佳实践。
- 订阅 IBM developerWorks 时事通讯,一份关于 developerWorks 指南、文章、下载、社区活动、网络广播和技术讲座的电子周刊。
- 学习 Hello World 系列教程,这是学习 IBM 软件工具的快速通道。在每一篇教程中,都会有快速入门产品演示动画。您可以通过其中的动画演示快速浏览如何使用 IBM 软件完成开发任务。
获得产品和技术
- 访问 IBM Rational 软件交付平台 V7 专题,了解 Rational V7 产品的方方面面。
- 获取免费的 Rational 软件工具包系列,了解最新的 IBM Rational 软件开发工具技术文档和资源。
- 下载免费的 IBM Rational 试用版软件,了解 IBM Rational 软件的最新特性。
- 获取更多 IBM 试用版软件,并熟练掌握来自 DB2®、Lotus®、Tivoli®,以及 WebSphere® 的开发工具和中间件产品,用这些试用版软件开发您的下一个项目。这些试用版软件可以免费直接从 developerWorks 下载。
讨论
- 查看 developerWorks 博客 并加入 developerWorks 社区。

