SAFE in packages/seat/src/checkout.mjs adds maintenance.auto=false and gc.auto=0; probe, tests, mutants m1-m3 (all killed), targeted gate (seat 132/0, cli 184/0) and the leftover sweep. Candidate is build.patch against 2727dd3ec; manifest digests check out. Co-Authored-By: Claude Opus 5.5 <[email protected]>
72 lines
3.0 KiB
Python
Executable File
72 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""Row 118 mutants: drop each new SAFE key, and both. Each edits checkout.mjs,
|
||
runs packages/seat/tests/checkout.test.mjs, and restores the file from .orig."""
|
||
import hashlib, os, re, shutil, signal, subprocess, sys
|
||
|
||
WT = os.path.expanduser('~/darkwing-scratch/r118/wt')
|
||
PKT = os.path.expanduser('~/darkwing-scratch/r118/pkt')
|
||
TMP = os.path.expanduser('~/darkwing-scratch/r118/tmp')
|
||
F = 'packages/seat/src/checkout.mjs'
|
||
MA = ' "-c", "maintenance.auto=false",\n'
|
||
GC = ' "-c", "gc.auto=0",\n'
|
||
M = [
|
||
('m1-drop-maintenance-auto', [MA]),
|
||
('m2-drop-gc-auto', [GC]),
|
||
('m3-drop-both', [MA, GC]),
|
||
]
|
||
|
||
def sha(p):
|
||
return hashlib.sha256(open(p, 'rb').read()).hexdigest()
|
||
|
||
# A detached `maintenance run` a mutant started runs in a test's temp dir;
|
||
# kill only those, by cwd under this row's TMPDIR.
|
||
def sweep():
|
||
left = []
|
||
for pid in filter(str.isdigit, os.listdir('/proc')):
|
||
try:
|
||
if os.readlink(f'/proc/{pid}/cwd').startswith(TMP + '/'):
|
||
left.append(int(pid)); os.kill(int(pid), signal.SIGKILL)
|
||
except OSError:
|
||
pass
|
||
return left
|
||
|
||
only = set(sys.argv[1:])
|
||
os.chdir(WT)
|
||
env = dict(os.environ, TMPDIR=TMP, NO_COLOR='1')
|
||
base = sha(F)
|
||
os.makedirs(os.path.join(PKT, 'mutants'), exist_ok=True)
|
||
log = open(os.path.join(PKT, 'mutants-run.log'), 'a')
|
||
for name, drops in M:
|
||
if only and name not in only:
|
||
continue
|
||
src = open(F).read()
|
||
if any(src.count(d) != 1 for d in drops):
|
||
print(f'{name}: pattern not found once, skipped', file=log, flush=True); print(name, 'BAD PATTERN'); continue
|
||
shutil.copy2(F, F + '.orig')
|
||
try:
|
||
out = src
|
||
for d in drops:
|
||
out = out.replace(d, '')
|
||
open(F, 'w').write(out)
|
||
p = subprocess.Popen(['node', '--test', 'tests/checkout.test.mjs'], cwd='packages/seat', stdout=subprocess.PIPE,
|
||
stderr=subprocess.STDOUT, env=env, start_new_session=True, text=True)
|
||
try:
|
||
res, _ = p.communicate(timeout=600)
|
||
except subprocess.TimeoutExpired:
|
||
os.killpg(p.pid, signal.SIGKILL); res, _ = p.communicate(); res += '\nTIMEOUT\n'
|
||
open(os.path.join(PKT, 'mutants', f'mutant-{name}.log'), 'w').write(res)
|
||
fails = sorted({l[2:] for l in res.splitlines() if l.startswith('✖ ') and l != '✖ failing tests:'})
|
||
tally = dict(re.findall(r'^ℹ (pass|fail) (\d+)$', res, re.M))
|
||
verdict = 'killed' if p.returncode != 0 else 'SURVIVED'
|
||
print(f'{name}: {verdict} (rc {p.returncode}, pass {tally.get("pass", "?")}, fail {tally.get("fail", "?")})', file=log, flush=True)
|
||
for l in fails:
|
||
print(f' fail: {l}', file=log, flush=True)
|
||
left = sweep()
|
||
if left:
|
||
print(f' swept {len(left)} leftover process(es) under TMPDIR: {left}', file=log, flush=True)
|
||
print(name, verdict, tally, flush=True)
|
||
finally:
|
||
shutil.move(F + '.orig', F)
|
||
assert sha(F) == base, f'{F} not restored'
|
||
print('restored:', sha(F) == base, file=log, flush=True)
|