第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,
var nocovered: Set
)
class JacocoParserOperation {
val Covered = setOf(ICounter.FULLY_COVERED, ICounter.PARTLY_COVERED)
val NoCovered = setOf(ICounter.NOT_COVERED)
fun readJacocoECFileContent(ecFileNames:Array,classesDir:File,sourceDir: Array,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()) { s, e -> s + e}.groupBy { it.name }
val bundle = coverageBuild.getBundle("")
val coverageMap = mutableMapOf()
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 } ?: continue
var 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]}