Adds wizard-work/cassian/q84_mutants.py (harness, rc 0). Written as '**Q<n> addendum' lines (the Q129/Q156 addendum form), so open_set.py's pin does not move: --check -> counts match the pin. docs/queue-index.md re-rendered with tools/queue_index.py --write in this commit.
47 lines
3.0 KiB
Python
47 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""q84_mutants.py -- re-grade Q84's cells (wizard-work/beam/test_interval_overlap.py) against
|
|
five named mutants of interval_overlap.py, plus an independent oracle (integer-set
|
|
intersection, not the module's own brute_force_pairs). Read-only: each mutant runs in a
|
|
fresh temp dir; nothing in the tree is edited. Default subject is the beam lane beside
|
|
this file (Path(__file__)-relative); pass another beam dir as argv[1].
|
|
|
|
python3 wizard-work/cassian/q84_mutants.py
|
|
"""
|
|
import itertools, os, shutil, subprocess, sys, tempfile
|
|
SRC = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "beam")
|
|
orig = open(os.path.join(SRC, "interval_overlap.py")).read()
|
|
MUT = {
|
|
"M1 kind ordering flipped": ("events.sort(key=lambda x: (x[0], x[1]))", "events.sort(key=lambda x: (x[0], -x[1]))"),
|
|
"M2 s>=e skip removed (pairs)": (" if s >= e: # zero-length interval: empty set, never overlaps\n continue\n for other", " for other"),
|
|
"M3 start-pair loop removed": (" for other in active:\n a, b = (idx, other) if idx < other else (other, idx)\n pairs.add((a, b))\n", ""),
|
|
"M4 has_any_overlap -> pair-list body": (" iv = list(intervals)\n for s, e in iv:\n if e < s:\n raise ValueError(f\"end < start: {(s, e)}\")\n # Sorted", " return bool(find_overlapping_pairs(intervals))\n iv = []\n # Sorted"),
|
|
"M5 boundary s<max_end -> s<=max_end": ("s < max_end:", "s <= max_end:"),
|
|
}
|
|
survivors = []
|
|
for name, (a, b) in MUT.items():
|
|
assert orig.count(a) == 1, (name, orig.count(a))
|
|
d = tempfile.mkdtemp()
|
|
shutil.copy(os.path.join(SRC, "test_interval_overlap.py"), d)
|
|
open(os.path.join(d, "interval_overlap.py"), "w").write(orig.replace(a, b))
|
|
r = subprocess.run([sys.executable, "-m", "unittest", "test_interval_overlap"], cwd=d, capture_output=True, text=True, timeout=600)
|
|
dead = sorted({l.split()[1] for l in r.stderr.splitlines() if l.startswith(("FAIL:", "ERROR:"))})
|
|
print(f"{name}: rc {r.returncode}, dead {len(dead)}: {', '.join(dead)}")
|
|
if r.returncode == 0:
|
|
survivors.append(name)
|
|
# independent oracle, not the module's brute_force_pairs
|
|
sys.path.insert(0, SRC)
|
|
import interval_overlap as m
|
|
pts = range(-2, 4); ivs = [(s, e) for s in pts for e in pts if e >= s]
|
|
n = mism = 0
|
|
for k in range(0, 4):
|
|
for combo in itertools.product(ivs, repeat=k):
|
|
truth = sorted((i, j) for i in range(k) for j in range(i+1, k)
|
|
if set(range(combo[i][0], combo[i][1])) & set(range(combo[j][0], combo[j][1])))
|
|
n += 1
|
|
if m.find_overlapping_pairs(combo) != truth or m.has_any_overlap(combo) != bool(truth):
|
|
mism += 1
|
|
print(f"oracle (integer-set intersection, ordered tuples over [-2,3], size 0-3): {n} cases, {mism} mismatches")
|
|
|
|
print(f"mutants: {len(MUT)}, survived {len(survivors)}: {survivors}")
|
|
sys.exit(0 if mism == 0 and not survivors else 1)
|