#!/usr/bin/env python3 # Row 300 mutant M12 (BUILD.md "Mutant"): serve.mjs keeps the owner's send for a user session. The # edit must match exactly once; the new file runs alone under timeout -k 10 300 with the TAP # reporter; git checkout restores and the tree must be clean. Killed = "R6-N1" reports "not ok". import json, os, re, subprocess, sys, time WT = '/mnt/storage/scratch/launchpad-r300/wt' LOG = '/mnt/storage/scratch/launchpad-r300/run1' SWAP = "({ bus, send, settingsWrite, projectFiles, projectFile, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing } = userOptions(session));" MUTANTS = [ ('M12', 'packages/webui/src/serve.mjs', SWAP, "({ bus, settingsWrite, projectFiles, projectFile, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing } = userOptions(session));", ('packages/webui', 'tests/user-option-swap.test.mjs'), 'R6-N1'), ] def git(*a): return subprocess.run(['git', '-C', WT, *a], capture_output=True, text=True, check=True).stdout def main(): if git('status', '--porcelain'): raise SystemExit('worktree not clean at start') summary, bad = [], 0 for mid, path, old, new, (pkg, test), killer in MUTANTS: p = os.path.join(WT, path) try: s = open(p).read() if s.count(old) != 1: raise SystemExit(f'{mid}: anchor count {s.count(old)} in {path}') open(p, 'w').write(s.replace(old, new)) diff = git('diff') t0 = time.time() r = subprocess.run(['timeout', '-k', '10', '300', 'node', '--test', '--test-reporter=tap', test], cwd=os.path.join(WT, pkg), capture_output=True, text=True) dt = round(time.time() - t0, 1) finally: git('checkout', '--', path) if git('status', '--porcelain'): raise SystemExit('worktree not clean after restore') open(f'{LOG}/{mid}.diff', 'w').write(diff) open(f'{LOG}/{mid}.tap', 'w').write(r.stdout + '\n# stderr\n' + r.stderr) res = {m.group(2): m.group(1) == 'ok' for m in re.finditer(r'^(ok|not ok) \d+ - (.*)$', r.stdout, re.M)} hit = [n for n in res if n.startswith(killer)] killed = r.returncode not in (0, 124, 137) and bool(hit) and all(not res[n] for n in hit) bad += not killed print(f'{mid} rc={r.returncode} seconds={dt} killed={killed} red={[n for n, ok in res.items() if not ok]}', flush=True) summary.append({'mutant': mid, 'rc': r.returncode, 'seconds': dt, 'killed': killed, 'killer': killer}) json.dump(summary, open(f'{LOG}/mutants.json', 'w'), indent=1) sys.exit(1 if bad else 0) main()