面向模型驱动代码生成的代码覆盖率测试、装置、计算机设备及存储介质的制作方法

摘要: 本技术涉及计算机,具体涉及一种面向模型驱动代码生成的代码覆盖率测试方法、装置、计算机设备及存储介质。背景技术:1、模型驱动开发(modeldrivendevel...
面向模型驱动代码生成的代码覆盖率测试、装置、计算机设备及存储介质的制作方法

本技术涉及计算机,具体涉及一种面向模型驱动代码生成的代码覆盖率测试方法、装置、计算机设备及存储介质。

背景技术:

1、模型驱动开发(model driven development,mdd)是指以图形化模型为中心进行软件开发的方式:开发人员在模型驱动开发平台中搭建图形化模型,由平台根据图形化模型自动生成产品代码。代码覆盖率是指在执行测试的过程中,代码中被实际执行到的部分所占的比例,是衡量测试充分性的常用指标。对代码覆盖率进行统计的常见手段是插桩,即在代码中插入用于记录执行情况的探针,在代码执行后收集各探针的数据,得到覆盖率统计结果。

2、在嵌入式软件领域,模型驱动开发的应用日益广泛。通过模型驱动开发平台自动生成产品代码,能够提高开发效率并保持代码与模型的一致性。在航空、汽车等功能安全相关领域,行业标准对代码覆盖率提出了明确要求,自动生成的代码同样需要进行覆盖率测试。

3、相关技术中,通用覆盖率工具在编译阶段对源代码统一插桩。此类工具将源文件视为普通文本,按照语法结构在代码的语句、分支、条件等位置均匀地插入探针,探针的初始化代码分散在代码各处或者由运行时库提供。在程序执行后,此类工具收集探针数据并生成覆盖率报告。

4、自动生成的代码与人工编写的代码存在明显差异。自动生成的代码由模型驱动开发平台依据图形化模型批量产生,其中既包含与图形化模型对应的模型代码,也包含平台提供的库函数代码。并且,自动生成的代码具有由平台定义的入口结构,例如被周期调用的主步进入口函数。

5、由于通用覆盖率工具不理解生成代码与图形化模型之间的对应关系,对生成代码进行统一插桩时存在以下问题。其一,探针被无差别地插入平台库函数等与图形化模型无关的代码中,引入大量与模型验证无关的插桩噪声。其二,采集到的覆盖率数据按照代码位置组织,无法映射回图形化模型的模型结构,测试人员难以从模型层面确认哪些模型元素已被验证、哪些模型元素尚未被验证。其三,探针的初始化代码分散在代码各处,与生成代码的入口结构不匹配,增加了在嵌入式目标环境中集成与维护插桩代码的工作量。上述问题影响了模型驱动开发模式下代码覆盖率测试的效率与结果的可用性。

技术实现思路

1、本技术实施例提供了一种面向模型驱动代码生成的代码覆盖率测试方法、装置、计算机设备及存储介质,能够使覆盖率数据的组织贴合模型结构、使探针初始化与生成代码的入口结构相匹配、使覆盖率统计聚焦于模型逻辑,达到低开销、可回溯、数据精准的覆盖率测试效果。本技术实施例提供的技术方案如下。

2、第一方面,提供了一种面向模型驱动代码生成的代码覆盖率测试方法。该方法以覆盖率测试系统为执行主体,覆盖率测试系统包括部署于开发主机的插桩引擎与分析工具,以及与开发主机连接的嵌入式目标平台。该方法包括以下步骤。

3、覆盖率测试系统获取目标代码。目标代码为由模型驱动开发平台根据目标模型自动生成的代码。目标模型由开发者在模型驱动开发平台中搭建,平台的代码生成器按照模型到代码的映射规则将目标模型转换为目标代码。

4、覆盖率测试系统识别目标代码中的模型入口函数,并将覆盖率探针的初始化代码聚合至模型入口函数。模型入口函数为模型驱动开发平台生成的目标代码中与目标模型对应的入口函数。覆盖率探针用于采集目标代码执行过程中的覆盖率数据,覆盖率探针在使用前需要将记录探针状态的存储区域复位。聚合是指将原本分散在各个函数中的初始化代码集中到模型入口函数一处执行。

5、覆盖率测试系统按照目标模型的模型结构为各覆盖率探针分配索引。索引是覆盖率探针与模型结构之间的映射键。凭借一个覆盖率探针的索引,该覆盖率探针采集的覆盖率数据能够定位到目标模型中对应的模型元素,覆盖率数据的组织顺序与模型的组织方式保持一致。

6、覆盖率测试系统确定目标代码中的平台库函数,并在目标代码中除平台库函数以外的部分插入覆盖率探针。平台库函数为由模型驱动开发平台提供的库函数。平台库函数的覆盖情况不反映用户搭建的模型逻辑,因此平台库函数内部不插入覆盖率探针。

7、覆盖率测试系统执行插入覆盖率探针后的目标代码,基于各覆盖率探针的索引采集覆盖率数据。插桩后的目标代码经编译后在嵌入式目标平台运行,每执行到一个覆盖率探针,该覆盖率探针按照自身的索引将探针状态写入对应的存储位置。

8、覆盖率测试系统按照各覆盖率探针的索引将覆盖率数据映射至目标模型的模型结构,得到并输出覆盖率测试结果。由于索引在分配时已与模型结构建立对应关系,映射过程无需重新解析源代码,输出的覆盖率测试结果供开发者查看各模型元素的覆盖情况。

9、通过该方法,由于初始化代码被聚合至与目标模型对应的模型入口函数,原本分散在每个函数中的初始化操作被合并为入口处的一次操作,初始化代码总量与运行时初始化开销随之下降,因此插桩后目标代码的体积与执行开销得到压缩,从而覆盖率测试能够适配资源受限的嵌入式环境。由于索引按照目标模型的模型结构分配,每条覆盖率数据都能凭借索引回溯到具体的模型元素,因此覆盖率测试结果与模型结构一一对应,从而开发者能够从模型视角直接定位未覆盖的代码区域,无需在文件与行号之间人工对照。由于平台库函数被排除在插桩范围之外,覆盖率数据中不再混杂平台库函数的执行信息,统计对象聚焦于用户搭建的模型逻辑,因此覆盖率指标更加精准,从而开发者无需从报告中手动筛除平台库函数的覆盖数据。三个模型语义特征相互配合,使覆盖率测试从面向代码文本转变为面向模型结构,形成模型生成代码、代码承载探针、探针产出数据、数据回归模型的完整闭环。

10、在一种可能的实现中,获取目标代码分为两个阶段进行。第一阶段,覆盖率测试系统获取由模型驱动开发平台根据目标模型生成的产品代码,产品代码为不含任何测试逻辑的纯净代码,用于作为产品交付。第二阶段,覆盖率测试系统对产品代码的副本触发插桩处理,得到用于覆盖率测试的目标代码。产品代码进入产品构建链路,目标代码进入覆盖率测试链路,二者同源而不同用。

11、通过这种实施方式,由于插桩只作用于产品代码的副本,代码生成器无需感知覆盖率测试的存在,产品代码中不含任何测试代码,因此交付给客户的代码与未经插桩的原始生成代码保持一致,从而保证了产品代码的纯净性,测试活动不会污染交付物。同时,插桩引擎以独立组件形式存在,覆盖率测试能力的启用、停用与升级均不涉及代码生成器的修改,因此代码生成器与插桩引擎能够独立演进,从而系统的可维护性得到提升,手工插入探针的工作量与出错风险也被消除。

12、在一些实施方式中,基于各覆盖率探针的索引采集覆盖率数据的具体方式为:将各覆盖率探针的探针状态存储于位图数组,位图数组中各覆盖率探针占用的存储空间为1比特。各覆盖率探针的探针状态按照覆盖率探针的索引写入位图数组中与索引对应的位,以使位图数组中的位通过索引与模型结构对应。

13、通过这种实施方式,由于每个覆盖率探针的探针状态仅占1比特,以1000个覆盖率探针为例,存储需求由通用工具计数器方案的4至8千字节(kilobyte,kb)降至约125字节,内存节省约97%至98%,因此覆盖率数据存储对目标平台内存的占用大幅降低,从而随机存取存储器(random access memory,ram)容量以kb计的微控制单元也能够承载完整的覆盖率测试,内存资源受限不再构成覆盖率测试的门槛。同时,由于探针状态按索引写入与索引对应的位,而索引按模型结构分配,位图数组中位的排布顺序与模型结构一致,因此位图数组本身即是模型结构的数字化镜像,从而分析端无需对原始数据重新排序即可将数据回溯至模型。

14、在一种可能的实现中,在上述位图数组方案的基础上,各覆盖率探针的探针状态通过预定义的位操作宏写入位图数组。位操作宏在编译期展开,位操作宏不依赖操作系统的应用程序接口(application programming interface,api)和文件系统。

15、通过这种实施方式,由于位操作宏在编译期展开为几条位运算指令,插桩后的目标代码在运行时不调用任何外部运行时服务,也不依赖操作系统api和文件系统,因此目标代码能够在无文件系统的微控制单元、数字信号处理器裸机环境中直接编译运行,从而覆盖率测试摆脱了对外部运行环境的依赖,实现了裸机兼容。与依赖文件系统持久化覆盖率数据的方案相比,零依赖方案无需在目标平台上配置文件系统或移植运行时库,从而覆盖率测试向各类嵌入式目标平台的移植成本明显降低。

16、在一些实施方式中,将覆盖率探针的初始化代码聚合至模型入口函数的具体方式为:覆盖率测试系统统计目标代码中覆盖率探针的总数,根据总数在模型入口函数的入口位置插入存储数组的定义代码以及对存储数组进行一次性清零的清零代码。存储数组用于存储各覆盖率探针的探针状态。模型入口函数每被执行一次,全部覆盖率探针的探针状态在入口处被统一复位一次,各子函数内部不再出现任何初始化代码。

17、通过这种实施方式,由于初始化操作由每个函数一次降为每个执行周期入口处一次,分散在各函数中的初始化冗余代码被整体消除,因此插桩后目标代码的体积与运行时初始化开销进一步压缩,从而与位压缩存储叠加后,插桩引入的额外开销被控制在嵌入式平台可承受的范围内。

18、在一种可能的实现中,模型入口函数为由模型驱动开发平台的代码生成器生成的主步进函数,主步进函数用于被调度执行以完成目标模型对应的单步计算。在模型驱动开发中,模型的仿真与生成代码的运行均以步进为基本节奏,每个步进周期完成一次从输入采样、模型计算到输出更新的完整过程,主步进函数正是这一过程在代码中的载体。

19、通过这种实施方式,由于主步进函数在每个步进周期被确定地调度,聚合于主步进函数的清零操作随之与模型的执行节奏严格同步,因此每个步进周期的覆盖率采集都从一个确定的全零状态开始,从而逐帧采集的覆盖率数据与模型的步进周期一一对应,保证了覆盖率数据在时序上的一致性与可复现性。同时,主步进函数具有固定命名或明确的代码生成标记,识别过程无需人工指定,从而聚合初始化的触发位置能够自动、准确地确定。

20、在一些实施方式中,按照目标模型的模型结构为各覆盖率探针分配索引的具体方式为:按照目标模型中各模型元素的拓扑排序,依次为目标代码中与各模型元素对应的代码部分分配覆盖率探针的索引,以使各覆盖率探针的索引顺序与目标模型的执行顺序一致。拓扑排序是指按照模型元素之间的信号依赖关系对模型元素排序,上游模型元素排在下游模型元素之前。

21、通过这种实施方式,由于索引顺序与目标模型的执行顺序一致,覆盖率数据在存储空间中的物理排布即反映模型执行流的方向,因此覆盖率报告中探针的分布呈现与模型结构一致的空间布局,从而开发者沿信号流向阅读报告即可直观定位未覆盖的代码区域。与按文件顺序线性分配索引的方式相比,拓扑排序分配使覆盖率数据的组织天然贴合模型,从而省去了开发者将线性探针分布与模型结构人工对照的环节。

22、在一种可能的实现中,确定目标代码中的平台库函数的具体方式为:覆盖率测试系统根据函数的文件路径、命名规则或代码生成标记中的至少一种,识别目标代码中由模型驱动开发平台生成的平台库函数。三种识别依据可单独使用,也可组合使用。

23、通过这种实施方式,由于三种判据相互独立又可互为印证,识别过程不依赖单一线索,因此在工程目录结构调整或函数命名风格变化时,识别机制仍能保持稳定,从而排除平台库函数的鲁棒性得到保障。准确的识别使覆盖率数据中不再混入平台库函数的执行信息,统计对象严格限定于用户搭建模型生成的代码,从而覆盖率指标真实反映模型逻辑的测试充分性,开发者无需对报告进行人工筛选。

24、在一些实施方式中,在目标代码中除平台库函数以外的部分插入覆盖率探针之前,覆盖率测试系统将目标代码中的链式条件分支结构展开为嵌套条件分支结构,并在展开得到的嵌套条件分支结构中插入对应的覆盖率探针,以使各层条件分支的执行被对应的覆盖率探针独立采集。链式条件分支结构即else if链。展开后,原来的每个else if分支独占一个独立的条件分支结构,每个独立的条件分支结构都是一个明确的语句级分支点。

25、通过这种实施方式,由于展开后的嵌套条件分支结构在语法层面显式表达了每一层分支,任何编译器都按照显式的嵌套结构生成跳转指令,编译器优化策略的差异不再影响分支点的数量与位置,因此覆盖率统计结果在不同编译环境下保持确定与一致,从而同一模型的覆盖率数据具备了跨编译器、跨平台的可比性。同时,由于每个else if分支独占独立的条件分支结构并被独立插桩,条件链中每一层条件的执行情况都被单独记录,因此分支覆盖的统计粒度细化到每一层条件,从而避免了链式结构中部分分支被执行而统计结果无法区分的覆盖盲区。

26、在一种可能的实现中,在目标代码中除平台库函数以外的部分插入覆盖率探针之前,覆盖率测试系统将目标代码中的三元运算符表达式递归展开为条件分支结构,并对三元运算符表达式的条件部分、真值部分和假值部分中的函数调用分别插入对应的覆盖率探针。递归展开是指每遇到一个三元运算符表达式,将其转换为等效的条件分支代码块,若真值部分或假值部分仍包含嵌套的三元运算符,则继续展开,直至不再存在三元运算符为止。

27、通过这种实施方式,由于递归展开把隐藏在表达式内部的分支提升为语句级的条件分支结构,条件部分、真值部分和假值部分中的函数调用各自获得独立的覆盖率探针,因此函数调用覆盖的统计精度细化到三元运算符表达式的每个组成部分,从而消除了三元运算符造成的覆盖盲区,即使函数调用嵌套在表达式深处也能够被精确度量。同时,展开后的语句级结构同样摆脱了编译器优化策略差异的影响,因此含三元运算符的代码在不同编译环境下的覆盖率统计同样确定,从而与链式条件分支结构的展开共同构成完整的确定性逻辑重构方案。

28、在一些实施方式中,该方法还包括:覆盖率测试系统对目标代码中的复合条件进行分解,得到复合条件包含的各原子条件;按照修正条件判定覆盖(modified condition/decision coverage,mc/dc)准则,生成用于验证各原子条件对复合条件的判定结果独立影响的最小测试用例集。最小测试用例集用于驱动执行插入覆盖率探针后的目标代码。复合条件包含的原子条件的个数为n时,最小测试用例集包括n+1组测试用例,n为正整数。

29、通过这种实施方式,由于按照修正条件判定覆盖准则自动生成n+1组最小测试用例,验证n个原子条件独立影响所需的用例规模由2的n次方组穷举用例降为n+1组,用例数量随原子条件个数线性增长而非指数增长,因此修正条件判定覆盖验证的执行成本大幅压缩,从而在不降低覆盖标准要求的前提下使修正条件判定覆盖测试在工程实践中切实可行。由于复合条件分解与用例集生成在插桩阶段自动完成,无需测试人员手工设计用例,因此高安全标准所要求的覆盖能力被原生集成进测试流程,从而方案能够直接支撑机载软件适航标准(do-178c)、道路车辆功能安全标准(iso 26262)等标准对修正条件判定覆盖的要求。

30、在一种可能的实现中,该方法还包括:覆盖率测试系统生成探针描述文件,探针描述文件记录各覆盖率探针的索引、覆盖率探针在目标代码中的位置以及覆盖率探针的类型。对应地,按照各覆盖率探针的索引将覆盖率数据映射至目标模型的模型结构的具体方式为:根据探针描述文件将覆盖率数据映射至目标模型的模型结构。

31、通过这种实施方式,由于探针的索引、位置、类型等元数据以独立描述文件的形式在插桩端与分析端之间传递,分析端无需重新解析源代码即可解释采集数据中每一位的含义,因此插桩环节与覆盖率计算环节被解耦为两个可独立运行的阶段,从而插桩端与分析端能够独立升级、独立部署。同时,探针描述文件将插桩元数据与覆盖率计算串联为从插桩到覆盖率计算的完整闭环,采集数据与元数据一一对应,从而形成一体化、可自动流转的测试工作流。

32、在一些实施方式中,输出覆盖率测试结果的具体方式为:按照目标模型的层级结构展示覆盖率测试结果,并将覆盖率测试结果与目标模型的模型文件中的模型元素关联显示。开发者先看到顶层模型的总体覆盖率,逐层展开后可看到每个子系统、每个模型元素的覆盖情况。报告中的覆盖条目与模型文件中的模型元素建立关联,开发者在报告中选中某个模型元素,模型画布中对应的模块被定位显示。

33、通过这种实施方式,由于报告按目标模型的层级结构组织并与模型文件中的模型元素关联显示,开发者面对的不再是抽象的文件与行号,而是自己搭建的模型结构,未覆盖区域可直接定位到具体的模型模块,因此从覆盖率结果到模型修改的反馈路径明显缩短,从而测试迭代的效率得到提升。层级化的展示方式还使管理者能够按子系统粒度掌握整体测试充分性,从而测试进度的评估与任务的分配更加直观。

34、第二方面,提供了一种面向模型驱动代码生成的代码覆盖率测试装置。该装置具有实现上述第一方面或第一方面任一种可选方式的功能。该装置包括至少一个单元,至少一个单元用于实现上述第一方面或第一方面任一种可选方式所提供的方法。

35、在一些实施方式中,该装置中的单元通过软件实现,该装置中的单元是程序模块。在另一些实施方式中,该装置中的单元通过硬件或固件实现。第二方面提供的装置的具体细节可参见上述第一方面或第一方面任一种可选方式,此处不再赘述。

36、第三方面,提供了一种计算机设备。该计算机设备包括处理器和存储器,处理器与存储器耦合,存储器中存储有至少一条计算机程序指令,至少一条计算机程序指令由处理器加载并执行,以使该计算机设备实现上述第一方面或第一方面任一种可选方式所提供的方法。第三方面提供的计算机设备的具体细节可参见上述第一方面或第一方面任一种可选方式,此处不再赘述。

37、第四方面,提供了一种计算机可读存储介质。该存储介质中存储有至少一条指令,该指令在计算机上运行时,使得计算机执行上述第一方面或第一方面任一种可选方式所提供的方法。

38、第五方面,提供了一种计算机程序产品。该计算机程序产品包括一个或多个计算机程序指令,当计算机程序指令被计算机加载并运行时,使得计算机执行上述第一方面或第一方面任一种可选方式所提供的方法。

39、上述技术方案中的一个技术方案至少具有如下优点或有益效果之一:探针初始化聚合至与目标模型对应的模型入口函数,插桩引入的体积与执行开销得到压缩,覆盖率测试能够适配资源受限的嵌入式环境;覆盖率数据凭借按模型结构分配的索引回溯至具体模型元素,测试结果与模型结构一一对应;平台库函数被排除在插桩范围之外,覆盖率指标精准聚焦于模型逻辑。整个方案使覆盖率测试从面向代码文本转变为面向模型结构,形成模型生成代码、代码承载探针、探针产出数据、数据回归模型的完整闭环。