41 lines
3.1 KiB
Python
41 lines
3.1 KiB
Python
# mutate.py <id>: apply one named mutant (each edit exact text, must match once). Run from the tree root.
|
|
import sys
|
|
CT = "packages/conversation/src/controller.mjs"
|
|
CO = "packages/conversation/src/cohort.mjs"
|
|
GUARD = " if (exec !== this.exec || !b || !this.claim?.record.engine || this.escalating || ending(this.stops.get(b.stop)?.mode)) return undefined;\n"
|
|
BINDADV = " if (ending(stop.mode)) this.b.state = state === \"uncertain\" ? \"uncertain\" : \"stopping\";\n"
|
|
ENDUNC = " if (stop.state !== \"superseded\") this.#advanceStop(stop, \"uncertain\");\n this.b.state = \"uncertain\";\n"
|
|
HEAD = "export async function engineExitCohort({ kind, unitName, invocationId, shimSocket, pid, start, settleMs = 2000 }) {\n if (kind !== \"scope\") return { outcome: \"unavailable\", reason: `a ${kind} cohort can't be enumerated completely` };\n const show = systemctlShow(unitName);\n"
|
|
INV = " if (!show || !invocationId || show.invocationId !== invocationId) {\n"
|
|
IDENT = " if (exit.pid !== pid || !Number.isInteger(exit.startTicks) || exit.startTicks < 1 || String(exit.startTicks) !== String(start) || exit.boot !== hello.boot) {\n"
|
|
M = {
|
|
"base": [],
|
|
# Dewey's five survivors, rewritten here from the packet's descriptions.
|
|
"noslotcheck": [(CT, GUARD, GUARD.replace("this.escalating || ", ""))],
|
|
"bindadvance": [(CT, BINDADV, BINDADV.replace("ending(stop.mode)", "stop.mode === \"force-stop\""))],
|
|
"emptyguard": [(CT, " if (s.mode === \"engine-exit\" && p.proof.members.length === 0) return false;\n", "")],
|
|
"nopopulated": [(CO, " if (e.populated === 0) {\n", " if (true) {\n")],
|
|
"nomembers": [(CO, " if (listed.members.length === 0) {\n", " if (true) {\n")],
|
|
# Mine. bindadvance plus #endStopUncertain not setting the binding: does
|
|
# any test see the binding follow an engine-exit stop to `uncertain`?
|
|
"bindboth": [(CT, BINDADV, BINDADV.replace("ending(stop.mode)", "stop.mode === \"force-stop\"")), (CT, ENDUNC, ENDUNC.replace(" this.b.state = \"uncertain\";\n", ""))],
|
|
# The engine-exit observation without the invocation ID check.
|
|
"noinv": [(CO, HEAD + INV, HEAD + " if (!show) {\n")],
|
|
# Without the hello's scope check.
|
|
"noscope": [(CO, " if (hello.invocationId !== invocationId || hello.scope !== show.controlGroup) return", " if (hello.invocationId !== invocationId) return")],
|
|
# Each half of the identity check alone, and the boot comparison.
|
|
"nopidcmp": [(CO, IDENT, IDENT.replace("exit.pid !== pid || ", ""))],
|
|
"nostartcmp": [(CO, IDENT, IDENT.replace(" || String(exit.startTicks) !== String(start)", ""))],
|
|
"noboot": [(CO, IDENT, IDENT.replace(" || exit.boot !== hello.boot", ""))],
|
|
# The engine-exit stop never moves from `fenced` to `stopping`.
|
|
"noadvance": [(CT, " this.#advanceStop(stop, \"stopping\");\n if (result.outcome !== \"proven\") return await fail(", " if (result.outcome !== \"proven\") return await fail(")],
|
|
}
|
|
if "--files" in sys.argv:
|
|
print(" ".join(sorted({f for f, _, _ in M[sys.argv[1]]}))); sys.exit(0)
|
|
for f, old, new in M[sys.argv[1]]:
|
|
s = open(f).read()
|
|
n = s.count(old)
|
|
if n != 1: sys.exit(f"{sys.argv[1]}: {n} matches in {f}")
|
|
open(f, "w").write(s.replace(old, new))
|
|
print(f)
|