我们介绍过DO-178,这是由美国航空无线电技术委员会(RTCA)提出、为国际民航领域所广泛采纳的民航客机机载软件适航认证标准。DO-178中明确要求,对于质量标准最高的A级机载软件,源码层面的测试必须满足“修改的条件/决策覆盖准则”。
要满足“修改的条件/决策覆盖准则”,测试集需要同时满足三个条件:
源码里任意决策的“真”或“假”两种结果都要被测试集覆盖至少一次;
源码里任意决策中的每个条件的“真”或“假”两种取值都要被测试集覆盖至少一次;
每个条件必须要显示出对决策结果的独立影响。
机载软件属于安全攸关系统,测试当然马虎不得。相比一般软件研发中常用的语句覆盖准则和决策(即分支)覆盖准则,“修改的条件/决策覆盖准则”已经是比较高的标准了。在此基础之上,DO-178对A级机载软件的测试充分性还提出了进一步的要求:“如果无法保证源码与目标码之间存在直接的对应关系,就需要对目标码的正确性进行额外的验证。”
秉持基于结构覆盖的测试充分准则的价值观,如果被测对象实现结构中的某个元素从未被测试覆盖过,我们就无法对测试充分性抱有足够的信心。谈及软件的实现结构,我们一般想到的是建立在源码层面的控制流结构和数据流结构,结构中的元素可以设定为代码行、分支、路径、变量的定义和使用等等。然而,这其实只是软件的一个临时结构。
我们知道,由C语言等高级语言编写的程序,源码经过编译器处理后生成汇编代码,再经过汇编器处理后生成目标码(即二进制机器码),最后通过链接产生可执行文件,这才化身为能够在计算机上运行的最终实现。源码到目标码的转换并非“透传”,而是经过了编译器和汇编器的加工处理,这个过程中就会引入额外的结构主张。因此,对源码的结构覆盖,并不能蕴含对目标码的结构覆盖。
来看一个简单的例子。被测程序的源码如下:
if ((a<0 || b<0) && c<0){
e=1;
}
e = e+1;
其控制流结构如下图所示:
显然,测试集T={(a = -1, b = 1, c = -1), (a = -1, b = -1, c = 1)}可以满足对源码的决策覆盖准则,即覆盖选择型结构的全部两个分支。当然,T也可以满足对源码的语句覆盖准则。
这段源码经过编译器和汇编器的处理后,生成的目标码如下:
目标码的控制流结构如下:
测试集T对目标码的覆盖情况如下表所示:
可见,T无法实现对目标码的语句覆盖,自然也无法实现对目标码的决策覆盖。
编译器、汇编器都是软件产品,难免也会出错。譬如,编译器为了提高程序运行时的性能,可能会将变量替换为常量,移除一些它认为是冗余的判断,或者调整代码执行顺序。但是在某些场景下,这些优化策略会显得相当武断,甚至错误地篡改了开发者的意图,将缺陷埋入了目标码的实现结构中。要想检出这样的缺陷,源码层面的结构覆盖准则就显得力不从心了。这就是DO-178要求对目标码进行额外验证的原因。