Skip to content

Instantly share code, notes, and snippets.

@alexvanyo
Last active August 29, 2024 09:09
Show Gist options
  • Save alexvanyo/9a3b38ef6c07907cfa7f9a4310c628bd to your computer and use it in GitHub Desktop.
Save alexvanyo/9a3b38ef6c07907cfa7f9a4310c628bd to your computer and use it in GitHub Desktop.
Kotlin DSL JaCoCo configuration for Android
import com.android.build.gradle.internal.tasks.factory.dependsOn
plugins {
id("com.android.application")
jacoco
}
// Register the main JaCoCo task to later depend on the per-variant tasks
val jacocoTestReport = tasks.register("jacocoTestReport")
tasks.withType<Test> {
configure<JacocoTaskExtension> {
isIncludeNoLocationClasses = true
}
}
android {
applicationVariants.all(closureOf<com.android.build.gradle.api.ApplicationVariant> {
val testTaskName = "test${this@closureOf.name.capitalize()}UnitTest"
val excludes = listOf(
// Android
"**/R.class",
"**/R\$*.class",
"**/BuildConfig.*",
"**/Manifest*.*"
)
val reportTask = tasks.register("jacoco${testTaskName.capitalize()}Report", JacocoReport::class) {
dependsOn(testTaskName)
reports {
xml.isEnabled = true
html.isEnabled = true
}
classDirectories.setFrom(
files(
fileTree(this@closureOf.javaCompileProvider.get().destinationDir) {
exclude(excludes)
},
fileTree("$buildDir/tmp/kotlin-classes/${this@closureOf.name}") {
exclude(excludes)
}
)
)
// Code underneath /src/{variant}/kotlin will also be picked up here
sourceDirectories.setFrom(this@closureOf.sourceSets.flatMap { it.javaDirectories })
executionData.setFrom(file("$buildDir/jacoco/$testTaskName.exec"))
}
jacocoTestReport.dependsOn(reportTask)
})
}
@agustinsivoplas
Copy link

@agustinsivoplas did you find a fix?

Unfortunately no, I didn't research anymore

@ThomasGoehringer
Copy link

Nevermind, I fixed it by replacing
applicationVariants.all(closureOf<com.android.build.gradle.api.ApplicationVariant> {
with
applicationVariants.all {

This way it's using the correct class. You'll have to remove @closureOf in line 19.

@agustinsivoplas
Copy link

Nevermind, I fixed it by replacing applicationVariants.all(closureOf<com.android.build.gradle.api.ApplicationVariant> { with applicationVariants.all {

This way it's using the correct class. You'll have to remove @closureOf in line 19.

Thanks for the answer!

@akki2298
Copy link

akki2298 commented Sep 22, 2023

@agustinsivoplas @ThomasGoehringer Is it working for you guys !!? it's giving me error like unresolved reference android etc

@agustinsivoplas
Copy link

@akki2298 sorry for the delay, can you post an image of the issue? This is my version (works fine)

 applicationVariants.all {
        val testTaskName = "test${this.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() }}UnitTest"

        val excludes = listOf(
            // Android
            "**/R.class",
            "**/R\$*.class",
            "**/BuildConfig.*",
            "**/Manifest*.*",
            "**/*Test*.*",
            "android/**/*.*",
            "**/*Binding.class",
            "**/*Binding*.*",
            "**/*Dao_Impl*.class",
            "**/*Args.class",
            "**/*Args.Builder.class",
            "**/*Directions*.class",
            "**/*Creator.class",
            "**/*Builder.class"
        )

        val reportTask = tasks.register("jacoco${testTaskName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}Report", JacocoReport::class) {
            group = "Reporting"
            description = "Generate Jacoco coverage reports for the ${testTaskName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }} build."
            dependsOn(testTaskName)

            reports {
                xml.required.set(true)
                html.required.set(true)
            }

            classDirectories.setFrom(
                files(
                    fileTree(javaCompileProvider.get().destinationDirectory) {
                        exclude(excludes)
                    },
                    fileTree("$buildDir/tmp/kotlin-classes/${this.name}") {
                        exclude(excludes)
                    }
                )
            )

            // Code underneath /src/{variant}/kotlin will also be picked up here
            sourceDirectories.setFrom(sourceSets.flatMap { it.javaDirectories })
            executionData.setFrom(file("$buildDir/jacoco/$testTaskName.exec"))
        }

        jacocoTestReport.dependsOn(reportTask)
    }

@akki2298
Copy link

Hi @agustinsivoplas , have you added this code in build.gradle.kts directly?
In my case i am creating new file called jacoco.gradel.kts and applying that file in my all modules build.gradle.kts like this

apply(from - "../jacoco.gradle.kts")

so it gives me error saying unresolved reference android
here is the main error issue
image

@ThomasGoehringer
Copy link

@akki2298 Not sure if it was the same issue, but adding the code directly to build.gradle.kts instead of applying from jacoco.gradle.kts resolved an error for me. Definitely worth a try I would say.

@akki2298
Copy link

akki2298 commented Sep 27, 2023

@ThomasGoehringer @agustinsivoplas yes it is working then but since i have multiple modules there will be lot of duplicate code so i want to have it common place

@DenisShov
Copy link

For other people who wonder why apply(from - "../jacoco.gradle.kts") doesn't work, check this article - https://medium.com/@kurtlemond/migrating-jacoco-reports-gradle-tasks-to-kotlin-dsl-7b566d89ea92

TLDR: Dynamic resolution, which Groovy DSL provides, does not exists in Kotlin DSL.

I ended up using "Precompiled Script Plugin".

I just added kotlin-dsl-precompiled-script-plugins to buildSrc/build.gradle.kts and created jacoco-reports.gradle.kts under buildSrc/src/main/kotlin folder. Then you can add id("jacoco-reports") plugin to your modules.

@rontho
Copy link

rontho commented Feb 29, 2024

@DenisShov other solution would be to revert jacoco.gradle.kts to groovy right ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment