Count the Robolectric tests, which JaCoCo has never counted
The three #52 test PRs landed 56 new tests and the coverage figure moved 29.8% -> 29.7%. That looked like the tests being worthless. It was the measurement. Robolectric loads every class it touches through its own sandbox classloader, and those classes arrive with no source location. JaCoCo skips no-location classes unless told otherwise, and nothing here told it. So not one Robolectric test has ever contributed coverage in this repo -- and Robolectric is what exercises the framework edge: both workers, the publisher, both ViewModels, every Compose screen. Same commit, same 335 tests, same 0 failures, only the block below added: LINE 652/2194 29.7% -> 1519/2194 69.2% BRANCH 425/1424 29.8% -> 758/1424 53.2% OutputPublisher 0.0% -> 97.5% MainActivityKt 6.8% -> 86.4% ConversionViewModel 0.0% -> 85.4% ConverterScreenKt 6.6% -> 62.8% The discriminator, so this is not cargo cult: inside ConverterScreenKt, `describe` is the one non-Composable and is exercised by a plain JVM test. It reported 8/8 covered while every @Composable in the same class reported 0 -- including ones whose mutations demonstrably failed the build when reverted. Across files the split is exactly Robolectric-vs-not: StagingSweep, tested purely, 100%; OutputPublisher, ConversionViewModel and FailureOutcome, tested under Robolectric, 0%. `excludes = listOf("jdk.internal.*")` is not decoration. Without it JaCoCo walks JDK-internal classes Robolectric has no location for either and the test JVM dies rather than reporting a number. CLAUDE.md's coverage bullet is rewritten, because it was wrong twice over. The figure was an artifact, and the explanation attached to it -- that coverage fell as the suite grew from 11 test files to 43 because the denominator outran the numerator on framework-edge code "the JVM cannot reach" -- described a cause that does not exist. The JVM reaches that code fine. The new tests were disproportionately Robolectric, so each one added denominator and no numerator: the measurement was punishing precisely the tests that were hardest to write, and the conclusion drawn from it was that writing them had not helped. Mutation, run both ways on this branch: remove the block and jacocoTestReport collapses back to 29.7% / 29.8%; restore it and it returns to 69.2% / 53.2%. Two things that were true stay true. There is still no coverage gate, and a floor still needs a settled baseline -- this one just moved 39 points in one build change. And "re-measure before quoting it" was already written down; following it is the only reason this was found.
This commit is contained in:
@@ -188,6 +188,30 @@ detekt {
|
||||
}
|
||||
|
||||
// Pin the coverage agent rather than inheriting whatever Gradle bundles.
|
||||
// Robolectric loads every class it touches through its own sandbox classloader, and those
|
||||
// classes arrive with no source location. JaCoCo skips no-location classes by default, so
|
||||
// without this block **not one Robolectric test counts** -- and Robolectric is what exercises
|
||||
// the framework edge here: the workers, the publisher, both ViewModels, every Compose screen.
|
||||
//
|
||||
// Measured on e06b082, same 335 tests, same 0 failures, only this block added:
|
||||
//
|
||||
// LINE 29.7% -> 69.2% OutputPublisher 0.0% -> 97.5%
|
||||
// BRANCH 29.8% -> 53.2% ConversionViewModel 0.0% -> 85.4%
|
||||
//
|
||||
// The discriminator, if this ever looks like superstition: inside ConverterScreenKt, `describe`
|
||||
// is the one non-Composable and is exercised by a plain JVM test -- it reported 8/8 covered while
|
||||
// every @Composable in the same class reported 0, including ones whose mutations demonstrably
|
||||
// failed the build when reverted.
|
||||
//
|
||||
// `excludes` is not optional. Without it JaCoCo walks JDK-internal classes that Robolectric has
|
||||
// no location for either, and the test JVM dies rather than reporting a number.
|
||||
tasks.withType<Test>().configureEach {
|
||||
extensions.configure<JacocoTaskExtension> {
|
||||
isIncludeNoLocationClasses = true
|
||||
excludes = listOf("jdk.internal.*")
|
||||
}
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = libs.versions.jacoco.get()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user