|
| 1 | +# 2026-09-17 — Is each fixture broken in exactly one way? |
| 2 | + |
| 3 | +`taskId: rustjava-adopt-class-format-mutation-audit-p0` · |
| 4 | +adopts `2026-09-16-class-format-mutation-audit#p0` |
| 5 | + |
| 6 | +The mutation audits so far asked one half of the question: *does each test fail when the thing it |
| 7 | +names is broken?* This is the other half — **is each fixture broken in exactly one way?** |
| 8 | + |
| 9 | +It matters because the failure is silent. A fixture that is wrong twice keeps its test green after |
| 10 | +one of the two is repaired, and nothing anywhere says that the other stopped being covered. |
| 11 | + |
| 12 | +## The test, and why byte equality rather than loading |
| 13 | + |
| 14 | +The proposal suggested "remove the named defect and check the file now loads". Every fixture here |
| 15 | +is produced by a generator that takes the defect **as a parameter**, which makes a stronger test |
| 16 | +available at lower cost: |
| 17 | + |
| 18 | +``` |
| 19 | +generator(name, …defect…) == the committed fixture (the generator still describes it) |
| 20 | +generator(name, …repaired…) == generator(name, …canonical…) (and nothing else differs) |
| 21 | +``` |
| 22 | + |
| 23 | +Stronger, because a second defect our loader happens not to care about would pass a loading test |
| 24 | +and fail this one. Cheaper, because it needs no JVM — it is bytes against bytes. |
| 25 | + |
| 26 | +## Result |
| 27 | + |
| 28 | +``` |
| 29 | +검사 18건 · 결함 없음(유효 파일) 5건 · 합계 23건 · rc=0 |
| 30 | +``` |
| 31 | + |
| 32 | +**All 18 defective fixtures are single-defect.** The five others carry no defect at all and are |
| 33 | +listed rather than skipped, so the census stays complete: |
| 34 | + |
| 35 | +| family | checked | axis examples | |
| 36 | +|---|---|---| |
| 37 | +| `indy` near misses | 6 | owning class · method name · descriptor · reference kind · static argument | |
| 38 | +| `ldc` | 9 | `ldc2_w` width · illegal tags 13/14/19 · class file version · missing BootstrapMethods · static argument index · bootstrap index · duplicate table | |
| 39 | +| `cp` | 3 | tag 13 / 14 / 19 | |
| 40 | +| no defect | 5 | `MakeConcat`, `LdcMethodHandle`, `LdcMethodType`, `LdcDynamic`, `Ldc2WDynamic` — valid files whose feature is unimplemented | |
| 41 | + |
| 42 | +This is the "row of already fine" the proposal predicted. It is still worth having: the discipline |
| 43 | +had been applied per round and **never measured across the corpus**, so until now "all our fixtures |
| 44 | +carry one defect" was a habit, not a fact. |
| 45 | + |
| 46 | +## What it does not cover — said in the script, not only here |
| 47 | + |
| 48 | +* **javac output** (`Lambda`, `StringConcat`, `ConstantKinds`, `LambdaKinds`) — not defective; |
| 49 | + "exactly one" does not apply to zero. |
| 50 | +* **Legal-but-unimplemented** files — same; the file is valid and the feature is missing. |
| 51 | +* **Fixtures byte-patched inside a test** — they never exist on disk, so this script cannot read |
| 52 | + them. Their single-defectness rests on the comment beside each, which is weaker, and that is the |
| 53 | + honest limit of this round. |
| 54 | + |
| 55 | +## Mutations |
| 56 | + |
| 57 | +| | mutation | result | |
| 58 | +|---|---|---| |
| 59 | +| **M1** | plant a second defect (`NotFactoryDescriptor` also gets reference kind 7) | **rc=1** · `MULTI-DEFECT — 수리 후에도 1 바이트 남는다` | |
| 60 | +| **M2** | flip one byte of a committed fixture | **rc=2** · `GENERATOR DRIFT — 커밋본을 재현하지 못한다` | |
| 61 | + |
| 62 | +M2 is the half that keeps the first equation honest: if the generator stops describing the |
| 63 | +committed bytes, every verdict above is vacuous, and the script says so instead of passing. |
0 commit comments