Files
Python-Profiler-Visualizer/engine/tests/test_harness.py
2026-09-12 14:19:56 +08:00

77 lines
3.2 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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))