【软件测试·研究向】Major 变异测试工具使用教程

2024-08-21 19:44

本文主要是介绍【软件测试·研究向】Major 变异测试工具使用教程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

1 安装

1.1 下载解压

1.2 配置环境变量

1.3 入门示例脚本

2  Mutator Plugin 编译器插件

2.1 如何使用插件?

2.2 插件配置选项

2.3 Major 支持的变异算子

2.4 突变体的日志记录

2.5 生成突变体的源代码

3 构建系统集成

4  Major 突变语言 (MML)

4.1 语句作用域

4.2 覆盖和扩展定义

4.3 自定义突变算子组

4.4 示例 MML 文件

5  Major 的程序分析器

5.1 程序分析器配置选项

5.2 设置分析目标

5.3 性能优化

6 兼容性问题和注意事项


本文出处链接:[https://blog.csdn.net/qq_59075481/article/details/141371759]

前言

Major (主要变异框架, The Major Mutation Framework)是一个高效、灵活的突变分析框架,支持:

  1. 在编译期间生成和嵌入突变体;
  2. 导出源代码突变体;
  3. 抑制等效突变体;
  4. 对于给定的一组测试,计算突变覆盖率和存活突变体集;
  5. 针对给定的一组测试计算完整的突变测试检测矩阵。

目前最新版本是 2024.5v.2.2.0 版本,程序运行在 JDK 8 环境中,并且最高支持 语言级别 8 的代码。

主要突变框架提供了三个主要组件:

  1. Major 变异器:Java 编译器 (javac) 的编译器插件;
  2. Mml 配置文件:一种用于定制突变生成的 DSL(领域特定语言);
  3. 分析器:一种可有效确定活突变体、突变体检测率或完整突变体测试矩阵的测试运行器;

变异器(Mutator) 是打包到 major.jar 包里面的,而现代分析器是集成到 Apache Ant 构建系统中的。除了基于 Ant 的增强分析器,Major 还保留了早期版本的经典分析器(Classic analyzer)。

1 安装

1.1 下载解压

Major 的安装配置是很简单的,首先进入 官网 (https://mutation-testing.org) 的下载页下载最新的发布版本:

Major 工具的官网下载页

下载下来是一个 zip 压缩包,使用解压缩软件如 7zip, WinRAR 等即可解压缩。

解压缩后你将得到下面的目录结构(在 major 目录中):

  • bin (Major 组件的可执行文件,是一些 Shell 脚本)
    • ant
    • major
    • mmlc
  • doc (当前版本的说明文档)
    • major.html
  • example (如何使用 Major 的示例)
    • ant (Ant 中编译和测试)
      • run.sh (执行脚本)
    • standalone (经典模式中编译和测试)
      • run.sh (执行脚本)
    • runAll.sh (合并执行脚本)
  • lib (Major 使用的库)
  • mml (mml 文件示例)

1.2 配置环境变量

major 需要添加到系统环境变量中,添加变量 MAJOR_HOME 并指定变量值为 major 根路径。

MAJOR_HOME 系统环境变量

然后,重新打开 Command Terminal (CMD) 终端输入 major 辅助参数进行测试运行:

java -jar %MAJOR_HOME%\lib\major.jar -h

结果如下: 

测试运行 major 工具

1.3 入门示例脚本

在 bin 和 example 目录下面有一些 shell 脚本供 Linux 用户使用,这些脚本通过在 java 和 javac 下调用 major.jar 来生成代码的变异体或者变异 mml 文件或者测试运行程序等。但是作者并没有提供 Windows 下面的可执行脚本,所以有些文献就误以为只能在 Linux 下运行,实际上并不是这样。(在 Windows 下面使用 Git Bash 执行这里的 Shell 脚本兼容性并不好)

下面我给出根据原脚本原理,修改的运行在 Windows 下的命令行脚本

注意,完整的 2.2.0 版本和修改后的版本可以在这里获取:

https://pan.baidu.com/s/1RouKYi2GL7GbVgZVtYeFkA?pwd=6666 

提取码:6666

major/bin/ant.bat >

@echo off
set BASE=%~dp0..java  -XX:ReservedCodeCacheSize=256M "-Xbootclasspath/a:%BASE%\lib\major-rt.jar" -jar "%BASE%\lib\ant\ant-launcher.jar" %*echo "java  -XX:ReservedCodeCacheSize=256M "-Xbootclasspath/a:%BASE%\lib\major-rt.jar" -jar "%BASE%\lib\ant\ant-launcher.jar" %*"

major/bin/major.bat >

@echo off
setlocal enabledelayedexpansion:: Get the base directory relative to the location of the batch script
set "BASE=%~dp0.."
set "CLASSPATH=%BASE%\lib\major.jar"
set "MML=%MML%"
set "LOGGING=%LOGGING%"
set "EXPORT=%EXPORT%"
set "REFACTOR=%REFACTOR%":: Process the command line arguments
:parse_args
if "%1"=="" goto :prepare_plugin_arg
if "%1"=="-classpath" set "CLASSPATH=%CLASSPATH%;%~2" & shift & shift & goto :parse_args
if "%1"=="-cp" set "CLASSPATH=%CLASSPATH%;%~2" & shift & shift & goto :parse_args
if "%1"=="--logging" (if "!LOGGING!"=="" (set "LOGGING=logging:%~2") else (set "LOGGING=!LOGGING!,%~2")shift & shiftgoto :parse_args
)
if "%1"=="--mml" (set "MML=mml:%~2"shift & shiftgoto :parse_args
)
if "%1"=="--export" (set "EXPORT=%~2"shift & shiftgoto :parse_args
)
if "%1"=="--refactor" (set "REFACTOR=%~2"shift & shiftgoto :parse_args
):: Collect additional Java arguments
set "JAVA_ARGS=!JAVA_ARGS! %1"
shift
goto :parse_args:prepare_plugin_arg
set "PLUGIN_ARG=-Xplugin:MajorPlugin"
if not "!LOGGING!"=="" set "PLUGIN_ARG=!PLUGIN_ARG! !LOGGING!"
if not "!MML!"=="" set "PLUGIN_ARG=!PLUGIN_ARG! !MML!"
if not "!EXPORT!"=="" set "PLUGIN_ARG=!PLUGIN_ARG! !EXPORT!"
if not "!REFACTOR!"=="" set "PLUGIN_ARG=!PLUGIN_ARG! !REFACTOR!":: Execute Java command
javac -cp "%CLASSPATH%" -encoding utf8 "%PLUGIN_ARG%" %JAVA_ARGS%endlocal

major/bin/mmlc.bat >

@echo off:: 获取脚本所在目录的上一级目录
set BASE=%~dp0..
set MML=%1:: 检查是否有输入文件
if "%MML%"=="" (echo usage: mmlc.bat file.mmlexit /b 1
):: 运行Java程序
java -jar "%BASE%\lib\major.jar" --mmlc "%MML%"

major/example/runAll.bat >

@echo offecho "Using Major standalone"
cd standalone
cmd/c .\runWinG.bat
cd ..echo.
echo "Using Major with Ant"
cd ant
cmd/c .\runWinG.bat
cd ..pause

major/example/ant/run.bat >

@echo off
set MAJOR_HOME=..\..echo.
echo Compiling and mutating project
echo (ant -Dmutator="=mml:%MAJOR_HOME%/mml/tutorial.mml.bin" clean compile)
echo.
cmd/c %MAJOR_HOME%\bin\ant.bat -Dmutator="mml:%MAJOR_HOME%/mml/tutorial.mml.bin" clean compileecho.
echo Compiling tests
echo (ant compile.tests)
echo.
cmd/c %MAJOR_HOME%\bin\ant.bat compile.testsecho.
echo Run tests without mutation analysis
echo (ant test)
cmd /c %MAJOR_HOME%\bin\ant.bat testecho.
echo Run tests with mutation analysis
echo (ant mutation.test)
cmd/c %MAJOR_HOME%\bin\ant.bat mutation.test
pause

major/example/ant/clean.bat >

@echo off
set MAJOR_HOME=..\..echo.
echo Clean project folder
echo (%MAJOR_HOME%\bin\ant.bat clean)
echo.
cmd/c %MAJOR_HOME%\bin\ant.bat clean
echo.
echo (del /f preprocessing.ser)
del /f preprocessing.ser 2>nul

major/example/standalone/run.bat >

警告:此脚本的部分功能正在使用 Windows PowerShell 的 Tee-Object cmdlet 重定向输出流,请确保脚本在部署了至少 PowerShell  3.0 的系统上运行,并允许执行 PowerShell 命令。

@echo offset MAJOR_HOME = ../../echo "- Running javac without the mutation plugin"
echo "  (javac triangle/Triangle.java)"
javac triangle/Triangle.javaecho.
echo "- Running javac with the major mutation plugin enabled"
echo "  (%MAJOR_HOME%/bin/major --mml %MAJOR_HOME%/mml/tutorial.mml.bin triangle/Triangle.java)"
cmd /c  "%MAJOR_HOME%\bin\major.bat" --mml %MAJOR_HOME%\mml\tutorial.mml.bin triangle\Triangle.javaecho.
echo "- Compiling test case (major-rt.jar has to be on the classpath!)"
echo "  (javac -cp .;%MAJOR_HOME%/lib/major-rt.jar TriangleTest.java)"
javac -cp .;%MAJOR_HOME%/lib/major-rt.jar TriangleTest.javaecho.
echo "- Executing test case (major-rt.jar has to be on the classpath!)"
echo "  (java -cp .;%MAJOR_HOME%/lib/major-rt.jar -Dfile.encoding=utf-8 TriangleTest)"
echo.
REM java -cp .;%MAJOR_HOME%/lib/major-rt.jar -Dfile.encoding=utf-8 TriangleTestset TIMESTAMP=%DATE%%TIME%
set TIMESTAMP=%TIMESTAMP: =_%
set TIMESTAMP=%TIMESTAMP:/=_%
set TIMESTAMP=%TIMESTAMP::=_%
powershell -command "java -cp '.;%MAJOR_HOME%/lib/major-rt.jar' '-Dfile.encoding=utf-8' TriangleTest | Tee-Object -FilePath 'test_%TIMESTAMP%.txt'"
pause

major/example/standalone/clean.bat >

@echo off
set MAJOR_HOME=..\..echo.
echo Clean project folder
del /f /s /q *.log 2>nul
del /f /s /q test_*.txt 2>nul:: 获取当前批处理文件的目录
pushd %~dp0:: 删除所有 .class 文件
echo Deleting .class files in the current directory and subdirectories...
del /S /Q "*.class"

示例脚本的运行:

运行独立模式的脚本:

独立模式执行的结果

将生成如下日志文件:

独立模式生成的日志文件

其中 test_时刻.txt 文件是运行时命令行输出结果,其他的为 major 原生的日志。

命令行中运行 Ant 集成模式:

清理、编译和突变

 测试结果:

测试运行的报告

将在目录下面生成 csv 数据文件记录测试运行过程中的变异体覆盖情况的数据:

Ant 模式生成的数据文件

注意:使用 Ant 集成模式需要配置 build.xml 文件,具体设置方式看后文说明。

bin 目录下面的三个文件其实是 .sh 文件。ant 负责生成变异和执行 Ant 分析模式,包括 complie 和 clean & complie 两个模式;major 通过生成变异体和用经典模式运行程序;mmlc 用于编译 mml 配置文件为 bin 文件,设置运行时配置时候必须使用编译后的 bin 文件。

最后,我还根据命令编写了一个 GUI 程序,由于版权审核限制就不发了:

Major GUI Tool 界面展示

其实,major 根本没有图形界面,只能通过命令执行;并且 major 的这些可执行脚本只通过一些支持的 major 命令提供基本功能,以供入门者使用。如果需要更多扩展,则需要引入下面的各项命令和参数。

2  Mutator Plugin 编译器插件

Major 的 mutator 是一个 javac 编译器插件,用于遍历和转换抽象语法树 (AST)。所有 mutants 都嵌入到 AST 中并编译为字节码。可以在运行时启用单个 mutant,而无需重新编译。

2.1 如何使用插件?

mutator 其实就被打包在 lib/major.jar 包里面,通过 javac 运行时参数 -Xplugin:MajorPlugin 即可调用此插件。具体来说,在安装完成后,每次运行 major 都得通过控制台命令行(命令提示符终端)来执行 mutator 插件的配置选项,这些命令我将在下面的 2.2 小节完全介绍。

一般地,用户可以通过在控制台执行 javac 命令来编译 Java 源文件。通过 javac -version 命令查看编译器版本,如果正确显示了版本,且版本号为 1.8 则说明 JDK 环境配置正确;否则,请参照 [      ] 的说明予以修复。

在命令行输入 javac -help 查看基本命令:

javac 的一般选项

然后再输入 javac -X 查看非标准选项的提示:

javac 的非标准扩展选项

1)生成变异体

使用 major 时需要通过 -cp 选项将 major.jar 的本地路径添加到运行时的类搜索路径。如果你想使得此路径长期生效,可以通过添加系统环境变量中的 classpath 类路径来完成。

通过 -Xplugin 选项来启用插件的功能,JVM 会尝试加载 major.jar 中的插件。

例如:

javac -cp ".;D:\软件测试\major\lib\major.jar" -encoding gbk "-Xplugin:MajorPlugin mml:D:\软件测试\major\mml\tutorial.mml.bin" triangle\Triangle.java

该命令设置了类搜索路径为当前目录和 "D:\软件测试\major\lib\major.jar" (本地计算机中 major.jar 的路径) ,然后指定了编码格式为 GBK,以及要编译的源代码类文件 ".\triangle\Triangle.java"。在编译时使用插件 MajorPlugin,并选择为插件传递特定选项 mml,提供了编译后的 .mml.bin 配置文件来设置突变生成器的自定义变异算子。

注意:在使用 -Xplugin 选项时,针对具体插件的选项需要跟随 -Xplugin 选项一起传入,方法是在 -Xplugin 选项前的位置和最后一个插件特定选项书写结束的位置各加上一个引号 ( " ) 。如:

  • "-Xplugin:MajorPlugin mml:D:\软件测试\major\mml\tutorial.mml.bin"
  • "-Xplugin:MajorPlugin mml:D:\软件测试\major\mml\tutorial.mml.bin mutants.log:./log1.log"

不同插件选项之间使用空格间隔。

如果执行成功,你将得到类似这样的提示信息:

Generated 170 mutants (90 ms)

其中,数字 170 表示生成的变异体总数,90 ms 代表执行生成所消耗的总时间。

然后,你将得到下面的三个日志:

其中,mutants.log 就是默认情况下生成的日志文件(当使用 mutants.log:<FILE> 修改路径和文件名时以具体情况为准)。

日志文件大致包含 7~8 个字段(旧版是 7 个字段),包含了生成的变异体信息:

变异日志文件内容

需要注意的是,为了节约磁盘资源,默认情况下 major 是禁用变异体源文件生成的,但可以通过选项开启。并且 major 的分析功能仅通过日志和未修改的源文件完成,不依赖于实体变异体的代码。

而 major.log 则包含了在运行过程中所产生的所有调试信息:

运行时调试信息

suppression.log 是抑制等价变异体的信息记录,一般地排除生成的互斥突变:

等价变异体抑制记录

注意:major 仅通过简单地检查是否冲突来排除等价变异体,但不能自动剔除大多数的等价变异体。

2)编译 MML 文件

类似地通过 java.exe 直接运行 major.jar 包,并通过 -mmlc 命令即可编译 (序列化) 指定路径下的 MML 配置文件为 BIN 二进制文件。

java -jar "./lib/major.jar" --mmlc "./mml/all.mml"

通过 -jar 选项执行 major.jar 文件,并传递 -mmlc 命令和 mml 文件路径来执行 MML2BIN 序列。

成功后将没有报错并在 mml 所在路径生成一个 .bin 格式的二进制序列化文件。

注意:

(1)必须通过编译后的 bin 传递配置给运行时的 mutator 插件;当不传递任何 bin 格式的配置时,程序将按照内置的默认处理模式实现处理。

(2)需要注意的是 Ant 集成中也可以使用 major 的变异生成器和分析器,但参数和配置有所不同。 

2.2 插件配置选项

mutator 插件支持以下配置选项,通过命令行执行 -Xplugin 命令并附加这些可选参数:

  • mml:<FILE>:运行已编译的 mml 文件中指定的突变配置
  • mutants.log:<FILE>:mutants.log 文件的位置(默认位置:./mutants.log)
  • export.mutants:如果设置,Major 将每个生成的突变体导出为源代码文件
  • mutants.directory:<DIR>:突变体源代码文件的导出目录(默认位置:./mutants)
  • export.context:如果设置,Major 将导出每个生成的突变体的上下文信息
  • context.file:<FILE>:突变上下文文件的位置(默认位置:./mutants.context)
  • strict.checks:如果设置,Major 会丢弃在源代码级别创建时不可编译的突变体。
  • enable.decl.refactor:如果设置,Major 会尝试重构大型静态声明,认为这些声明会因包含突变而触发“代码太大”错误
  • decl.refactor.params:MAX_TOTAL_ELEMS,MAX_INDIVIDUAL_ELEMS:用逗号分隔的重构参数列表,用于覆盖默认值
    • MAX_TOTAL_ELEMS:任何给定方法中所有声明的变异常量的阈值总数(默认值为 1000)
    • MAX_INDIVIDUAL_ELEMS:任何给定声明的变异常量的阈值数量(默认值为 50)
  • enable.method.refactor:如果设置,Major 会尝试重构大型方法,因为这些方法可能会因包含突变而触发“代码过大”错误
  • method.refactor.params:MUTANT_THRESHOLD,MUTANTS_PER_METHOD:用逗号分隔的重构参数列表,用于覆盖默认值
    • MUTANT_THRESHOLD:触发​​重构的方法中突变体总数的阈值(默认值为 1000)
    • MUTANTS_PER_METHOD:大型方法重构版本中允许的突变体数量(默认 250)
  • logging:ARG1,ARG2,ARG3...:以分号分隔的参数列表
    • file:FILE:指定保存的运行日志文件的文件名
    • file-level:LEVEL:指定写入文件的最低日志记录级别
    • console-level:LEVEL:指定记录到控制台的最低日志记录级别
    • console-format:LEVEL:指定控制台日志的格式化程序
    • file-format:LEVEL:指定文件记录的格式化程序

日志记录级别:all, finest, finer, fine, warning, severe, 以及 none

日志格式化程序:xml, color, verbose-color, oneline, verbose, simple

Mml 文件定义要启用哪些变异运算符以及要变异哪些程序元素。这允许对变异运算符进行细粒度定义和灵活应用。程序包的 major/mml 中提供了示例 mml 文件。请注意,Major 的变异器会解释预编译的 mml 文件。使用 mml 编译器 mmlc 来验证和编译 mml 文件。

2.3 Major 支持的变异算子

Major 支持以下变异算子组,每组包含多个相关的变异算子:

AOR:算术运算符替换

使用兼容的方法替换二进制算术运算符。

例子:

  • a + b==>a - b
  • a % b==>a * b

COR:条件运算符替换

使用兼容的方法替换条件运算符。major 还会将原子布尔条件替换为 true 和 false (例如,if(flag) 或 if(isSet() )。

例子:

  • a || b==>a && b
  • if(flag)==>if(true)

LOR:逻辑运算符替换

使用兼容的方法替换二元逻辑运算符。

例子:

  • a ^ b==>a | b

ROR:关系运算符替换

使用兼容的方法替换关系运算符。

例子:

  • a == b==>a >= b

SOR:移位运算符替换

使用兼容的方法替换位移位运算符。

例子:

  • a >> b==>a << b

ORU:一元运算符替换

使用兼容的方法替换一元运算符;Major 目前支持基本一元运算符的替换(不支持增量运算符替换),但不支持一元运算符插入和删除(包括增量运算符插入和删除)。

例子:

  • -a==>~a
  • -a==>a
  • ~a==>a
  • ~a==>-a
  • a==>~a
  • a==>-a

LVR:字面量替换

用默认值替换字面量。

  • 数字字面量被替换为正数、负数和零:
    • val = 0 ==> 1 和 -1
    • val < 0 ==> 0 和 -val
    • val > 0 ==> 0 和 -val
  • 指定范围的布尔值被其逻辑补码替换。
  • 指定范围的字符串变量被替换为空字符串。

EVR:表达式值替换

用默认值替换表达式 (在 LVR 未改变的其他语句中)。

例子:

  • return a==>return 0
  • int a = b==>int a = 0

STD:语句删除

删除(省略)以下类型的单个语句:

  • return
  • break
  • continue
  • 方法调用 (Method call)
  • 赋值 (Assignment)
  • 前缀/后缀 自增运算符 (Pre/post increment)
  • 前缀/后缀 自减运算符 (Pre/post decrement)

例子:

  • return a
  • break
  • continue
  • foo(a,b)
  • a = b
  • ++a
  • a--

值得注意的是,Major 支持的变异算子基本上是运算符替换类变异算子,对比其他变异测试工具(如 mujava、PITest),它缺少很多运算符插入和运算符删除类变异算子。

Mujava 的变异算子表

注:

1)Major 变异算子中的一部分需要额外通过 mml 选项启用才能够生成。

2)后文中除特别指出外,"替换" 以及 "替换操作/模式" 一般指的是 major 所启用的突变操作算子(组)。原因是因为 major 目前基本只支持运算符的替换类型的突变操作,"替换" 是根据原文翻译所得,这里保留原义。

