软件工具的自动化测试总是需要某种方式来比较该工具的功能与我们期望的功能。例如,测试编译器通常需要验证已编译程序的行为、检查编译错误消息或分析生成的机器代码。对于静态或动态分析工具,这通常涉及检查工具输出是否有明确定义的输入集。
下面介绍了一个为结构覆盖分析工具的内部测试而开发的框架,其中预期的覆盖结果在源注释中表示为注释。该框架用于验证该工具是否可用于在航空电子领域具有严格认证限制的多个安全关键软件项目。
我们首先总结了需要支持的覆盖标准和工具输出格式,然后介绍了我们描述工具预期结果的方案的主要原则,并解释了与基线输出进行比较相比的优势。
覆盖标准和输出报告格式
我们需要测试的代码覆盖率分析工具支持机载软件DO-178C认证标准定义的三种覆盖率标准:Statement Coverage、Decision Coverage和Modified Condition/Decision Coverage,通常称为MC/DC。它产生两种输出报告格式:
带注释的来源,从要分析的来源生成,行前缀为行号和覆盖结果指示;
一份文本报告,列出违反需要评估的覆盖标准的情况。
图 1 中的摘录显示了一个带注释的源结果示例,该结果由该工具对仅调用一次的 Ada 函数执行决策覆盖率评估,其值 X 大于 Max 参数。
【图1 | 此处显示的是结构覆盖分析工具对 Ada 函数执行的决策覆盖评估的示例注释源结果。]
每行开头的信息是显示覆盖结果的工具输出。“-#”文本是原始源中的特殊注释(Ada 中的注释以“-”开头)被框架识别为引入标签,允许用户表示预期覆盖结果的行,稍后将对此进行描述。
在 DO-178C 用语中,布尔表达式(例如控制 if 语句的表达式)称为决策,实现决策覆盖需要测试评估每个决策 True 至少一次,False 至少一次,此外还要执行每个源语句。
在手头的示例中,控制 if 语句的决策仅被评估一次,对于 X 》 Max 的布尔值 False。因此,该决定仅被部分覆盖,并且从不执行第 4 行的 return 语句。这是由“!”传达的。和第 3 行和第 4 行的行号旁边的“-”字符,以及第 6 行的“+”注释,表明那里的 return 语句的正确覆盖。
此评估的另一种输出格式,一个列出与标准相关的覆盖违规的文本报告,将包括如图 2 中的消息,其中第一部分是文件名:行号:列号源位置一致带有带注释的源结果。
【图2 | 此处显示了结构覆盖分析工具对 Ada 函数执行的决策覆盖评估的文本报告中生成的消息。]
陈述基本的预期覆盖结果
在上一节中,说明了该工具的实际覆盖结果是什么样的。接下来将描述测试编写人员如何指定给定测试场景的预期结果。
该工具的主要目标是让测试人员使用正在测试的源代码有效地陈述期望,同时抽象出报告格式细节。它还鼓励积极思考测试的预期结果应该是什么。
该工具将测试定义为三类源文件的组合:
功能源,这是测试人员想要评估并检查结果是否符合覆盖工具要求的代码;
驱动程序源,以特定方式调用功能代码,具有精确的覆盖目标;
和helper sources,它们只是为了完整性而需要的,不需要覆盖分析。
然后,预期的覆盖结果在驱动程序源中以特殊格式的注释表示,指的是功能源中也由特殊格式的注释标记的行。
前面介绍的 In_Range 示例函数显示了引入标签的特殊注释的实例。例如,“expr_eval”标签允许表示对决策表达式求值的行。给定的标签可能出现在多行上。
描述驱动程序源中预期覆盖结果的特殊注释是注释序列,如图 3 所示,其中“xp”代表“预期”。第一行标记了名为functional-source-filename的功能源的预期结果的开始。/tag1/行声明了对该源代码中所有标记为tag1的源代码行的期望。xp-source-line-note传达这些行的注释源输出格式中预期的覆盖指示字符(所有行的注释相同),xp-violation-notes传达文本报告格式中预期的一组违规消息这些行(所有行的设置相同)。
【图3 | 此处显示了用于在示例 Ada 函数上执行的结构覆盖分析评估的驱动程序源中描述预期覆盖结果的特殊注释。]
一个驱动程序源可能包含几个/tag/行用于给定的功能源和对多个功能源的期望。
在/tag/行上,可以使用短标识符以紧凑的方式表示各种可能的覆盖指示。例如,“l+”、“l-”或“l!”可用于xp-source-line-note以表示预期的“+”、“-”或“!” 分别在带注释的源行上的覆盖率指示。对于xp-violation-notes,例如,在所有可能性中,“s-”表示预期的“从未执行过的语句”违规,或“dF-”表示“从未执行过的决策结果假”违规。
图 4 显示了驱动程序源的草图,以说明对提供 In_Range 函数(名为 in_range.adb)的源文件进行决策覆盖测试。此驱动程序实现了之前用于说明输出报告格式的执行场景,使用 X 》 Max 调用一次 In_Range 函数:
【图4 | 此驱动程序源草图说明了对提供 In_Range 函数 (in_range.adb) 的源文件的决策覆盖测试,其中该函数使用 X 》 Max 调用一次,如上一个评估示例中所示。]
/expr_eval/ 行说明了 in_range.adb 中标记为“expr_eval”的行集的预期覆盖结果。在示例中,这是对决策进行评估的单行(此特定驱动程序仅对 False 一次),因此注释行(l!)上的部分覆盖指示和“决策结果 True 未执行”违规诊断文本报告(dT-)应该是预期的。
/expr_true/ 和 /expr_false/ 行声明了标记为“expr_true”和“expr_false”的源行的预期覆盖结果,选择这些标签来表示当决策评估为 True 或 False 时执行的语句的行。用作“expr_false”的xp-violations-notes的“0”表示一个空集,这意味着文本报告中的这些行不会违反预期。这与注释源格式中“+”的预期一致(l+ 作为xp-source-line-note),对应于行上所有项目的完全覆盖(在示例中,单个 return 语句单行)由执行场景强制执行。
这些期望与初始示例中显示的实际结果完全一致;使用结构覆盖分析工具测试框架,该测试将“通过”。
高级期望
上一节展示了预期工具行为的基本公式示例,无条件并引用整行。然而,允许针对目标标准集的完整测试套件需要开发许多高级功能。
最迫切的需求是在xp-violation-notes中提供精确的源位置,以允许在可以合理地预期该行上的不同项目的不同诊断时引用该行的特定部分。
例如,在评估 MC/DC 时,工具诊断指的是布尔表达式中的特定操作数( DO-178C 术语中的决策中的条件),并且大多数编码标准允许布尔表达式在同一行上具有多个操作数。测试人员必须能够指定预期覆盖诊断的线路上的特定条件。其他条件也会出现类似的需求,例如,当多个语句共享同一源代码行时,使用语句覆盖率,或者当表达式中涉及嵌套决策时,使用决策覆盖率。
这通过在违规指示符末尾使用以下形式的扩展来支持:“行摘录”,如以下示例期望行,用于对示例 In_Range 函数进行 MC/DC 评估的假设测试。c!:“X 》= Min”表示我们期望不完整的条件覆盖诊断 (c!) 指定行的“X 》= Min”部分,这只是决策的第一个操作数(图 5)。
【图5 | 此处的示例期望线描述了对先前使用的 In_Range 函数示例的假设测试,其中 c!: “X 》= Min” 表示对该行的“X 》+ Min”部分的不完整条件覆盖诊断的期望。]
引入了一些其他工具来支持,例如,单个语句跨越多行的情况,对于不同版本的工具或编译工具链的期望不同,或者使用通用驱动程序来评估多个覆盖标准。确切的细节超出了本文的范围。
执行模型概述
工具测试框架下的通用执行模型包括关于覆盖结果指示集的推理,称为覆盖注释。通过结合两个独立的方面来处理四种覆盖笔记对象:笔记来源和笔记适用的输出格式的种类。
关于音符来源,区分以下几点:
预期注释,来自预期结果声明,以及
发出的注释,可在该工具生成的报告中找到。
关于输出格式的种类,定义如下:
行注释,用于注释源中的覆盖指示字符,以及
违规说明,用于在覆盖文本报告中发现的违规消息。
/tag/ line spec中的xp-source-line-note然后在内部建模为预期的 line note对象。xp-violation-notes被建模为预期的违规注释对象,并且从工具生成的覆盖率报告中提取发出的线或发出的违规说明对象。
本质上,测试套件引擎为每个测试执行以下步骤:
解析测试源以构建预期的行和违规注释集,每个功能源一组。引擎将驱动程序源中的/tag/规范与功能源中的标记行进行匹配,并使用特定种类和源位置信息实例化单个注释对象。
从源代码构建可执行文件,执行它,然后针对所需标准运行覆盖率分析工具,生成覆盖率报告。
解析报告以构建发出的行和违规注释集。
将预期的行/违规注释与发出的注释匹配并报告差异。当工具已根据评估的标准报告了所有预期的覆盖率指示并且对工具报告的所有覆盖率偏差有预期时,测试通过。
方案的主要特点
该方案的一个重要特征是将测试的覆盖结果期望从字面上放置在驱动功能代码如何执行的源中,因此覆盖了哪些部分以及覆盖到什么程度。这使得验证测试代码所做的事情和相应的预期覆盖结果之间的一致性变得很方便,并提供了一种直接的机制来通过源代码中的注释记录两者之间的联系。
另一个关键方面是开发一种专门的语法来描述期望,鼓励测试作者积极思考预期的结果。这与使用与基线输出进行比较的方法不同,其中基线通常是通过使用工具生成输出并验证输出是否正确来获得的。无法在此框架内生成预期结果的规范。
基于 DejaGNU 框架 (www.gnu.org/software/dejagnu) 的一些测试套件变体中使用了类似的想法,例如 GCC 项目 (gcc.gnu.org) 使用的测试套件。
该方法对于测试套件的长期维护也很有趣。首先,报告格式的任何变化都通过对测试套件执行引擎的调整来处理,该引擎非常本地化且控制良好。这与面向基线的框架不同,其中报告格式的更改通常会导致对完整测试基线的调整,当测试库变大时,这变得乏味且容易出错。其次,测试源维护也更容易,因为覆盖预期与指定线路在源中的相对位置完全脱节。例如,可以添加注释或重新排序子程序,而无需更新预期结果。
该框架的主要缺点是专业化。它目前是为覆盖分析工具量身定制的,并且代码只支持最初开发环境的工具。然而,可以在多个方向上进行泛化。例如,已经为 C 语言开发了支持,并且可以根据客户的需求为其他语言添加支持。当该工具具有命令行界面时,该框架还可以适用于其他覆盖分析工具。在这方面没有基本的限制。
抽象能力
允许指定行集的标记方案提供了比单独的行命名工具更大的抽象,其中每个特定行都需要通过期望进行机械匹配。实际上,/tag/ lines 中的标签被解释为一个正则表达式,因此有很多强大的方法可以构建精细的行集模式,并且仔细选择标签可以帮助显着简化预期结果的表达。在某种程度上,为测试设计一组标签可以被视为定义一种非常基本的微语言来指定源代码行集,从这个角度来看,标签方案提供了一种元语言,可以为每个测试实例化。
另一个级别的分解是通过在测试之间共享源的能力实现的,特别是为功能惯用语的不同实现提供一组通用的驱动程序源。
例如,考虑在 Ada 中测试工具在布尔表达式(如“A 然后 B”)上的正确行为的目标。一个自然的起点是最简单的情况,其中 A 和 B 是简单的布尔变量,具有如图 6 所示的功能代码。
【图6 | 此处显示的是使用 Ada 语言中的简单布尔表达式“A and then B”对结构覆盖分析工具的行为进行的示例测试。]
为了练习图 6 中的代码,可以编写一些驱动程序,以不同的方式直接调用 Eval_Andthen 过程,一次或多次,将不同的值传递给 A 和 B,并相应地说明预期结果。
人们意识到,对于具有比基本布尔变量更复杂的操作数的功能代码,额外的测试将是有意义的。如果这些测试是作为独立实体编写的,从作为模型的基本案例开始,几乎可以立即看出所需的驱动程序源集与第一次编写的驱动程序源非常相似;只需以不同的方式调用功能代码并具有相同的覆盖预期。
相反,可以设置一个环境,其中针对一种操作数的每组测试都提供了一个帮助 API,驱动程序代码始终可以以相同的方式使用该 API,而不管实际操作数的种类如何。图 7 中的驱动程序代码提供了一个示例,其中 FUAND 代表“Functional And”。帮助程序包预计将提供一个“Eval_TT_T”子程序,该子程序调用功能代码,安排两个操作数评估 True (_TT_),因此决策也评估 True(尾随 _T):
【图7 | 此处显示的示例驱动程序代码使用了一个帮助 API,无论操作数的类型如何,该 API 始终可以以相同的方式使用,其中“Eval_TT_T”子程序调用功能代码以启动两者对 True (_TT_) 和 (_T) 的评估操作数。]
为新的操作数类型组合添加测试只需要提供功能代码和帮助程序包,并且添加驱动程序源会自动使所有已经存在的操作数类型变体受益。这是一个非常强大的机制,甚至可以进一步推广以支持在一般上下文中对决策进行覆盖评估,而不仅仅是作为 if 语句中的控制表达式。
总结和观点
作为开发覆盖分析工具内部测试框架的一部分,我们设计了一种方法,其中对覆盖结果的期望在测试源中表示为特殊注释。此处概述了这些方案的一些重要方面。所描述的框架鼓励积极思考每个测试的预期结果应该是什么,并提供抽象设施,允许对开发和维护工作进行分解。
所描述的方法作为我们 GNATcoverage 工具资格认证的基础,这些项目使用该工具作为航空电子领域 DO-178B 和 DO-178C 认证的一部分,达到最严格的认证级别,这需要 MC/DC。基于这项工作,我们正在评估可能的方法来形式化覆盖分析问题的测试策略的各个方面,特别是关于适当的 MC/DC 测试对表达式拓扑、表达式上下文以及操作数的种类和复杂性的影响。
审核编辑:郭婷
-
API
+关注
关注
2文章
1481浏览量
61767 -
编译器
+关注
关注
1文章
1617浏览量
49023 -
航空电子
+关注
关注
15文章
489浏览量
45154
发布评论请先 登录
相关推荐
评论