Как я могу объединить исключения из модулей в исключения root? - PullRequest
1 голос
/ 17 марта 2020

У нас есть многомодульный проект 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. Мы попытались создать списки с дубликатами, чтобы увидеть, если это имеет значение, и это не так. Исключения работают правильно, даже если есть дубликаты. Но если есть чистый декларативный способ удаления дубликатов, это было бы хорошим дополнением к решению.

Ответы [ 3 ]

0 голосов
/ 22 марта 2020

Я хотел более Groovy -i sh решение (декларативное, замыкания), и благодаря @barfuin смог, наконец, туда добраться.

В модулях добавьте это после определения exclude:

ext {
    analysisExcludes = analysisExcludes()
}

В root добавьте:

def aggregatedAnalysisExcludes() {
    evaluationDependsOnChildren()
    def excludes = new HashSet<>()
    subprojects.each {
        subproject -> subproject.property("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude;
        }
    }
    return (String[])excludes.stream().sorted().toArray()
}

Приведение (String[]) раздражает, но без него типы не совпадают при назначении для исключения : потом. Изменение на new HashSet<String>() не помогает, и .toArray(String[]::new) получает ошибку компиляции.

0 голосов
/ 24 марта 2020

Версия очищенного агрегатора исключений:

def aggregatedAnalysisExcludes() {
evaluationDependsOnChildren()
def excludes = new HashSet<>()
subprojects.each {
    subproject ->
        subproject.findProperty("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude
        }
}
return excludes.stream().sorted().toArray({ length -> new String[length] })

}

0 голосов
/ 20 марта 2020

Я понимаю, что вы хотите убедиться, что исключаемые Jacoco, которые были сделаны на уровне подпроекта, автоматически распространяются на уровень агрегации.

Это может быть достигнуто путем проверки того, что файловые деревья не оцениваются слишком рано, и, конечно же, спрашивая задачи JacocoReport о том, как они были настроены. Я нашел этот Gist с решением и написал небольшой плагин, который создает агрегированный отчет, под названием gradle-jacoco-log (загляните внутрь, чтобы найти исходный код , который явно выполняет агрегацию , включая исключения).

Вы в основном добавляете к своему build.gradle :

plugins {
    id 'org.barfuin.gradle.jacocolog' version '1.1.0'
}

и затем запускаете

gradle jacocoAggregatedReport

Вот полный пример проекта , в котором есть подпроекты с исключениями JacocoReport, которые автоматически агрегируются.

...