2.4 突变体的日志记录

Major 的突变器会生成一个日志文件 mutants.log,该文件提供有关生成的突变体的详细信息,并使用冒号 (':') 作为分隔符。日志文件为每个生成的突变体包含一行;每行包含 8 列,包含以下信息:

  1. 突变体的唯一编号(id)
  2. 所应用变异运算符的名称
  3. 原始运算符号
  4. 替换运算符号
  5. 变异方法的完全限定名称
  6. 原始源文件中的行号
  7. 替换在源文件中的开始字符位置
  8. 应用变换的总结 ( <from> |==> <to>)

下面是一个 ROR 突变的日志条目示例,该突变具有突变 ID 23,在 Triangle 类的一个名为 classify 的方法中产生。具体来说是在第 19 行(相对文本开头 350 字符处)生成的,将 a == c 变异为 a >= c,即符号 == 替换为 >=

23:ROR:==(int,int):>=(int,int):triangle.Triangle@classify(int,int,int):19:350:a == c |==> a >= c

除此之外,Major 在运行过程中还可能会产生以下日志:

  • major.log:程序执行过程中的操作信息日志;
  • suppression.log:抑制等价变异体生成的信息日志;
  • mmlc.log:编译 mml 过程中的异常和错误信息日志;

2.5 生成突变体的源代码

