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.
105 lines
3.9 KiB
Python
105 lines
3.9 KiB
Python
"""Coverage batch v4-D6: host.py parallel-branch fallback paths
|
|
(4186 dep-branch dispatch failure, 4198 dep cwd copy).
|
|
|
|
Stubbing discipline: the branch code under test is never stubbed.
|
|
Patched collaborators are dispatch, dispatch_from_branch,
|
|
run_task_turn_with_retry and copy_tree_excluding_git (all of them
|
|
sinks the branch delegates to). Assertions read real stdout and the
|
|
real copy_tree call record.
|
|
"""
|
|
|
|
import contextlib
|
|
import io
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
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
|
|
|
|
|
|
class _FakeServer:
|
|
def __init__(self, *args, **kwargs):
|
|
pass
|
|
|
|
def initialize(self):
|
|
pass
|
|
|
|
def set_trace_file(self, *args, **kwargs):
|
|
pass
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
class ParallelFallbackTestCase(unittest.TestCase):
|
|
def test_dep_branch_dispatch_falls_back_to_head_plus_copy(self):
|
|
# 4186 + 4198
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dep_dir = os.path.join(tmp, "dep-cwd")
|
|
os.makedirs(dep_dir)
|
|
with open(os.path.join(dep_dir, "seed.txt"), "w") as f:
|
|
f.write("seed")
|
|
new_dir = os.path.join(tmp, "new-cwd")
|
|
jobs_path = os.path.join(tmp, "jobs.json")
|
|
jobs = [
|
|
{"name": "j1", "task": "t1", "repo": "/tmp/repo1"},
|
|
{"name": "j2", "task": "t2", "repo": "/tmp/repo1",
|
|
"depends_on": ["j1"]},
|
|
]
|
|
with open(jobs_path, "w", encoding="utf-8") as f:
|
|
json.dump(jobs, f)
|
|
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": {}, "automations": []}, f)
|
|
with open(cfg_path, "w", encoding="utf-8") as f:
|
|
json.dump({}, f)
|
|
|
|
def _dispatch(srv, state, name, task, repo, *a, **k):
|
|
if name == "j1":
|
|
return {"name": "j1", "status": "completed",
|
|
"branch": "b1", "cwd": dep_dir}
|
|
return {"name": "j2", "status": "completed",
|
|
"cwd": new_dir}
|
|
|
|
copy_calls = []
|
|
argv = ["host.py", "--state", state_path,
|
|
"--config", cfg_path, "parallel",
|
|
"--jobs", jobs_path]
|
|
buf = io.StringIO()
|
|
with contextlib.redirect_stdout(buf):
|
|
with mock.patch.object(sys, "argv", argv):
|
|
with mock.patch.object(
|
|
host, "Server",
|
|
lambda **kw: _FakeServer()):
|
|
with mock.patch.object(
|
|
host, "dispatch",
|
|
side_effect=_dispatch):
|
|
with mock.patch.object(
|
|
host, "dispatch_from_branch",
|
|
return_value=(None, "boom")):
|
|
with mock.patch.object(
|
|
host,
|
|
"run_task_turn_with_retry",
|
|
return_value={"status": "completed",
|
|
"text": "ok"}):
|
|
with mock.patch.object(
|
|
host,
|
|
"copy_tree_excluding_git",
|
|
side_effect=lambda s, d: copy_calls.append((s, d))):
|
|
host.main()
|
|
out = buf.getvalue()
|
|
self.assertIn("从依赖分支派发失败", out)
|
|
self.assertEqual(copy_calls, [(dep_dir, new_dir)])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|