HarshCode commited on
Commit
1fe2781
·
verified ·
1 Parent(s): 428d17e

Upload synthetic test suite

Browse files
Files changed (1) hide show
  1. test_eyeguard_synthetic.py +276 -0
test_eyeguard_synthetic.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Synthetic test suite for EyeGuard 20-20-20. No webcam required."""
2
+ import time, sys
3
+ sys.path.insert(0, '/app')
4
+ from eye_guard_2020 import EyeGuard2020, Status, update_eye_state
5
+ from eye_guard_2020 import REST_DURATION_SECONDS, SCREEN_TIME_ALERT_SECONDS, MIN_CONSECUTIVE_CLOSED_FRAMES
6
+
7
+
8
+ def run_state_machine(guard, ear_l, ear_r, now_override=None):
9
+ now = now_override if now_override is not None else time.time()
10
+ state = guard.state
11
+ ear_left = guard._smooth_ear(state.ear_history_left, ear_l)
12
+ ear_right = guard._smooth_ear(state.ear_history_right, ear_r)
13
+ state.left_eye = update_eye_state(state.left_eye, ear_left, guard.ear_open, guard.ear_closed)
14
+ state.right_eye = update_eye_state(state.right_eye, ear_right, guard.ear_open, guard.ear_closed)
15
+
16
+ screen_elapsed = now - state.screen_timer_start
17
+ both_eyes_closed = state.left_eye.is_closed and state.right_eye.is_closed
18
+ one_eye_closed = (state.left_eye.is_closed and not state.right_eye.is_closed) or \
19
+ (not state.left_eye.is_closed and state.right_eye.is_closed)
20
+
21
+ if state.status == Status.RESTING and one_eye_closed:
22
+ state.spoofing_detected_at = now
23
+ state.spoofing_count += 1
24
+ state.status = Status.SPOOFING
25
+ state.failed_rests += 1
26
+ state.rest_start_time = None
27
+ return
28
+
29
+ if state.status == Status.ALERT and one_eye_closed:
30
+ state.spoofing_detected_at = now
31
+ state.spoofing_count += 1
32
+
33
+ if state.status == Status.SCREENING:
34
+ if screen_elapsed >= SCREEN_TIME_ALERT_SECONDS:
35
+ state.status = Status.ALERT
36
+ elif state.status == Status.ALERT:
37
+ if both_eyes_closed and state.left_eye.closed_frames >= MIN_CONSECUTIVE_CLOSED_FRAMES:
38
+ state.status = Status.RESTING
39
+ state.rest_start_time = now
40
+ elif state.status == Status.RESTING:
41
+ if not both_eyes_closed:
42
+ state.failed_rests += 1
43
+ state.status = Status.ALERT
44
+ state.rest_start_time = None
45
+ else:
46
+ rest_elapsed = now - state.rest_start_time
47
+ if rest_elapsed >= REST_DURATION_SECONDS:
48
+ state.status = Status.REST_COMPLETE
49
+ state.rest_completed_time = now
50
+ state.total_rests_completed += 1
51
+ state.total_rest_seconds += rest_elapsed
52
+ state.screen_timer_start = now
53
+ elif state.status == Status.REST_COMPLETE:
54
+ if now - state.rest_completed_time > 3.0:
55
+ state.status = Status.SCREENING
56
+ elif state.status == Status.SPOOFING:
57
+ if now - state.spoofing_detected_at > 3.0:
58
+ state.status = Status.ALERT
59
+ elif state.status == Status.NO_FACE:
60
+ if screen_elapsed >= SCREEN_TIME_ALERT_SECONDS:
61
+ state.status = Status.ALERT
62
+ else:
63
+ state.status = Status.SCREENING
64
+
65
+
66
+ def print_frame(i, guard, desc):
67
+ le = "C" if guard.state.left_eye.is_closed else "O"
68
+ re = "C" if guard.state.right_eye.is_closed else "O"
69
+ print(f" Frame {i:3d}: Eyes=({le},{re}) Status={guard.state.status.name:<12} | {desc}")
70
+
71
+
72
+ def test_normal_rest():
73
+ print("\n" + "=" * 60)
74
+ print("TEST 1: Normal rest cycle (both eyes closed)")
75
+ print("=" * 60)
76
+ guard = EyeGuard2020()
77
+ guard.ear_open = 0.20
78
+ guard.ear_closed = 0.15
79
+ t0 = 1000.0
80
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
81
+ guard.state.status = Status.SCREENING
82
+
83
+ for i in range(5):
84
+ run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033)
85
+ print_frame(i, guard, "eyes open")
86
+ assert guard.state.status == Status.ALERT, f"Expected ALERT, got {guard.state.status.name}"
87
+ print(" -> ALERT triggered correctly")
88
+
89
+ for i in range(5, 30):
90
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
91
+ assert guard.state.status == Status.RESTING
92
+ print(" -> RESTING entered correctly")
93
+
94
+ for i in range(30, 650):
95
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
96
+ if i == 35:
97
+ print_frame(i, guard, "resting...")
98
+
99
+ assert guard.state.status == Status.REST_COMPLETE
100
+ assert guard.state.total_rests_completed >= 1
101
+ print(f" -> REST_COMPLETE! rests={guard.state.total_rests_completed}")
102
+ print(" PASS")
103
+
104
+
105
+ def test_spoofing():
106
+ print("\n" + "=" * 60)
107
+ print("TEST 2: Spoofing detection (one eye closed)")
108
+ print("=" * 60)
109
+ guard = EyeGuard2020()
110
+ guard.ear_open = 0.20
111
+ guard.ear_closed = 0.15
112
+ t0 = 2000.0
113
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
114
+ guard.state.status = Status.ALERT
115
+
116
+ for i in range(20):
117
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
118
+ if i < 3:
119
+ print_frame(i, guard, "entering rest")
120
+ assert guard.state.status == Status.RESTING
121
+ print(" -> RESTING entered")
122
+
123
+ for i in range(20, 50):
124
+ run_state_machine(guard, 0.25, 0.10, t0 + i * 0.033)
125
+ if i < 23:
126
+ print_frame(i, guard, "SPOOFING: left eye open!")
127
+
128
+ assert guard.state.status == Status.SPOOFING
129
+ assert guard.state.spoofing_count >= 1
130
+ print(f" -> SPOOFING detected! count={guard.state.spoofing_count}")
131
+
132
+ for i in range(50, 200):
133
+ run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033)
134
+ if i == 150:
135
+ print_frame(i, guard, "after spoof penalty")
136
+ assert guard.state.status == Status.ALERT
137
+ print(" -> Back to ALERT after penalty")
138
+ print(" PASS")
139
+
140
+
141
+ def test_blink_filter():
142
+ print("\n" + "=" * 60)
143
+ print("TEST 3: Blink filtering (brief closures ignored)")
144
+ print("=" * 60)
145
+ guard = EyeGuard2020()
146
+ guard.ear_open = 0.20
147
+ guard.ear_closed = 0.15
148
+ t0 = 3000.0
149
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
150
+ guard.state.status = Status.ALERT
151
+
152
+ for i in range(5):
153
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
154
+ print_frame(i, guard, "blink!")
155
+ for i in range(5, 20):
156
+ run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033)
157
+ if i < 7:
158
+ print_frame(i, guard, "eyes open again")
159
+
160
+ assert guard.state.status == Status.ALERT, f"Expected ALERT, got {guard.state.status.name}"
161
+ print(" -> Blink correctly filtered, still in ALERT")
162
+
163
+ for i in range(20, 650):
164
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
165
+ if i == 30:
166
+ print_frame(i, guard, "proper rest...")
167
+
168
+ assert guard.state.status == Status.REST_COMPLETE
169
+ assert guard.state.total_rests_completed >= 1
170
+ print(f" -> Rest completed after blink! rests={guard.state.total_rests_completed}")
171
+ print(" PASS")
172
+
173
+
174
+ def test_interruption():
175
+ print("\n" + "=" * 60)
176
+ print("TEST 4: Rest interruption (early opening)")
177
+ print("=" * 60)
178
+ guard = EyeGuard2020()
179
+ guard.ear_open = 0.20
180
+ guard.ear_closed = 0.15
181
+ t0 = 4000.0
182
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
183
+ guard.state.status = Status.ALERT
184
+
185
+ for i in range(30):
186
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
187
+ if i < 3:
188
+ print_frame(i, guard, "starting rest")
189
+ assert guard.state.status == Status.RESTING
190
+ print(" -> RESTING entered")
191
+
192
+ for i in range(30, 350):
193
+ run_state_machine(guard, 0.25, 0.25, t0 + i * 0.033)
194
+ if i == 35:
195
+ print_frame(i, guard, "REST INTERRUPTED!")
196
+
197
+ assert guard.state.status == Status.ALERT
198
+ assert guard.state.failed_rests >= 1
199
+ print(f" -> Rest failed (interrupted). failed={guard.state.failed_rests}")
200
+
201
+ guard.state.status = Status.ALERT
202
+ for i in range(350, 1000):
203
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
204
+ if i == 360:
205
+ print_frame(i, guard, "retry rest...")
206
+
207
+ assert guard.state.status == Status.REST_COMPLETE
208
+ assert guard.state.total_rests_completed >= 1
209
+ print(f" -> Retry succeeded! rests={guard.state.total_rests_completed}")
210
+ print(" PASS")
211
+
212
+
213
+ def test_no_face():
214
+ print("\n" + "=" * 60)
215
+ print("TEST 5: No face visible during rest")
216
+ print("=" * 60)
217
+ guard = EyeGuard2020()
218
+ guard.ear_open = 0.20
219
+ guard.ear_closed = 0.15
220
+ t0 = 5000.0
221
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
222
+ guard.state.status = Status.ALERT
223
+
224
+ for i in range(20):
225
+ run_state_machine(guard, 0.10, 0.10, t0 + i * 0.033)
226
+ if i < 3:
227
+ print_frame(i, guard, "entering rest")
228
+ assert guard.state.status == Status.RESTING
229
+ print(" -> RESTING entered")
230
+
231
+ guard.state.status = Status.NO_FACE
232
+ guard.state.failed_rests += 1
233
+ guard.state.rest_start_time = None
234
+
235
+ assert guard.state.status == Status.NO_FACE
236
+ print(" -> NO_FACE detected, rest failed")
237
+ assert guard.state.failed_rests >= 1
238
+ print(f" -> failed={guard.state.failed_rests}")
239
+ print(" PASS")
240
+
241
+
242
+ def test_one_eye_alert():
243
+ print("\n" + "=" * 60)
244
+ print("TEST 6: One-eye trick during ALERT phase")
245
+ print("=" * 60)
246
+ guard = EyeGuard2020()
247
+ guard.ear_open = 0.20
248
+ guard.ear_closed = 0.15
249
+ t0 = 6000.0
250
+ guard.state.screen_timer_start = t0 - SCREEN_TIME_ALERT_SECONDS - 1
251
+ guard.state.status = Status.ALERT
252
+
253
+ for i in range(50):
254
+ run_state_machine(guard, 0.10, 0.25, t0 + i * 0.033)
255
+ if i < 3:
256
+ print_frame(i, guard, "one-eye trick (left closed)")
257
+
258
+ assert guard.state.spoofing_count >= 1
259
+ print(f" -> Spoofing detected during ALERT! count={guard.state.spoofing_count}")
260
+ print(f" -> Final status: {guard.state.status.name}")
261
+ print(" PASS")
262
+
263
+
264
+ if __name__ == "__main__":
265
+ print("=" * 60)
266
+ print("EyeGuard 20-20-20 Synthetic Test Suite")
267
+ print("=" * 60)
268
+ test_normal_rest()
269
+ test_spoofing()
270
+ test_blink_filter()
271
+ test_interruption()
272
+ test_no_face()
273
+ test_one_eye_alert()
274
+ print("\n" + "=" * 60)
275
+ print("ALL TESTS PASSED!")
276
+ print("=" * 60)