from collections import deque import pytest from src.face_processor import FaceTrackManager from src.models import BoundingBox, FaceROI, HeadPoseResult def result(track_id: int, yaw: float | None, detected: bool = True) -> HeadPoseResult: success = yaw is not None return HeadPoseResult(track_id, 1, "now", .1, detected, success, 1.0 if success else None, yaw, 2.0 if success else None, 6 if detected else 0, None, .9 if detected else None, "" if success else "face_not_detected") def test_face_roi_properties() -> None: bbox = BoundingBox(10, 20, 110, 220) roi = FaceROI(1, 5, 10, 115, 100, bbox, .8) assert (roi.width, roi.height) == (110, 90) assert (roi.center_x, roi.center_y) == (60.0, 55.0) def test_head_pose_result_success_and_failure_validation() -> None: assert result(1, -12.0).pose_estimated assert not result(1, None, False).face_detected with pytest.raises(ValueError): HeadPoseResult(1, 1, "now", 0, True, True, None, 1.0, 2.0, 6, None, .5) def test_face_track_statistics_and_history_limit() -> None: manager = FaceTrackManager(2) manager.update([result(1, -10.0), result(1, 20.0), result(1, None, False)]) state = manager.get_state(1) assert state is not None assert state.total_frames == 3 assert state.face_detection_rate == pytest.approx(2 / 3) assert state.pose_estimation_rate == pytest.approx(2 / 3) assert (state.min_yaw, state.max_yaw, state.mean_yaw) == (-10.0, 20.0, 5.0) assert len(state.history) == 2 finalized = manager.finalize_track(1) assert finalized and finalized.finalized