### Jacoco #### Gradle project level ```kotlin // build.gradle.kts buildscript { dependencies { classpath ("org.jacoco:org.jacoco.core:$jacoco_version") } } ``` #### Gradle module level ```groovy // module/build.gradle plugins { id("jacoco") } apply( from = "${project.rootDir}/jacoco.gradle" ) android { buildTypes { getByName("debug") { isTestCoverageEnabled = true } } } ``` #### Gradle jacoco ```groovy // jacoco.gradle jacoco{ toolVersion = "0.8.7" } project.afterEvaluate { project -> setupAndroidReporting() } def setupAndroidReporting() { tasks.withType(Test) { // Whether or not classes without source location should be instrumented jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] } def fileFilter = [ // data binding 'android/databinding/**/*.class', '**/android/databinding/*Binding.class', '**/android/databinding/*', '**/androidx/databinding/*', '**/BR.*', // android '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', // kotlin '**/*MapperImpl*.*', '**/*$ViewInjector*.*', '**/*$ViewBinder*.*', '**/BuildConfig.*', '**/*Component*.*', '**/*BR*.*', '**/Manifest*.*', '**/*$Lambda$*.*', '**/*Companion*.*', '**/*Module*.*', '**/*Dagger*.*', '**/*Hilt*.*', '**/*MembersInjector*.*', '**/*_MembersInjector.class', '**/*_Factory*.*', '**/*_Provide*Factory*.*', '**/*Extensions*.*', // sealed and data classes '**/*$Result.*', '**/*$Result$*.*', // adapters generated by moshi '**/*JsonAdapter.*', ] // Grab all build types with coverage enabled def buildTypes = android.buildTypes .findAll { type -> type.testCoverageEnabled } .collect { type -> type.name } // Grab all product flavors def productFlavors = android.productFlavors.collect { flavor -> flavor.name } // When no product flavors defined, use empty if (!productFlavors) productFlavors.add('') productFlavors.each { productFlavorName -> buildTypes.each { buildTypeName -> def sourceName, sourcePath if (!productFlavorName) { sourceName = sourcePath = "${buildTypeName}" } else { sourceName = "${productFlavorName}${buildTypeName.capitalize()}" sourcePath = "${productFlavorName}/${buildTypeName}" } def testTaskName = "test${sourceName.capitalize()}UnitTest" System.out.println("Task -> $testTaskName") // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest' task "${testTaskName}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") { group = "Reporting" description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build." def javaTree = fileTree(dir: "${project.buildDir}/intermediates/javac/$sourceName/classes", excludes: fileFilter) def kotlinTree = fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/$sourceName", excludes: fileFilter) classDirectories.from = files([javaTree], [kotlinTree]) // find . -name *.exec //executionData.from = files("${project.buildDir}/outputs/unit_test_code_coverage/${testTaskName}.exec") //executionData.from = files("${project.buildDir}/jacoco/${testTaskName}.exec") executionData.from = fileTree( dir: project.buildDir, includes: ["**/*.exec", "**/*.ec"]) def coverageSourceDirs = ["src/main/java", "src/$productFlavorName/java", "src/$buildTypeName/java"] sourceDirectories.setFrom(files(coverageSourceDirs)) //additionalSourceDirs.setFrom(files(coverageSourceDirs)) reports { csv.enabled false // change if needed xml { enabled true destination file("${buildDir}/coverage-report/${testTaskName}Coverage.xml") } html { enabled true destination file("${buildDir}/coverage-report") } } } } } } android { buildTypes { debug { testCoverageEnabled true } } } ```