""" Smallest check that fails if the counting / PCU / id-validation logic breaks. python backend/test_core.py No framework on purpose — these are the branches that silently produce wrong numbers rather than crashing, so they need a tripwire. """ import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) from geometry import _side, _point_to_segment_dist from pcu import compute_pcu, get_pcu_factor from speed import estimate_speeds from config import IMGSZ def test_side(): a, b = [0, 0], [10, 0] # horizontal line assert _side((5, 5), a, b) > 0, "above the line must be positive" assert _side((5, -5), a, b) < 0, "below the line must be negative" assert _side((5, 0), a, b) == 0, "on the line must be zero (skipped by engine)" def test_point_to_segment(): assert abs(_point_to_segment_dist(5, 3, 0, 0, 10, 0) - 3.0) < 1e-9 # Past the end of the segment: clamps to the endpoint, not the infinite line. assert abs(_point_to_segment_dist(20, 0, 0, 0, 10, 0) - 10.0) < 1e-9 # Degenerate segment (both counting-line points identical) must not divide by zero. assert abs(_point_to_segment_dist(4, 5, 1, 1, 1, 1) - 5.0) < 1e-9 # 3-4-5 def test_pcu(): assert get_pcu_factor(4) == 3.0 # Bus assert get_pcu_factor(7) == 0.5 # Two-wheeler assert get_pcu_factor(999) == 1.0 # unknown class falls back to 1 # 2 buses in + 4 two-wheelers out = 2*3.0 + 4*0.5 = 8.0 out = compute_pcu({"4": 2}, {"7": 4}) assert out["pcu_in"] == 6.0, out assert out["pcu_out"] == 2.0, out assert out["total_pcu"] == 8.0, out assert out["per_class"]["Bus"]["count"] == 2, out empty = compute_pcu({}, {}) assert empty["total_pcu"] == 0.0 and empty["per_class"] == {} def test_speeds(): # One track moving 10px/frame, one barely moving, one too short to score. tracks = { 1: [(0, 0, 0), (1, 10, 0), (2, 20, 0)], 2: [(0, 0, 0), (1, 1, 0), (2, 2, 0)], 3: [(0, 5, 5)], } res = estimate_speeds(tracks) assert 3 not in res["per_track"], "single-sample tracks have no speed" assert res["per_track"][1]["px_per_frame"] == 10.0 assert res["per_track"][1]["category"] == "fast" assert res["per_track"][2]["category"] == "slow" assert sum(res["distribution"].values()) == 100.0 blank = estimate_speeds({}) assert blank["distribution"] == {"slow": 0, "normal": 0, "fast": 0} def test_id_validation(): # Imported lazily: server.py pulls in torch/ultralytics, which is slow and # unavailable outside the container. Re-check the regex contract instead. import re id_re = re.compile(r"^[a-f0-9]{8}$") assert id_re.match("a1b2c3d4") assert not id_re.match("../../etc"), "traversal must not validate" assert not id_re.match("A1B2C3D4"), "uuid4().hex is lowercase" assert not id_re.match("a1b2c3d"), "wrong length must not validate" def test_imgsz_matches_engine(): # engine.py hardcodes imgsz to match the compiled OpenVINO graph; if these # two ever drift, inference silently runs at the wrong input size. engine_src = (Path(__file__).parent / "engine.py").read_text(encoding="utf-8") assert f"imgsz={IMGSZ}," in engine_src, f"engine.py must call track(imgsz={IMGSZ})" if __name__ == "__main__": for name, fn in sorted(globals().items()): if name.startswith("test_") and callable(fn): fn() print(f" ok {name}") print("all core checks passed")