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 buildTypes = ["debug", "fullDebug"] 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 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.*', ] 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]) executionData.from = files("${project.buildDir}/jacoco/${testTaskName}.exec") def coverageSourceDirs = ["src/main/java", "src/$productFlavorName/java", "src/$buildTypeName/java"] sourceDirectories.setFrom(files(coverageSourceDirs)) additionalSourceDirs.setFrom(files(coverageSourceDirs)) reports { csv.required = false xml.required = true html.required = true } } } } } android { buildTypes { debug { testCoverageEnabled true } } }