From f5fe1d1e5069c9f29ce66312abb982bdeaff7385 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Fri, 21 May 2021 20:29:51 +1000 Subject: [PATCH] Merge JaCoCo coverage reports the "right" way (#3650) * Merge JaCoCo coverage reports the "right" way * Generate code coverage report when "check" lifecycle task is run --- .github/workflows/codecoverage.yaml | 2 +- build.gradle.kts | 29 ------- buildSrc/src/main/kotlin/module.gradle.kts | 34 ++++++++ code-coverage-report/build.gradle.kts | 91 ++++++++++++++++++++++ settings.gradle.kts | 1 + 5 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 code-coverage-report/build.gradle.kts diff --git a/.github/workflows/codecoverage.yaml b/.github/workflows/codecoverage.yaml index f0d4d4ccc..ceff9b535 100644 --- a/.github/workflows/codecoverage.yaml +++ b/.github/workflows/codecoverage.yaml @@ -25,7 +25,7 @@ jobs: java-version: 11 - name: Generate Coverage Report - run: ./gradlew jacocoTestReport --no-build-cache -x :detekt-gradle-plugin:test + run: ./gradlew jacocoMergedReport --no-build-cache -x :detekt-gradle-plugin:test - name: Publish Coverage if: success() diff --git a/build.gradle.kts b/build.gradle.kts index 264d022c7..8b158929e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,8 +2,6 @@ import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { - kotlin("jvm") apply false - jacoco packaging releasing detekt @@ -19,33 +17,6 @@ allprojects { version = Versions.currentOrSnapshot() } -jacoco.toolVersion = libs.versions.jacoco.get() - -tasks { - jacocoTestReport { - executionData.setFrom(fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")) - - val examplesOrTestUtils = setOf( - "detekt-bom", - "detekt-test", - "detekt-test-utils", - "detekt-sample-extensions" - ) - - subprojects - .filterNot { it.name in examplesOrTestUtils } - .forEach { - this@jacocoTestReport.sourceSets(it.sourceSets.main.get()) - this@jacocoTestReport.dependsOn(it.tasks.test) - } - - reports { - xml.isEnabled = true - xml.destination = file("$buildDir/reports/jacoco/report.xml") - } - } -} - val analysisDir = file(projectDir) val baselineFile = file("$rootDir/config/detekt/baseline.xml") val configFile = file("$rootDir/config/detekt/detekt.yml") diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index 8a0053880..4496d76e1 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -1,5 +1,6 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -46,6 +47,39 @@ tasks.withType().configureEach { } } +// Share sources folder with other projects for aggregated JaCoCo reports +configurations.create("transitiveSourcesElements") { + isVisible = false + isCanBeResolved = false + isCanBeConsumed = true + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("source-folders")) + } + sourceSets.main.get().withConvention(KotlinSourceSet::class) { kotlin }.srcDirs.forEach { + outgoing.artifact(it) + } +} + +// Share the coverage data to be aggregated for the whole product +configurations.create("coverageDataElements") { + isVisible = false + isCanBeResolved = false + isCanBeConsumed = true + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("jacoco-coverage-data")) + } + // This will cause the test task to run if the coverage data is requested by the aggregation task + outgoing.artifact( + tasks.test.map { task -> + task.extensions.getByType().destinationFile!! + } + ) +} + tasks.withType().configureEach { kotlinOptions { jvmTarget = Versions.JVM_TARGET diff --git a/code-coverage-report/build.gradle.kts b/code-coverage-report/build.gradle.kts new file mode 100644 index 000000000..dde67a047 --- /dev/null +++ b/code-coverage-report/build.gradle.kts @@ -0,0 +1,91 @@ +plugins { + id("java") + id("jacoco") +} + +jacoco.toolVersion = libs.versions.jacoco.get() + +dependencies { + implementation(projects.customChecks) + implementation(projects.detektApi) + implementation(projects.detektCli) + implementation(projects.detektCore) + implementation(projects.detektFormatting) + implementation(projects.detektGenerator) + implementation(projects.detektGradlePlugin) + implementation(projects.detektMetrics) + implementation(projects.detektParser) + implementation(projects.detektPsiUtils) + implementation(projects.detektReportHtml) + implementation(projects.detektReportSarif) + implementation(projects.detektReportTxt) + implementation(projects.detektReportXml) + implementation(projects.detektRules) + implementation(projects.detektRulesComplexity) + implementation(projects.detektRulesCoroutines) + implementation(projects.detektRulesDocumentation) + implementation(projects.detektRulesEmpty) + implementation(projects.detektRulesErrorprone) + implementation(projects.detektRulesExceptions) + implementation(projects.detektRulesNaming) + implementation(projects.detektRulesPerformance) + implementation(projects.detektRulesStyle) + implementation(projects.detektTooling) +} + +// A resolvable configuration to collect source code +val jacocoSourceDirs: Configuration by configurations.creating { + isVisible = false + isCanBeResolved = true + isCanBeConsumed = false + isTransitive = false + extendsFrom(configurations.implementation.get()) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("source-folders")) + } +} + +// A resolvable configuration to collect JaCoCo coverage data +val jacocoExecutionData: Configuration by configurations.creating { + isVisible = false + isCanBeResolved = true + isCanBeConsumed = false + isTransitive = false + extendsFrom(configurations.implementation.get()) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("jacoco-coverage-data")) + } +} + +val jacocoClassDirs: Configuration by configurations.creating { + extendsFrom(configurations.implementation.get()) + isVisible = false + isCanBeResolved = true + isCanBeConsumed = false + isTransitive = false + attributes { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES)) + } +} + +val jacocoMergedReport by tasks.registering(JacocoReport::class) { + description = "Merge JaCoCo reports from dependencies." + group = "verification" + + executionData.from(jacocoExecutionData.incoming.artifacts.artifactFiles) + sourceDirectories.from(jacocoSourceDirs.incoming.artifacts.artifactFiles) + classDirectories.from(jacocoClassDirs.incoming.artifacts.artifactFiles) + + reports { + xml.required.set(true) + html.required.set(true) + } +} + +tasks.check { + dependsOn(jacocoMergedReport) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index c8da6cabf..c334dc49b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ rootProject.name = "detekt" include( + "code-coverage-report", "custom-checks", "detekt-api", "detekt-cli",