突变体可以导出为单独的源代码文件。默认情况下,此功能处于禁用状态要启用此功能,请设置 export.mutants 选项。如果启用,Major 会复制每个突变体的原始源文件,将突变体注入副本,并导出生成的错误副本(./mutants 默认情况下)。使用 mutants.directory:<DIR> 选项控制输出目录。Major 会在必要时自动创建导出目录和父目录。

请注意,对大型代码库进行变异并将所有变异体导出到单独的源文件会增加变异/编译时间,并且需要比日志文件多得多的磁盘空间。

生成的变异体按照日志中的变异体编号 (ID) 生成到独立的文件夹中,如图所示:

生成的变异体源代码目录

3 构建系统集成

Major 的编译器插件可以单独使用或在构建系统中使用。

Gradle

待办事项

Apache Maven

待办事项

Apache Ant

考虑 Apache Ant 的 build.xml 文件中的以下 compile 目标:

<target name="compile" depends="init" description="Compile"><javac srcdir="src" destdir="bin" debug="yes"></javac>
</target>

添加以下选项以启用 Major 的 mutator 插件:

<property name="major.jar" value="<path to major.jar>"/>
<property name="mml" value="<path to compiled mml file>"/><target name="compile" depends="init" description="Compile"><javac srcdir="src" destdir="bin" debug="yes"><classpath location="${major.jar}"/><compilerarg value="-Xplugin:MajorPlugin mml:${mml}"/></javac>
</target>

