第6.2节 Android Agent开发<三>
6.2.2 覆盖率文件解析jacoco-parser
由于移动端的特殊性,jacoco插件无法直接在java项目中直接引用,然后解析覆盖率文件。于是就开发了一下android端的工具 ,打包成jar包来解析覆盖率文件。
1,项目整体情况

早期这个工具只有EC文件解析功能,后来做覆盖率合并的时候,我又添加上了覆盖率数据的去除和添加功能,形成jacoco-parser2.0.jar,针对这个工具的功能,会有另外的文章介绍,此处只介绍覆盖率EC文件的解析。
2,覆盖率文件解析类
通过在项目中添加jacoco插件,然后引用插件的类处理EC文件,根据探针解析出对应的覆盖率数据在文件中的行,再反查对应的函数,输出为json文件。
i
import com.google.gson.Gson
import org.jacoco.core.analysis.Analyzer
import org.jacoco.core.analysis.CoverageBuilder
import org.jacoco.core.analysis.ICounter
import org.jacoco.core.tools.ExecFileLoader
import java.io.File/******************************************************************************** Read jacoco exec file, java class file, and source file to produce coverage lines.* @author songxianfeng@kuaishou.com*/
data class CoverageInfo(val filename: String,var covered: Set<Int>,var nocovered: Set<Int>
)class JacocoParserOperation {val Covered = setOf(ICounter.FULLY_COVERED, ICounter.PARTLY_COVERED)val NoCovered = setOf(ICounter.NOT_COVERED)fun readJacocoECFileContent(ecFileNames:Array<File>,classesDir:File,sourceDir: Array<File>,output: File){val ecFileLoader = ExecFileLoader()for (file in ecFileNames) {ecFileLoader.load(file)}val coverageBuild = CoverageBuilder()val analyzer = Analyzer(ecFileLoader.executionDataStore, coverageBuild)analyzer.analyzeAll(classesDir)val sourceFileGroup = sourceDir.map {it.walk().filter { file -> file.isFile }.toSet()}.fold(setOf<File>()) { s, e -> s + e}.groupBy { it.name }val bundle = coverageBuild.getBundle("")val coverageMap = mutableMapOf<String, CoverageInfo>()bundle.packages.map { pkg ->//println("${pkg.name} - ${pkg.sourceFiles} - ${pkg.lineCounter}")for (c in pkg.classes) {val sourceFile =sourceFileGroup[c.sourceFileName]?.findLast { f -> f.path.indexOf(c.packageName) > 0 } ?: continuevar coverageInfo = coverageMap[sourceFile.toString()]if (coverageInfo == null) {coverageInfo = CoverageInfo(sourceFile.toString(), setOf(), setOf())}
// println("class: ${c.name}, source: ${c.sourceFileName}")for (i in c.firstLine..c.lastLine) {if (c.getLine(i).status in Covered) {coverageInfo.covered += i} else if (c.getLine(i).status in NoCovered) {coverageInfo.nocovered += i}}coverageMap[sourceFile.toString()] = coverageInfo}}// coverageMap.forEach { (k, v) -> println("$k -> $v") }val gson = Gson()val content = gson.toJson(coverageMap)output.writeText(content)}
}
3,工具使用方法
(1)先对项目进行打包
在项目根目录下执行以下命令:
./grwdlew jar
(2)调用工具解析文件
java -jar ./build/libs/jacoco-parser-2.0-SNAPSHOT.jar
-e /Users/***/first_merged.ec
-c /Users/****/build_classes_8900
-s /Users/****/packages
-o /*****/ecparse.json
- -e 指定EC文件路径
- -c 项目源码文件对应的类文件
- -s 项目的源码文件路径
- -o 解析后的输出结果,格式为{"文件路径":[函数列表],"文件路径1":[函数列表1]}