A jacoco test coverage reporter gradle plugin for coveralls.io.
The plugin supports non-root packages in line with the recommended Kotlin directory structure
which was missing in many other plugins for the Kotlin ecosystem.
The plugin automatically detects the root package, if it conforms to Kotlin guidelines and has a .kt file on the root level.
To consolidate multiple JaCoCo coverage reports, the following code can be used to add a new task codeCoverageReport
tasks.register<JacocoReport>("codeCoverageReport") {
val jacocoReportTask =this// If a subproject applies the 'jacoco' plugin, add the result it to the report
subprojects {
val subproject =this
subproject.plugins.withType<JacocoPlugin>().configureEach {
subproject.tasks.matching({ it.extensions.findByType<JacocoTaskExtension>() !=null }).configureEach {
val testTask =this
sourceSets(subproject.sourceSets.main.get())
executionData(testTask)
}
// To automatically run `test` every time `./gradlew codeCoverageReport` is called,// you may want to set up a task dependency between them as shown below.// Note that this requires the `test` tasks to be resolved eagerly (see `forEach`) which// may have a negative effect on the configuration time of your build.
subproject.tasks.matching({ it.extensions.findByType<JacocoTaskExtension>() !=null }).forEach {
rootProject.tasks["codeCoverageReport"].dependsOn(it)
}
}
}
// enable the different report types (html, xml, csv)
reports {
// xml is usually used to integrate code coverage with// other tools like SonarQube, Coveralls or Codecov
xml.isEnabled =true// HTML reports can be used to see code coverage// without any external tools
html.isEnabled =true
}
coverallsJacoco.dependsOn(jacocoReportTask)
}
Multi-Project Support - Android
To consolidate multiple JaCoCo coverage reports on Android multi-project configurations, the following code can be used to add a new task jacocoFullReport
Groovy DSL build.gradle
// ignore any subproject, if required `subprojects.findAll{ it.name != 'customSubProject' }`def coveredProjects = subprojects
// configure() method takes a list as an argument and applies the configuration to the projects in this list.
configure(coveredProjects) { p->
p.evaluate()
// Here we apply jacoco plugin to every project
apply plugin: 'jacoco'// Set Jacoco version
jacoco {
toolVersion ="0.8.5"
}
// Here we create the task to generate Jacoco report// It depends to unit test task we don't have to manually running unit test before the task
task jacocoReport(type: JacocoReport, dependsOn: 'test') {
// Define what type of report we should generate// If we don't want to process the data further, html should be enough
reports {
xml.enabled =true
html.enabled =true
}
// Setup the .class, source, and execution directoriesfinal fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', 'android/**/*.*']
sourceDirectories.setFrom files(["${p.projectDir}/src/main/java"])
classDirectories.setFrom files([
fileTree(dir: "${p.buildDir}/classes", excludes: fileFilter),
fileTree(dir: "${p.buildDir}/intermediates/javac/debug", excludes: fileFilter),
fileTree(dir: "${p.buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter),
])
executionData.setFrom fileTree(dir: p.buildDir, includes: [
'jacoco/*.exec', 'outputs/code-coverage/connected/*coverage.ec'
])
}
}
apply plugin: 'jacoco'
apply plugin: 'com.github.nbaztec.coveralls-jacoco'
task jacocoFullReport(type: JacocoReport, group: 'Coverage reports') {
def projects = coveredProjects
// Here we depend on the jacocoReport task that we created before
dependsOn(projects.jacocoReport)
final source = files(projects.jacocoReport.sourceDirectories)
additionalSourceDirs.setFrom source
sourceDirectories.setFrom source
classDirectories.setFrom files(projects.jacocoReport.classDirectories)
executionData.setFrom files(projects.jacocoReport.executionData)
reports {
html {
enabled true
destination file("$buildDir/reports/jacoco/html")
}
xml {
enabled true
destination file("$buildDir/reports/jacoco/jacocoFullReport.xml")
}
}
doFirst {
executionData.setFrom files(executionData.findAll { it.exists() })
}
coverallsJacoco {
reportPath ="$buildDir/reports/jacoco/jacocoFullReport.xml"
reportSourceSets = projects.jacocoReport.sourceDirectories.collect{ it.getFiles() }.flatten()
}
tasks.coverallsJacoco.dependsOn(it)
}
Kotlin DSL build.gradle.kts
// ignore any subproject, if required `subprojects.findAll{ it.name != 'customSubProject' }`val coveredProjects = subprojects
// configure() method takes a list as an argument and applies the configuration to the projects in this list.
configure(coveredProjects) {
val p = (thisas org.gradle.api.internal.project.DefaultProject)
p.evaluate()
// Here we apply jacoco plugin to every projectapply(jacoco)
// Set Jacoco version
jacoco {
toolVersion ="0.8.5"
}
// Here we create the task to generate Jacoco report// It depends to unit test task we don't have to manually running unit test before the task
p.task("jacocoReport", JacocoReport::class) {
// Define what type of report we should generate// If we don't want to process the data further, html should be enough
reports {
xml.isEnabled =true
html.isEnabled =true
}
// Setup the .class, source, and execution directoriesval fileTreeConfig: (ConfigurableFileTree) ->Unit= {
it.exclude("**/R.class", "**/R$*.class", "**/BuildConfig.*", "**/Manifest*.*", "android/**/*.*")
}
sourceDirectories.setFrom(files("${p.projectDir}/src/main/java"))
classDirectories.setFrom(listOf(
fileTree("${p.buildDir}/classes", fileTreeConfig),
fileTree("${p.buildDir}/intermediates/javac/debug", fileTreeConfig),
fileTree("${p.buildDir}/tmp/kotlin-classes/debug", fileTreeConfig)
))
executionData.setFrom(fileTree(p.buildDir) {
include("jacoco/*.exec", "outputs/code-coverage/connected/*coverage.ec")
})
}.dependsOn("test")
}
plugins {
jacoco
id("com.github.nbaztec.coveralls-jacoco") version "1.2.4"
}
tasks {
register("jacocoFullReport", JacocoReport::class) {
val jacocoReportTask =this
group ="Coverage reports"val projects = coveredProjects
// Here we depend on the jacocoReport task that we created beforeval subTasks = projects.map { it.task<JacocoReport>("jacocoReport") }
dependsOn(subTasks)
val subSourceDirs = subTasks.map { files(it.sourceDirectories) }
additionalSourceDirs.setFrom(subSourceDirs)
sourceDirectories.setFrom(subSourceDirs)
classDirectories.setFrom(subTasks.map { files(it.classDirectories) })
executionData.setFrom(subTasks.map { files(it.executionData) })
reports {
html.isEnabled =true
html.destination = file("$buildDir/reports/jacoco/html")
xml.isEnabled =true
xml.destination = file("$buildDir/reports/jacoco/jacocoFullReport.xml")
}
doFirst {
executionData.setFrom(files(executionData.filter { it.exists() }))
}
coverallsJacoco {
dependsOn(jacocoReportTask)
reportPath ="$buildDir/reports/jacoco/jacocoFullReport.xml"
reportSourceSets = subSourceDirs.flatMap { it.files }
}
}
}
CI Usage
The plugin can be used with the following CI providers:
请发表评论