3.4 KiB
3.4 KiB
ADR 0009: Kotlin/JVM Basis Support
- Status: accepted
- Date: 2026-10-11
- PR: #61
Context
The specification makes Kotlin/JVM a limited, optional goal (section 7). werkcrappit joins PIT's mutants and JaCoCo's complexity by JVM class, method name and descriptor, never by source lines. The Kotlin compiler turns one source function into several JVM methods, or into none of its own, so the question is what each construct gives in the report. Werkator, written in Kotlin, has run werkcrappit every night since PR#30 of werkcrappit, with a limit of 15.
Decision
werkcrappit supports Kotlin/JVM as it is, without code of its own for Kotlin.
KotlinBasicTest runs PIT with the packaged plugin on the fixture kotlin-basic and pins what each construct gives;
the versions are those in compatibility.md.
| Kotlin construct | JVM methods | In the report |
|---|---|---|
ordinary method, when, overloads |
one per function, overloads by descriptor | SCORED, each overload apart |
| top-level function | a static method of <File>Kt |
SCORED |
| companion object | methods of <Class>$Companion |
SCORED; its constructors EXCLUDED by JaCoCo |
| property with a getter | get…/is…, set… |
SCORED like any method; a setter without mutants has no score |
| default arguments | the function and a static …$default |
the function SCORED; …$default NO_MUTATION_RESULTS |
| data class | equals, hashCode, toString, copy, componentN |
EXCLUDED, jacoco-filtered:other or :synthetic |
| bridge of a generic override | a synthetic bridge method | EXCLUDED, jacoco-filtered:bridge |
| lambda passed to an inline function | none: inlined into the caller | counts for the caller |
| other lambda | a static …$lambda$0 |
SCORED apart; the function around it keeps only its own code |
| inline function | the function, and a copy in every caller | its own body never runs: its mutants stay NO_COVERAGE |
| suspend function | the function and a continuation class | SCORED; the continuation EXCLUDED; see the limits |
Limits
- Inline functions: PIT mutates the inline function itself, which no test runs, as every caller runs its own copy. Its score is then C² + C, whatever the tests do. The copies count for the callers, which carry those mutants.
- Suspend functions with a suspension point: JaCoCo leaves the compiler's state machine out of the complexity, but PIT mutates it; some of these mutants are never covered or survive, which lowers the mutation score. A suspend function can so score above its complexity although its tests check its logic.
- Kotlin's extra branches: JaCoCo counts branches the compiler adds,
e.g. the
elseof an exhaustivewhenor an inlinedlet. This raises the complexity, so a Kotlin project may set a higher limit than a Java one: Werkator uses 15, against 10 in hsadmin-ng. - Arcmutate's Kotlin support for PIT is not part of the tested setup.
Consequences
- The report can be read for Kotlin as for Java, with the limits above in mind; no Kotlin method gets an invented score.
- A new Kotlin, PIT or JaCoCo version can change the table: their filters change;
KotlinBasicTestshows it. - The specification's IDs
@MC-032and@MC-033for Kotlin are taken by the progress scenarios of04-pit-lifecycle.feature; the Kotlin scenarios use@MC-KT-…in11-kotlin-jvm.feature.