code-to10 v4 coverage thickening: 219 new assertion tests (batches A/B1/C/D1-D6)
Baseline merge head e5ff26a: 28144 stmts, 2807 missing, exact 90.0263%, margin +7. After thickening (full run): 2350 missing, exact 91.6501%, margin +464. Full suite 3516 items: 3509 pass, 1 failure (pre-existing November date failure, untouched), 6 skipped. No production code changed.
This commit is contained in:
1 parent
773945d136
commit
12e72dab9b
9 files changed
+3597
No files matched your search
@@ -0,0 +1,233 @@
|
||||
"""Coverage batch v4-D5: host.py leftover branches (wait-turn first
|
||||
slice, run-turn interrupt raise, daemon-stop inner break, daemon
|
||||
subcommand failure, interrupt poll paths, goal verify warning).
|
||||
|
||||
Stubbing discipline: the function under test is never stubbed. Patched
|
||||
collaborators are: time.monotonic / time.sleep / os.kill /
|
||||
subprocess.run / interrupt_turn / _wait_turn / dispatch /
|
||||
run_goal_loop / json.load (sinks, clocks and predicates). Every
|
||||
assertion reads a real return value or real stdout.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
|
||||
import host
|
||||
from host import Server, _wait_turn
|
||||
|
||||
|
||||
def _server():
|
||||
srv = object.__new__(Server)
|
||||
srv.proc = None
|
||||
srv.codex_bin = "codex"
|
||||
srv.codex_home = None
|
||||
srv.approval = "deny"
|
||||
srv.model_provider = None
|
||||
srv.progress_interval = 30
|
||||
srv.send_lock = threading.Lock()
|
||||
srv.next_id = 1
|
||||
srv.pending = {}
|
||||
srv.pending_lock = threading.Lock()
|
||||
srv.turn_queues = {}
|
||||
srv.events = []
|
||||
srv.events_lock = threading.Lock()
|
||||
srv.restart_count = 0
|
||||
srv.trace_path = None
|
||||
srv._trace_lines = 0
|
||||
return srv
|
||||
|
||||
|
||||
class _FakeServer:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def set_trace_file(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def resume_thread(self, thread_id):
|
||||
pass
|
||||
|
||||
def interrupt_turn(self, thread_id, turn_id):
|
||||
return {"error": "x"}
|
||||
|
||||
|
||||
class WaitTurnTestCase(unittest.TestCase):
|
||||
def test_first_slice_timeout(self):
|
||||
# 154: elapsed already past timeout before the first slice.
|
||||
q = queue.Queue()
|
||||
with mock.patch.object(
|
||||
host.time, "monotonic",
|
||||
side_effect=[100.0, 100.5]):
|
||||
self.assertEqual(_wait_turn(q, 0.1), ("timeout", None))
|
||||
|
||||
|
||||
class RunTurnInterruptRaiseTestCase(unittest.TestCase):
|
||||
def test_interrupt_turn_raises(self):
|
||||
# 777-780
|
||||
srv = _server()
|
||||
srv.request = mock.Mock(return_value={
|
||||
"result": {"turn": {"id": "turn1"}}})
|
||||
srv.interrupt_turn = mock.Mock(
|
||||
side_effect=RuntimeError("interrupt down"))
|
||||
with mock.patch.object(host, "_wait_turn",
|
||||
return_value=("interrupted-requested",
|
||||
None)):
|
||||
out = srv.run_turn("th1", "do work")
|
||||
self.assertEqual(out["status"], "interrupted")
|
||||
|
||||
|
||||
class MainLeftoverTestCase(unittest.TestCase):
|
||||
def _state_cfg(self, tmp, tasks):
|
||||
state_path = os.path.join(tmp, "host-state.json")
|
||||
cfg_path = os.path.join(tmp, "host-config.json")
|
||||
with open(state_path, "w", encoding="utf-8") as f:
|
||||
json.dump({"tasks": tasks, "automations": []}, f)
|
||||
with open(cfg_path, "w", encoding="utf-8") as f:
|
||||
json.dump({}, f)
|
||||
return state_path, cfg_path
|
||||
|
||||
def _run_main(self, argv, patches=()):
|
||||
with contextlib.ExitStack() as stack:
|
||||
for p in patches:
|
||||
stack.enter_context(p)
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
with mock.patch.object(sys, "argv", argv):
|
||||
host.main()
|
||||
return buf.getvalue()
|
||||
|
||||
def test_daemon_stop_inner_break(self):
|
||||
# 3407-3408: SIGTERM succeeds, the liveness probe then raises.
|
||||
# (A real sleep child would stay a zombie until reaped, so the
|
||||
# probe would succeed and the break would never fire; the
|
||||
# kill calls are therefore faked for this branch.)
|
||||
def _fake_kill(pid, sig):
|
||||
if sig == 0:
|
||||
raise ProcessLookupError("gone")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
state_path, cfg_path = self._state_cfg(tmp, {})
|
||||
pidfile = os.path.join(tmp, "host-daemon.pid")
|
||||
with open(pidfile, "w", encoding="utf-8") as f:
|
||||
f.write("12345")
|
||||
argv = ["host.py", "--state", state_path,
|
||||
"--config", cfg_path, "daemon-stop"]
|
||||
out = self._run_main(
|
||||
argv, patches=[mock.patch("os.kill",
|
||||
side_effect=_fake_kill)])
|
||||
self.assertIn("daemon 已停", out)
|
||||
self.assertFalse(os.path.exists(pidfile))
|
||||
|
||||
def test_daemon_subcommand_failure(self):
|
||||
# 3504-3505
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
state_path, cfg_path = self._state_cfg(tmp, {})
|
||||
argv = ["host.py", "--state", state_path,
|
||||
"--config", cfg_path, "daemon", "--foreground",
|
||||
"--interval", "300"]
|
||||
old_term = signal.getsignal(signal.SIGTERM)
|
||||
old_int = signal.getsignal(signal.SIGINT)
|
||||
|
||||
def sender():
|
||||
time.sleep(0.5)
|
||||
try:
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sender_thread = threading.Thread(target=sender, daemon=True)
|
||||
try:
|
||||
sender_thread.start()
|
||||
out = self._run_main(argv, patches=[
|
||||
mock.patch("subprocess.run",
|
||||
side_effect=RuntimeError("child down")),
|
||||
])
|
||||
finally:
|
||||
sender_thread.join(timeout=5)
|
||||
signal.signal(signal.SIGTERM, old_term)
|
||||
signal.signal(signal.SIGINT, old_int)
|
||||
self.assertIn("daemon subcommand", out)
|
||||
|
||||
def test_interrupt_poll_responded(self):
|
||||
# 3963-3964
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
state_path, cfg_path = self._state_cfg(tmp, {
|
||||
"t1": {"name": "t1", "thread_id": "th1",
|
||||
"status": "interrupted",
|
||||
"turns": [{"result": {"turn_id": "turn9"}}]}})
|
||||
argv = ["host.py", "--state", state_path,
|
||||
"--config", cfg_path, "interrupt", "--task", "t1"]
|
||||
out = self._run_main(argv, patches=[
|
||||
mock.patch.object(host, "Server",
|
||||
lambda **kw: _FakeServer()),
|
||||
mock.patch("time.sleep", lambda *a, **k: None),
|
||||
])
|
||||
self.assertIn("属主已响应", out)
|
||||
|
||||
def test_interrupt_poll_read_failure(self):
|
||||
# 3965-3967 (+ else branch): state reads fail during polling.
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
state_path, cfg_path = self._state_cfg(tmp, {
|
||||
"t1": {"name": "t1", "thread_id": "th1",
|
||||
"status": "running",
|
||||
"turns": [{"result": {"turn_id": "turn9"}}]}})
|
||||
argv = ["host.py", "--state", state_path,
|
||||
"--config", cfg_path, "interrupt", "--task", "t1"]
|
||||
real_load = json.load
|
||||
calls = {"n": 0}
|
||||
|
||||
def _load(fp, *a, **k):
|
||||
calls["n"] += 1
|
||||
if calls["n"] <= 2:
|
||||
return real_load(fp, *a, **k)
|
||||
raise RuntimeError("poll read down")
|
||||
|
||||
out = self._run_main(argv, patches=[
|
||||
mock.patch.object(host, "Server",
|
||||
lambda **kw: _FakeServer()),
|
||||
mock.patch("time.sleep", lambda *a, **k: None),
|
||||
mock.patch("json.load", side_effect=_load),
|
||||
])
|
||||
self.assertIn("10 秒内未响应", out)
|
||||
|
||||
def test_goal_verify_argv_warning(self):
|
||||
# 4025
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
state_path, cfg_path = self._state_cfg(tmp, {})
|
||||
argv = ["host.py", "--state", state_path,
|
||||
"--config", cfg_path, "goal", "--name", "g",
|
||||
"--task", "t", "--repo", "/tmp",
|
||||
"--verify", "echo hi", "--verify-argv", "echo", "hi"]
|
||||
out = self._run_main(argv, patches=[
|
||||
mock.patch.object(host, "Server",
|
||||
lambda **kw: _FakeServer()),
|
||||
mock.patch.object(host, "dispatch",
|
||||
return_value={"name": "g"}),
|
||||
mock.patch.object(host, "run_goal_loop",
|
||||
return_value=None),
|
||||
])
|
||||
self.assertIn("verify_argv 已指定", out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user