#!/usr/bin/env python3 # Row 110 round 2 (#1604), Sage's ask in comment 28355: the narrowing mutant. # server.mjs:50 refuses an envelope that carries more than one of cap, human and # tester. The mutant narrows the conflict check so a cap counts only when it is # alone or is a reader cap (role null, not human). A role cap sent with a tester # proof then passes the envelope and the request runs as the tester. # Runs the FULL bus suite (node --test tests/*.test.mjs in packages/bus), not # one named test. Restores server.mjs from the .orig copy whatever happens. import os, shutil, subprocess, sys, signal WT = os.path.expanduser('~/darkwing-scratch/r110/wt') SRC = os.path.join(WT, 'packages/bus/src/server.mjs') LOG = sys.argv[1] OLD = " [r.cap, r.human, r.tester].filter(Boolean).length !== 1\n" NEW = (" [r.cap && (!(r.human || r.tester) || ((c) => { try { const i = broker.identity(c); " "return i.role === null && !i.human; } catch { return true; } })(r.cap)), r.human, r.tester]" ".filter(Boolean).length !== 1\n") text = open(SRC).read() if text.count(OLD) != 1: sys.exit('STOP the line at server.mjs:50 is not unique or has moved') if os.path.exists(SRC + '.orig'): sys.exit('STOP server.mjs.orig already exists') shutil.copy2(SRC, SRC + '.orig') try: open(SRC, 'w').write(text.replace(OLD, NEW)) env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r110/tmp')) with open(LOG, 'w') as out: p = subprocess.Popen('node --test tests/*.test.mjs', shell=True, cwd=os.path.join(WT, 'packages/bus'), stdout=out, stderr=subprocess.STDOUT, env=env, start_new_session=True) try: rc = p.wait(timeout=600) except subprocess.TimeoutExpired: os.killpg(p.pid, signal.SIGKILL) p.wait() rc = 'timeout' finally: shutil.move(SRC + '.orig', SRC) restored = open(SRC).read() == text log = open(LOG).read() counts = [l for l in log.splitlines() if l.startswith(('ℹ pass', 'ℹ fail', 'ℹ skipped'))] failed = [l.strip() for l in log.splitlines() if l.lstrip().startswith('✖') and 'failing tests' not in l] print(f'mutant n1 rc={rc} {" ".join(counts)} restored={restored}') for f in sorted(set(failed)): print(' ' + f) print('KILLED' if rc != 0 else 'SURVIVED')