// build.gradle.kts
buildscript {
dependencies {
classpath ("org.jacoco:org.jacoco.core:$jacoco_version")
}
}
// module/build.gradle
plugins {
id("jacoco")
}
apply( from = "${project.rootDir}/jacoco.gradle" )
android {
buildTypes {
getByName("debug") {
isTestCoverageEnabled = true
}
}
}
// 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
}
}
}