test(loop-lock): tests no longer take the checkout's loop lock (v4.8.9) #54

Merged
Godopu merged 2 commits from test/loop-marker-tmp into main 2026-10-09 01:59:58 +00:00
3 changed files with 55 additions and 0 deletions
Showing only changes of commit a93c9bf292 - Show all commits

No files matched your search

@@ -68,6 +68,7 @@ def test_w1_no_test_writes_into_the_checkout_it_runs_in(tmp_path):
assert int(re.search(r"(\d+) passed", summary).group(1)) >= len(NODES) and "skipped" not in summary, summary
written = sorted(str(p.relative_to(copy)) for p in (copy / ".mam").rglob("*")) if (copy / ".mam").exists() else []
assert written == [], f"tests wrote into the checkout they ran in: {written}"
assert not (copy / ".mam").exists(), "tests created the checkout's .mam (e.g. the loop lock's directory)"
_ONBOARD_PROBE = r"""
+50
View File
@@ -0,0 +1,50 @@
"""Tests never take the loop lock of the checkout they run in.
run_loop.sh puts its control root at its own checkout (MAM_REAL_ROOT =
REPO_ROOT) and takes `${MAM_LOOP_MARKER:-$MAM_REAL_ROOT/.mam/loop-guard-active}`
once the arguments parse (mam_acquire_loop_lock creates the directory). In the
main checkout that is the live loop's lock: a test run would take it for a
moment - refusing a real loop, or being refused by one - and leave `.mam/`
behind in a review tree. Every test that reaches the lock through the
checkout's own run_loop.sh therefore names a marker under its tmp_path.
L1 copies this checkout and runs those tests there: the copy gets no `.mam`.
"""
from __future__ import annotations
import os
import re
import subprocess
import sys
from test_issue17_workspace_root_isolation import _copy_checkout
# every test that runs the checkout's run_loop.sh far enough to take the lock
# (a sweep of the run_loop.sh users on 97c2946; --help and argument errors
# end before the lock, copies of the tree and mam_sandbox trees are not the
# checkout). The first two took the checkout's lock at 97c2946.
NODES = [
"tests/test_o3_scoped_guard.py::test_b13_reexec_preserves_original_argv",
"tests/test_o3_scoped_guard.py::test_b13_no_freeze_switch_disables_reexec",
"tests/test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_delegates_to_python_default",
"tests/test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_rollback_mode",
"tests/test_loop_cli.py::test_creator_parses_and_reaches_session_check",
"tests/test_o2_race_free_lock.py::test_o2_12_run_loop_exits_on_lock_failure",
"tests/test_issue25_plan_reuse.py::test_p7b_the_legacy_shell_loop_refuses_the_option",
]
def test_l1_no_test_takes_the_loop_lock_of_its_checkout(tmp_path):
copy = tmp_path / "checkout"
_copy_checkout(copy)
assert not (copy / ".mam").exists()
env = {k: v for k, v in os.environ.items() if not k.startswith(("HERDR_", "MAM_", "MQTT_", "SKS_"))}
env.update(WORKSPACE_ROOT=str(copy), AGENT_PYTHON_BIN=sys.executable, PYTHONDONTWRITEBYTECODE="1")
env.pop("PYTHONPATH", None)
res = subprocess.run([sys.executable, "-m", "pytest", "-p", "no:cacheprovider", "-q",
f"--basetemp={tmp_path / 'bt'}", *NODES],
capture_output=True, text=True, cwd=str(copy), env=env, timeout=900)
assert res.returncode == 0, res.stdout[-3000:] + res.stderr[-2000:]
summary = res.stdout.strip().splitlines()[-1]
assert int(re.search(r"(\d+) passed", summary).group(1)) >= len(NODES) and "skipped" not in summary, summary
assert not (copy / ".mam").exists(), "a test took the checkout's loop lock (its .mam was created)"
+4
View File
@@ -382,10 +382,13 @@ def test_b13_reexec_preserves_original_argv(tmp_path):
The arg parser consumes "$@" (shift), so capturing argv beforehand is mandatory.
"""
run_loop_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh"
# The run reaches the loop lock: a marker of this test's own, never the
# checkout's .mam/loop-guard-active (the live loop's, in the main checkout).
res = subprocess.run(
["bash", str(run_loop_sh), "--creator", "nonexistent-agent", "--task", "test goal with spaces"],
capture_output=True,
text=True,
env=dict(os.environ, MAM_LOOP_MARKER=str(tmp_path / "loop-guard-active")),
cwd=str(REPO_ROOT),
)
combined = res.stdout + res.stderr
@@ -464,6 +467,7 @@ def test_b13_no_freeze_switch_disables_reexec(tmp_path):
run_loop_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh"
env = os.environ.copy()
env["MAM_LOOP_NO_FREEZE"] = "1"
env["MAM_LOOP_MARKER"] = str(tmp_path / "loop-guard-active") # not the checkout's loop lock
res = subprocess.run(
["bash", str(run_loop_sh), "--creator", "nonexistent-agent", "--task", "test goal"],
capture_output=True,