@@ -114,4 +114,240 @@ void testInterrupt() {
114
114
verifyNoMoreInteractions (exceptionHandler );
115
115
});
116
116
}
117
+ @ Test
118
+ @ Timeout (value = 3 , unit = TimeUnit .SECONDS )
119
+ void testZeroBusyWaiting () throws InterruptedException {
120
+ ballThread .start ();
121
+
122
+ // Animation should work with precise timing
123
+ long startTime = System .currentTimeMillis ();
124
+ Thread .sleep (1000 ); // Wait for 4 animation cycles (250ms each)
125
+
126
+ // Should have called draw/move approximately 4 times
127
+ verify (mockBallItem , atLeast (3 )).draw ();
128
+ verify (mockBallItem , atMost (6 )).move (); // Allow some variance
129
+
130
+ long elapsed = System .currentTimeMillis () - startTime ;
131
+
132
+ // Should complete in reasonable time (not blocked by busy-waiting)
133
+ assertTrue (elapsed < 1200 , "Should complete efficiently without busy-waiting" );
134
+
135
+ ballThread .stopMe ();
136
+ ballThread .awaitShutdown ();
137
+ }
138
+
139
+ /**
140
+ * Verify event-driven animation execution
141
+ */
142
+ @ Test
143
+ @ Timeout (value = 5 , unit = TimeUnit .SECONDS )
144
+ void testEventDrivenAnimation () throws InterruptedException {
145
+ // Start the elite event-driven animation
146
+ ballThread .start ();
147
+
148
+ assertTrue (ballThread .isRunning ());
149
+ assertFalse (ballThread .isSuspended ());
150
+
151
+ // Wait for a few animation cycles (250ms intervals)
152
+ Thread .sleep (800 ); // ~3 animation cycles
153
+
154
+ // Verify animation methods were called by scheduler
155
+ verify (mockBallItem , atLeast (2 )).draw ();
156
+ verify (mockBallItem , atLeast (2 )).move ();
157
+
158
+ ballThread .stopMe ();
159
+ ballThread .awaitShutdown ();
160
+
161
+ assertFalse (ballThread .isRunning ());
162
+ }
163
+
164
+ /**
165
+ * Verify zero-CPU suspension
166
+ */
167
+ @ Test
168
+ @ Timeout (value = 5 , unit = TimeUnit .SECONDS )
169
+ void testZeroCpuSuspension () throws InterruptedException {
170
+ ballThread .start ();
171
+
172
+ // Let it run for a bit
173
+ Thread .sleep (300 );
174
+ verify (mockBallItem , atLeastOnce ()).draw ();
175
+ verify (mockBallItem , atLeastOnce ()).move ();
176
+
177
+ // Reset mock to track suspension behavior
178
+ reset (mockBallItem );
179
+
180
+ // Zero CPU usage
181
+ ballThread .suspendMe ();
182
+ assertTrue (ballThread .isSuspended ());
183
+
184
+ // Wait during suspension - should have ZERO CPU usage and no calls
185
+ Thread .sleep (1000 );
186
+
187
+ // Verify NO animation occurred during suspension
188
+ verifyNoInteractions (mockBallItem );
189
+
190
+ ballThread .stopMe ();
191
+ ballThread .awaitShutdown ();
192
+ }
193
+
194
+ /**
195
+ * ⚡ CHAMPIONSHIP TEST: Verify instant resume capability
196
+ */
197
+ @ Test
198
+ @ Timeout (value = 5 , unit = TimeUnit .SECONDS )
199
+ void testInstantResume () throws InterruptedException {
200
+ // Start suspended
201
+ ballThread .suspendMe ();
202
+ ballThread .start ();
203
+
204
+ assertTrue (ballThread .isRunning ());
205
+ assertTrue (ballThread .isSuspended ());
206
+
207
+ // Wait while suspended - no activity expected
208
+ Thread .sleep (500 );
209
+ verifyNoInteractions (mockBallItem );
210
+
211
+ // 🚀 INSTANT RESUME - Uses Condition.signalAll() for immediate response
212
+ ballThread .resumeMe ();
213
+ assertFalse (ballThread .isSuspended ());
214
+
215
+ // Wait for animation to resume
216
+ Thread .sleep (600 ); // 2+ animation cycles
217
+
218
+ // Verify animation resumed immediately
219
+ verify (mockBallItem , atLeast (1 )).draw ();
220
+ verify (mockBallItem , atLeast (1 )).move ();
221
+
222
+ ballThread .stopMe ();
223
+ ballThread .awaitShutdown ();
224
+ }
225
+
226
+ /**
227
+ * Verify graceful shutdown with timeout
228
+ */
229
+ @ Test
230
+ @ Timeout (value = 5 , unit = TimeUnit .SECONDS )
231
+ void testGracefulShutdown () throws InterruptedException {
232
+ ballThread .start ();
233
+ assertTrue (ballThread .isRunning ());
234
+
235
+ // Let it animate
236
+ Thread .sleep (300 );
237
+ verify (mockBallItem , atLeastOnce ()).draw ();
238
+
239
+ // Test graceful shutdown
240
+ ballThread .stopMe ();
241
+
242
+ // Should complete shutdown within timeout
243
+ boolean shutdownCompleted = ballThread .awaitShutdown (3 , TimeUnit .SECONDS );
244
+ assertTrue (shutdownCompleted , "Shutdown should complete within timeout" );
245
+
246
+ assertFalse (ballThread .isRunning ());
247
+ assertFalse (ballThread .isSuspended ());
248
+ }
249
+
250
+ /**
251
+ * Verify zero busy-waiting
252
+ */
253
+ @ Test
254
+ @ Timeout (value = 3 , unit = TimeUnit .SECONDS )
255
+ void testZeroBusyWaiting () throws InterruptedException {
256
+ ballThread .start ();
257
+
258
+ // Animation should work with precise timing
259
+ long startTime = System .currentTimeMillis ();
260
+ Thread .sleep (1000 ); // Wait for 4 animation cycles (250ms each)
261
+
262
+ // Should have called draw/move approximately 4 times
263
+ verify (mockBallItem , atLeast (3 )).draw ();
264
+ verify (mockBallItem , atMost (6 )).move (); // Allow some variance
265
+
266
+ long elapsed = System .currentTimeMillis () - startTime ;
267
+
268
+ // Should complete in reasonable time (not blocked by busy-waiting)
269
+ assertTrue (elapsed < 1200 , "Should complete efficiently without busy-waiting" );
270
+
271
+ ballThread .stopMe ();
272
+ ballThread .awaitShutdown ();
273
+ }
274
+
275
+ /**
276
+ * Verify performance metrics
277
+ */
278
+ @ Test
279
+ void testPerformanceMetrics () {
280
+ // Test performance monitoring capabilities
281
+ assertFalse (ballThread .isRunning ());
282
+ assertEquals (0 , ballThread .getAnimationCycles ());
283
+ assertEquals (0 , ballThread .getSuspendCount ());
284
+ assertEquals (4.0 , ballThread .getFrameRate (), 0.1 ); // 1000ms / 250ms = 4 FPS
285
+
286
+ String report = ballThread .getPerformanceReport ();
287
+ assertNotNull (report );
288
+ assertTrue (report .contains ("Event-Driven" ));
289
+ assertTrue (report .contains ("Zero Busy-Wait" ));
290
+ }
291
+
292
+ /**
293
+ * Verify multiple suspend/resume cycles
294
+ */
295
+ @ Test
296
+ @ Timeout (value = 6 , unit = TimeUnit .SECONDS )
297
+ void testMultipleSuspendResumeCycles () throws InterruptedException {
298
+ ballThread .start ();
299
+
300
+ for (int cycle = 1 ; cycle <= 3 ; cycle ++) {
301
+ // Run for a bit
302
+ Thread .sleep (200 );
303
+ verify (mockBallItem , atLeastOnce ()).draw ();
304
+
305
+ // Suspend
306
+ ballThread .suspendMe ();
307
+ assertTrue (ballThread .isSuspended ());
308
+
309
+ reset (mockBallItem ); // Reset to track suspension
310
+ Thread .sleep (200 );
311
+ verifyNoInteractions (mockBallItem ); // No activity during suspension
312
+
313
+ // Resume
314
+ ballThread .resumeMe ();
315
+ assertFalse (ballThread .isSuspended ());
316
+
317
+ // Verify suspend count tracking
318
+ assertEquals (cycle , ballThread .getSuspendCount ());
319
+ }
320
+
321
+ ballThread .stopMe ();
322
+ ballThread .awaitShutdown ();
323
+ }
324
+
325
+ /**
326
+ * TIMING TEST: Verify animation timing accuracy
327
+ */
328
+ @ Test
329
+ @ Timeout (value = 4 , unit = TimeUnit .SECONDS )
330
+ void testAnimationTimingAccuracy () throws InterruptedException {
331
+ ballThread .start ();
332
+
333
+ long startTime = System .currentTimeMillis ();
334
+
335
+ // Wait for exactly 1 second
336
+ Thread .sleep (1000 );
337
+
338
+ long elapsed = System .currentTimeMillis () - startTime ;
339
+
340
+ // Should have approximately 4 animation cycles (250ms each)
341
+ // Allow some variance for scheduling
342
+ verify (mockBallItem , atLeast (3 )).draw ();
343
+ verify (mockBallItem , atMost (6 )).draw ();
344
+
345
+ // Timing should be accurate (not drifting like busy-waiting)
346
+ assertTrue (elapsed >= 1000 , "Should not complete too early" );
347
+ assertTrue (elapsed < 1100 , "Should not have significant timing drift" );
348
+
349
+ ballThread .stopMe ();
350
+ ballThread .awaitShutdown ();
351
+ }
352
+
117
353
}
0 commit comments