Files
deusdata--codebase-memory-mcp/tests/test_parallel_harness_contract.sh
Martin Vogel ae52db3147 test: make three Windows nondeterminisms deterministic
All three cost real release cycles, and none is fixed by widening a budget.

daemon_application_cancels_physical_job_only_after_final_session waited for the
SUBSCRIBER COUNT to reach 2, then cancelled both sessions and asserted the
physical job had started exactly once. The job starts asynchronously after
subscription, so both cancels could land first and leave starts == 0 --
arguably the correct outcome. It now waits for the state the assertions
actually require. Verified 47/47 on Windows, the only platform it ever failed.

The parallel harness refused outright when the suite leader had already exited,
because taskkill /T cannot walk a tree from a dead PID. But the leader can exit
between the timeout decision and that call, so the harness itself lost a race:
a natural exit at the wrong moment failed the whole wave. It now proves cleanup
the only way still available -- nothing parented to that PID -- and its contract
asserts the PROPERTY rather than the phrase "tree cleanup" it used to grep for.
That string pin is what broke when the guard was reworded while behaving
correctly; the contract now checks rc==2 AND that the descendant really did
survive, which would also catch a guard that claims to fail closed while
leaking.

extract_wide_flat_file_is_linear took ONE sample per size, so the ratio carried
the noise of both. On a loaded Windows VM linear code measured 51x against a 40x
bound (184ms -> 9387ms). Best-of-N instead: timing noise only ever adds time, so
the minimum is the cheapest good estimate of the noise-free cost. The bound is
deliberately unchanged -- it sits where linear (~20x) and quadratic (~128x) are
each >=2x away, so raising it would move the test toward the very signal it
exists to catch. Now measures 19.1x on Windows, 21.4x on macOS.

Also: the smoke's `cli` helper redirected stderr to a file and discarded it, so
any of the 10 bare `VAR=$(cli ...)` assignments could kill the run under
`set -euo pipefail` printing NOTHING. One such abort cost a full Windows cycle
just to locate and still could not be attributed. It now surfaces the command
and its stderr. Neutral wording on purpose: one call site expects a non-zero
exit and must not read as a failure.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-29 17:05:10 +02:00

