"""Synthetic test suite for EyeGuard 20-20-20. No webcam required.""" import time, sys sys.path.insert(0, '/app') from eye_guard_2020 import EyeGuard2020, Status, update_eye_state from eye_guard_2020 import REST_DURATION_SECONDS, SCREEN_TIME_ALERT_SECONDS, MIN_CONSECUTIVE_CLOSED_FRAMES def run_state_machine(guard, ear_l, ear_r, now_override=None): now = now_override if now_override is not None else time.time() state = guard.state ear_left = guard._smooth_ear(state.ear_history_left, ear_l) ear_right = guard._smooth_ear(state.ear_history_right, ear_r) state.left_eye = update_eye_state(state.left_eye, ear_left, guard.ear_open, guard.ear_closed) state.right_eye = update_eye_state(state.right_eye, ear_right, guard.ear_open, guard.ear_closed) screen_elapsed = now - state.screen_timer_start both_eyes_closed = state.left_eye.is_closed and state.right_eye.is_closed one_eye_closed = (state.left_eye.is_closed and not state.right_eye.is_closed) or \ (not state.left_eye.is_closed and state.right_eye.is_closed) if state.status == Status.RESTING and one_eye_closed: state.spoofing_detected_at = now state.spoofing_count += 1 state.status = Status.SPOOFING state.failed_rests += 1 state.rest_start_time = None return if state.status == Status.ALERT and one_eye_closed: state.spoofing_detected_at = now state.spoofing_count += 1 if state.status == Status.SCREENING: if screen_elapsed >= SCREEN_TIME_ALERT_SECONDS: state.status = Status.ALERT elif state.status == Status.ALERT: if both_eyes_closed and state.left_eye.closed_frames >= MIN_CONSECUTIVE_CLOSED_FRAMES: state.status = Status.RESTING state.rest_start_time = now elif state.status == Status.RESTING: if not both_eyes_closed: state.failed_rests += 1 state.status = Status.ALERT state.rest_start_time = None else: rest_elapsed = now - state.rest_start_time if rest_elapsed >= REST_DURATION_SECONDS: state.status = Status.REST_COMPLETE state.rest_completed_time = now state.total_rests_completed += 1 state.total_rest_seconds += rest_elapsed state.screen_timer_start = now elif state.status == Status.REST_COMPLETE: if now - state.rest_completed_time > 3.0: state.status = Status.SCREENING elif state.status == Status.SPOOFING: if now - state.spoofing_detected_at > 3.0: state.status = Status.ALERT elif state.status == Status.NO_FACE: if screen_elapsed >= SCREEN_TIME_ALERT_SECONDS: state.status = Status.ALERT else: state.status = Status.SCREENING def print_frame(i, guard, desc): le = "C" if guard.state.left_eye.is_closed else "O" re = "C" if guard.state.right_eye.is_closed else "O" print(f" Frame {i:3d}: Eyes=({le},{re}) Status={guard.state.status.name:<12} | {desc}") def test_normal_rest(): print("\n" + "=" * 60) print("TEST 1: Normal rest cycle (both eyes closed)") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 1000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.SCREENING for i in range(5): run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033) print_frame(i, guard, "eyes open") assert guard.state.status == Status.ALERT, f"Expected ALERT, got {guard.state.status.name}" print(" -> ALERT triggered correctly") for i in range(5, 30): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) assert guard.state.status == Status.RESTING print(" -> RESTING entered correctly") for i in range(30, 650): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i == 35: print_frame(i, guard, "resting...") assert guard.state.status == Status.REST_COMPLETE assert guard.state.total_rests_completed >= 1 print(f" -> REST_COMPLETE! rests={guard.state.total_rests_completed}") print(" PASS") def test_spoofing(): print("\n" + "=" * 60) print("TEST 2: Spoofing detection (one eye closed)") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 2000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.ALERT for i in range(20): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i < 3: print_frame(i, guard, "entering rest") assert guard.state.status == Status.RESTING print(" -> RESTING entered") for i in range(20, 50): run_state_machine(guard, 0.25, 0.10, t0 + i * 0.033) if i < 23: print_frame(i, guard, "SPOOFING: left eye open!") assert guard.state.status == Status.SPOOFING assert guard.state.spoofing_count >= 1 print(f" -> SPOOFING detected! count={guard.state.spoofing_count}") for i in range(50, 200): run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033) if i == 150: print_frame(i, guard, "after spoof penalty") assert guard.state.status == Status.ALERT print(" -> Back to ALERT after penalty") print(" PASS") def test_blink_filter(): print("\n" + "=" * 60) print("TEST 3: Blink filtering (brief closures ignored)") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 3000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.ALERT for i in range(5): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) print_frame(i, guard, "blink!") for i in range(5, 20): run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033) if i < 7: print_frame(i, guard, "eyes open again") assert guard.state.status == Status.ALERT, f"Expected ALERT, got {guard.state.status.name}" print(" -> Blink correctly filtered, still in ALERT") for i in range(20, 650): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i == 30: print_frame(i, guard, "proper rest...") assert guard.state.status == Status.REST_COMPLETE assert guard.state.total_rests_completed >= 1 print(f" -> Rest completed after blink! rests={guard.state.total_rests_completed}") print(" PASS") def test_interruption(): print("\n" + "=" * 60) print("TEST 4: Rest interruption (early opening)") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 4000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.ALERT for i in range(30): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i < 3: print_frame(i, guard, "starting rest") assert guard.state.status == Status.RESTING print(" -> RESTING entered") for i in range(30, 350): run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033) if i == 35: print_frame(i, guard, "REST INTERRUPTED!") assert guard.state.status == Status.ALERT assert guard.state.failed_rests >= 1 print(f" -> Rest failed (interrupted). failed={guard.state.failed_rests}") guard.state.status = Status.ALERT for i in range(350, 1000): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i == 360: print_frame(i, guard, "retry rest...") assert guard.state.status == Status.REST_COMPLETE assert guard.state.total_rests_completed >= 1 print(f" -> Retry succeeded! rests={guard.state.total_rests_completed}") print(" PASS") def test_no_face(): print("\n" + "=" * 60) print("TEST 5: No face visible during rest") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 5000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.ALERT for i in range(20): run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033) if i < 3: print_frame(i, guard, "entering rest") assert guard.state.status == Status.RESTING print(" -> RESTING entered") guard.state.status = Status.NO_FACE guard.state.failed_rests += 1 guard.state.rest_start_time = None assert guard.state.status == Status.NO_FACE print(" -> NO_FACE detected, rest failed") assert guard.state.failed_rests >= 1 print(f" -> failed={guard.state.failed_rests}") print(" PASS") def test_one_eye_alert(): print("\n" + "=" * 60) print("TEST 6: One-eye trick during ALERT phase") print("=" * 60) guard = EyeGuard2020() guard.ear_open = 0.20 guard.ear_closed = 0.15 t0 = 6000.0 guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1 guard.state.status = Status.ALERT for i in range(50): run_state_machine(guard, 0.10, 0.25, t0 + i * 0.033) if i < 3: print_frame(i, guard, "one-eye trick (left closed)") assert guard.state.spoofing_count >= 1 print(f" -> Spoofing detected during ALERT! count={guard.state.spoofing_count}") print(f" -> Final status: {guard.state.status.name}") print(" PASS") if __name__ == "__main__": print("=" * 60) print("EyeGuard 20-20-20 Synthetic Test Suite") print("=" * 60) test_normal_rest() test_spoofing() test_blink_filter() test_interruption() test_no_face() test_one_eye_alert() print("\n" + "=" * 60) print("ALL TESTS PASSED!") print("=" * 60)