引言
随着嵌入式系统的广泛应用,人们对嵌入式系统软件的质量提出了更高的要求,而作为保证软件质量最有效手段的测试技术,也越来越受到关注。目前嵌入式软件的开发语言以C等高级语言为主,因此,对这些高级语言的测试显得尤为重要[1]。
目前实用的嵌入式系统测试平台主要由国外厂商开发,比较具有代表性的测试平台有Teleloglc公司的Logiscope、Metrowerks公司的CodeTest,Windriver公司的CoverageScope和IPL公司的Cantata等。国外厂商的嵌入式系统测试平台功能较强,技术较先进,但一套测试系统价格往往高达数万到十多万美元,极大增加了嵌入式系统的开发成本,无法得到普遍的应用,影响了国内嵌入式系统的开发质量[2]。
国内的嵌入式系统测试平台有基于目标开发的测试系统。电子科技大学研究了基于目标的嵌入式软件测试系统,实现了对汇编语言进行自动插装,并实现基本的覆盖测试功能。但由于嵌入式软件需要基于硬件电路进行调试,当软件程序写入硬件电路后,如果电路工作不正常甚至无法工作,则难以断定问题所在,这就限制了嵌入式软件测试必须按照先硬件后软件的模式进行[3]。
本系统实现了基于仿真的嵌入式C语言软件覆盖测试分析。通过有效的词法语法分析、自动插装、测试用例的生成和加入、动态仿真运行及覆盖报告分析等模块对源代码进行覆盖分析,缩短了嵌入式系统开发周期,有效地解决了传统手工测试程序运行效率低、繁琐等问题,保证了嵌入式软件系统高效稳定的运行。
1嵌入式软件测试系统框图
嵌入式软件测试系统的结构框图如图1所示。它主要由5部分组成:程序的预处理过程(包括词法语法分析)、程序的自动插装过程、测试用例的加入、动态仿真运行及覆盖报告分析[4]。
图1嵌入式测试系统框图
被测试程序首先经过预处理。由于源程序中存在宏定义、文件包含和条件编译等预处理命令,因此在进行词法分析前必须进行预处理,将宏展开,以利于查找变量。词法分析是将预处理阶段产生的中间代码分解成单独的词的表示,语法分析是将输入字符串识别为单词符号流,主要用来确定插装的位置,在一些关键的字段和函数处插入,然后编译后产生含有插装函数的目标文件。
在动态仿真运行过程中,将测试用例注入到分析后的目标文件中,动态运行后,将产生的测试结果存放到记录文件或缓冲区中,此时可以动态生成覆盖率报告和GUI显示,并将测试的结果与测试用例库生成的测试用例期望值作比较,得出测试结果,并将测试结果保存与打印。
2功能模块设计
2.1词法语法分析模块
(1) 预处理
在源程序中,空白符、跳格符、回车符和换行符等编辑性字符除了出现在文字常数中之外,在别处任何地方出现都没意义,而注释部分出现在程序中的任何地方。对于这些不是程序必要组成的部分,预处理时可以将其剔掉[5]。
(2) 词法分析
将预处理阶段产生的中间代码分解成单独词的表示,并将词的基本信息表,如词的行号、词的列号及词的类型等信息,为语法分析做准备。词的类型有:普通变量标识符、数字表、关键字表运算符表[6]。
(3) 语法分析
语法分析的主要任务是在词法分析识别出的单词符号串的基础上,分析并判断程序的语法结构是否符合语法规则。由于本系统分析的程序是经过嵌入式软件编译器编译的,因此认为不存在语法上的错误,只是通过语法分析得到程序结构及函数信息,为插装做准备。
2.2插装模块
程序插装概念由J. G. Huang教授首次提出,简单地说就是借助向被测程序中插入操作来实现测试目的。在调试程序时,常常要在程序中插入一些打印语句,希望执行程序时打印出所关心的信息。通过这些信息进一步了解执行过程中程序的一些动态特性。比如,程序的实际执行路径,或是特定变量在特定时刻的取值。从这一思想发展出的程序插装技术能够按用户的要求,获取程序的各种信息,成为测试工作的有效手段[7]。嵌入式软件覆盖率测试的核心问题是代码插装,要求插装技术先进且代码量少。
设计程序插装时需要考虑的问题包括:探测哪些信息;在程序的什么部位设置探测点;需要设置多少个探测点[7]。
为了减少测试的复杂性,本系统设计过程中只采用了1次插装的方式。只在能产生分支的语句之后、程序开始及结束部分插入桩函数,该函数能在执行过程中将执行标记写入特定的文件。该模块算法流程如图2所示。
图2插装流程图
2.3动态仿真运行模块
在嵌入式软件测试中,由于嵌入式软件具有实时性、嵌入式等特性,因此嵌入式软件编译后不能直接在宿主机执行,需要嵌入式仿真软件来模拟目标机环境执行。但毕竟是在模拟环境中进行的,因而与定时问题有关的白盒测试、中断测试、硬件接口测试只能在目标环境中进行。
本系统选择Proteus仿真软件作为该系统的模拟仿真环境,Proteus软件可以仿真51系列、AVR,PIC等常用的MCU及其外围电路。在动态运行过程中,通过嵌入式软件相对应的编译环境编译成含有插装函数的目标文件。将生成的插装文件在Proteus仿真环境中动态运行,将产生的测试结果存放到记录文件或缓冲区中[8]。
2.4覆盖率分析
通过动态仿真运行模块,覆盖信息被写入文件中,读取存放记录文件,分别对语句块数和执行分支数进行计算。根据覆盖率公式,结合计算的结果,得到测试语句覆盖、分支覆盖与程序的有效代码率并GUI显示。将测试的结果与测试用例库生成的测试用例期望值作比较,得出测试结论[9]。
2.4.1语句覆盖率算法分析
语句覆盖是软件测试中最基本的覆盖标准,因此在测试时应将每个语句至少执行1次。在TestImp系统中得到语句覆盖率的算法流程如图3所示,通过词法分析后,读单独的词语,如遇到能产生分支的关键词(for、if、else、switch、goto、return、break、continue、do、“{”和“}”)则继续往下判断,否则返回继续读词语。如果是能产生分支的关键词,则将关键词对应的当前标号(flagif、flagelse、flagwhile、flagfor、flagswitch)推进栈中的account单元保存,并将当前标号加1。当读取到关键词之后第1个“{”时,标志fflag加1,且判断是否大于1:如果大于1,则将此时的行号推入到栈当前位置中的nline单元中保存;否则直接返回读词。如果是“}”标号,则判断链表的节点是否大于0,如果是且为1,则用“}”当前行数减去保存栈中“{”的行数,然后减去最近嵌套内层的总行数即得到该层对应行数;如果链表节点大于1,则判断链表节点数与对应层数是否相等,如果相等则计算出该层的总行数,即用此时“}”行数减去栈中保存“{”的总行数,然后保存该层总行数,并用总行数减去最近嵌套内层行数之和,即得到该层行数对应记录。如果链表节点数与对应层数不相等,则将对应层数加1循环作判断。当所有的词语全部读取完毕之后,读取动态执行时写入的插装文件,读取关键词标号,取对应关键词链表中的行数与总行数变量相加,直至插装文件读取完毕,此时可以输出被覆盖语句数。
图3计算覆盖语句流程
2.4.2分支覆盖率算法分析
分支覆盖率的计算一般也需要达到一定的指标。在TestImp系统中得到分支覆盖率的算法流程如图4所示,初始化定义1个链表队列。首先读取插装记录文件,读取时将分支标号与链表队列中的关键词标号分别作比较,如果此时链表队列中已经存在了,则读取下一条记录,否则覆盖分支总数加1,并将分支标号添加到队列中,直至插装记录文件全部读取完毕,输出被覆盖的分支数。分支总数在插装时已经记录在全局变量中。
图4计算覆盖分支流程
3嵌入式软件测试系统实现
根据设计框架,在VC++2005上实现了基于宿主的嵌入式软件测试系统。整个系统包括对源程序的词法语法分析,自动插装,并保存插装后文件用以编译,能配置各种嵌入式编译环境。
该系统实现如下几个模块:
① 分析模块。用打开文件菜单选择要分析的源文件,并进行词法分析。在词法分析过程中,对关键词、符号、数字、变量等用不同的标记,并去除源文件中的注释,给出每个词的行数和列数。语法分析并没有生成可见的语法树,只是形成提供插装的结构信息。
② 插装模块。选择自动插装菜单,系统会读取词法语法分析的结果,根据图2算法流程实现对源程序的自动插装,插装点有for、if、else、case、goto、return、break、continue、do、while语句后及程序开始与结束处插入记录函数指针,并在程序最后插入插装函数体。
③ 编译器配置模块。根据不同嵌入式软件选择不同的编译环境,该部分需在使用该系统之前对各种嵌入式编译软件进行配置,在本系统中有Visual C++、ARM编译器ADS、Keil环境、伟福环境、Borland C环境、DSP编译器CCS。
④ 覆盖率报告。本系统已实现的覆盖率报告有语句覆盖与分支覆盖,并将覆盖率通过三维图形形式体现。后续将实现条件覆盖、多条件判定覆盖、多条件覆盖、MC/DC、路径覆盖等覆盖率报告。
⑤ 报告生成及帮助文档。将测试覆盖结果与测试前生成设定的覆盖率指标进行比较得到测试结果。如果覆盖率没有达到要求,则继续增加测试用例,以提高测试覆盖率。另外有帮助文档,提供使用说明、注意事项及应用范围等。
4实验结果
在实验中,采用1个AT89C51单片机键盘识别系统作为实验对象。在该系统中,通过AT89C51的P1口连接1个4×4的矩阵键盘,并通过P0.4~P0.6所连接的74LS138扩展连接到型号位7SEGMPX6CA的6位LED选通端上,可以显示所按下键盘的编号,将RXD和TXD连接到串口。
实验过程中,将测试源程序进行分析并自动插装,然后通过单片机的Keil编译器对插装后的源程序进行编译,得到.hex文件,添加到Proteus6.9仿真软件原理图中的AT89C51单片机中,点击运行按钮后可以执行程序。此时可以在Proteus6.9界面中输入生成的测试用例[10],本实例中的测试用例是键盘按键的输入,只需点击Proteus6.9原理图中的键盘。此时插装后的程序会将插装的信息通过串口传送到TestImp测试系统的联机串口传送界面中,并将其保存在指定文件中,为覆盖测试做准备。
为该系统设计的基本测试用例是按下单个1号键。在此测试用例下,手工运行推算出语句覆盖率和分支覆盖率分别为55.6%和42.8%,在测试系统运行后,得出的语句覆盖率和分支覆盖率分别为55.6%和42.8%。测试结果如图5所示。
图5测试系统实验结果图
实验结果表明:在特定的测试用例下,对嵌入式系统应用本文设计的基于仿真嵌入式软件白盒覆盖测试系统与理论上推导出来的语句和分支覆盖率指标完全相同。