421 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
# Regression contract for the cross-platform parallel suite scheduler.
#
# On real Windows/MSYS2, a native suite completed and printed its green
# summary, but the exported `run_one` function's nested `bash -c` worker never
# returned to xargs or appended its result. The native process was gone; the
# orphaned MSYS shell could not be terminated, so the whole gate waited
# forever. Keep process ownership in one Python parent and forbid that nested
# shell-worker shape from returning.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
driver="$ROOT/scripts/run-tests-parallel.sh"
scheduler="$ROOT/scripts/run-test-wave.py"
fixture="$(mktemp -d "${TMPDIR:-/tmp}/cbm-parallel-harness.XXXXXX")"
trap 'rm -rf -- "$fixture"' EXIT
if grep -Eq '(^|[[:space:]])xargs([[:space:]]|$)|export[[:space:]]+-f|bash[[:space:]]+-c' \
"$driver"; then
echo "FAIL: parallel harness must not put native runners behind nested MSYS bash workers" >&2
exit 1
fi
if [ ! -f "$scheduler" ]; then
echo "FAIL: parent-owned parallel scheduler is missing: $scheduler" >&2
exit 1
fi
if ! grep -Fq 'run-test-wave.py' "$driver"; then
echo "FAIL: parallel harness is not wired to the parent-owned scheduler" >&2
exit 1
fi
cat >"$fixture/fake_runner.py" <<'PY'
from __future__ import annotations
import os
import pathlib
import signal
import subprocess
import sys
import time
pid_path = pathlib.Path(sys.argv[1])
suite = sys.argv[-1]
if suite == "hang_after_summary":
print(" 1 passed", flush=True)
time.sleep(30)
elif suite in ("stubborn_tree", "timeout_exit_race"):
child = subprocess.Popen(
[
sys.executable,
"-c",
(
"import os,signal,time;"
"signal.signal(signal.SIGTERM,signal.SIG_IGN) "
"if os.name != 'nt' else None;"
"time.sleep(30)"
),
]
)
pid_path.write_text(str(child.pid), encoding="utf-8")
print(" 1 passed", flush=True)
time.sleep(30)
elif suite == "no_summary":
pass
else:
print(" 1 passed", flush=True)
PY
seq 1 32 | sed 's/^/pass_/' >"$fixture/suites.txt"
: >"$fixture/results.txt"
python3 "$scheduler" \
--suite-file "$fixture/suites.txt" \
--log-dir "$fixture/logs" \
--results-file "$fixture/results.txt" \
--jobs 8 \
--timeout 5 \
--slow-timeout 5 \
--kill-grace 1 \
"$(command -v python3)" "$fixture/fake_runner.py" "$fixture/descendant.pid"
if [ "$(wc -l <"$fixture/results.txt" | tr -d ' ')" -ne 32 ] ||
grep -qvE '^pass_[0-9]+ rc=0 pass=1 fail=0 skip=0 secs=[0-9]+$' \
"$fixture/results.txt"; then
echo "FAIL: scheduler lost or corrupted a completed child's result" >&2
cat "$fixture/results.txt" >&2
exit 1
fi
printf '%s\n' hang_after_summary no_summary pass_after >"$fixture/suites.txt"
: >"$fixture/results.txt"
python3 "$scheduler" \
--suite-file "$fixture/suites.txt" \
--log-dir "$fixture/logs" \
--results-file "$fixture/results.txt" \
--jobs 1 \
--timeout 1 \
--slow-timeout 1 \
--kill-grace 1 \
"$(command -v python3)" "$fixture/fake_runner.py" "$fixture/descendant.pid"
if ! grep -qE '^hang_after_summary rc=124 pass=1 fail=0 skip=0 secs=[0-9]+$' \
"$fixture/results.txt"; then
echo "FAIL: scheduler did not bound and record a child that stayed alive after its summary" >&2
cat "$fixture/results.txt" >&2
exit 1
fi
if ! grep -qE '^no_summary rc=97 pass=0 fail=0 skip=0 secs=[0-9]+$' \
"$fixture/results.txt"; then
echo "FAIL: scheduler accepted a zero-test child" >&2
cat "$fixture/results.txt" >&2
exit 1
fi
if ! grep -qE '^pass_after rc=0 pass=1 fail=0 skip=0 secs=[0-9]+$' \
"$fixture/results.txt"; then
echo "FAIL: scheduler did not continue after a bounded child failure" >&2
cat "$fixture/results.txt" >&2
exit 1
fi
printf '%s\n' stubborn_tree >"$fixture/suites.txt"
: >"$fixture/results.txt"
python3 "$scheduler" \
--suite-file "$fixture/suites.txt" \
--log-dir "$fixture/logs" \
--results-file "$fixture/results.txt" \
--jobs 1 \
--timeout 1 \
--slow-timeout 1 \
--kill-grace 1 \
"$(command -v python3)" "$fixture/fake_runner.py" "$fixture/descendant.pid"
python3 - "$fixture/descendant.pid" <<'PY'
import ctypes
import os
import pathlib
import signal
import subprocess
import sys
pid = int(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
alive = False
if os.name == "nt":
process = ctypes.windll.kernel32.OpenProcess(0x1000, False, pid)
if process:
code = ctypes.c_ulong()
if ctypes.windll.kernel32.GetExitCodeProcess(process, ctypes.byref(code)):
alive = code.value == 259
ctypes.windll.kernel32.CloseHandle(process)
else:
try:
os.kill(pid, 0)
alive = True
except ProcessLookupError:
pass
if alive:
stat_path = pathlib.Path("/proc") / str(pid) / "stat"
state = ""
if stat_path.exists():
state = stat_path.read_text(encoding="utf-8", errors="replace").rsplit(
")", 1
)[-1].strip()
if state.startswith("Z "):
alive = False
if alive:
if os.name == "nt":
subprocess.run(
["taskkill.exe", "/PID", str(pid), "/T", "/F"],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
else:
os.kill(pid, signal.SIGKILL)
raise SystemExit("FAIL: timed-out suite left a stubborn descendant alive")
PY
printf '%s\n' pass_after >"$fixture/suites.txt"
: >"$fixture/results.txt"
mkdir "$fixture/barrier"
: >"$fixture/barrier/pass_after.hold"
python3 - "$scheduler" "$fixture" "$(command -v python3)" <<'PY'
from __future__ import annotations
import pathlib
import subprocess
import sys
import time
scheduler = sys.argv[1]
fixture = pathlib.Path(sys.argv[2])
python = sys.argv[3]
results = fixture / "results.txt"
barrier = fixture / "barrier"
process = subprocess.Popen(
[
python,
scheduler,
"--suite-file",
str(fixture / "suites.txt"),
"--log-dir",
str(fixture / "logs"),
"--results-file",
str(results),
"--jobs",
"1",
"--timeout",
"5",
"--slow-timeout",
"5",
"--kill-grace",
"1",
"--test-post-exit-barrier-dir",
str(barrier),
python,
str(fixture / "fake_runner.py"),
str(fixture / "descendant.pid"),
]
)
try:
deadline = time.monotonic() + 5
while not (barrier / "pass_after.ready").exists():
if process.poll() is not None:
raise SystemExit(
f"FAIL: scheduler exited before exposing the post-exit barrier "
f"(rc={process.returncode})"
)
if time.monotonic() >= deadline:
raise SystemExit("FAIL: scheduler never reached the post-exit barrier")
time.sleep(0.02)
if results.read_text(encoding="utf-8"):
raise SystemExit("FAIL: scheduler recorded a result before the forced barrier released")
(barrier / "pass_after.release").write_text("release\n", encoding="utf-8")
returncode = process.wait(timeout=5)
if returncode != 0:
raise SystemExit(f"FAIL: scheduler failed after the post-exit release (rc={returncode})")
finally:
if process.poll() is None:
process.kill()
process.wait()
expected = "pass_after rc=0 pass=1 fail=0 skip=0 secs="
if not results.read_text(encoding="utf-8").startswith(expected):
raise SystemExit("FAIL: exited child result was lost after the forced barrier")
PY
printf '%s\n' timeout_exit_race >"$fixture/suites.txt"
: >"$fixture/results.txt"
rm -f "$fixture/descendant.pid"
: >"$fixture/barrier/timeout_exit_race.hold"
python3 - "$scheduler" "$fixture" "$(command -v python3)" <<'PY'
from __future__ import annotations
import ctypes
import os
import pathlib
import signal
import subprocess
import sys
import time
scheduler = sys.argv[1]
fixture = pathlib.Path(sys.argv[2])
python = sys.argv[3]
results = fixture / "results.txt"
barrier = fixture / "barrier"
ready = barrier / "timeout_exit_race.ready"
leader_exited = barrier / "timeout_exit_race.leader-exited"
release = barrier / "timeout_exit_race.release"
descendant_path = fixture / "descendant.pid"
def process_state(pid: int) -> str:
if os.name == "nt":
handle = ctypes.windll.kernel32.OpenProcess(0x101000, False, pid)
if not handle:
return "gone"
code = ctypes.c_ulong()
active = (
ctypes.windll.kernel32.GetExitCodeProcess(handle, ctypes.byref(code))
and code.value == 259
)
ctypes.windll.kernel32.CloseHandle(handle)
return "live" if active else "gone"
try:
os.kill(pid, 0)
except ProcessLookupError:
return "gone"
stat_path = pathlib.Path("/proc") / str(pid) / "stat"
if stat_path.exists():
state = stat_path.read_text(encoding="utf-8", errors="replace").rsplit(
")", 1
)[-1].strip()
if state.startswith("Z "):
return "zombie"
return "live"
def force_cleanup(pid: int) -> None:
if process_state(pid) != "live":
return
if os.name == "nt":
subprocess.run(
["taskkill.exe", "/PID", str(pid), "/T", "/F"],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
else:
os.kill(pid, signal.SIGKILL)
process = subprocess.Popen(
[
python,
scheduler,
"--suite-file",
str(fixture / "suites.txt"),
"--log-dir",
str(fixture / "logs"),
"--results-file",
str(results),
"--jobs",
"1",
"--timeout",
"1",
"--slow-timeout",
"1",
"--kill-grace",
"1",
"--test-pre-terminate-barrier-dir",
str(barrier),
python,
str(fixture / "fake_runner.py"),
str(descendant_path),
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
try:
deadline = time.monotonic() + 5
while not ready.exists():
if process.poll() is not None:
stdout, stderr = process.communicate()
raise SystemExit(
f"FAIL: scheduler exited before the timeout-race barrier "
f"(rc={process.returncode}, stdout={stdout!r}, stderr={stderr!r})"
)
if time.monotonic() >= deadline:
raise SystemExit("FAIL: scheduler never reached the timeout-race barrier")
time.sleep(0.02)
leader_pid = int(ready.read_text(encoding="utf-8"))
os.kill(leader_pid, signal.SIGTERM)
deadline = time.monotonic() + 3
while not leader_exited.exists():
if process.poll() is not None:
stdout, stderr = process.communicate()
raise SystemExit(
f"FAIL: scheduler exited before observing the forced leader exit "
f"(rc={process.returncode}, stdout={stdout!r}, stderr={stderr!r})"
)
if time.monotonic() >= deadline:
raise SystemExit("FAIL: scheduler did not observe the forced leader exit")
time.sleep(0.02)
release.write_text("release\n", encoding="utf-8")
stdout, stderr = process.communicate(timeout=8)
if os.name == "nt":
# Assert the PROPERTY, not the wording. This used to require the phrase
# "tree cleanup" in stderr, which pinned one specific refusal message:
# rewording the guard broke the contract while the behaviour was still
# correct. What "fails closed" actually means is that the scheduler
# refused (rc=2) AND did not silently leave the descendant behind as if
# cleanup had succeeded.
descendant_pid = int(descendant_path.read_text(encoding="utf-8"))
if process.returncode != 2:
raise SystemExit(
f"FAIL: Windows timeout race did not fail closed "
f"(rc={process.returncode}, stdout={stdout!r}, stderr={stderr!r})"
)
if process_state(descendant_pid) != "live":
raise SystemExit(
"FAIL: Windows timeout race refused without a surviving descendant "
"to refuse over -- the fixture no longer exercises the race"
)
if "cleanup" not in stderr.lower():
raise SystemExit(
f"FAIL: Windows timeout race refused without naming a cleanup "
f"failure (stderr={stderr!r})"
)
else:
if process.returncode != 0:
raise SystemExit(
f"FAIL: POSIX timeout race cleanup failed "
f"(rc={process.returncode}, stdout={stdout!r}, stderr={stderr!r})"
)
if not results.read_text(encoding="utf-8").startswith(
"timeout_exit_race rc=124 pass=1 fail=0 skip=0 secs="
):
raise SystemExit("FAIL: POSIX timeout race lost its bounded result")
descendant_pid = int(descendant_path.read_text(encoding="utf-8"))
if process_state(descendant_pid) == "live":
raise SystemExit("FAIL: POSIX timeout race leaked the surviving descendant")
finally:
release.write_text("release\n", encoding="utf-8")
if process.poll() is None:
process.kill()
process.wait()
if descendant_path.exists():
force_cleanup(int(descendant_path.read_text(encoding="utf-8")))
PY
echo "Parallel harness contract passed"