项目文件夹

文件
2026-07-13 12:34:57 +08:00

136 行
4.8 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
"""run_primary_first / clear_markers 单元测试"""
import os
import threading
import time
import pytest
from myboot.utils import worker_sync
from myboot.utils.worker_sync import run_primary_first, clear_markers
@pytest.fixture(autouse=True)
def isolated_marker_dir(tmp_path, monkeypatch):
"""每个测试使用独立的标记目录,避免相互污染。"""
import tempfile
monkeypatch.setattr(tempfile, "gettempdir", lambda: str(tmp_path))
monkeypatch.setenv("MYBOOT_APP_NAME", "testapp")
monkeypatch.delenv("MYBOOT_IS_PRIMARY_WORKER", raising=False)
yield tmp_path
def _marker_dir(tmp_path):
return tmp_path / "myboot_worker_sync_testapp"
def test_single_process_passthrough():
"""无环境变量时默认 primary,直通执行并返回值。"""
assert run_primary_first("m1", lambda: 42) == 42
def test_primary_writes_done(monkeypatch, isolated_marker_dir):
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
assert run_primary_first("model", lambda: "ok") == "ok"
done = _marker_dir(isolated_marker_dir) / "model.done"
assert done.exists()
assert done.read_text(encoding="utf-8") # ISO 时间戳非空
def test_secondary_waits_then_runs_secondary_fn(monkeypatch, isolated_marker_dir):
"""secondary 在另一线程先等待,primary 后完成 → secondary 执行 secondary_fn。"""
results = {}
def secondary_thread():
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
results["secondary"] = run_primary_first(
"model",
primary_fn=lambda: "heavy",
secondary_fn=lambda: "light",
timeout=5.0,
poll_interval=0.05,
)
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
t = threading.Thread(target=secondary_thread)
t.start()
time.sleep(0.2)
# primary 完成
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
assert run_primary_first("model", lambda: "heavy") == "heavy"
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
t.join(timeout=5)
assert not t.is_alive()
assert results["secondary"] == "light"
def test_secondary_defaults_to_primary_fn(monkeypatch, isolated_marker_dir):
"""secondary_fn 为 None 时 secondary 执行 primary_fn。"""
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
run_primary_first("m2", lambda: 1)
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
assert run_primary_first("m2", lambda: 1, timeout=1.0, poll_interval=0.05) == 1
def test_primary_failure_writes_failed_and_raises(monkeypatch, isolated_marker_dir):
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
def boom():
raise ValueError("download failed")
with pytest.raises(ValueError, match="download failed"):
run_primary_first("model", boom)
failed = _marker_dir(isolated_marker_dir) / "model.failed"
assert failed.exists()
assert "download failed" in failed.read_text(encoding="utf-8")
# secondary 检测到 .failed → RuntimeError 含异常信息
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
with pytest.raises(RuntimeError, match="download failed"):
run_primary_first("model", lambda: 1, timeout=2.0, poll_interval=0.05)
def test_secondary_timeout(monkeypatch):
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
with pytest.raises(TimeoutError):
run_primary_first("never", lambda: 1, timeout=0.3, poll_interval=0.05)
def test_stale_done_marker_ignored(monkeypatch, isolated_marker_dir):
"""上一轮遗留的旧 .donemtime 过旧)不会让 secondary 跳过等待。"""
d = _marker_dir(isolated_marker_dir)
d.mkdir(parents=True, exist_ok=True)
stale = d / "model.done"
stale.write_text("old-run", encoding="utf-8")
old = worker_sync._MODULE_LOAD_TIME - 100
os.utime(stale, (old, old))
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "0")
with pytest.raises(TimeoutError):
run_primary_first("model", lambda: 1, timeout=0.3, poll_interval=0.05)
def test_primary_clears_stale_markers(monkeypatch, isolated_marker_dir):
"""primary 执行前清理旧标记,成功后 .failed 不残留。"""
d = _marker_dir(isolated_marker_dir)
d.mkdir(parents=True, exist_ok=True)
(d / "model.failed").write_text("old failure", encoding="utf-8")
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
assert run_primary_first("model", lambda: "ok") == "ok"
assert not (d / "model.failed").exists()
assert (d / "model.done").exists()
def test_clear_markers(monkeypatch, isolated_marker_dir):
monkeypatch.setenv("MYBOOT_IS_PRIMARY_WORKER", "1")
run_primary_first("a", lambda: 1)
run_primary_first("b", lambda: 1)
d = _marker_dir(isolated_marker_dir)
clear_markers("a")
assert not (d / "a.done").exists()
assert (d / "b.done").exists()
clear_markers()
assert not (d / "b.done").exists()