83 lines
4.9 KiB
Python
83 lines
4.9 KiB
Python
"""Row 97 independent verification. Do not run before its assigned test slot."""
|
||
import os, subprocess, signal, pathlib, json, hashlib, sys, time
|
||
root=pathlib.Path.home()/'filbert-scratch/r97-review/wt'
|
||
out=root.parent/'verification';out.mkdir(exist_ok=True)
|
||
env=dict(os.environ,TMPDIR=str(pathlib.Path.home()/'filbert-scratch/tmp'),DOCKER_HOST='unix:///nonexistent.sock')
|
||
seats=['darkwing','dewey','rocko','researcher','sage']
|
||
other=[str(pathlib.Path.home()/f'{seat}-scratch') for seat in seats]+[str(pathlib.Path('/mnt/storage/scratch')/seat) for seat in seats]
|
||
def quiet():
|
||
busy=[]
|
||
for p in pathlib.Path('/proc').iterdir():
|
||
if not p.name.isdigit():continue
|
||
try:
|
||
cwd=str((p/'cwd').resolve(strict=True))
|
||
if any(cwd==x or cwd.startswith(x+'/') for x in other):busy.append((p.name,cwd))
|
||
except (OSError,RuntimeError):pass
|
||
if busy:raise SystemExit('Other scratch processes active: '+repr(busy))
|
||
def matches():
|
||
m=pathlib.Path('/mnt/storage/src/mosaic-stack/agents/researcher/work/queue-97/candidate-manifest.sha256')
|
||
for line in m.read_text().splitlines():
|
||
sha,path=line.split();assert hashlib.sha256((root/path).read_bytes()).hexdigest()==sha,path
|
||
quiet();matches()
|
||
def active_group(group):
|
||
found=[]
|
||
for d in pathlib.Path('/proc').iterdir():
|
||
if not d.name.isdigit():continue
|
||
try:
|
||
if os.getpgid(int(d.name))==group:
|
||
stat=(d/'stat').read_text().split(') ',1)[1].split()[0]
|
||
if stat!='Z':found.append((d.name,(d/'comm').read_text().strip()))
|
||
except OSError:pass
|
||
return found
|
||
def run(cmd,cwd,log,limit=180):
|
||
quiet();start=time.monotonic()
|
||
with log.open('w') as f:
|
||
p=subprocess.Popen(cmd,cwd=cwd,env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True)
|
||
try:p.wait(timeout=limit);rc=p.returncode
|
||
except subprocess.TimeoutExpired:
|
||
rc='timeout'
|
||
try:os.killpg(p.pid,signal.SIGKILL)
|
||
except ProcessLookupError:pass
|
||
p.wait()
|
||
# Record normal cleanup BEFORE safety cleanup. Do not conceal leftover browser processes.
|
||
leftovers=active_group(p.pid)
|
||
if leftovers:
|
||
try:os.killpg(p.pid,signal.SIGKILL)
|
||
except ProcessLookupError:pass
|
||
return {'rc':rc,'seconds':round(time.monotonic()-start,2),'leftovers_before_safety_cleanup':leftovers}
|
||
rows=[];stage=sys.argv[1]
|
||
if stage in ['webui','control-board']:
|
||
cwd=root/'packages'/stage;r=run(['node','--test','tests/'],cwd,out/f'{stage}.log')
|
||
r.update(suite=stage,stats=[s for s in (out/f'{stage}.log').read_text().splitlines() if s.startswith(('ℹ tests ','ℹ pass ','ℹ fail ','# tests ','# pass ','# fail '))])
|
||
(out/f'{stage}-summary.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps(r),flush=True)
|
||
raise SystemExit(r['rc']!=0 or bool(r['leftovers_before_safety_cleanup']))
|
||
if stage=='shell':
|
||
for script in sorted((root/'scripts').glob('test-*.sh')):
|
||
r=run(['bash',str(script)],root,out/f'{script.stem}.log');r.update(suite=script.stem,last=(out/f'{script.stem}.log').read_text().splitlines()[-1:])
|
||
rows.append(r);print(json.dumps(r),flush=True)
|
||
(out/'shell-summary.json').write_text(json.dumps(rows,indent=2)+'\n')
|
||
raise SystemExit(any(r['rc']!=0 or r['leftovers_before_safety_cleanup'] for r in rows))
|
||
if stage!='mutants':raise SystemExit('usage: verify.py webui|control-board|shell|mutants')
|
||
A='packages/webui/src/public/app.js';B='packages/webui/src/public/bus.js'
|
||
M=[
|
||
('13 toggle-off schedule',A,'if (!paused && machine && !out()) timer','if (!paused && !out()) timer',['tests/board-held.test.mjs'],120),
|
||
('27 signed-out bus schedule',B,'if (!paused() && !out()) timer','if (!paused()) timer',['tests/board-held.test.mjs'],120),
|
||
('19 toggle on reads nothing',A,'fresh(); refresh(); } else hideMachine();','fresh(); } else hideMachine();',['tests/browser-edge.test.mjs'],30),
|
||
]
|
||
start={path:hashlib.sha256((root/path).read_bytes()).hexdigest() for path in [A,B]}
|
||
for name,path,old,new,tests,limit in M:
|
||
quiet();p=root/path;orig=p.read_bytes();src=orig.decode();assert src.count(old)==1,(name,src.count(old))
|
||
try:
|
||
p.write_text(src.replace(old,new));log=out/f'mutant-{name.split()[0]}.log'
|
||
r=run(['node','--test',*tests],root/'packages/webui',log,limit)
|
||
r.update(mutant=name,result='survived' if r['rc']==0 else 'stalled' if r['rc']=='timeout' else 'killed')
|
||
if name.startswith('19 '):r['five_second_message_seen']='Show machine sessions made no /api/board request in 5 s' in log.read_text()
|
||
rows.append(r);print(json.dumps(r),flush=True)
|
||
finally:p.write_bytes(orig)
|
||
assert all(hashlib.sha256((root/path).read_bytes()).hexdigest()==sha for path,sha in start.items())
|
||
matches()
|
||
r=run(['node','--test','tests/board-held.test.mjs','tests/browser-edge.test.mjs'],root/'packages/webui',out/'restored-tests.log')
|
||
r.update(restored_tests=True,manifest_matches=True,product_sources_restored=True);rows.append(r);print(json.dumps(r),flush=True)
|
||
(out/'mutant-summary.json').write_text(json.dumps(rows,indent=2)+'\n')
|
||
raise SystemExit(r['rc']!=0 or any(x.get('result') in ['survived','stalled'] or x['leftovers_before_safety_cleanup'] or x.get('five_second_message_seen') is False for x in rows))
|