最近看Jacoco源码并进行修改来实现增量覆盖率的功能,Jacoco的插桩使用分为on-the-fly和offline两种方式:
- on-the-fly:通过在程序运行时指定javaagent,运行产出只包含覆盖率的结果Jacoco.exec,对原始jar不做修改。
- offline:通过修改字节码生成插桩后的字节码,然后执行插桩后的字节码运行,同样产出覆盖率文件Jacoco.exec。
通过maven插件均可以应用on-the-fly和offline两种模式。
offline模式
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8-SNAPSHOT</version>
<executions>
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
</execution>
<execution>
<id>default-restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.2</version>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
首先看下maven的生命周期:
<phases>
<phase>validate</phase>
<phase>initialize</phase>
<phase>generate-sources</phase>
<phase>process-sources</phase>
<phase>generate-resources</phase>
<phase>process-resources</phase>
<phase>compile</phase>
<phase>process-classes</phase> --------------- instrument
<phase>generate-test-sources</phase>
<phase>process-test-sources</phase>
<phase>generate-test-resources</phase>
<phase>process-test-resources</phase>
<phase>test-compile</phase>
<phase>process-test-classes</phase>
<phase>test</phase>
<phase>prepare-package</phase> --------------- restore-instrumented-classes
<phase>package</phase>
<phase>pre-integration-test</phase>
<phase>integration-test</phase>
<phase>post-integration-test</phase>
<phase>verify</phase> ------------------------ report / check
<phase>install</phase>
<phase>deploy</phase>
</phases>
而上述pom配置明确指定了4项配置:
compile:编译成class文件,相当于执行以下命令:
mvn clean compile
instrument:对class文件进行,相当于执行以下命令:
mvn org.jacoco:jacoco-maven-plugin:0.8.8-SNAPSHOT:instrument
会对原class进行插桩,生存插桩后的class文件,例如:
protected boolean canEqual(Object other) {
boolean[] var2 = $jacocoInit();
boolean var10000 = other instanceof BasicStatic;
var2[13] = true;
return var10000;
}
public int hashCode() {
boolean[] var1 = $jacocoInit();
int PRIME = true;
int result = 1;
Object $statics = this.getStatics();
int var10000 = result * 59;
int var10001;
if ($statics == null) {
var10001 = 43;
var1[14] = true;
} else {
var10001 = $statics.hashCode();
var1[15] = true;
}
int result = var10000 + var10001;
var1[16] = true;
return result;
}
通过插桩后的代码很清楚的验证了jacoco的插桩规则。
- 同一个类用一个插桩数组表示;
- 在逻辑分支后插桩;
test:执行测试,生成覆盖率文件Jacoco.exec
restore-instrumented-classes:删除插桩标记,恢复class文件,相当于执行:
mvn org.jacoco:jacoco-maven-plugin:0.8.8-SNAPSHOT:restore-instrumented-classes
report:生成测试报告,相当于执行:
mvn org.jacoco:jacoco-maven-plugin:0.8.8-SNAPSHOT:report
在target目录下生成site文件夹,内含报告文件
check:按配置的覆盖率阈值来校验结果
on-the-fly模式
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8-SNAPSHOT</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
prepare-agent:准备mvn编译参数,添加jacocoAgent,执行完输出以下日志:
argLine set to -javaagent:/Users/test/.m2/repository/org/jacoco/org.jacoco.agent/0.8.8-SNAPSHOT/org.jacoco.agent-0.8.8-SNAPSHOT-runtime.jar=destfile=/Users/test/project/hive-udf/target/jacoco.exec
关于添加此参数后的完整命令需要了解mvn编译具体执行。
添加此参数后执行compile,不会生产带有插桩信息的类;执行test,会产生覆盖率数据jacoco.exec。
然后执行report,生产覆盖率报告信息。