Files

48 lines
2.5 KiB
Python

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