关于运行时设置

突变类文件引用 Major 的运行时配置类来访问突变标识符(_M_NO)和/或监视突变覆盖(COVERED)。默认实现的存档文件和源文件在 lib 目录中由 major-rt.jar 提供(或早期版本中由 config 目录提供)。可以对其进行扩展以执行其他分析。注意,在突变体生成期间,配置类不需要在类路径上可用(突变体不会尝试在编译时解析该类,而是根据该类定义的接口为突变体标识符和覆盖调用创建 AST 节点)。

Config 的示例代码内容如下:

// 这个包名是 Major!
package major.mutation;import java.util.*;/*** 一个简单的驱动程序类 ——* 这个类名是 Major 所需要的!*/
public class Config {/** 变异体标识符:** __M_NO <  0 -> 运行原始版本** __M_NO == 0 -> 运行原始版本并收集覆盖信息** __M_NO >  0 -> 执行具有相应 id 的突变体*/public static int __M_NO = -1;// 设置为存储覆盖的突变体的 idpublic static BitSet covSet = new BitSet();// 覆盖方法当且仅当// 突变体标识符设置为 0 时!public static boolean COVERED(int from, int to) {synchronized (covSet) {covSet.set(from, to + 1);}// 总是按照条件突变的要求返回 false!return false;}/** 用于变异分析 back-end 阶段的附加方法*/// 重置覆盖信息public static void reset() {synchronized (covSet) {covSet.clear();}}// 获得所有被覆盖的变异体列表public static List<Integer> getCoverageList() {synchronized (covSet) {List<Integer> covList = new ArrayList<>(covSet.cardinality());for (int i = covSet.nextSetBit(0); i >= 0; i = covSet.nextSetBit(i+1)) {covList.add(i);}return covList;}}
}

