Spaces:
Sleeping
Sleeping
File size: 3,513 Bytes
f048b9a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | """
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")
|