Skip to content

Commit 63668a8

Browse files
committed
docs(pitest): document audited timeout mutants in README
- Add detailed explanations of `TIMED_OUT` mutants, covering causes and structural behavior. - List audited set members with rationale for each timeout scenario. - Provide context for mutation triage and timeout classification process.
1 parent a8eb7c1 commit 63668a8

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

json-iterator/config/pitest/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,50 @@ timeout). The accepted remainder is equivalent by construction:
193193
it (invalid input throws first), so the copy is defensive; forcing it is
194194
allocation-only.
195195

196+
## Timed-out mutants (audited set)
197+
198+
`TIMED_OUT` is detected — these mutants never enter a baseline — but the
199+
watchdog observed slowness, not wrongness: for exactly these mutants the
200+
ratchet cannot see a weakened covering assertion, because a timeout keeps
201+
"detecting" no matter what the test asserts. Per HARDENING.md, the summary's
202+
`N timed out (load-dependent)` is therefore an audited set, not a count: every
203+
member is listed here with the structural cause that makes it spin, and a
204+
mutant timing out that is *not* on this list is something a reviewer stops on.
205+
Members flip `KILLED``TIMED_OUT` run to run — the covering test reaching a
206+
failure races the watchdog over a dead mutant, benign in both directions — so
207+
per-run counts sit at or below the set size. (The 2026-07-21 convergence check
208+
recorded 7 iterator members; one has since settled to `KILLED`.)
209+
210+
As of 2026-07-26 — 8 members, 6 iterator + 2 util, numbers none:
211+
212+
**iterator**
213+
- `BaseJsonIterator.reduceScale:1021`, two mutants (`scale--``scale++`;
214+
loop condition → `true`): the counter crossing the negative `scaleLimit` is
215+
the loop's only exit; both remove it and the divide loop spins on a settled
216+
quotient of 0.
217+
- `BaseJsonIterator.skipObject:1122` (scan cursor `i++``i--`): the
218+
`i == tail` bound is an equality a backward walk never meets, and the
219+
string-skip re-entry (`i = head - 1` after `skipPastEndQuote()`) can pull
220+
the cursor back into the same cycle indefinitely.
221+
- `BytesJsonIterator.parseMultiByteString:575` (escape-decode `buf[head++]`
222+
`buf[head--]`): the cursor backs away from the `head == tail` guard and
223+
re-decodes earlier bytes; the spin races the eventual bounds fault, which is
224+
why this member often lands `KILLED` instead.
225+
- `CharsJsonIterator.parse:136` (escape skip `++i``--i`): cancels the for
226+
loop's own `++i`, pinning the cursor on the same backslash — a pure
227+
oscillation with no exit and no fault.
228+
- `CharsJsonIterator.skipPastEndQuote:156` (`buf[head++]``buf[head--]`):
229+
same reversed-cursor family as `parseMultiByteString:575`.
230+
231+
**util**
232+
- `JIUtil.escapeQuotesChecked:170` (do-while `++from``--from`): after an
233+
odd, already-escaped quote the backward `from` makes `indexOf('"', from)`
234+
re-find the same quote every pass (a negative fromIndex clamps to 0), so
235+
the loop never reaches `len`.
236+
- `FieldMatcher.of:51` (`names.length << 2``>> 2`): collapses the table
237+
capacity below the entry count, and the linear-probe insert loop exits only
238+
on an empty slot or a duplicate name — a full table offers neither.
239+
196240
## Mutator-set trial (2026-07-21)
197241

198242
Per HARDENING.md ("the mutator set bounds what the ratchet can see"),

0 commit comments

Comments
 (0)