JaCoCo plugin with multi-project builds

I have an inherited source tree that I’ve tried to restructure as a multi-project build. There are 4 subprojects: A, B, C, D. The lib dependency graph is D -> C -> B -> A.

There are no test source sets except in D, which includes tests for all the classes from A, B, C, and D. This is because the tests were written in such a way that they can’t be split out such that tests for A depend only on classes from A.

So, assuming that this is a constraint: that the tests cannot be partitioned out into test source sets under A, B, C, and D; I am trying to get a JaCoCo report that includes coverage for all of the classes from A, B, and C as well as those from D when I run D’s tests.

However, I am only seeing coverage reports from classes in the main source set for project D.

How can I get a coverage report for classes from all projects when the test task only runs in project D?

1 Like

TLDR; You’ll need to set additionalClassDirs and additionalSourceDirs properties on the jacocoTestReport task

eg:

apply plugin: 'jacoco'
apply plugin: 'java'

def otherProjects = [':A', ':B', ':C']

otherProjects.each {
   // ensure other projects evaluated first so sourceSets are configured
   evaluationDependsOn it
}

jacocoTestReport {
   FileTree sourceTree = files().asFileTree
   FileTree classTree = files().asFileTree
   otherProjects.each {
      sourceTree += project(it).sourceSets.main.allJava
      classTree += project(it).sourceSets.main.output.asFileTree
   }
   additionalSourceDirs = sourceTree
   additionalClassDirs = classTree
}
2 Likes

Fantastic! Thank you @Lance! This worked exactly as I was hoping. Especially welcome after hours of fruitless project and config restructuring.

This is great!!! in our case, is there any way not to ‘list’ the projects so as they change, they automatically get included in the test coverage? We test like so

and therefore as people add new libs that we depend on, we still want our tests hitting those libraries without needing to remember to go change a build file(which we are likely to forget since this doesn’t happen to often). ie. we may refactor and split out code from C creating a new library that D still uses.

thanks,
Dean

Of course! See from the Project javadocs there are getters for allProjects and subprojects which return Set<Project>. So, with a bit of groovy knowledge you can do things like

def filteredProjects = allProjects.findAll { 
   it.name.contains('foo') || it.pluginManager.hasPlugin('java') 
} 
filteredProjects.each {...}