官方jacoco里的运行方法只提供了java -jar的方式去生成报告,合成exec,本次改造以能生成差异代码的jacoco二开版本为基础,开放了API的方式进行调用,其中需要用到jacoco二开项目里编译出来的三个jar:
jacococli.jar
org.jacoco.core-0.8.7-SNAPSHOT.jar
org.jacoco.report-0.8.7-SNAPSHOT.jar
在idea里project structure引入上述三个包,也可以通过mvn本地安装的方式安装这三个包,然后在pom里引入本地依赖,通过采取maven引入的方式便于后续的打包,这样就可以把本地依赖包一起打到包里,注意这里需要开启mvn配置里的本地仓库:将下面语句填入maven setting文件里
<localRepository>E:\repository</localRepository>
再在idea中的terminal执行如下命令进行本地安装jar:
mvn install:install-file -Dfile=lib/org.jacoco.report-0.8.7-SNAPSHOT.jar -DgroupId=local.jacoco -DartifactId=jacoco-report -Dversion=3.4.10 -Dpackaging=jar -Dmaven.repo.local=E:\repository
Dfile指的是jar的路径,DgroupId,DartifactId,Dversion这三个变量用来后续的dependency的引入,Dmaven.repo.local指定了安装的repository地址,不指定的话就会默认安装到C盘中
<dependency>
<groupId>local.jacoco</groupId>
<artifactId>jacoco-report</artifactId>
<version>3.4.10</version>
</dependency>
<dependency>
<groupId>local.jacoco</groupId>
<artifactId>jacoco-core</artifactId>
<version>3.4.10</version>
</dependency>
<dependency>
<groupId>local.jacoco</groupId>
<artifactId>diff-jacococli</artifactId>
<version>3.4.10</version>
</dependency>
准备工作已就绪,下面直接来看代码:
package com.hello.hellowold;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.internal.diff.JsonReadUtil;
import org.jacoco.core.tools.ExecFileLoader;
import org.jacoco.report.DirectorySourceFileLocator;
import org.jacoco.report.FileMultiReportOutput;
import org.jacoco.report.IReportVisitor;
import org.jacoco.report.html.HTMLFormatter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* @author Cheertan
* @date 2022-08-24
*/
public class JacocoUtils {
public static void main(String[] args) throws IOException {
// List<String> files = new ArrayList<>();
// String targetMergeFile = "F:\\jacoco\\service\\merge.exec";
// files.add("F:\\jacoco\\service\\jacoco_tcp_02.exec");
// files.add("F:\\jacoco\\service\\jacoco_tcp_03.exec");
// mergeExec(files, targetMergeFile);
// String differFiles = "F:\\jacoco\\service\\differ.json";
// String classesDirectory = "F:\\workspace\\hellowold\\target\\classes";
// String sourceDirectory = "F:\\workspace\\hellowold\\src\\main\\java";
// String executionDataFile = "F:\\jacoco\\service\\merge.exec";
// String reportDirectory = "F:\\jacoco\\service\\report-merge";
//
// generateReport(classesDirectory, sourceDirectory, executionDataFile, reportDirectory);
// generateReport(classesDirectory, sourceDirectory, executionDataFile, reportDirectory, differFiles);
}
public static ExecutionDataStore load(File file) throws IOException {
ExecFileLoader loader = new ExecFileLoader();
loader.load(file);
return loader.getExecutionDataStore();
}
public static void generateReport(String classesDirectory,
String sourceDirectory, String executionDataFile, String reportDirectory,
String differFile) throws IOException {
File classesDire = new File(classesDirectory);
File soureDire = new File(sourceDirectory);
File executionFile = new File(executionDataFile);
File reportDire = new File(reportDirectory);
ExecFileLoader execFileLoader = loadExecutionData(executionFile);
IBundleCoverage bundleCoverage;
// 对比exec和class类,生成覆盖数据
if (differFile != null) {
bundleCoverage = analyzeStructure(execFileLoader, classesDire,
differFile);
} else {
bundleCoverage = analyzeStructure(execFileLoader, classesDire);
}
// 生成报告
createReport(bundleCoverage, execFileLoader, reportDire,
soureDire);
}
public static void generateReport(String classesDirectory, String sourceDirectory, String executionDataFile, String reportDirectory)
throws IOException {
generateReport(classesDirectory, sourceDirectory, executionDataFile,
reportDirectory, null);
}
public static void mergeExec(List<String> execFiles, String destFile)
throws IOException {
final ExecFileLoader loader = new ExecFileLoader();
List<File> files = new ArrayList<>();
for (String ef : execFiles) {
files.add(new File(ef));
}
for (final File file : files) {
// 开始加载exec文件,遍历多个文件加载
loader.load(file);
}
loader.save(new File(destFile), true);
}
/**
* 第一步解析exec文件
*
* @throws IOException
*/
public static ExecFileLoader loadExecutionData(File executionDataFile)
throws IOException {
// 解析exec
ExecFileLoader execFileLoader = new ExecFileLoader();
execFileLoader.load(executionDataFile);
return execFileLoader;
}
public static IBundleCoverage analyzeStructure(
ExecFileLoader execFileLoader, File classesDirectory)
throws IOException {
return analyzeStructure(execFileLoader, classesDirectory, null);
}
public static IBundleCoverage analyzeStructure(
ExecFileLoader execFileLoader, File classesDirectory,
String diffCodeFiles) throws IOException {
CoverageBuilder builder;
// 如果有增量参数将其设置进去
if (null != diffCodeFiles) {
builder = new CoverageBuilder(
JsonReadUtil.readJsonToString(diffCodeFiles));
} else {
builder = new CoverageBuilder();
}
final Analyzer analyzer = new Analyzer(
execFileLoader.getExecutionDataStore(), builder);
analyzer.analyzeAll(classesDirectory);
return builder.getBundle("报告率");
}
public static void createReport(final IBundleCoverage bundleCoverage,
ExecFileLoader execFileLoader, File reportDirectory,
File sourceDirectory) throws IOException {
// Create a concrete report visitor based on some supplied
// configuration. In this case we use the defaults
final HTMLFormatter htmlFormatter = new HTMLFormatter();
System.out.println("***");
final IReportVisitor visitor = htmlFormatter
.createVisitor(new FileMultiReportOutput(reportDirectory));
// Initialize the report with all of the execution and session
// information. At this point the report doesn't know about the
// structure of the report being created
visitor.visitInfo(execFileLoader.getSessionInfoStore().getInfos(),
execFileLoader.getExecutionDataStore().getContents());
// Populate the report structure with the bundle coverage information.
// Call visitGroup if you need groups in your report.
visitor.visitBundle(bundleCoverage,
new DirectorySourceFileLocator(sourceDirectory, "utf-8", 4));
visitor.visitEnd();
}
}