77 lines
3.2 KiB
Python
77 lines
3.2 KiB
Python
import pytest
|
||
|
||
from engine.harness import calibrate_cprofile_overhead, collect_environment, load_source
|
||
|
||
|
||
def test_calibrate_returns_well_formed_calibration():
|
||
"""校准应该返回一个 well-formed Calibration:ratio > 0,两段耗时 > 0,workloadName 已知。
|
||
|
||
不强求 ratio > 1.0:在极快的机器上校准本身 < 5ms,会走 fallback ratio=1.0;
|
||
此时 instrumentedWorkloadSec 可能比 clean 略小(perf_counter 在 1ms 量级有 noise),
|
||
也不强求 instrumented >= clean。这条测试只锁住「返回结构对 + 数值非负」。
|
||
"""
|
||
calib = calibrate_cprofile_overhead()
|
||
assert calib.ratio > 0, f"ratio 必须正:{calib.ratio}"
|
||
assert calib.ratio <= 10.0, f"ratio 太大,可能校准失效:{calib.ratio}"
|
||
assert calib.cleanWorkloadSec > 0
|
||
assert calib.instrumentedWorkloadSec > 0
|
||
assert calib.workloadName == "tight-loop"
|
||
|
||
|
||
def test_calibrate_fallback_when_workload_too_fast(monkeypatch):
|
||
"""校准本身耗时 < 5ms 时应回退 ratio=1.0 —— 见 calibrate_cprofile_overhead 的边界处理。
|
||
|
||
直接 mock calibrate_cprofile_overhead 内部的 _exec_once_* 行为不可行(它们是模块内 helper),
|
||
改成 monkeypatch time.perf_counter 返回极短耗时:第一次返回 t0=0.0,第二次返回 t0+1e-6。
|
||
"""
|
||
from engine import harness
|
||
|
||
counter = {"n": 0}
|
||
base = {"t": 1000.0}
|
||
|
||
def fake_perf_counter():
|
||
counter["n"] += 1
|
||
# 第一次调用返回 0,之后每次 + 极小 delta,让 elapsed = ~3us (远小于 5ms)
|
||
idx = counter["n"]
|
||
return base["t"] + idx * 1e-6
|
||
|
||
monkeypatch.setattr(harness.time, "perf_counter", fake_perf_counter)
|
||
calib = harness.calibrate_cprofile_overhead()
|
||
assert calib.ratio == 1.0, f"短耗时下应回退 ratio=1.0,实际:{calib.ratio}"
|
||
|
||
|
||
def test_calibrate_does_not_leak_files(monkeypatch, tmp_path):
|
||
"""校准自己 scratch 一个临时文件,跑完应清干净 —— 不污染系统临时目录。"""
|
||
# 让 mkstemp 落到 tmp_path 方便断言
|
||
import tempfile
|
||
monkeypatch.setattr(tempfile, "gettempdir", lambda: str(tmp_path))
|
||
before = set(tmp_path.iterdir())
|
||
calibrate_cprofile_overhead()
|
||
after = set(tmp_path.iterdir())
|
||
# 不应有残留 —— 之前 / 之后 iterdir 集合应相等
|
||
assert after == before, f"残留文件: {after - before}"
|
||
|
||
|
||
def test_collect_environment():
|
||
env = collect_environment()
|
||
assert env.python.count(".") >= 2
|
||
assert env.timerResolution > 0
|
||
|
||
|
||
def test_load_source_strips_bom(tmp_path):
|
||
"""load_source 必须剥掉 BOM —— 引擎内所有读源码的地方都依赖这一点。"""
|
||
script = tmp_path / "bom.py"
|
||
script.write_text("x = 1\n", encoding="utf-8-sig")
|
||
assert script.read_bytes().startswith(b"\xef\xbb\xbf")
|
||
src = load_source(str(script))
|
||
assert not src.startswith("")
|
||
# 剥离后必须能直接过 compile(),这正是 runner 语法预检做的事
|
||
compile(src, str(script), "exec")
|
||
|
||
|
||
def test_load_source_raises_on_binary(tmp_path):
|
||
"""二进制文件抛 UnicodeDecodeError(而不是 OSError)—— runner 依赖这个类型来归类错误。"""
|
||
script = tmp_path / "bin.py"
|
||
script.write_bytes(b"\xff\xfe\x00\x01\x80\x81")
|
||
with pytest.raises(UnicodeDecodeError):
|
||
load_source(str(script)) |