Treat zombie workers as terminated in tests
Why: Gitea containers may leave SIGKILLed children as zombies until PID 1 reaps them, while kill -0 still reports success. Evidence: Both timeout tests and the full AgentExecControllerTests suite pass; git diff --check passes. Impact: Changes test liveness detection only; genuinely running child processes still fail the assertions. Refs: #567
This commit is contained in:
+23
-13
@@ -41,6 +41,25 @@ def _canonical_snapshot(repo: Path) -> dict[str, str]:
|
|||||||
return {"head": head, "tree": tree, "status": status, "index": index}
|
return {"head": head, "tree": tree, "status": status, "index": index}
|
||||||
|
|
||||||
|
|
||||||
|
def _pid_alive(pid: int) -> bool:
|
||||||
|
"""Return true only for a running, non-zombie process."""
|
||||||
|
try:
|
||||||
|
text = Path(f"/proc/{pid}/stat").read_text(
|
||||||
|
encoding="utf-8", errors="replace"
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
rpar = text.rfind(")")
|
||||||
|
if rpar != -1 and rpar + 2 < len(text) and text[rpar + 2] == "Z":
|
||||||
|
return False
|
||||||
|
return (
|
||||||
|
subprocess.run(
|
||||||
|
["kill", "-0", str(pid)], check=False, capture_output=True
|
||||||
|
).returncode
|
||||||
|
== 0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _write_config(
|
def _write_config(
|
||||||
repo: Path,
|
repo: Path,
|
||||||
*,
|
*,
|
||||||
@@ -740,13 +759,8 @@ class AgentExecControllerTests(unittest.TestCase):
|
|||||||
self.assertNotIn("idle timeout", data["reason"])
|
self.assertNotIn("idle timeout", data["reason"])
|
||||||
if child_pid_file.exists():
|
if child_pid_file.exists():
|
||||||
child = int(child_pid_file.read_text(encoding="utf-8").strip())
|
child = int(child_pid_file.read_text(encoding="utf-8").strip())
|
||||||
# Child must not still be alive.
|
# A zombie is terminated even if a container PID 1 has not reaped it.
|
||||||
still = subprocess.run(
|
self.assertFalse(_pid_alive(child))
|
||||||
["kill", "-0", str(child)],
|
|
||||||
check=False,
|
|
||||||
capture_output=True,
|
|
||||||
)
|
|
||||||
self.assertNotEqual(still.returncode, 0)
|
|
||||||
self._assert_canonical_untouched()
|
self._assert_canonical_untouched()
|
||||||
|
|
||||||
def test_idle_timeout_kills_silent_worker_before_total(self) -> None:
|
def test_idle_timeout_kills_silent_worker_before_total(self) -> None:
|
||||||
@@ -821,12 +835,8 @@ class AgentExecControllerTests(unittest.TestCase):
|
|||||||
self.assertEqual(cleanup["reason"], "idle timeout")
|
self.assertEqual(cleanup["reason"], "idle timeout")
|
||||||
self.assertTrue(child_pid_file.is_file())
|
self.assertTrue(child_pid_file.is_file())
|
||||||
child = int(child_pid_file.read_text(encoding="utf-8").strip())
|
child = int(child_pid_file.read_text(encoding="utf-8").strip())
|
||||||
still = subprocess.run(
|
# A zombie is terminated even if a container PID 1 has not reaped it.
|
||||||
["kill", "-0", str(child)],
|
self.assertFalse(_pid_alive(child))
|
||||||
check=False,
|
|
||||||
capture_output=True,
|
|
||||||
)
|
|
||||||
self.assertNotEqual(still.returncode, 0)
|
|
||||||
self._assert_canonical_untouched()
|
self._assert_canonical_untouched()
|
||||||
|
|
||||||
def test_progress_keeps_worker_alive_past_one_idle_interval(self) -> None:
|
def test_progress_keeps_worker_alive_past_one_idle_interval(self) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user