build: aggregate Kover coverage for SonarQube import

- Apply Kover (Apache-2.0, build-time only) to the KMP/JVM modules and aggregate at the root
- include amethyst playDebug in the aggregated Kover coverage
This commit is contained in:
davotoula
2026-07-09 23:35:05 +01:00
parent b8570797fe
commit dca345212b
12 changed files with 83 additions and 0 deletions

View File

@@ -278,6 +278,25 @@ call does not guarantee lint finishes first):
The reports persist under each module's `build/reports/`, so re-run lint only
when you want fresh lint data in the next scan.
### 5. Optional: include test coverage
Coverage comes from [Kover](https://github.com/Kotlin/kotlinx-kover). The root
`koverXmlReportCoverage` task runs the unit-test suites of the covered modules
(`quartz`, `commons`, `quic`, `nestsClient`, `geode`, `cli`, `relayBench`,
`desktopApp`, and `amethyst`'s `fdroidDebug` variant) and merges the result
into `build/reports/kover/reportCoverage.xml`, which the scanner imports
automatically when it exists. Same recipe as lint — generate first, scan
second:
```bash
./gradlew koverXmlReportCoverage
./gradlew sonar
```
For a human-readable version of the same data, run
`./gradlew koverHtmlReportCoverage` and open
`build/reports/kover/htmlCoverage/index.html`.
Every `sonar.*` entry in `local.properties` is forwarded to the scanner, so any
[analysis parameter](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/analysis-parameters/)
can be set there. `sonar.projectKey` / `sonar.projectName` default to the root

View File

@@ -6,6 +6,7 @@ plugins {
alias(libs.plugins.jetbrainsComposeCompiler)
alias(libs.plugins.serialization)
alias(libs.plugins.googleKsp)
alias(libs.plugins.kotlinxKover)
}
fun getCurrentBranch(workingDir: java.io.File): String =

View File

@@ -45,6 +45,50 @@ plugins {
alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false
alias(libs.plugins.serialization)
alias(libs.plugins.googleKsp) apply false
alias(libs.plugins.kotlinxKover)
}
// Aggregated code coverage: `./gradlew koverXmlReportCoverage` runs the unit
// test suites of the modules below and merges their coverage into
// build/reports/kover/reportCoverage.xml (JaCoCo-compatible XML, importable by
// SonarQube — see BUILDING.md). `koverHtmlReportCoverage` renders the same
// data for humans. A named variant is used instead of Kover's default total
// report because the total for an Android project unconditionally merges
// EVERY build variant — compiling all release/benchmark variants of
// :amethyst just to measure unit tests. The "coverage" variant below picks
// one representative compilation per project: the `jvm` target for KMP/JVM
// modules and `playDebug` for :amethyst — play is the primary flavor and the
// larger code surface (both `add`s are optional so the one block fits every
// project). :nappletHost stays out — it has no tests.
dependencies {
kover(project(":amethyst"))
kover(project(":quartz"))
kover(project(":commons"))
kover(project(":quic"))
kover(project(":nestsClient"))
kover(project(":geode"))
kover(project(":cli"))
kover(project(":relayBench"))
kover(project(":desktopApp"))
}
kover {
currentProject {
createVariant("coverage") {}
}
}
subprojects {
plugins.withId("org.jetbrains.kotlinx.kover") {
configure<kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension> {
currentProject {
createVariant("coverage") {
add("jvm", optional = true)
add("playDebug", optional = true)
}
}
}
}
}
// Shared app version for all subprojects — read from gradle/libs.versions.toml.
@@ -120,6 +164,15 @@ if (sonarEnabled) {
.filter { it.startsWith("sonar.") }
.forEach { System.setProperty(it, sonarProperties.getProperty(it)) }
// Import Kover's aggregated coverage (run `./gradlew koverXmlReportCoverage`
// first) unless the operator pointed the scanner somewhere else.
if (System.getProperty("sonar.coverage.jacoco.xmlReportPaths") == null) {
System.setProperty(
"sonar.coverage.jacoco.xmlReportPaths",
layout.buildDirectory.file("reports/kover/reportCoverage.xml").get().asFile.absolutePath,
)
}
// The scanner's sonarResolver task reads AGP's generated-res-values provider
// but doesn't depend on the task that produces it — wire it up in every
// module that has both (today only :amethyst enables resValues, but the

View File

@@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.jetbrainsKotlinJvm)
application
alias(libs.plugins.kotlinxKover)
}
kotlin {

View File

@@ -25,6 +25,7 @@ plugins {
alias(libs.plugins.jetbrainsComposeCompiler)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.serialization)
alias(libs.plugins.kotlinxKover)
}
kotlin {

View File

@@ -6,6 +6,7 @@ plugins {
alias(libs.plugins.jetbrainsKotlinJvm)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.jetbrainsComposeCompiler)
alias(libs.plugins.kotlinxKover)
}
// RPM rejects dashes in version strings — replace with tilde (~) which RPM uses

View File

@@ -5,6 +5,7 @@ plugins {
alias(libs.plugins.serialization)
application
`java-test-fixtures`
alias(libs.plugins.kotlinxKover)
}
application {

View File

@@ -35,6 +35,7 @@ kmpTorResource = "409.5.0"
junit = "4.13.2"
kchesslib = "1.0.5"
kotlin = "2.4.0"
kover = "0.9.8"
kotlinxCollectionsImmutable = "0.5.0"
kotlinxCoroutinesCore = "1.11.0"
kotlinxSerialization = "1.11.0"
@@ -246,6 +247,7 @@ jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
serialization = { id = 'org.jetbrains.kotlin.plugin.serialization', version.ref = 'kotlin' }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinxKover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
vanniktech-mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }

View File

@@ -5,6 +5,7 @@ plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidKotlinMultiplatformLibrary)
alias(libs.plugins.serialization)
alias(libs.plugins.kotlinxKover)
}
kotlin {

View File

@@ -9,6 +9,7 @@ plugins {
alias(libs.plugins.androidKotlinMultiplatformLibrary)
alias(libs.plugins.serialization)
alias(libs.plugins.vanniktech.mavenPublish)
alias(libs.plugins.kotlinxKover)
}
kotlin {

View File

@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidKotlinMultiplatformLibrary)
alias(libs.plugins.kotlinxKover)
}
kotlin {

View File

@@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.jetbrainsKotlinJvm)
application
alias(libs.plugins.kotlinxKover)
}
application {