From b559853ded72110929e3887631f5b4c64aabf167 Mon Sep 17 00:00:00 2001 From: "Todd V. Jonker" Date: Sun, 15 Feb 2026 12:07:34 -0800 Subject: [PATCH] Generate Fusion coverage report in the root project. This avoids running CLI code in the `runtime` subproject, so the relevant code can move out later. Creating the conventions script will allow us to share all that setup across projects. It's much like an informal Gradle plugin. --- build.gradle.kts | 45 ++++++++++ ...logic.fusion-common-conventions.gradle.kts | 83 +++++++++++++++++++ fcov.properties | 4 + runtime/build.gradle.kts | 52 +----------- 4 files changed, 135 insertions(+), 49 deletions(-) create mode 100644 buildSrc/src/main/kotlin/buildlogic.fusion-common-conventions.gradle.kts create mode 100644 fcov.properties diff --git a/build.gradle.kts b/build.gradle.kts index 1fe7f62a..65cde06e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 plugins { + id("buildlogic.fusion-common-conventions") id("buildlogic.java-common-conventions") id("jacoco-report-aggregation") id("test-report-aggregation") @@ -20,6 +21,7 @@ dependencies { tasks.check { dependsOn(tasks.named("testCodeCoverageReport")) dependsOn(tasks.named("testAggregateTestReport")) + dependsOn(fcovAggregateReport) } tasks.named("testCodeCoverageReport") { @@ -33,3 +35,46 @@ tasks.named("testCodeCoverageReport") { tasks.named("testAggregateTestReport") { destinationDirectory = reporting.baseDirectory.dir("tests") } + + +//====================================================================================== +// Fusion coverage aggregate report + +// Declare the dataDirs for aggregate reporting. +dependencies { + "fcovReportData"(project(path = ":runtime", configuration = "fcovData")) +} + + +// The task is here instead of the conventions file because it is tied to the CLI's +// classpath, which can't be used from every subproject (notably `runtime`). + +val fcovReportData by configurations.getting +val fcovReportDir = reporting.baseDirectory.dir("fcov") + +val fcovAggregateReport = tasks.register("fcovAggregateReport") { + group = "verification" + description = "Generates Fusion code coverage report" + + dependsOn(fcovReportData) + outputs.dir(fcovReportDir) + + javaLauncher = javaToolchains.launcherFor { + languageVersion = java.toolchain.languageVersion + } + + classpath = project(":sdk").sourceSets["main"].runtimeClasspath + mainClass = "dev.ionfusion.fusion.cli.Cli" + args = listOf("report_coverage", + "--configFile", "fcov.properties", + "--htmlDir", fcovReportDir.get().asFile.path) + + fcovReportData.files.map { it.toString() } + + enableAssertions = true + + doFirst { + // Print the fcovReportData artifacts for easy debugging. + logger.lifecycle("Reporting Fusion coverage data from: ${fcovReportData.files}") + // TODO This information should appear in the report itself. + } +} diff --git a/buildSrc/src/main/kotlin/buildlogic.fusion-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.fusion-common-conventions.gradle.kts new file mode 100644 index 00000000..c27efdc0 --- /dev/null +++ b/buildSrc/src/main/kotlin/buildlogic.fusion-common-conventions.gradle.kts @@ -0,0 +1,83 @@ +// Copyright Ion Fusion contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Conventions for Fusion projects. + +plugins { + base + java // This is too much, but I can't find `reporting` and `check` otherwise. +} + + +// Paths for Fusion code coverage. +val fcovConfig = layout.projectDirectory.file("fcov.properties") +val fcovDataDir = layout.buildDirectory.dir("fcov") +val fcovReportDir = reporting.baseDirectory.dir("fcov") + + +//============================================================================= +// Code Coverage Data Collection + +tasks.named("test") { + // Rerun tests if the coverage config changes; we might record different things. + inputs.file(fcovConfig) + outputs.dir(fcovDataDir) + + jvmArgumentProviders.add(::instrumentationArguments) +} + +private fun instrumentationArguments(): List { + // Only collect coverage data when the `fcovTestCollect` task is needed. + if (!gradle.taskGraph.hasTask(fcovTestCollect.get())) { + return emptyList() + } + + logger.lifecycle("Enabling Fusion code coverage instrumentation") + return listOf( + "-Ddev.ionfusion.fusion.coverage.DataDir=${fcovDataDir.get().asFile.path}", + "-Ddev.ionfusion.fusion.coverage.Config=${fcovConfig.asFile.path}" + ) +} + + +// Signal the test task to collect Fusion coverage data. +val fcovTestCollect = tasks.register("fcovTestCollect") { + dependsOn(tasks.named("test")) + outputs.dir(fcovDataDir) + doLast { + logger.lifecycle("Collected Fusion coverage data into ${fcovDataDir.get().asFile.path}") + } +} + +tasks.check { + // To speed up the dev cycle, only instrument for coverage when running full checks. + dependsOn(fcovTestCollect) +} + + +//============================================================================= +// Code Coverage Reporting + +// Consumable configuration for providing access to coverage data. +val fcovData by configurations.creating { + description = "Fusion code coverage data" + isCanBeConsumed = true + isCanBeResolved = false +} + +artifacts { + add("fcovData", fcovDataDir) { + builtBy(fcovTestCollect) + // ...indirectly: `test` make the artifacts when `fcovTestCollect` is enabled. + } +} + + +// Resolvable configuration for reporting to gather all the coverage data. +val fcovReportData by configurations.creating { + description = "Fusion code coverage data to report" + isCanBeConsumed = false + isCanBeResolved = true +} + +// The reporting task is in the root project, since it needs the CLI classpath. diff --git a/fcov.properties b/fcov.properties new file mode 100644 index 00000000..1d26b14b --- /dev/null +++ b/fcov.properties @@ -0,0 +1,4 @@ +# Copyright Ion Fusion contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +IncludedModules = /fusion diff --git a/runtime/build.gradle.kts b/runtime/build.gradle.kts index 7975c73d..989c2e29 100644 --- a/runtime/build.gradle.kts +++ b/runtime/build.gradle.kts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 plugins { + id("buildlogic.fusion-common-conventions") id("buildlogic.java-library-conventions") jacoco } @@ -31,11 +32,6 @@ java { val mainFusionRepo = layout.projectDirectory.dir("src/main/fusion") val testFusionRepo = layout.projectDirectory.dir("src/test/fusion") -// New output paths for Fusion code coverage. -val fcovConfig = layout.projectDirectory.file("fcov.properties") -val fcovDataDir = layout.buildDirectory.dir("fcov") -val fcovReportDir = reporting.baseDirectory.dir("fcov") - // Various resources refer to the current version label. tasks.processResources { @@ -63,19 +59,7 @@ tasks.test { // dev.ionfusion.fusion.ClassLoaderModuleRepositoryTest uses ftst-repo.jar. dependsOn(ftstRepo) - inputs.file(fcovConfig) inputs.dir(testFusionRepo) - - jvmArgumentProviders.add { - if (fcovRunning) { - logger.lifecycle("Enabling Fusion code coverage instrumentation") - listOf("-Ddev.ionfusion.fusion.coverage.DataDir=" + fcovDataDir.get().asFile.path, - "-Ddev.ionfusion.fusion.coverage.Config=" + fcovConfig.asFile.path) - } - else { - listOf() - } - } } val ftstRepo = tasks.register("ftstRepo") { @@ -90,7 +74,7 @@ val ftstRepo = tasks.register("ftstRepo") { //============================================================================= -// Code Coverage +// Java Code Coverage // https://docs.gradle.org/current/userguide/jacoco_plugin.html @@ -127,35 +111,6 @@ tasks.check { } -// Name is ick but mirrors jacocoTestReport -val fcovTestReport = tasks.register("fcovTestReport") { - dependsOn(tasks.test) - - group = "verification" - description = "Generates Fusion code coverage report" - - javaLauncher = javaToolchains.launcherFor { - languageVersion = java.toolchain.languageVersion - } - - classpath = sourceSets["main"].runtimeClasspath - mainClass = "dev.ionfusion.fusion.cli.Cli" - args = listOf("report_coverage", - "--configFile", fcovConfig.asFile.path, - "--htmlDir", fcovReportDir.get().asFile.path, - fcovDataDir.get().asFile.path) - - enableAssertions = true -} - - -// Signal the test task to collect Fusion coverage data. -var fcovRunning = false -gradle.taskGraph.whenReady { - fcovRunning = hasTask(fcovTestReport.get()) -} - - //============================================================================= // Documentation @@ -185,6 +140,5 @@ tasks.javadoc { // Distribution tasks.build { - // To speed up the dev workflow, only enable FCOV when doing a full build. - dependsOn(tasks.jacocoTestReport, fcovTestReport) + dependsOn(tasks.jacocoTestReport) }