JaCoCo | Android | Gradle 8.1.1 | Coverage report not covering subprojects packages.

206 views
Skip to first unread message

Kalu Khan Luhar

unread,
Sep 30, 2023, 1:47:09 PM9/30/23
to JaCoCo and EclEmma Users
Hi All,

After our project updated from Gradle 7.4.2 to 8.1.1, the Jacoco coverage not getting correctly.
We have updated project's jacoco.gradle file becacuse JacocoMerge is not part of Gradle 8.1.1.
The unit test execution heppening but the coverage report shows "No class files specified".
Not picking test files from subprojects. Any help appriciated.
Versions in main gradle as:
classpath 'org.jacoco:org.jacoco.core:0.8.9'
classpath 'gradle.plugin.palantir:jacoco-coverage:0.4.0'


And jacoco.gradle is as below:

apply plugin: 'jacoco'
//In here we can filter out what modules we wanted to cover
def coveredProject = subprojects
configure(coveredProject) { proj ->
if (proj.name == "testProject") {
println("====skip: " + proj.toString() + "=======")
return
}
//Here we apply plugin to every project
apply plugin: 'jacoco'
//set jacoco version
jacoco {
toolVersions = versions.jacoco
}
//Here we create the task to generate the jacocoReport
//It depends on Unit Test task. We don't have to run manually unit test before task
task jacocoReport(type: JacocoReport) {
reports {
csv.required.set(true)
xml.required.set(true)
html.required.set(true)
}
//setup the .class, source and exclusion directory
def fileFilter = ['**/R.class', '**/R2.class', '**/R$.class', '**/BuildConfig.*',
'**/Menifest.*', 'android/**/*.*', '**/androidx/**/*.*']
def daggerExcluded = ['**/*_MembersInjectors.class',
'**/Dagger*Component*.class',
'**/Dagger*Subcomponent*.class',
'**/*Subcomponent$Builder.class',
'**/*_Factory.class',
'**/*Module_*Factory.class',]
def generatedExcluded = ['**/*_Impl.*', '**/*_Impl$*.*',
'**/*_ViewBinding.*', '**/*_ViewBinding$*.*']
fileFilter += generatedExcluded
fileFilter += daggerExcluded
localCoverageExclusionEnabled = proj.hasProperty('localCoverageExclusionEnabled') ? proj.property('localCoverageExclusionEnabled') : false
if (localCoverageExclusionEnabled) {
List<String> exclusionList = []
new File("$rootDir/coverage_config/classExclusionJacoco.txt").eachLine { line ->
exclusionList.add(line)
}
new File("$rootDir/coverage_config/SonarqubeExclusionJacoco.txt").eachLine { line ->
exclusionList.add(line)
}
new File("$rootDir/cqa_exclude.cfg").eachLine { line ->
exclusionList.add(line)
}
fileFilter += exclusionList
}
if (proj.name != "app") {
dependsOn('testReleaseUnitTest')
final kotlinTree = fileTree(dir: "${proj.buildDir}/temp/kotlin-classes/release", excludes: fileFilter)
final javaTree = fileTree(dir: "${proj.buildDir}/intermediates/javac/release", excludes: fileFilter)
final mainSrc = "${proj.projectDir}/src/main/java"

sourceDirectories.setFrom files([mainSrc])
classDirectories.setFrom files([kotlinTree, javaTree])
executionData.setFrom fileTree(dir: proj.buildDir, includes: ['outputs/unit_test_code_coverage/releaseUnitTest/testReleaseUnitTest.exec',
'outputs/unit_test_code_coverage/connected/*coverage.ec'])

} else {
dependsOn('testMyProjectReleaseUnitTest')
final kotlinTree = fileTree(dir: "${proj.buildDir}/temp/kotlin-classes/myProjectRelease", excludes: fileFilter)
final javaTree = fileTree(dir: "${proj.buildDir}/intermediates/javac/myProjectRelease", excludes: fileFilter)
final mainSrc = "${proj.projectDir}/src/main/java"

sourceDirectories.setFrom files([mainSrc])
classDirectories.setFrom files([kotlinTree, javaTree])
executionData.setFrom fileTree(dir: proj.buildDir, includes: ['outputs/unit_test_code_coverage/myProjectReleaseUnitTest/test*ReleaseUnitTest.exec',
'outputs/unit_test_code_coverage/connected/*coverage.ec'])
}
}
def getProjectList() {
//These projects are considered
subprojects + project
}

//We commented out below because 'JacocoMerge' not part of Gradle 8.1.1
/*task jacocoMerge(type: JacocoMerge) {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = 'Merge the JaCoCo data files from all subprojects into one'
project.afterEvaluate{
FileCollection execFiles = project.objects.fileCollection()//An empty file collection
getProjectList().each{ Project subproject ->
if(subproject.pluginManager.hasPlugin("jacoco")){
if(subproject.name == "testUtil"){
println("=======Skip====")
}else{
def testTasks = subproject.tasks.matching{
it.name.endsWith("ReleaseUnitTest") && it.name.startsWith("test")
}
dependsOn(testTasks) //ensure that .exec file present
testTasks.each{Test task ->
//The JacocoTaskExtension is the source of truth for the location of the .exec file.
JacocoTaskExtension extention = task.getExtensions().findByType(JacocoTaskExtension.class)
logger.info("=====Test name: "+task.name+"========")
if(extention!=null){
execFiles.from extention.getDestinationFile()
}
}
}
}else {
println("=======No jacoco====")
}
}
executionData = execFiles
}
doFirst{
//.exec file may be missing if any project has no tests. Filter in execution phase.
executionData = executionData.filter {it.canRead()}
}
}*/
def getReportTasks(JacocoReport pRootTask){
getProjectList().collect{
it.tasks.withType(JacocoReport).findAll{
it != pRootTask
}
}.flatten()

}
task jacocoFullReport(type: JacocoReport){//,dependsOn: task.jacocoMerge
reports {
csv.required.set(true)
xml.required.set(true)
html.required.set(true)
}
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = 'generates an aggregate report from all subprojects'

//Below was working with Gradle 7.+. Not working with Gradle 8.1.1
/*logger.lyfecycle 'Using aggregated file: '+tasks.jacocoMerge.destinationFile
executionData.from tasks.jacocoMerge.destinationFile*/

//Below is used as alternative of above but still not working. Not much idea...
//I believe this is causing issue.
subprojects.each {
dependsOn it.tasks.withType(JacocoReport)
executionData.from it.tasks.withType(JacocoReport).collect{it.executionData}
}
project.afterEvaluate{
def reportTasks = getReportTasks(tasks.jacocoFullReport)
classDirectories.from project.files({reportTasks.collect{it.classDirectories}.findAll{it!=null}})
sourceDirectories.from project.files({reportTasks.collect{it.sourceDirectories}.findAll{it!=null}})
}
}

}

Reply all
Reply to author
Forward
0 new messages