A Path Coverage-Based Reduction of Test Cases and Execution Time Using Parallel Execution
基于路径覆盖的并行执行减少测试用例和执行时间
© Springer Nature Singapore Pte Ltd. 2019 M. N. Hoda et al. (eds.), Software Engineering, Advances in Intelligent Systems and Computing
在本文中,生成一组测试,每个测试遍历指定的路径。 目的是确保代码中没有任何路径不被覆盖。 对于这些路径,生成大量测试数据是非常繁琐的任务。 结果,应该应用某种策略来减少大量冗余的测试用例的数量,即重复的同一组测试用例。 通过应用策略来避免冲突,将超出测试路径生成以减少冗余并生成有效的测试用例以覆盖控制流图中的所有路径。
现有技术的潜在缺点是它们仅覆盖了一条单一路径。
算法的过程:
1.使用源代码(对于确定三个给定整数f,s和t 的中间值的程序),绘制问题的相应控制流图。
2.然后,从图(流图)确定圈复杂度。
3.之后,确定线性独立路径的基组。
4.然后,使用ReduceDomains算法减少每个输入变量的域。
5.在生成测试套件之后,通过使用代数条件将特定值分配给变量并检查覆盖标准,判断是否满足。
6.应用并行测试用例执行器,以减少大量冗余的测试用例。
控制流图的描述信息:
每一个状态由一个节点表示,每一个边表示状态的转移。圈复杂度用于确定来自流图的线性独立路径的数量,它是获得最大代码覆盖率所需的测试集总数的标志,随后的测试集提供了比语句和分支覆盖更深入的测试,
V (G)=e − n +2 p,e和n是控制流图中边的总数和节点总数,p是图的连接组件数目(图的组件数是相连节点的最大集合)。控制流图都是连通的,所以p=1。V(G)的值是程序中所有可能执行路径的标志,表示完全测试方法所需的测试用例数量的下界。
由于每个独立的路径都是通过线程并行执行的,所以它包含了测试用例的所有组合。
圈复杂度详解:
https://www.jianshu.com/p/a21cc7579691
其他计算圈复杂度的方法:
计算公式2:V(G)=区域数=判定节点数+1。其实,圈复杂度的计算还有更直观的方法,因为圈复杂度所反映的是“判定条件”的数量,所以圈复杂度实际上就是等于判定节点的数量再加上1,也即控制流图的区域数。
对于多分支的CASE结构或IF-ELSEIF-ELSE结构,统计判定节点的个数时需要特别注意一点,要求必须统计全部实际的判定节点数,也即每个ELSEIF语句,以及每个CASE语句,都应该算为一个判定节点。
计算公式3:V(G)=R。其中R代表平面被控制流图划分成的区域数。
适用场景:
针对程序的控制流图计算圈复杂度V(G)时,最好还是采用第一个公式,也即V(G)=e-n+2;而针对模块的控制流图时,可以直接统计判定节点数,这样更为简单;针对复杂的控制流图是,使用区域计算公式V(G)=R更为简单。
路径覆盖:
路径覆盖是指选取足够多的测试数据,使程序的每条可能路径都至少执行一次(如果程序图中有环,则要求每个环至少经过一次)。路径覆盖是覆盖率最高的一种覆盖技术。
路径覆盖率的公式:路径覆盖率=被执行到的路径数/程序中总的路径数。
独立路径数的计算:
第一步,从流图中找出程序所有的必经节点(流图中任何独立路径都必定经过的节点叫做必经节点),记作IV(i),其中i为整数且。
第二步,从流图中找出从必经节点N(i)到必经节点N(i+1)的独立路径数W(i),其中i为整数且
。
第三步,重复上一步,直到程序结尾。
第四步,根据乘法法则,独立路径数= W(i),其中i为整数且
,即独立路径数=W(0) * W(1) * ⋯ * W(N一1)。
完全路径是指所有独立路径的集合,非完全路径就是所有独立路径集合的真子集。由于程序中可能会包含有多个条件的判定,所以程序流程图可能包含有隐含路径,从而有程序流图转换成的对应流图可能包含有隐藏路径。
消除隐含路径的办法就是将含有多个条件的判定分为多个判定。
完全路径覆盖的具体步骤:
1、将判定语句的条件进行分离,细化程序流程图,使其不含隐含路径。
2、根据程序流程图画出流图,找出必经节点,必经节点数为N。
3、将程序流程图在必经节点处割断,将整个程序分解为N+1个程序片断。
4、找出程序片断i的完全路径,为程序片断i的每条独立路径设计用例,其中:
。
5、结合所设计的测试用例,将程序片断i的参数初始化,其中
。
6、将测试用例付诸测试,重复第四步至第六步,直到i=N+1。
Coverall algorithm覆盖算法
A path-aware approach to mutant reduction in mutation testing
Information and Software Technology
Chang-ai Sun ,Feifei Xue, Huai Liu ,Xiangyu Zhang
减少变异体的数量来减少测试代价。定义了两个path-aware启发式规则:分别名为模块深度和循环深度规则,并且结合基于状态和变异算子选择提出四种变异体选择策略。与随机变异体选择策略相比,有更高的有效性。
我们应该对变异体给予更高的优先级,因为变异体的相关缺陷位于较深的位置。
减少变异体数量的一些前期工作:
1.random mutant selection(可能会丢弃一些很难被杀死的变异体)
2.operator-based mutant selection
一些定义:
m->n 模块m调用模块n
Callers(m)={x|x->m}模块m直接调用者的集合。
MD(m):m基于所有模块调用关系的模块深度
LD(b)循环/分支深度
路径感知减少变异体的方法:
Depth:变异体的相关缺陷位于较深的位置
Diversity:选择变异体的多样性由不同的启发式规则获得
传统变异分析 T=<P,S,D,L,A>
本文的方法中:
E=<P,S,D,L,A,P>
P是A中每一个可替代a发生的可能性
L=<Loc,MD,LD>
启发式规则:
**module depth,loop/branch depth,**statement selection,Operator selection
减少变异体的策略:
md-ld-op,ld-md-op表现最好,
stm-ld-md,op-ld-md非path-qware技术
实验步骤:
a.选择与所有非等价变异体相关的目标程序Mu
b.取0到100的实数x,变异体的抽样比x%。
c.应用一个变异体减少策略选择x%以获得变异体子集Mu
d.使用随机测试技术构建测试套件,在目标程序的测试池中随机选择测试用例直到所有Mu中的变异体被杀死。
e.应用TS去测试Mu中所有的变异体并且计算变异评分MS
未来工作:
1.发现更多的启发式规则并且设计更多的精确减少策略。
2.构建在大型系统中评估路径感知方法有效性和应用的方法,不同领域的应用。
文献:
[28]对于单元和集成测试选择部分变异算子,这个方法可以有效减少变异体数量没有考虑变异评分的危害。
[29]java程序的三种典型的变异算子,发现已经存在的冗余变异体可能会影响包括有效性和变异测试的质量,并且也给出了一些移除冗余变异体的方法。
[30]one-op变异。
[10]随机变异体选择等价于基于变异算子的选择。
[31]并行变异算子的选择可以有效地减少变异体的数量,并且表现胜过随机选择。
[32]随机选择和基于变异算子的变异体选择结合。
[33]高阶变异体
[44]检测等价变异体的技术
优化执行时间的方法:
[19]在编译器中使用工具减少变异体生成和编译的时间。
[45]schema-based方法,字节码翻译技术允许变异体直接执行因此可以节省编译时间
[47]提出一个技术ReMT去提高变异测试有效性通过软件测试进化过程
[48]提出了另一种技术FaMT最小化和最优化每个变异体的测试用例
[49,50]变异测试工具
[51]变异测试中应用程序路径,提出了一种基于路径的测试用例生成方法,在杀死变异体方面是有效的
Selective Mutation Testing for Concurrent Code
并发代码的选择性突变测试
选择性变异:仅使用一部分来减少变异体数量,使得杀死由该子集产生的所有变异体的测试套件也能杀死所有变异算子产生的所有变异体。
并发bug:数据竞争,原子性违背,死锁。
选择变异体子集的两种方式:
1.selective mutation
2.random selection
比较的方面:
1.**变异体数量:**未选择的变异体数量/产生的变异体总数
2.**非选择性变异得分的有效性:**杀死所有选定的非等价变异体的测试套件杀死的所有生成的非等价变异体的百分比。
对于并发代码的评估:
1.详尽的分析了所有可能的变异算子子集 的cost和effectiveness的权衡。
(用尽可能少的并发算子,尝试所有可能变异子集最佳成本效率)
2.比较不同选择性方法之间的成本。
(突变体的数量,并发测试的勘探成本)
3.评估顺序程序和并发程序的变异算子是独立的还是相互包含的。
并发测试套件可以获得对于顺序执行程序来说高变异分数。
本文提出了三种新的变异算子:RTS,MTS,WTIS
RTS:删除Thread对象上start方法的调用。
MTS:在Thread对象上移动start方法的调用。
RTS和MTS产生的变异体可能会导致程序死锁。
WTIS:在同步块内将while替换成if,可能会导致原子性违背。
插入变异体的方法:
1.创建SUT的多个拷贝并在每个拷贝中插入一个变异体,从而产生与变异体一样多的拷贝。
2.使用变异模式:为所有变异体仅创建SUT的一个变异版本,使得全局ID指定启用哪个变异体。
并行测试与顺序执行程序之间的测试的一个显著差异在于并行测试依赖于线程调度。
难点:给定并发程序,测试和变异体,有必要执行原始程序和变异体所有可能的进程以确定变异体是否被杀死。
false negative:变异代码中从未执行导致不同输出的调度,则可能无法检测变异体被杀死。
false positive:变异代码上获得的输出与原始代码上获得的输出不同,则可能会报告变异体被杀死。
研究中提出9个问题,分别是:
1.并发代码中最主要的变异算子是什么?
Answer:RSK是并发代码中最主要的变异操作符,其次是MSP和RSB。
2.n-选择性变异是否适用于并发代码的变异测试?
不同于选择性突变序列变异算子,n-selective突变测试(n∈{ 2 4 6 })并不适用于突变测试并发突变算子。
3.哪一类的变异算子能够获得最高的(非选择性的)变异分数?
“修改关键字”(MK)类别是最有效的。然而,与顺序程序变异算子不同的是,没有一个类别能够获得足够的分数。
4.获得高(非选择性)突变分数和显著节省(就突变体数量而言)的变异算子集是什么?
{MSP, RSK, RTXC, SHCR, SKCR, SPCR, WTIS}是在充分突变得分(99.67%)的情况下达到最高节约(46.37%)的集合。
5.对于(非选择性)变异分数的不同值,可以实现的最大节省是什么?
图1显示了每个突变值的最大节省。如果得分超过95%,则可节省40%至50%。如果节省超过90%,分数约为60%。
6.在未来的研究中,哪组并发变异算子为使用提供了良好的权衡?
7.如何比较基于操作符和随机变异算子的并发变异算子?
对于并发变异算子,基于操作符的突变选择要比随机选择好一点。
8.突变体数量上的节约和勘探成本上的节约有什么关系?
从突变体数量上的节省对应于从并发代码的探索成本上的节省。
9.并发和顺序突变操作符是独立的,即没有一个能包含另一个?
并发和顺序变异算子是独立的。两者均不包含,说明了并发变异算子研究的重要性
实验所研究的程序:
三个(大)程序来自真实世界的开放源代码:Guava[21]是一个谷歌项目,包括并发集合和并发库;Lucene[32]是一个实现并发文本搜索引擎的Apache项目;而Pool[46]是一个Apache项目,它提供了并发的对象池API。
小程序是由并发构造实现的。
变异体的产生:
选择性突变是多线程代码的理想选择。
并发突变算子生成的突变体分布高度不均匀。
测试组件:
变异体执行:
(1)压力测试只是在常规JVM上运行原始的或突变的SUT(不需要进一步修改)。
(2) Java PathFinder (JPF)[52]是一个明确的模型检查器,实现为一个特殊的JVM,可以探索所有可能的时间表;它还支持有限数量的线程抢占。
杀死变异体:
(1)选择一个突变算子子集;
(2)为所选择的突变算子生成的突变体找到合适的测试套件;
(3)测量测试套件对所有突变算子的非选择性突变得分。
选择性变异的结果:
n-Selective mutation