Jacoco code coverage in Android Studio with flavors

Adr3nl picture Adr3nl · Jan 22, 2015 · Viewed 10.5k times · Source

I've been trying to run Jacoco test coverage for quiet some time now. I've tried several possible solutions reported in these topics:

Android test code coverage with JaCoCo Gradle plugin

How do I get a jacoco coverage report using Android gradle plugin 0.10.0 or higher?

Im running the tests in a emulatated device using genymotion. Here is what i added to build.gradle:

apply plugin: 'jacoco'

android{       
    jacoco {
        version "0.7.1.201405082137"
    }        
    buildTypes{
        debug{
                    testCoverageEnabled = true
        }
    }
}

jacoco {
    toolVersion "0.7.1.201405082137"
}

To run it i use something like

./gradlew clean
./gradlew createFLAVOR_NAMEDebugCoverageReport

The relevant generated files/folder are:

/build/intermediates/coverage-instrumented-classes
/build/intermediates/jacoco
/build/outputs/code-coverage/connected/flavors/MyFlavor/coverage.ec

However, there is nothing @ build/reports/jacoco/test/html/index.html or any html page/code coverage report @ /build/outputs.

I've also tried to create a dedicated task to build a coverage report:

def coverageSourceDirs = [
    'src/main/java',
]

task jacocoTestReport(type: JacocoReport, dependsOn: "connectedAndroidTestFLAVOR_NAMEDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
    classDirectories = fileTree(
        dir: './build/intermediates/classes/debug',
        excludes: ['**/R*.class',
                   '**/*$InjectAdapter.class',
                   '**/*$ModuleAdapter.class',
                   '**/*$ViewInjector*.class'
        ])
    sourceDirectories = files(coverageSourceDirs)
    executionData = files("$buildDir/jacoco/connectedAndroidTestMyFlavorDebug.exec")
    // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
    // We iterate through the compiled .class tree and rename $$ to $.
    doFirst {
       new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

Then ./gradlew clean and ./gradlew jacocoTestReport. The output is the same as above, so, no html page with coverage report or any other coverage file.

I'm currently using Android Studio v1.0.2 with the latest gradle version. Im fairly new to gradle, so it is possible im missing something basic here.

Thanks

Answer

Adr3nl picture Adr3nl · Jan 22, 2015

After spending the whole day chasing this issue i found out what's the problem. Contrary to the examples i've seen the file generated by the testDebug build is not the .exec file @$buildDir/jacoco/testDebug.exec.

With my gradle and studio version the file generated is a .ec @build/outputs/code-coverage/connected/flavors/myFlavor/coverage.ec

I didn't found any relevant information related to this. It may be a recent change, however, by creating a custom JacocoReport task and changing the executionData variable accordingly i've solved the problem. Here is my implementation:

task jacocoTestReport(type: JacocoReport) {

  def coverageSourceDirs = [
        'src/main/java'
  ]

  group = "Reporting"
  description = "Generates Jacoco coverage reports"
  reports {
      xml{
          enabled = true
          destination "${buildDir}/reports/jacoco/jacoco.xml"
      }
      csv.enabled false
      html{
          enabled true
          destination "${buildDir}/jacocoHtml"
      }
  }

  classDirectories = fileTree(
          dir: 'build/intermediates/classes',
          excludes: ['**/R.class',
                     '**/R$*.class',
                     '**/BuildConfig.*',
                     '**/Manifest*.*',
                     '**/*Activity*.*',
                     '**/*Fragment*.*'
          ]
  )

  sourceDirectories = files(coverageSourceDirs)
  additionalSourceDirs = files(coverageSourceDirs)
  executionData = files('build/outputs/code-coverage/connected/flavors/smartcompanion/coverage.ec')
}