中国电子科技集团公司第三十二研究所 苏青琴 李凤雯 周兰英
针对目前箭载嵌入式软件集成测试工具测试用例编写时间长、人力成本高、测试用例报告适用性低等方面的问题,提出一种基于SmartRocket 的箭载嵌入式软件集成测试方法。SmartRocket 采用广度优先自顶向下集成测试方法确定集成测试组合,基于动态符号执行技术自动生成集成测试用例,在满足函数覆盖和调用覆盖指标下生成与实际要求相符的测试用例报告。通过实验发现,该集成测试方法能够很好地实现集成测试用例自动化,提高了测试用例报告适用性,一定程度地增加了箭载嵌入式软件集成测试人员的测试效率。
随着嵌入式软件在各个领域的广泛应用,对嵌入式软件的各项要求也越来越高,特别是作为航天产业的箭载嵌入式软件的测试工作变得尤为重要。集成测试作为一种重要的箭载嵌入式软件测试方法,能在早期发现与软件设计相关的模块调用关系以及模块间接口方面的问题。目前主要使用的箭载嵌入式软件集成测试工具为Testbed[1]和VeterCast。Testbded 无法自动生成集成测试用例,增加了测试时间和人力成本;VeterCast 虽然可以自动生成测试用例,但生成的测试用例报告需要编写脚本转化成实际使用的测试用例报告,导致适用性不高。为了改进Testbed 和VeterCast 这两种集成测试工具的不足,结合具体箭载嵌入式软件实践,形成了基于SmartRocket 的箭载嵌入式软件集成测试方法。
SmartRocket 是一种基于动态符号执行技术的集成测试工具。SmartRocket 工具能自动生成集成测试用例,并采用函数覆盖、调用覆盖作为集成测试指标,自动生成符合实际需要的测试用例报告。
动态符号执行技术[2]是实际执行和符号执行同时执行的技术。对于与符号无关的操作或变量等程序会实际执行,对于与符号相关的操作或变量等程序会符号执行。符号执行将程序中的每一个输入变量作为符号处理,将不同的路径约束条件用输入的符号来执行,符号执行会模拟程序执行并收集所有路径上的约束信息[3]。最终,约束求解器会根据每条路径上的符号组合信息求得每条路径的输入。
如图1 所示是一个程序代码转化成符号执行控制流程图的示例。程序的输入是一个整型变量i和一个整型变量j,若i+j>0,则将返回i;否则,返回-i。
图1 程序代码转化符号执行流程图Fig.1 Program code conversion symbol execution flowchart
如图2 所示是符号执行的路径图。首先随机输入i为1,j为2,依据该随机输入值可以到达一条路径终点4;接着对约束条件取反得到新的输入i为1,j为-2,依据该新的输入可以到达另一条路径终点6,从而覆盖了所有的路径。
图2 符号执行路径图Fig.2 Symbol execution path diagram
SmartRocket 能够自动生成集成测试用例。以C 语言为例说明SmartRocket 自动生成集成测试用例的过程。SmartRocket 将接收到的.c 和.h 源文件转化成可识别格式,使用C 语言编译器对源文件进行预处理和编译,得到抽象语法树。在抽象语法树的基础上构造出含有程序结构和信息的控制流程图。以控制流图的初始节点为起始点,采用搜索算法[4]寻找下一个节点,只有当识别出分支条件时才会执行该分支。在遍历整个控制流程图过程中,将所有约束条件收集起来添加到一个路径,当搜索到终点时求解该路径,并转换成能够识别的集成测试用例。
集成测试依据软件概要设计并遵循特定的策略和步骤,将已经通过单元测试的各个软件单元(或模块)逐步组合在一起进行测试,以期通过测试发现各软件单元接口之间存在的问题。
集成测试方法包括渐增式和非渐增式两大类,其中渐增式测试又可分为自顶向下、自底向上和三明治集成三种方法[5],而自顶向下的渐增式测试方法又可以分为深度优先自顶向下测试法和广度优先自顶向下测试法。
SmartRocket 工具采用广度优先自顶向下集成测试法。
广度优先自顶向下的集成测试分为以下三个步骤:
(1)用主控制模块(如图3 中D1 所示)做测试驱动模块,用桩模块代替所有间接被主控模块调用的模块。
图3 广度优先自顶向下集成测试Fig.3 Breadth first top down integration testing
(2)根据广度优先法(如图3 中从D1 出发,先集成M1、M2 及M3,接着是S1、S2 这一层,以此类推)和新模块的选择原则,每次用一个实际单元替代一个被调用的桩模块,并开发该单元可能需要的桩模块。
(3)每组装一个新模块,测试一个,直到新模块为系统函数。
SmartRocket 工具支持集成测试的函数覆盖和调用覆盖两大覆盖准则。
2.2.1 函数覆盖
函数覆盖是指程序中所有被调不同函数(除系统函数)均被调用一次。函数覆盖是条件最弱的一种集成覆盖准则。其原因是它只考虑了被调不同函数只调用一次,忽略了同一个被调函数[6]可能在不同的分支被调用多次。举例说明函数覆盖的局限性,如图4 所示。
图4 函数覆盖局限性实例Fig.4 Function coverage limitation instance
按照函数覆盖的标准,该集成的覆盖率已经达到了100%,然而却没有发现明显的错误,如当x为-1 时,程序会出现除零异常。因此,函数覆盖没有考虑到相同被调函数的逻辑性,需要结合其他的集成测试方法来使用。
2.2.2 调用覆盖
调用覆盖是指程序中所有被调函数均被调用。调用覆盖需要结合分支覆盖指标,确保程序真假分支上的被调函数都遍历过至少一次。调用覆盖更能准确地反映集成测试的集成关系。
以编译环境为Rede 的箭载嵌入式软件中的BC 模式初始化功能为例,使用SmartRocket 进行集成测试。集成关系如图5 所示,其中printf 函数为系统函数。
图5 集成关系Fig.5 Integration relationship
首先新建编译环境Rede 和GCC;其次创建项目信息(项目名称、版本号、项目类型、编译器),并上传被测项目文件;最后选择集成测试的覆盖准则(调用覆盖、函数覆盖),并填写配置信息(运行环境、编译环境、包含目录等)。
解析被测项目文件,在被测项目文件解析无误的情况下,依据广度优先的自顶向下的集成方法确定图4 中的集成组合并选择集成测试层级,运行自动生成的测试用例,并在调用覆盖率和函数覆盖率指标满足要求的情况下导出测试用例报告。SmartRocket 集成测试界面如图6 所示。
图6 SmartRocket 集成测试界面Fig.6 SmartRocket integration test interface
部分集成测试用例报告如表1 所示。
表1 部分测试用例报告Pig.1 Partial test case report
SmartRocket 工具选择广度优先的自顶向下的集成方法,利用符号执行技术自动生成集成测试用例,并以函数覆盖和调用覆盖作为集成覆盖标准,自动生成所需测试用例报告,实现了对箭载嵌入式软件自动化集成测试,并解决了集成测试用例自动生成和测试用例报告适用性问题。
目前,箭载嵌入式软件含有绝对地址变量时,Smart Rocket 自动生成的集成测试用例无法满足函数覆盖和调用覆盖100%的指标,需人工设计集成测试用例,后续自动生成集成测试用例时需针对绝对地址变量自动识别并赋值进一步研究。