fix(mutants): a surviving mutant exits 1 (H4)
`verdict(survived, errors)` is the one place the run's exit code is decided: 2 when a mutant could not be applied (it was never measured, and that outranks everything), 1 when one survived, 0 otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6abe4b4358
commit
f5b263f5ef
1 changed files with 20 additions and 1 deletions
|
|
@ -18,6 +18,10 @@ edited, so an interrupted run cannot leave a mutant behind. A pattern that does
|
|||
not match the expected number of times is reported as an ERROR and the run
|
||||
exits 2 -- a mutant that could not be applied was never measured, and counting
|
||||
it as killed is the same mistake as reading an empty search as an absence.
|
||||
|
||||
A SURVIVOR EXITS 1. Until 2026-09-19 the run ended `2 if errors else 0`, so
|
||||
`killed 0 of 1` with the survivor printed beside it was an exit 0 and nothing
|
||||
could fail on the finding this harness exists to produce (H4).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -26,6 +30,7 @@ import shutil
|
|||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -267,6 +272,20 @@ def _apply(text: str, mutant: Mutant) -> str:
|
|||
return text.replace(mutant.old, mutant.new, 1 if mutant.first_only else -1)
|
||||
|
||||
|
||||
def verdict(survived: Sequence[str], errors: Sequence[str]) -> int:
|
||||
"""The run's exit code: 0 clean, 1 a mutant survived, 2 one was not measured.
|
||||
|
||||
Until 2026-09-19 this was `2 if errors else 0`, so a run that printed
|
||||
`killed 0 of 1` and named its survivor exited 0 and no caller could fail
|
||||
on it (H4). A mutant that survived names a check nothing holds, which is
|
||||
the finding this harness exists to produce; a mutant that could not be
|
||||
applied was never measured at all, and that outranks it.
|
||||
"""
|
||||
if errors:
|
||||
return 2
|
||||
return 1 if survived else 0
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
del argv
|
||||
survived: list[str] = []
|
||||
|
|
@ -311,7 +330,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
print(f" survived: {label}")
|
||||
for problem in errors:
|
||||
print(f" ERROR: {problem}")
|
||||
return 2 if errors else 0
|
||||
return verdict(survived, errors)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue