Skip to content

Instantly share code, notes, and snippets.

@kenyee
Forked from notsatyarth/test-report.gradle
Created December 24, 2019 13:49
Show Gist options
  • Save kenyee/9c549e706f243811328eff03ebfc5db5 to your computer and use it in GitHub Desktop.
Save kenyee/9c549e706f243811328eff03ebfc5db5 to your computer and use it in GitHub Desktop.
/**
* We are combining unit and intrumentation test reports
* if no one uses instrumentation then delete createDebugCoverageReport
* and it output outputs/code-coverage/connected/*coverage.ec
*/
/**
* Since jacoco does not support android out of the box we create a custom task per variant
* for test coverage.All the coverage reports are created in the root projects build directory.
* These are then archived in the pages folder which is then deployed on the project.
*/
apply plugin: "jacoco"
project.ext.codeCoverage = [
"enabled" : false,
"fileFilter" : [''],
"package" : null,
"sourceDirectories": [],
"classDirectory" : null
]
jacoco {
toolVersion = project.ext.jacocoCoreVersion
reportsDir = file("$rootDir/build/jacoco/$project.name")
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
task('jacocoReports') {
group = "Reporting"
description = "Generate Jacoco coverage reports for all variants"
}
task('jacocoVerifications') {
group = "Verification"
description = "Generate Jacoco coverage reports for all variants"
}
variants().all { variant ->
if (project.ext.codeCoverage.enabled) {
def params = prepareJacocoParams(variant)
def reportTask = createReportTask(params)
def verificationTask = createVerificationTask(params)
if (params.enabled) {
verificationTask.dependsOn reportTask
jacocoReports.dependsOn reportTask
}
}
}
/**
* Creates identification parameters required to generate the corresponding coverage tasks.
*
*
* Adding extra filters for projects would be done by project extras. These would be defined in the
* project's build.gradle as follows
*
*
* // project/build.gradle
*
* {@code
*
* codeCoverage{*
* fileFilter = ['com/mypackage/to/exclude']
* package=""
* sourceDirectories=['additional source directories']
* classDirectory=""
*}*}
* This needs to be set to the project.ext using `ext.codeCoverage=codeCoverage`
*
*
*
*/
def prepareJacocoParams(variant) {
def params = [:]
params.variantName = variant.name
params.variantCapName = variant.name.capitalize()
params.fileFilter = ['**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*',
'**/*_MembersInjector.class',
'**/Dagger*Component.class',
'**/Dagger*Component$Builder.class',
'**/*Module_*Factory.class',
'**/App*.*'
]
def modulePackage = variant.applicationId
def projectSourcesDirectories = []
def projectClassDirectoriesFilter = null
def projectFileFilters = []
if (project.ext.codeCoverage.fileFilter != null)
projectFileFilters = project.ext.codeCoverage.fileFilter
if (project.ext.codeCoverage.package != null)
modulePackage = project.ext.codeCoverage.package
if (project.ext.codeCoverage.sourceDirectories != null)
projectSourcesDirectories = project.ext.codeCoverage.sourceDirectories
if (project.ext.codeCoverage.classDirectory != null)
projectClassDirectoriesFilter = project.ext.codeCoverage.classDirectory
params.fileFilter += projectFileFilters
params.enabled = project.ext.codeCoverage.enabled
params.classDirectories = files([fileTree(
dir: variant.javaCompileProvider.get().destinationDir,
excludes: params.fileFilter
) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/$params.variantName",
excludes: params.fileFilter
)])
if (projectClassDirectoriesFilter != null) {
params.classDirectories = files([params.classDirectories.collect {
fileTree(dir: it, excludes: params.fileFilter)
}], fileTree(dir: projectClassDirectoriesFilter, excludes: params.filter))
}
//The application is the default package name for a module
params.package = modulePackage
params.sourceDirectories = files([
"src/main/java", "src/main/kotlin",
"src/$params.variantName/java", "src/$params.variantName/kotlin"
] + projectSourcesDirectories)
def execPath = "${buildDir}/jacoco/test${params.variantCapName}UnitTest.exec"
params.executionData = files(execPath)
return params
}
def createReportTask(params) {
return task("jacoco${params.variantCapName}Report", type: JacocoReport, dependsOn: "test${params.variantCapName}UnitTest") {
group = "Reporting"
description = "Generate Jacoco coverage reports for $params.variantCapName"
reports {
xml.enabled = true
html.enabled = true
csv.enabled = false
}
classDirectories = params.classDirectories
sourceDirectories = params.sourceDirectories
executionData = params.executionData
}
}
def createVerificationTask(params) {
return task("jacoco${params.variantCapName}Verification", type: JacocoCoverageVerification) {
sourceDirectories = params.sourceDirectories
classDirectories = params.classDirectories
executionData = params.executionData
violationRules {
failOnViolation true
rule {
element = 'PACKAGE'
includes = ["${params.package}.*"]
excludes = ['']
limit {
counter = 'CLASS'
value = 'MISSEDCOUNT'
maximum = 0
}
}
rule {
element = 'CLASS'
includes = ["${params.package}.*"]
excludes = ['']
limit {
counter = 'INSTRUCTION'
value = 'COVEREDRATIO'
minimum = 0.8
}
}
}
}
}
def variants() {
if (project.android != null) {
if (project.android.hasProperty('libraryVariants')) {
return project.android.libraryVariants
} else {
return project.android.applicationVariants
}
} else {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment