-
Notifications
You must be signed in to change notification settings - Fork 2
coverage
title: "Coverage" description: "Kover-based test coverage setup + the worker-kmp 100%-coverage gate for cmp-worker-* modules."
worker-kmp uses kotlinx-kover for line-coverage
measurement + enforcement. Since v3.1.3, 6 testable commonMain modules carry a hard 100%
line-coverage floor; CI fails PRs that regress below it. Two additional modules
(cmp-worker-scheduler, cmp-worker-compose) are test-complete but cannot opt in yet — see
Known limitation: AGP 9 + Kover 0.9.1.
# Generate aggregate HTML report
./gradlew koverHtmlReport
open build/reports/kover/html/index.html # macOS
# Generate XML report (CI / codecov consumption)
./gradlew koverXmlReport
# Verify 100% line coverage per opted-in module (fails on regression)
./gradlew koverVerifyKover instruments JVM bytecode only. KMP commonMain code compiles to JVM (via jvmTest)
so it IS measured. iOS, JS, and wasmJs platform actuals do NOT get coverage — see the exclusions
below.
| Module | Status |
|---|---|
cmp-worker-kmp |
100% commonMain (gated by koverVerify) |
cmp-worker-store5 |
100% commonMain (gated by koverVerify) |
cmp-worker-storeflow |
100% commonMain (gated by koverVerify) |
cmp-worker-koin |
100% commonMain (gated by koverVerify) |
cmp-worker-sync |
100% commonMain (gated by koverVerify) |
cmp-worker-test |
100% commonMain (gated by koverVerify) |
| Module | Why excluded |
|---|---|
cmp-worker-scheduler |
Test suite complete (39 @Tests across 8 commonTest files run via jvmTest) but Kover plugin NOT applied. Blocked on upstream Kover support for AGP 9's android.kotlin.multiplatform.library extension shape. Tracked by follow-up plan kover-agp9-opt-in-followup. |
cmp-worker-compose |
Test suite complete (21 @Tests incl. NonComposableSurfacesTest) but Kover plugin NOT applied for the same AGP 9 reason. @Composable bodies would be filter-excluded anyway; the testable non-@Composable utilities (state-mapping, request builders) are covered. Tracked by the same follow-up plan. |
cmp-worker-app-annotations |
Annotation-only module — no executable code to measure. |
cmp-worker-android, cmp-worker-ios, cmp-worker-desktop, cmp-worker-desktop-daemon, cmp-worker-web, cmp-worker-web-push
|
Platform actuals; Kover only measures JVM bytecode. Tracked separately by the Tier-2 follow-up epic worker-kmp-platform-engine-tests, which adds Robolectric (Android) + Xcode-sim (iOS) + browser-test (Web) harnesses. |
cmp-worker-app-ksp, cmp-worker-app-plugin
|
Build-tooling (KSP processor + Gradle plugin) — needs Gradle TestKit; deferred. |
cmp-worker-bench, cmp-worker-migrate
|
Non-production tooling. |
samples/* |
Illustrative; not under coverage threshold. |
Kover 0.9.1 cannot introspect the new android.kotlin.multiplatform.library Gradle plugin
extension shape that AGP 9+ uses for KMP-Android library modules. Applying
id("io.github.mobilebytelabs.kover") to cmp-worker-scheduler or cmp-worker-compose
fails at configuration time.
Workaround in place: the convention plugin is intentionally NOT applied to those two
modules. Their tests still run via ./gradlew :cmp-worker-scheduler:jvmTest and
./gradlew :cmp-worker-compose:jvmTest; they just don't contribute to Kover aggregation.
Revisit criteria: when Kover ships a release that supports the AGP 9 KMP-Android
library extension (track Kover #649
or the equivalent), bump kover in gradle/libs.versions.toml, apply
id("io.github.mobilebytelabs.kover") to both modules' plugins {} blocks, and remove the
explanatory comment block. The follow-up plan kover-agp9-opt-in-followup
carries the full checklist.
The root KoverConventionPlugin
delegates to configureKoverRootReports() in
Kover.kt,
which excludes:
excludes {
classes(
"*.di.*", // Koin DI declarations
"*.BuildConfig", // Android-generated BuildConfig
"*ComposableSingletons*", // Compose generated lambda holders
"*_*Factory*", // Generated factories
"*\$ComposableLambda\$*",
"*Preview*", // @Preview functions
"*Test*", // Test helpers themselves
)
packages(
"*.generated.*",
"*.ksp.*",
"*.android", "*.ios", "*.jvm", "*.js", "*.wasmJs", // platform actuals
)
annotatedBy("androidx.compose.runtime.Composable")
}The verify rule enforces:
verify {
rule { minBound(100) }
}This is LINE aggregation per the Kover 0.9.1 default; per-module — each opted-in module
gates its own bytecode.
- Add
id("io.github.mobilebytelabs.kover")to the new module'sbuild.gradle.ktsplugins {}block. - The convention plugin auto-registers it with the root aggregator via
rootProject.dependencies.add("kover", project). -
./gradlew :{new-module}:koverVerifywill start failing until 100% line coverage is reached. - If the new module is platform-heavy (mostly
androidMain/iosMain/ etc.), do NOT apply the plugin — add a row to the "Modules NOT in scope" table above and surface the gap in the Tier-2 follow-up epic.
For provably-uncoverable lines (e.g. sealed when synthetic else branches that the bytecode
verifier mis-counts as missed), two options:
-
Per-class exclusion — extend the
classes(...)list inKover.ktwith the FQN. Document the reason in a code comment. -
@ExcludeFromCoverage(future) — once the marker annotation lands, annotate the uncoverable member and reference it via the filter'sannotatedBy(...).
Every escape hatch must carry a code-comment rationale and a linked issue. The quarterly maintenance pass prunes stale exclusions.
The Test Coverage / Kover 100% gate job at
.github/workflows/test-coverage.yml runs on every
PR + push to main / development. It:
- Runs
./gradlew koverHtmlReport koverXmlReport koverVerify. - Uploads the aggregate HTML report as
kover-html-report(14-day retention). - Posts the aggregate
%to the PR Step Summary.
koverVerify exit ≠ 0 fails the PR. There is no soft-warn mode.
- Coverage epic plan:
plan-layer/project-plans/mbs/worker-kmp/active/kover-100-coverage/ - Tier-2 platform-engine tests:
worker-kmp-platform-engine-tests - Convention plugin pattern:
convention-plugin.md
Getting Started
Platform Support
Features
Operations
Release