zoukankan      html  css  js  c++  java
  • android 代码覆盖率

    背景

    项目使用的是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('
    ')
        for line in ls:
            if line.endswith('device'):
                id_ = line.split('	')[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

      

  • 相关阅读:
    VC++ MFC工程中中如何将一个工程的资源(如对话框)复制到另外一个工程
    VS调试_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));崩溃原因及解决方法
    使用libcurl下载文件小例
    [工具] 程序员必须软件
    [Linux] Linux软连接和硬链接
    [Android Pro] Android的Animation之LayoutAnimation使用方法
    [Java基础] java的守护线程与非守护线程
    [Linux] 守护进程和守护线程
    [Linux] linux文件系统学习
    [Java基础] Java线程复习笔记
  • 原文地址:https://www.cnblogs.com/guhan121/p/7763468.html
Copyright © 2011-2022 走看看