关于这里的 config 的使用方法以及如何集成到 Ant 中均可以参考:

https://blog.csdn.net/qq_52883908/article/details/127721055。

4  Major 突变语言 (MML)

Major 的领域特定语言 mml 支持突变生成过程的细粒度配置。mml 文件包含一系列语句,其中每个语句可以属于以下语句之一:

  1. 变量定义,例如作用域或替换;
  2. 突变操作符的替换定义;
  3. 定义 STD 突变操作符的语句类型;
  4. 定义 LVR 变异操作符的文字类型;
  5. 调用预定义的或自定义的突变操作符(组);
  6. 自定义突变操作符的定义;
  7. 单行注释;

前五种语句类型以分号 ( ; ) 结束,操作符组定义由花括号 ( { } ) 封装,单行注释 ( // ) 以行尾结束。

4.1 语句作用域

Mml 为替换操作的定义和操作符调用提供语句作用域,以支持对待测程序中某个包、类或方法的突变。下图显示了语句作用域的定义:

作用域对应于包、类或方法。作用域被定义为一个完全限定名——称为 flatnameflatname 既可以作为带引号的字符串提供,也可以作为变量提供(注:变异体日志中作为不加引号的字符串提供)。注意,语句作用域是可选的。如果没有提供语句作用域,则相应的定义或调用将默认应用于根包(全局)

下图显示了 flatname 的语法规则:

假设 flatname 标识程序元素,则有效标识符(IDENT) 的命名约定遵循 Java 编程语言的命名约定(定义为 "包名.类名[@,或 ::]方法名或有其他效标识符",中间使用句号 "." 作为分隔符)。下面四个例子展示了包、类、一组重载方法和一个特定方法的有效 flatname

  • "java.lang"
  • "java.lang.String"
  • "java.lang.String@<init>"
  • "java.lang.String@substring"
  • "java.lang.String::substring(int,int)"

注意:major 的变异体日志中 flatname 字段默认采取 "@" 间隔类名和方法标识符,而不是使用 "::",尽管两种用法在 mml 中都是支持并且等效的。

flatname 语法还支持内部类和构造函数的标识,这与 Java 的命名约定一致。例如,定义中的后缀部分涉及对内部类、构造函数和静态类初始化项的标识:

  • "foo.Bar$InnerClass"
  • "foo.Bar@<init>"
  • "foo.Bar@<clinit>"

4.2 覆盖和扩展定义

对于给定的范围(作用域),可以启用 ( + ,省略标志时默认为 +) 或者禁用 ( - ) 突变操作符。在下面的例子中,AOR 突变操作符通常对包 org 是启用的,但对包中的类 Foo 是禁用的:

+AOR<"org">;-AOR<"org.Foo">;

注意,启用/禁用操作符的标志(前缀)是可选的。启用操作符的默认标志 ( + ) 提高了可读性,但可以省略。即 +AOR<"org">;AOR <"org">; 是等价语句。

对于替换操作的定义,有两种可能性:可以将单个替换定义添加 ( + ) 到现有列表中(称为变异操作列表的扩展),或者可以覆盖整个替换列表 ( ! ,称为变异操作列表的重写)—— 如果忽略此可选标志,则默认为重写列表。在下面的示例中,包 org 的替换的一般定义被类 Foo 扩展,但被类 Bar 重写(在注释中给出了有效应用于包和类的替换列表)。

BIN(*)<"org">     -> {+,/};    // * -> {+,/}+BIN(*)<"org.Foo"> -> {%};      // * -> {+,/,%}!BIN(*)<"org.Bar"> -> {-};      // * -> {-}

4.3 自定义突变算子组

Mml 允许定义自定义操作符组以最小化代码重复(例如,可以对多个作用域使用相同的替换操作的定义)。自定义操作符组可以包含对 mml 文件有效的任何语句,但不支持调用另一个自定义操作符组(指:自定义操作符组的嵌套和任何外部调用是未定义的行为,不会解释为要合并组,并且会引起编译错误)。自定义操作符组具有唯一标识符,其语句用花括号括起来:

myOp {BIN(*) -> {+,/};AOR;
}

4.4 示例 MML 文件

下面的示例 mml 文件执行三个任务:

  1. 定义 AOR 和 ROR 的特定替换模式。
  2. 使用定义的替换模式调用 AOR 和 ROR。
  3. 不加限制地调用 LVR 操作符。
// 为 AOR 声明自定义替换列表
BIN(*) -> {/,%};
BIN(/) -> {*,%};
BIN(%) -> {*,/};// 为 ROR 声明自定义替换列表
BIN(>)  -> {<=,!=,==};
BIN(==) -> {<,!=,>};// 定义应该由 LVR 操作符改变的字面值类型。
// 文字类型是 {BOOLEAN, NUMBER, STRING} 之一,分别是 布尔值,数值,字符(串)。
LIT(NUMBER);
LIT(BOOLEAN);// 启用和调用上面定义的自定义突变操作符
AOR;
ROR;
LVR;

下一个示例在第 9 行和第 15-21 行使用了作用域特性,并在第 12 行定义了一个变量,以避免在随后的作用域声明中出现代码重复。如果只有某个包、类或方法需要改变,这两个特性都是有用的。

1.     // 设置根节点的替换模式(全局)
2.     BIN(>=)->{TRUE,>,==};
3.     BIN(<=)->{TRUE,<,==};
4.     BIN(!=)->{TRUE,<,> };
5.     LIT(NUMBER);
6.     LVR;
7.     
8.     // 对包 org 应用的特定的替换模式
9.     ROR<"org">;
10.    
11.    // 对类 Foo 的变量的定义和赋值
12.    foo="org.x.y.z.Foo";
13.    
14.    // 限制替换列表的作用域(作用范围),这里相当于是只针对 && 和 || 条件运算符实施指定的突变
15.    BIN(&&)<foo>->{LHS,RHS,==,FALSE};
16.    BIN(||)<foo>->{LHS,RHS,!=,TRUE };
17.    
18.    // 实施变异操作符的作用域,对不同作用域应用不同的操作符,或者开关某些操作符
19.    -LVR<foo>;
20.    ROR<foo>;
21.    COR<foo>;

下一个示例演示了自定义操作符特性,该特性主要功能是同一组操作(定义或调用)可以应用于多个作用域,这是非常有用的。

myOp{// 操作符组的定义BIN(>=)->{TRUE,>,==};BIN(<=)->{TRUE,<,==};BIN(!=)->{TRUE,<,> };BIN(&&)->{LHS,RHS,==,FALSE};BIN(||)->{LHS,RHS,!=,TRUE };// 在该组中启用指定的突变操作符ROR;COR;
}// 对定义的操作符组的调用
myOp<"org">;
myOp<"de">;
myOp<"com">;

下一个示例来自对 example 目录下面的 Trangle 程序的配置,配置文件位于 mml/tutorial.mml,此示例演示为目标方法调用已定义的操作符组,如针对 triangle 包中的 Triangle 类的 classify 方法对进行调用已定义的替换模式。

targetOp{// 为 AOR 声明自定义替换模式BIN(>)->{>=,!=,FALSE};BIN(<)->{<=,!=,FALSE};BIN(>=)->{>,==,TRUE};BIN(<=)->{<,==,TRUE};BIN(==)->{<=,>=,FALSE,LHS,RHS};BIN(!=)->{<,>,TRUE,LHS,RHS};// 为 COR 声明自定义替换模式BIN(&&)->{==,LHS,RHS,FALSE};BIN(||)->{!=,LHS,RHS,TRUE};// 定义STD应该删除的语句类型DEL(RETURN);// 启用自定义的 STD、COR 和 ROR 变异操作符STD;COR;ROR;
}
// 为目标方法调用已定义的操作符组
targetOp<"triangle.Triangle::classify(int,int,int)">;

最后一个示例演示了启用所有变异类型的配置,此设置所产生的突变个数一般比默认模式要多,但包含的变异有所区别:

// 生成所有变异体的简单 mml 文件// 定义一些操作符列表
list_aor={+,-,*,/,%};
list_lor={&,|,^};
list_sor={<<,>>,>>>};
list_oru={+,-,~};// 使用足够的 AOR 替换操作列表
BIN(+)->list_aor;
BIN(-)->list_aor;
BIN(*)->list_aor;
BIN(/)->list_aor;
BIN(%)->list_aor;// 使用足够的 SOR 替换操作列表
BIN(>>)->list_sor;
BIN(<<)->list_sor;
BIN(>>>)->list_sor;// 使用足够的 LOR 替换操作列表
BIN(&)->list_lor;
BIN(|)->list_lor;
BIN(^)->list_lor;// 使用足够的 ORU 替换操作列表
UNR(+)->list_oru;
UNR(-)->list_oru;
UNR(~)->list_oru;// 使用足够的 ROR 替换操作列表
BIN(>)->{>=,!=,FALSE};
BIN(<)->{<=,!=,FALSE};
BIN(>=)->{>,==,TRUE};
BIN(<=)->{<,==,TRUE};
BIN(==)->{<=,>=,FALSE,LHS,RHS};
BIN(!=)->{<,>,TRUE,LHS,RHS};// 使用足够的 COR 替换操作列表
BIN(&&)->{==,LHS,RHS,FALSE};
BIN(||)->{!=,LHS,RHS,TRUE};// 定义应该由 LVR 操作符改变的字面值类型。
// 文字类型是 {BOOLEAN, NUMBER, STRING} 之一,分别是 布尔值,数值,字符(串)。
LIT(NUMBER);
LIT(BOOLEAN);
LIT(STRING);// 尝试删除所有支持的语句类型
DEL(CALL);
DEL(INC);
DEL(DEC);
DEL(ASSIGN);
DEL(CONT);
DEL(BREAK);
DEL(RETURN);// 启用所有操作符
AOR;
LOR;
SOR;
COR;
ROR;
LVR;
EVR;
ORU;
STD;

5  Major 的程序分析器

Major 提供了一个默认分析器,它扩展了 Apache Ant 的 junit 任务。该分析器支持 JUnit 3 和 4 测试。请注意,该分析器目前在执行 JUnit 测试时不支持对 JVM 进行分叉,这意味着必须将 fork 选项设置为 false

5.1 程序分析器配置选项

该表总结了 Major 的默认分析器的配置选项。(请参考 Junit 任务的官方文档了解所有其他选项。)

选项

描述

可选数值

默认值

mutationAnalysis

启用突变分析。如果设置为false,则忽略所有选项

[true|false]

false

analysisType

运行预处理和突变分析,或只运行两者中的一个

 [preproc_mutation|

preproc|

mutation]

preproc_mutation

serializedMapsFile

存储序列化预处理结果时使用的文件名

<String>

preprocessing.ser

mutantsLogFile

使用自定义变异算子处理生成的 mutants.log 日志文件的保存路径

<String>

mutants.log

testOrder

测试变异体集的先后顺序和程度

[original|random|

sort_classes|

sort_methods]

original

debug

打开调试输出开关

[true|false]

false

timeoutFactor

在(原)测试运行时基础上的乘法因子,用于计算测试超时

<int>

8

timeoutOffset

以毫秒为单位添加到计算的测试超时的偏移量

<int>

0

excludeFailingTests

排除所有失败的测试(haltonfailure 必须为 false)

[true|false]

true

excludeMutantsFile

指定一个文本文件中所列出的全部突变 (每行设置 1 个突变 id,这些 id 对应的变异体将被过滤)

 <String>

null

includeMutantsFile

仅包含此文本文件中列出的突变体(每行 1 个突变体 id)

<String>

null

excludeTestsFile

排除此文本文件中列出的所有测试 (每行 1 个测试 id )。

<String>

null

includeTestsFile

仅包括此文本文件中列出的测试 (每行 1 个测试 id)

<String>

null

summaryFile

将结果摘要导出到此文件路径 (csv)

<String>

summary.csv

executionDetailsFile

将详细的执行信息导出到此文件路径 (csv)。

<String>

null

mutantDetailsFile

将每个突变体的分类详细信息导出到此文件路径 (csv)

<String>

null

covMapFile

突变覆盖矩阵的文件名 (csv)

<String>

covMap.csv

testMapFile

测试 id 到测试名映射的文件名(csv)

<String>

testMap.csv

exportKillMap

导出突变查杀矩阵(对每个被覆盖的突变体执行每个测试!

[true|false]

false

killMapFile

保存突变查杀矩阵的文件名 (csv)

<String>

killMap.csv

备注:

  • analysisType 参数:默认允许运行分析的两个步骤(预处理和突变),或者只运行两个步骤中的一个。对于应该并行化突变分析本身的大型代码库,只运行预处理一次并缓存结果(serializedMapsFile)将大大加快分析时间。如果设置为 mutation,则必须提供序列化映射。
  • testOrder 参数:
    • sort_methods:以测试方法细粒度报告结果。
    • sort_classes:以测试类细粒度报告结果。
    • originalrandom:报告结果的细粒度由 build.xml 文件定义。

5.2 设置分析目标

大多数 Apache Ant 构建文件提供了一个 test 目标,该目标执行相应的单元测试。即使不存在这样的目标,也可以很容易地将其设置为执行一组给定的单元测试。下面的代码片段显示了一个示例 test 目标(请参阅 junit.html 了解该任务的详细描述):

<target name="test" description="Run all unit test cases"><junit printsummary="true"showoutput="true"haltonfailure="true"><formatter type="plain" usefile="true"/><classpath path="bin"/><batchtest fork="no"><fileset dir="test"><include name="**/*Test*.java"/></fileset></batchtest></junit>
</target>

要启用突变分析,请将 mutationAnalysis 选项设置为 true。为了清晰起见,最好复制并调整现有的 test 目标。

<target name="mutation.test" description="Run mutation analysis"><junit printsummary="false"showoutput="false"haltonfailure="true"mutationAnalysis="true"summaryFile="summary.csv"mutantDetailsFile="details.csv"><classpath path="bin"/><batchtest fork="no"><fileset dir="test"><include name="**/*Test*.java"/></fileset>                                             </batchtest></junit>
</target>

5.3 性能优化

变异分析重复地在变异的代码上执行测试。出于性能考虑,在设置突变分析目标时需要考虑以下几点:

  • 关闭日志输出(选项 showsummaryshowoutput 等);
  • 不要使用结果格式化器(嵌套任务格式化器(formatter),尤其是 usefile 选项);

由于频繁的类加载和线程执行,建议使用以下JVM选项,特别是对于大型项目:

  • -XX:ReservedCodeCacheSize=256M
  • -XX:MaxPermSize=1G

6 兼容性问题和注意事项

Major 已知的兼容性问题并不多。典型的比如 Major 在处理一些代码时可能存在错误,比如 major 使用不够精确的浮点数处理方法,导致浮点数的第 16 个小数位会出现舍入精度损失的问题,使得生成的代码和原本的代码不同。

Major 会对源代码进行自动代码格式化,从日志就可以看出,但是不能够通过原生接口生成格式化后的源代码,这导致我们在代码格式化方面面临一些挑战。我通过一些技术实现了代码格式化并确保了兼容处理 Major 的舍入精度损失问题,但此工具系统受到保护原因,所以暂不可透露实现细节。

独立 Java 代码格式化工具

Major 支持的变异算子较少,一般只支持运算符替换和删除算子,不支持运算符插入算子。

Major 仅支持 JAVA 8 和语言级别不高于 8 的代码分析,且缺乏一些可视化和便捷操作的功能。 

Major 支持解析带有依赖的源文件,但一次只能处理和分析单个类,不能分析整个项目。

所以理论上只能作为研究学习使用,而不能用于实际的软件测试。


本文出处链接:https://blog.csdn.net/qq_59075481/article/details/141371759。

本文发布于:2024.08.21;更新于:2024.08.21。

这篇关于【软件测试·研究向】Major 变异测试工具使用教程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094103

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti