werkator build succeeded after 00:35
Step 4 of 8 from section 8.3 of the specification. - `MutantAggregator` (core): statuses per distinct mutant (MethodId + mutator + instruction indexes); duplicates count once, contradicting final results count under none and make the method INCONCLUSIVE (`contradicting-results:<n>`), preliminary NOT_STARTED/STARTED give way to the final status, block order no longer matters. - Listener test with real PIT result objects and a real `ReportOptions`: merged blocks, byte-identical report for every order, duplicates, contradictions. - Functional test: every mutant of PIT's `mutations.xml` is counted exactly once; PIT's analysis ran once. - ADR 0003, feature 04, MC-014 to MC-018 mapped. CI on werkator.javagil.de: green (33 s), 79 unit and 10 functional tests. PR-doc: `docs/prs/2026-10-01-PR#5-result-aggregation.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #5
30 lines
1.8 KiB
Markdown
30 lines
1.8 KiB
Markdown
# ADR 0003: Result Aggregation
|
|
|
|
- Status: accepted
|
|
- Date: 2026-10-01
|
|
- PR: #5
|
|
|
|
## Context
|
|
|
|
PIT delivers results in `ClassMutationResults` blocks; one class may arrive in several of them.
|
|
PIT holds some results back and delivers them later, for example to consolidate inlined code.
|
|
The specification (4.5) forbids double counting, "last write wins" and confusing preliminary states with extra mutants.
|
|
|
|
## Decision
|
|
|
|
- **Threading.** PIT 1.30.0 calls every listener from one thread, in `MutationAnalysisExecutor.processResult`, then `runEnd()`.
|
|
The listener's methods stay synchronized anyway; its state lives in the listener instance, one per run, with no static caches.
|
|
- **Identity.** A mutant is identified by its `MethodId` plus PIT's mutator id and instruction indexes from `MutationIdentifier`.
|
|
The listener keeps only identities and status names, never PIT's result objects.
|
|
- **Aggregation.** `MutantAggregator` collects the set of statuses per mutant and decides at `runEnd()`:
|
|
- one final status, however often delivered → counted once under it;
|
|
- several different final statuses → counted under none and reported as contradicted; the method becomes `INCONCLUSIVE` with `contradicting-results:<n>`;
|
|
- `NOT_STARTED` or `STARTED` beside a final status → preliminary, gives way to the final one;
|
|
- only preliminary statuses → counted under the latest of them, which makes the method `INCONCLUSIVE` under `strict-v1`.
|
|
- **Order.** Statuses are kept as sets and results sorted by identity, so the report depends only on what arrived.
|
|
|
|
## Consequences
|
|
|
|
- The sum of all status counts in the report equals the number of mutants in PIT's `mutations.xml`, minus contradicted mutants; the functional test checks it.
|
|
- Which statuses count as preliminary is the policy's knowledge (`StatusTreatment.INCOMPLETE`), not the aggregator's.
|