40 lines
3.5 KiB
Python
40 lines
3.5 KiB
Python
# Row 63 round 2 review mutants (Dewey), round 1's ten unchanged, now with a process group per run: each swaps one exact string in the scratch copy, runs the tasks
|
|
# verbs and tree tests, restores the file, and reports killed (tests fail) or SURVIVED.
|
|
import subprocess, sys, os, signal
|
|
M = os.path.expanduser('~/dewey-scratch/r63mut2')
|
|
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
|
|
V = 'packages/tasks/src/verbs.mjs'
|
|
T = ['packages/tasks/tests/verbs.test.mjs', 'packages/tasks/tests/tree.test.mjs']
|
|
ALL = ['packages/tasks/tests/*.test.mjs']
|
|
muts = [
|
|
('located: first relation only', V, " for (const r of list) {", " for (const r of list.slice(0, 1)) {", ALL),
|
|
('located: a missing task passes', V, "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') fail(refusal(x));", "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') continue;", ALL),
|
|
('relation: keeps the root project', V, "return { kind: x.kind, other: other.task, project: other.project };", "return { kind: x.kind, other: other.task, project: ctx.project };", ALL),
|
|
('step: re-read on the root board', V, " after = await read(at);\n } catch {\n // the re-read failed too", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // the re-read failed too", ALL),
|
|
('handle: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below: a failed", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below: a failed", ALL),
|
|
('create: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below, as in handle()", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below, as in handle()", ALL),
|
|
('create: fallback todo of the root', V, "taskFields(r.json, at.view.ids.todo)", "taskFields(r.json, ctx.view.ids.todo)", ALL),
|
|
('create: authorize before located', V, " await located(plan.others);\n ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });", " ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });\n await located(plan.others);", ALL),
|
|
('installed: outside is not-installed', V, " if (!ctx.tree.has(project)) fail('task-project');\n", "", ALL),
|
|
('create: ref under the root', V, "const taskRef = ref(at.project, id);", "const taskRef = ref(ctx.project, id);", ALL),
|
|
]
|
|
only = sys.argv[1:]
|
|
for name, f, old, new, tests in muts:
|
|
if only and not any(o in name for o in only): continue
|
|
p = os.path.join(M, f); src = open(p).read()
|
|
n = src.count(old)
|
|
if n != 1: print(f'BAD-ANCHOR ({n}) {name}', flush=True); continue
|
|
open(p, 'w').write(src.replace(old, new))
|
|
proc = subprocess.Popen(['node', '--test', *tests], cwd=M, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True)
|
|
try:
|
|
out, _ = proc.communicate(timeout=300)
|
|
except subprocess.TimeoutExpired:
|
|
os.killpg(proc.pid, signal.SIGKILL); proc.communicate()
|
|
out = None
|
|
finally:
|
|
open(p, 'w').write(src)
|
|
if out is None:
|
|
print(f'killed {name} (hung: killed after 300 s)', flush=True); continue
|
|
fails = [l for l in out.splitlines() if l.startswith('not ok')]
|
|
print(('killed ' if proc.returncode else 'SURVIVED') + f' {name}' + (f' ({len(fails)} failing)' if fails else ''), flush=True)
|