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
1.8 KiB
1.8 KiB
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, thenrunEnd(). 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
MethodIdplus PIT's mutator id and instruction indexes fromMutationIdentifier. The listener keeps only identities and status names, never PIT's result objects. - Aggregation.
MutantAggregatorcollects the set of statuses per mutant and decides atrunEnd():- one final status, however often delivered → counted once under it;
- several different final statuses → counted under none and reported as contradicted; the method becomes
INCONCLUSIVEwithcontradicting-results:<n>; NOT_STARTEDorSTARTEDbeside a final status → preliminary, gives way to the final one;- only preliminary statuses → counted under the latest of them, which makes the method
INCONCLUSIVEunderstrict-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.