120 lines
5.7 KiB
Markdown
120 lines
5.7 KiB
Markdown
# PR#64: Mutation Tests in Each Build, in Full Each Night
|
|
|
|
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
|
|
|
|
## Related Links
|
|
|
|
- [PR#59](https://git.javagil.de/mi/werkregie/pulls/59), which brought the other required checks.
|
|
- hs.admin.ng, whose split this PR follows: a partial run in each branch build, a full run each night.
|
|
|
|
## Problem
|
|
|
|
The shared workflow requires mutation tests for Kotlin: werkcrappit measures the Mutation-CRAP of each method,
|
|
and the gate `maxMutationCrap` fails the build above the limit.
|
|
werkregie has no mutation tests yet; they are the last required check it lacks.
|
|
A nightly run alone finds a weakly tested method only after the merge; each branch build shall find it before.
|
|
|
|
## Non-Goals
|
|
|
|
- No refactoring of the 67 methods above 15; they follow in PRs of their own.
|
|
- No public CodeCharta; it stays an artifact of the nightly build in Werkator.
|
|
- No thresholds of PIT itself, e.g. `mutationThreshold`; werkcrappit's gate decides.
|
|
|
|
## Specification
|
|
|
|
### Feature: mutation tests in each build, in full each night
|
|
|
|
#### Scenario#64.01: a branch build judges only the files the branch changes
|
|
|
|
… so that the mutation tests fit into each branch build.
|
|
|
|
- **Given** a branch that changes only the file `Summaries.kt`
|
|
- **When** Werkator builds the branch
|
|
- **Then** the build judges the Mutation-CRAP of the methods in `Summaries.kt` only, nested classes included
|
|
|
|
##### Verified by
|
|
|
|
- Manual: `pitestPartial mutationCrapCheck` with a changed `Summaries.kt`: 77 mutants, 43 s;
|
|
PIT selected 8 classes: `Summarizer`, `ChatSummarizer`, `Summaries` and their nested ones.
|
|
|
|
#### Scenario#64.02: a weakly tested method in a changed file fails the build
|
|
|
|
… so that each change leaves the files it touches at the target.
|
|
|
|
- **Given** a branch that changes the file `Summaries.kt`, whose method `Summaries.of` has a Mutation-CRAP of 16.4
|
|
- **When** Werkator builds the branch
|
|
- **Then** the build fails and names `Summaries.of`, since 16.4 is above 15
|
|
|
|
##### Verified by
|
|
|
|
- Manual: the same run failed with `Summaries.of … has 16.4282 > 15`.
|
|
|
|
#### Scenario#64.03: a branch without changed code judges nothing
|
|
|
|
… so that a change of documents or configuration costs no mutation tests.
|
|
|
|
- **Given** a branch that changes only documents and the build configuration
|
|
- **When** Werkator builds the branch
|
|
- **Then** the build judges no Mutation-CRAP and passes
|
|
|
|
##### Verified by
|
|
|
|
- Manual: `pitestPartial mutationCrapCheck` on this branch: `pitest` and `mutationCrapCheck` skipped.
|
|
- Werkator's build of this PR: the same.
|
|
|
|
#### Scenario#64.04: main is judged in full each night
|
|
|
|
… so that the CodeCharta shows all of main and no method gets worse unnoticed.
|
|
|
|
- **Given** main, whose highest Mutation-CRAP is 1806
|
|
- **When** Werkator builds main at night
|
|
- **Then** the build judges all methods and keeps the CodeCharta of all classes
|
|
|
|
##### Verified by
|
|
|
|
- Manual: `pitestFull mutationCrapCheck`: 187 classes, 3099 mutants, 12 min 54 s with 2 threads;
|
|
the highest value, 1806, is `GiteaSource.pullRequestItem`.
|
|
- Werkator's build `mutation` on main.
|
|
|
|
## Solution
|
|
|
|
- `dashboard/build.gradle.kts`: the plugins `info.solidsoft.pitest` with PIT 1.30.0 and Kotest's PIT extension,
|
|
and `de.javagil.werkcrappit` 0.26.12 from the Maven registry of git.javagil.de.
|
|
- `pitestPartial` mutates the classes of the Kotlin files the branch changes against the merge base with `origin/main`,
|
|
uncommitted changes included; `-PmutationBaseline` names another ref.
|
|
Per file it takes the top-level classes and the file class `<Name>Kt`, each with its nested classes.
|
|
Without a changed file, `pitest` and `mutationCrapCheck` are skipped.
|
|
- `pitestFull` mutates all classes.
|
|
- `dashboard/gradle/gradle-daemon-jvm.properties`, written by `updateDaemonJvm`: Gradle runs on Temurin 25 from Foojay,
|
|
since werkcrappit's plugin needs Java 21 and Werkator starts Gradle with the host's Java 17.
|
|
- The gate: 15 for `pitestPartial`, the target for Kotlin;
|
|
1806 for `pitestFull`, a ratchet from today's highest value, lowered with each refactoring.
|
|
- `.werkator.yml`:
|
|
- the build `default` runs `pitestPartial mutationCrapCheck` after `build`; its timeout rises from 10 to 20 min;
|
|
- the nightly build `mutation` runs `pitestFull mutationCrapCodeCharta mutationCrapCheck` on main at 04:00 UTC,
|
|
with the status `werkator/mutation`.
|
|
- `doc/agentic-workflow.md`, the master copy: each branch build mutates the classes the branch changes,
|
|
a nightly build all classes; only a branch build made too slow may leave them to the night, and the cause is fixed.
|
|
- `doc/risk-analysis.md` and `AGENTS.md` follow.
|
|
- Version 0.41.0.
|
|
|
|
PIT's incremental history, which hs.admin.ng uses, does not suit Kotest.
|
|
Without any change, a second run took 6 min 17 s: it kept only the 520 surviving and timed-out mutants
|
|
and ran the tests of all 1593 killed ones again.
|
|
The killing test of a Kotest spec reads e.g. `…JournalProposerTest.…JournalProposerTest/one entry per Taiga story…`,
|
|
which the history does not recognize as an unchanged test class.
|
|
|
|
## Open Questions
|
|
|
|
- [x] Shall the branch build use PIT's incremental history, as hs.admin.ng does?
|
|
No, chosen by the human after the measurement: `git diff` against the merge base with main.
|
|
- [x] Which limit holds in the branch build and each night?
|
|
Chosen by the human: 15 for the classes a branch changes; each night a ratchet from today's highest value.
|
|
|
|
## Follow-up PRs
|
|
|
|
- Refactorings and tests that bring each method of werkregie to a Mutation-CRAP of at most 15.
|
|
Each of these PRs lowers the limit of the nightly build to the new highest value.
|