背景

项目使用的是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. hdu2010(dfs+剪枝)

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  2. ocrosoft Contest1316 - 信奥编程之路~~~~~第三关 问题 P: 【数组】1234方阵(phalanx)

    http://acm.ocrosoft.com/problem.php?cid=1316&pid=15 题目描述 编程打印如下规律的n*n方阵.输入n,按规律输出方阵. 方阵规律如下图:使左对 ...

  3. nagios客户端安装

    在被监控服务器(Linux/unix)上安装Nagios-plugins和nrpe 1.添加用户   1 2 ; html-script: false ]/usr/sbin/useradd -m na ...

  4. 【bzoj1875】[SDOI2009]HH去散步 矩阵乘法

    题目描述 一张N个点M条边的无向图,从A走到B,要求:每一次不能立刻沿着上一次的边的反方向返回.求方案数. 输入 第一行:五个整数N,M,t,A,B. N表示学校里的路口的个数 M表示学校里的路的条数 ...

  5. Reversion windows 2008 R2 STD to Datacenter

    1.To determine the installed edition, run: DISM /online /Get-CurrentEdition 2.To check the possible ...

  6. Linux 软连接 & 硬链接

    1.Linux链接概念Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link).默认情况下,ln命令产生硬链接. [硬连接]硬连接指通过索引节点 ...

  7. js获取url参数,操作url参数

    function getParam(key) { var tmp = location.search; tmp = decodeURIComponent(tmp); var index = tmp.i ...

  8. HDU 1841 Find the Shortest Common Superstring----KMP

    题意:给两个字符串,问包含这两个字符串的最小的字符串的长度 kmp返回匹配串长度 #include "iostream" #include<cstdio> #inclu ...

  9. Action中动态方法的调用 Action中通配符的使用 Result的配置

       Action中动态方法的调用 动态方法调用(Dynamic Method Invocation,DMI) 标识符:! 一.通过以下选中的文件来查看是否禁止调用动态方法

  10. 【原创】Linux环境下的图形系统和AMD R600显卡编程(5)——AMD显卡显命令处理机制

    通常通过读写设备寄存器对设备进行编程,在X86系统上,有专门的IO指令进行编程,在其他诸如MIPS.SPARC这类系统上,通过将设备的寄存器映射到内存地址空间直接使用读写内存的方式对设备进行编程. R ...