#!/usr/bin/env python3 # Row 113 (#1607): the brief's two mutants, one per fix. # m1 drops the id prefix check in t3.mjs, so every role "reasoning" row is # skipped, including one whose id doesn't start "reasoning:". # m2 stops the issue read after page 1, as if it were still one call. # Each runs the FULL ledger suite (node --test tests/*.test.mjs in # packages/ledger) and restores the source from the .orig copy whatever happens. # Usage: mutants.py import os, shutil, subprocess, sys, signal WT, LOGS = sys.argv[1], sys.argv[2] MUTANTS = { 'm1': ('packages/ledger/src/t3.mjs', "if (m.role === 'reasoning' && typeof m.message_id === 'string' && m.message_id.startsWith('reasoning:')) {", "if (m.role === 'reasoning') {"), 'm2': ('packages/ledger/src/ledger.mjs', " if (rows.length < PAGE_SIZE) return { issues: issues.filter(i => !i.pull_request), pages: page };\n", " return { issues: issues.filter(i => !i.pull_request), pages: page };\n"), } os.makedirs(LOGS, exist_ok=True) env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r113/tmp')) for name, (rel, cur, new) in MUTANTS.items(): src = os.path.join(WT, rel) text = open(src).read() if text.count(cur) != 1: sys.exit(f'STOP {name}: the target text in {rel} is not unique or has moved') if os.path.exists(src + '.orig'): sys.exit(f'STOP {rel}.orig already exists') log_path = os.path.join(LOGS, f'mutant-{name}.log') shutil.copy2(src, src + '.orig') try: open(src, 'w').write(text.replace(cur, new)) with open(log_path, 'w') as out: p = subprocess.Popen('node --test tests/*.test.mjs', shell=True, cwd=os.path.join(WT, 'packages/ledger'), 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_path).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 {name} rc={rc} {" ".join(counts)} restored={restored}') for f in sorted(set(failed)): print(' ' + f) print('KILLED' if rc != 0 else 'SURVIVED') if not restored: sys.exit(f'STOP {rel} was not restored')