У нас есть многомодульный проект Gradle. Все модули являются непосредственными подкаталогами каталога проекта root.
В этих модулях мы запускаем JUnit и генерируем покрытие с помощью Jacoco. В root мы агрегируем покрытие. Это все работает, но в настоящее время мы должны ввести исключения дважды:
MODULE1:
apply plugin: "jacoco"
jacoco {
toolVersion = "0.8.5"
}
def static analysisExcludes() {
return [
"com/ourcompany/module1/*Config*",
"com/ourcompany/module1/endpoint/exception/**",
"com/ourcompany/module1/v2/**",
"com/ourcompany/module1/v3/**",
"src/generated/**",
"src/*test*/**",
"wrapper/dists/**"
]
}
def execData() {
return files(fileTree(buildDir).include("jacoco/*.exec"))
}
jacocoTestReport {
getExecutionData().setFrom(execData())
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: analysisExcludes())
}))
}
}
jacocoTestCoverageVerification {
getExecutionData().setFrom(execData());
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: analysisExcludes())
}))
}
violationRules {
rule {
limit {
minimum = 0.93
}
}
}
}
MODULE2:
apply plugin: "jacoco"
jacoco {
toolVersion = "0.8.5"
}
def static analysisExcludes() {
return [
"com/ourcompany/module2/*IgnoreMe*"
"src/generated/**",
"src/*test*/**",
"wrapper/dists/**"
]
}
def execData() {
return files(fileTree(buildDir).include("jacoco/*.exec"))
}
jacocoTestReport {
getExecutionData().setFrom(execData())
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: analysisExcludes())
}))
}
}
jacocoTestCoverageVerification {
getExecutionData().setFrom(execData());
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: analysisExcludes())
}))
}
violationRules {
rule {
limit {
minimum = 0.87
}
}
}
}
ROOT:
apply plugin: "jacoco"
jacoco {
toolVersion = "0.8.5"
}
// TODO: Eliminate double-entry bookkeeping by getting the excludes from the modules.
def static aggregatedAnalysisExcludes() {
return [
"**/com/ourcompany/module1/*Config*",
"**/com/ourcompany/module1/endpoint/exception/**",
"**/com/ourcompany/module1/v2/**",
"**/com/ourcompany/module1/v3/**",
"**/com/ourcompany/module2/*IgnoreMe*"
"**/src/generated/**",
"**/src/*test*/**",
"**/wrapper/dists/**"
]
}
def execData() {
return files(fileTree(rootDir).include("**/build/jacoco/*.exec"))
}
def includedClasses() {
return files(fileTree(dir: rootDir, include: "**/build/classes/**", exclude: aggregatedAnalysisExcludes()))
}
task jacocoAggregateReport(type: JacocoReport) {
getExecutionData().setFrom(execData());
afterEvaluate {
classDirectories.setFrom(includedClasses())
}
}
task jacocoAggregateTestCoverageVerification(type: JacocoCoverageVerification) {
getExecutionData().setFrom(execData());
afterEvaluate {
classDirectories.setFrom(includedClasses())
}
violationRules {
rule {
limit {
minimum = 0.91
}
}
}
}
task mergeJacocoExecData(type: JacocoMerge) {
setExecutionData(execData());
setDestinationFile(new File("build/jacoco/all.exec"))
}
apply plugin: "org.sonarqube"
def junitResultsDirs() {
def dirs = []
rootDir.eachDirRecurse { dir ->
if (dir.absolutePath.matches("^.*/build/test-results/(test|(component|integration)Test)\$")) {
dirs << dir
}
}
return dirs;
}
sonarqube {
properties {
property "sonar.projectName", "Multimodule Repo"
property "sonar.projectKey", "multimodule-repo"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.jacoco.reportPath", "build/jacoco/all.exec"
property "sonar.junit.reportsPath", junitResultsDirs()
property "sonar.issuesReport.html.enable", true
property "sonar.issuesReport.console.enable", true
property 'sonar.exclusions', aggregatedAnalysisExcludes()
}
}
Мы хотели бы как-то итерировать модули, чтобы получить их исключения, и объединить их в агрегированные исключения, добавив "** / "Префиксы, которые делают пути относительно root.
В псевдокоде:
def aggregatedAnalysisExcludes() {
return rootDir.modules.excludes.withPrefixes("**/")
}
или:
def aggregatedAnalysisExcludes() {
def excludes = []
for (Module m : rootDir.modules) {
excludes << m.excludes.withPrefixes("**/")
}
return excludes
}
Как мы пишем реальный код?
Примечание. Предполагая, что есть способ сделать то, что мы хотим, результирующий список будет содержать дубликаты src / generate, src / test и wrapper / dists. Мы попытались создать списки с дубликатами, чтобы увидеть, если это имеет значение, и это не так. Исключения работают правильно, даже если есть дубликаты. Но если есть чистый декларативный способ удаления дубликатов, это было бы хорошим дополнением к решению.