#!/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 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)