背景

项目使用的是small插件。一个app分为main和多个插件,为了统计插件的代码覆盖率。

1 修改插件

修改插件build.gradle

    buildTypes {
release {
...
}
debug{
minifyEnabled false
testCoverageEnabled = true //打开debug版本的代码覆盖率开关
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

  

因为工程原因插件生成的classes文件在下次生成的时候会变动。因此要讲classes文件拷贝到其他位置暂存。

  tasks.whenTaskAdded { task ->
if (task.name == 'assembleRelease' || task.name == 'assembleDebug') {
task.doLast {
println "copy classes to jacoco"
def applicationId = android.defaultConfig.applicationId
def artifactName = applicationId.substring(applicationId.lastIndexOf(".") + 1, applicationId.length())
project.copy {
from "build/intermediates/classes/debug"
into "${rootDir}/../jacoco/${artifactName}/classes/debug"
}
}
}
}

2 修改main

main作为插件的容器,我们的测试代码也在这里。所有的测试用例继承于BaseTest.

给BaseTest的finishZ增加覆盖率保存功能。

        @Override
protected void finished(Description description) {
super.finished(description);
.....
generateEcFile(true);
} /**
* 生成ec文件
*
* @param isNew 是否重新创建ec文件
*/
public static void generateEcFile(boolean isNew) { SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmss");
final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage/" + String.format("coverage_%s.ec", df.format(new Date()));
LogUtils.i("生成覆盖率文件: " + DEFAULT_COVERAGE_FILE_PATH);
OutputStream out = null;
File mCoverageFilePath = new File(DEFAULT_COVERAGE_FILE_PATH);
try {
if (isNew && mCoverageFilePath.exists()) {
LogUtils.i("JacocoUtils_generateEcFile: 清除旧的ec文件");
mCoverageFilePath.delete();
}
if (!mCoverageFilePath.exists()) {
File d = new File("/sdcard/coverage/");
d.mkdirs();
mCoverageFilePath.createNewFile();
}
out = new FileOutputStream(mCoverageFilePath.getPath(), true); Object agent = Class.forName("org.jacoco.agent.rt.RT")
.getMethod("getAgent")
.invoke(null); out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class)
.invoke(agent, false)); } catch (Exception e) {
LogUtils.i("generateEcFile: " + e.getMessage());
} finally {
if (out == null)
return;
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

编译main,生成测试apk和debug版本的apk.

3.测试

使用测试系统进行测试

4.收集所有测试被测收集的测试数据。

    import subprocess
import os,sys o = subprocess.check_output(['adb','devices'])
ls = o.split('\n')
for line in ls:
if line.endswith('device'):
id_ = line.split('\t')[0]
os.makedirs('./report/'+id_)
c1 = ['adb','-s',id_,'pull','/sdcard/coverage','./report/'+id_]
print ' '.join( c1 )
o1 = subprocess.check_output(c1)
print o1

5.建立新的gradle工程,生成报告。

新建文件夹jacocReport。

进入文件夹运行gradle init。

修改build.gradle

apply plugin: 'jacoco'

buildscript {
repositories {
mavenLocal()
mavenCenter()
maven { url 'plugins' }
}
dependencies {
classpath 'net.researchgate:gradle-release:2.4.1'
classpath 'com.android.tools.build:gradle:2.2.3'
}
} allprojects {
repositories {
mavenLocal()
mavenCenter()
flatDir {
dirs 'libs'
}
}
} //首先先删除旧的merge结果文件
task removeOldMergeEc(type: Delete) {
delete "${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec"
} task mergeReport(type:JacocoMerge,dependsOn:removeOldMergeEc){
group = "Reporting"
description = "merge jacoco report."
destinationFile= file("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec")
//这里的ec_dir是存储ec文件的文件夹
FileTree tree = fileTree("$projectDir/../jacoco/report") {
include '**/*.ec'
}
def cnt =0
tree.each{
cnt++
}
println "ec file conut:"+cnt
// tree.each{f->println f}
executionData = tree //executionData(files)
} ["plugin1",'plugin2'].each { it1->
task "Report$it1"(type: JacocoReport,dependsOn: [mergeReport]) {it ->
println "I'm task $it"
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
def pluginName1 = "$it1"
println pluginName1 reports {
xml.enabled = true
html.enabled = true
} classDirectories = fileTree(
dir: "${rootDir}/../jacoco/${pluginName1}/classes/debug",
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
])
def coverageSourceDirs =[
//不需要代码路径
]
sourceDirectories = files(coverageSourceDirs)
File f = new File("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec")
println ""+f.exists()
println "" + f.length()+ " bytes"
executionData = files("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec") doFirst {
//修改claess 文件
println "${rootDir}/../jacoco/${pluginName1}/classes/debug"
new File("${rootDir}/../jacoco/${pluginName1}/classes/debug").eachFileRecurse { file ->
if (file.name.contains('$$')) {
println "modify " + file.name
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
} task allReport(dependsOn: [Reportplugin1,Reportplugin2]){
doLast{
println "done!"
}
}

最后上目录

.
├── main          #主apk代码的目录
├── jacoco #数据目录-存放ec文件,classes文件
├── jacocoReport #生成报告的工程目录 build.gradle在此
├── plugin1       #插件1
├── plugin2       #插件2

  

android 代码覆盖率的更多相关文章

  1. Android自动化测试探索(五)代码覆盖率统计

    Android 代码覆盖率统计 本周开始准备统计Android自动化用例的代码覆盖率,将最终使用的方法记录下来. 覆盖率监测的原理 覆盖率监测的原理跟iOS上的原理差不多,大致的思路参考下吧, iOS ...

  2. Jenkins构建Android项目持续集成之单元测试及代码覆盖率

    单元测试 在软件开发中一直在推崇TDD(测试驱动开发),但是一直不能被有效的执行或者并不是真正的测试驱动开发(先开发后写单元测试),因为我们懒!而Android开发又是大多应用层面的开发,很多都是和视 ...

  3. Android自动化测试探索(七)代码覆盖率统计

    之前在 https://www.cnblogs.com/zhouxihi/p/11453738.html 这篇写了一种统计Android覆盖率的方式 但是对于一些比较复杂或者代码结构不够规范的项目,有 ...

  4. 安卓代码覆盖率:android studio+ gradle+jacoco

    在工程的oncreate()方法添加如下代码,目的是创建ec文件. String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec& ...

  5. 测试代码覆盖率工具学习(Android Emma)

    博客分类: 工具分享 eclipseeclemmaemmatestng       关于eclemma的历史和怎么安装,请参考http://www.ibm.com/developerworks/cn/ ...

  6. Android源码目录结构详解(转载)

    转自:http://blog.csdn.net/xiangjai/article/details/9012387 在学习Android的过程中,学习写应用还好,一开始不用管太多代码,直接调用函数就可以 ...

  7. android源码的目录结构

    android源码的目录结构 [以下网络摘抄] |-- Makefile ! l/ a5 n% S% @- `0 d# z# a$ P4 V3 o7 R|-- bionic              ...

  8. Android 4.0 源代码结构

    Android源码的第一级目录结构   Android/abi (abi相关代码.ABI:application binary interface,应用程序二进制接口)   Android/bioni ...

  9. Android源码-学习随笔

    在线代码网站1:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ 书籍: ...

随机推荐

  1. Qt编程的一些技巧

    1.Qt程序在运行过程中,调用函数(如lcdNumber->display(num))显示数据到界面上时,并不会马上刷新屏幕显示,而是要等主程序运行到函数a.exec()时,才刷新屏幕,如下 因 ...

  2. BZOJ3236 [Ahoi2013]作业 【莫队 + 树状数组】

    题目链接 BZOJ3236 题解 没想到这题真的是如此暴力 #include<algorithm> #include<iostream> #include<cstring ...

  3. 洛谷 P3573 [POI2014]RAJ-Rally 解题报告

    P3573 [POI2014]RAJ-Rally 题意: 给定一个\(N\)个点\(M\)条边的有向无环图,每条边长度都是\(1\). 请找到一个点,使得删掉这个点后剩余的图中的最长路径最短. 输入输 ...

  4. webpack的css,less,sass中使用绝对路径

    用法: 使用~表示绝对路径,如下: @import "~otherfile.scss" .yourClass { background: url('~img/wallpaper.p ...

  5. NOIP2017赛前模拟11月6日—7日总结

    收获颇丰的两天··· 题目1:序列操作 给定n个非负整数,进行m次操作,每次操作给出c,要求找出c个正整数数并将它们减去1,问最多能进行多少操作?n,m<=1000000 首先暴力贪心肯定是每次 ...

  6. Linux操作常识

    1.分区 linux如果手动选择分区,必须的两个分区是根分区和swap分区,swap分区是与内存的交换分区,通常设置大小为内存的两倍(如果内存够大也可以不用设置) 2.关机重启      命令:shu ...

  7. 解决:laravel出现Please provide a valid cache path.

    SVN检出版本库上的项目后,访问首页出现如下提示: Please provide a valid cache path. 解决方法如下: 1.确保storage目录下有如app,framework,v ...

  8. 计蒜客 28201.Choosing Ice Cream-gcd (BAPC 2014 Preliminary ACM-ICPC Asia Training League 暑假第一阶段第一场 A)

    开始水一波博客 题目链接: A. Choosing Ice Cream 传送门 题意就是n个冰淇淋,骰子有k个面,问你是否能在公平的概率下转几次骰子能确定买哪个冰淇淋. 举个例子,假设我只有一个冰淇淋 ...

  9. Codeforces 798 B. Mike and strings-String的find()函数

    好久好久好久之前的一个题,今天翻cf,发现这个题没过,补一下. B. Mike and strings time limit per test 2 seconds memory limit per t ...

  10. Hadoop打包成jar包在集群上运行时出现的各种问题以及解决方案

    之前将eclipse下编好的mapreduce代码放到集群上面跑,发现速度很慢,namenode节点的cpu和内存使用率很低,datanode节点基本上处于没有运行的状态,然后通过查看hadoop-e ...