Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2024, Cloud Software Group, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #define _GNU_SOURCE /* Needed for F_GETPIPE_SZ */
32 : :
33 : : #include <setjmp.h>
34 : : #include <stdarg.h>
35 : : #include <stddef.h>
36 : : #include <cmocka.h>
37 : :
38 : : #include <errno.h>
39 : : #include <limits.h>
40 : : #include <stdio.h>
41 : : #include <sys/time.h>
42 : :
43 : : #include "scheduler.c"
44 : :
45 : : struct timeval fake_gettimeofday;
46 : 90 : int __wrap_gettimeofday(struct timeval* tv, struct timezone* tz)
47 : : {
48 : 90 : *tv = fake_gettimeofday;
49 : 90 : return 0;
50 : : }
51 : :
52 : : void scheduler_gc_events(scheduler_t *s);
53 : :
54 : : typedef struct {
55 : : bool was_called;
56 : : char mode;
57 : : event_id_t id;
58 : : } event_cb_spy_t;
59 : :
60 : 6 : void mock_event_cb(event_id_t id, char mode, void *private)
61 : : {
62 : 6 : event_cb_spy_t* out = (event_cb_spy_t*)private;
63 : 6 : out->was_called = true;
64 : 6 : out->mode = mode;
65 : 6 : out->id = id;
66 : 6 : }
67 : :
68 : : static int
69 : 6 : event_queue_length(const scheduler_t* s)
70 : : {
71 : 6 : int len = 0;
72 : : event_t *event;
73 [ + + ]: 17 : scheduler_for_each_event(s, event){
74 : 11 : len += 1;
75 : : }
76 : 6 : return len;
77 : : }
78 : :
79 : 0 : void fake_event_cb (event_id_t id, char mode, void *private) {}
80 : :
81 : : void
82 : 1 : test_scheduler_set_max_timeout(void **state)
83 : : {
84 : : scheduler_t s;
85 : 1 : s.max_timeout.tv_sec = 42;
86 : 1 : struct timeval timeout = {.tv_sec = 0 };
87 : 1 : scheduler_set_max_timeout(&s, timeout);
88 : 1 : assert_int_equal(s.max_timeout.tv_sec, 0);
89 : 1 : }
90 : :
91 : : void
92 : 1 : test_scheduler_set_max_timeout_lower(void **state)
93 : : {
94 : : // Setting a new lower value will stick
95 : : scheduler_t s;
96 : 1 : s.max_timeout.tv_sec = 430;
97 : 1 : struct timeval timeout = {.tv_sec = 360 };
98 : 1 : scheduler_set_max_timeout(&s, timeout);
99 : 1 : assert_int_equal(s.max_timeout.tv_sec, 360);
100 : 1 : }
101 : :
102 : : void
103 : 1 : test_scheduler_set_max_timeout_higher(void **state)
104 : : {
105 : : // Setting a new higher value will be ignored
106 : : scheduler_t s;
107 : 1 : s.max_timeout.tv_sec = 430;
108 : 1 : struct timeval timeout = {.tv_sec = 458 };
109 : 1 : scheduler_set_max_timeout(&s, timeout);
110 : 1 : assert_int_equal(s.max_timeout.tv_sec, 430);
111 : 1 : }
112 : :
113 : : void
114 : 1 : test_scheduler_set_max_timeout_negative(void **state)
115 : : {
116 : : // Setting a new negative value will be ignored
117 : : scheduler_t s;
118 : 1 : s.max_timeout.tv_sec = 458;
119 : 1 : struct timeval timeout = {.tv_sec = -2 };
120 : 1 : scheduler_set_max_timeout(&s, timeout);
121 : 1 : assert_int_equal(s.max_timeout.tv_sec, 458);
122 : 1 : }
123 : :
124 : : void
125 : 1 : test_scheduler_set_max_timeout_inf(void **state)
126 : : {
127 : : // Setting TV_INF will be ignored
128 : : scheduler_t s;
129 : 1 : s.max_timeout.tv_sec = 458;
130 : 1 : struct timeval timeout = TV_INF;
131 : 1 : scheduler_set_max_timeout(&s, timeout);
132 : 1 : assert_int_equal(s.max_timeout.tv_sec, 458);
133 : 1 : }
134 : :
135 : : void
136 : 1 : test_scheduler_register_event_null_callback(void **state)
137 : : {
138 : : // Callback cannot be NULL
139 : : scheduler_t s;
140 : 1 : scheduler_initialize(&s);
141 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
142 : 1 : int fd = 0;
143 : 1 : struct timeval timeout = {};
144 : 1 : event_cb_t cb = NULL;
145 : 1 : void *private = NULL;
146 : :
147 : 1 : const int r = scheduler_register_event(&s, mode, fd, timeout, cb, private);
148 : 1 : assert_int_equal(r, -EINVAL);
149 : 1 : }
150 : :
151 : : void
152 : 1 : test_scheduler_register_event_bad_mode(void **state)
153 : : {
154 : : // Bad mode
155 : : scheduler_t s;
156 : 1 : scheduler_initialize(&s);
157 : 1 : char mode = 0;
158 : 1 : int fd = 0;
159 : 1 : struct timeval timeout = {};
160 : 1 : event_cb_t cb = &fake_event_cb;
161 : 1 : void *private = NULL;
162 : :
163 : 1 : const int r = scheduler_register_event(&s, mode, fd, timeout, cb, private);
164 : 1 : assert_int_equal(r, -EINVAL);
165 : 1 : }
166 : :
167 : : void
168 : 1 : test_scheduler_register_multiple_events(void **state)
169 : : {
170 : : scheduler_t s;
171 : 1 : scheduler_initialize(&s);
172 : :
173 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
174 : 1 : int fd = 0;
175 : 1 : struct timeval timeout = {};
176 : 1 : event_cb_t cb = &fake_event_cb;
177 : 1 : void *private = NULL;
178 : :
179 : : /* Event queue starts off empty */
180 : 1 : assert_int_equal(event_queue_length(&s), 0);
181 : :
182 : : /* Event queue starts now has one event */
183 : 1 : const int event_id1 = scheduler_register_event(&s, mode, fd, timeout, cb, private);
184 : 1 : assert_int_equal(event_queue_length(&s), 1);
185 : :
186 : : /* Event queue starts now has two events */
187 : 1 : const int event_id2 = scheduler_register_event(&s, mode, fd, timeout, cb, private);
188 : 1 : assert_int_equal(event_queue_length(&s), 2);
189 : :
190 : : /* The two event IDs we were given are different */
191 : 1 : assert_int_not_equal(event_id1, event_id2);
192 : 1 : }
193 : :
194 : : void
195 : 1 : test_scheduler_register_event_populates_event(void **state)
196 : : {
197 : : /* scheduler_register_event will fill out all fields in event */
198 : : scheduler_t s;
199 : 1 : scheduler_initialize(&s);
200 : :
201 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
202 : 1 : int fd = 458;
203 : 1 : struct timeval timeout = { .tv_sec = 575, .tv_usec = 599 };
204 : 1 : event_cb_t cb = &fake_event_cb;
205 : 1 : int private = 430;
206 : :
207 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 2, .tv_usec = 3};
208 : 1 : const int event_id1 = scheduler_register_event(&s, mode, fd, timeout, cb, &private);
209 : :
210 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
211 : :
212 : 1 : assert_int_equal(e->id, event_id1);
213 : 1 : assert_int_equal(e->mode, mode);
214 : 1 : assert_int_equal(e->fd, fd);
215 : 1 : assert_int_equal(e->timeout.tv_sec, timeout.tv_sec);
216 : 1 : assert_int_equal(e->timeout.tv_usec, timeout.tv_usec);
217 : 1 : assert_ptr_equal(e->cb, &fake_event_cb);
218 : 1 : assert_ptr_equal(e->private, &private);
219 : 1 : assert_ptr_equal(e->masked, 0);
220 : :
221 : 1 : assert_int_equal(e->deadline.tv_sec, fake_gettimeofday.tv_sec + timeout.tv_sec);
222 : 1 : assert_int_equal(e->deadline.tv_usec, fake_gettimeofday.tv_usec + timeout.tv_usec);
223 : 1 : }
224 : :
225 : : void
226 : 1 : test_scheduler_set_timeout_inf(void **state)
227 : : {
228 : : /* If timeout is TV_INF then deadline is TV_INF */
229 : : scheduler_t s;
230 : 1 : scheduler_initialize(&s);
231 : :
232 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
233 : 1 : int fd = 458;
234 : 1 : struct timeval timeout = TV_INF;
235 : 1 : event_cb_t cb = &fake_event_cb;
236 : :
237 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
238 : :
239 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
240 : :
241 : 1 : assert_true(TV_IS_INF(e->timeout));
242 : 1 : }
243 : :
244 : : void
245 : 1 : test_scheduler_set_timeout_invalid_event(void **state)
246 : : {
247 : : // Invalid event ID will return EINVAL
248 : : scheduler_t sched;
249 : 1 : event_id_t event_id = 0;
250 : : struct timeval timeo;
251 : 1 : const int r = scheduler_event_set_timeout(&sched, event_id, timeo);
252 : 1 : assert_int_equal(r, -EINVAL);
253 : 1 : }
254 : :
255 : : void
256 : 1 : test_scheduler_set_timeout_on_non_polled_event(void **state)
257 : : {
258 : : // Set timeout on none polled event returns EINVAL
259 : : scheduler_t s;
260 : 1 : scheduler_initialize(&s);
261 : :
262 : 1 : char mode = SCHEDULER_POLL_READ_FD; // Not a POLL_TIMEOUT event
263 : 1 : int fd = 1;
264 : 1 : struct timeval timeout = { .tv_sec = 996 };
265 : 1 : event_cb_t cb = &fake_event_cb;
266 : :
267 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
268 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
269 : 1 : const int r = scheduler_event_set_timeout(&s, e->id, (struct timeval){});
270 : 1 : assert_int_equal(r, -EINVAL);
271 : 1 : }
272 : :
273 : : void
274 : 1 : test_scheduler_set_timeout_missing_event(void **state)
275 : : {
276 : : // Set timeout returns ENOENT if event is not in list
277 : : scheduler_t s;
278 : 1 : scheduler_initialize(&s);
279 : :
280 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
281 : 1 : int fd = 1;
282 : 1 : struct timeval timeout = { .tv_sec = 996 };
283 : 1 : event_cb_t cb = &fake_event_cb;
284 : :
285 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
286 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
287 : 1 : const int r = scheduler_event_set_timeout(&s, e->id+1, (struct timeval){});
288 : 1 : assert_int_equal(r, -ENOENT);
289 : 1 : }
290 : :
291 : : void
292 : 1 : test_scheduler_set_timeout(void **state)
293 : : {
294 : : // Set timeout will update timeout and deadline for affected event
295 : : scheduler_t s;
296 : 1 : scheduler_initialize(&s);
297 : :
298 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
299 : 1 : int fd = 1;
300 : 1 : struct timeval timeout1 = { .tv_sec = 996 };
301 : 1 : struct timeval timeout2 = { .tv_sec = 993 };
302 : 1 : struct timeval timeout3 = { .tv_sec = 964 };
303 : 1 : event_cb_t cb = &fake_event_cb;
304 : :
305 : 1 : int fake_gettimeofday_tv_sec = 1;
306 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = fake_gettimeofday_tv_sec, .tv_usec = 0};
307 : :
308 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout1, cb, NULL);
309 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout2, cb, NULL);
310 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout3, cb, NULL);
311 : 1 : const event_t* e1 = list_first_entry(&s.events, event_t, next);
312 : 2 : const event_t* e2 = list_next_entry(e1, next);
313 : 2 : const event_t* e3 = list_next_entry(e2, next);
314 : :
315 : 1 : assert_int_equal(e1->timeout.tv_sec, timeout1.tv_sec);
316 : 1 : assert_int_equal(e2->timeout.tv_sec, timeout2.tv_sec);
317 : 1 : assert_int_equal(e3->timeout.tv_sec, timeout3.tv_sec);
318 : 1 : assert_int_equal(e1->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout1.tv_sec);
319 : 1 : assert_int_equal(e2->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout2.tv_sec);
320 : 1 : assert_int_equal(e3->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout3.tv_sec);
321 : :
322 : 1 : struct timeval new_timeout2 = { .tv_sec = 911 };
323 : 1 : scheduler_event_set_timeout(&s, e2->id, new_timeout2);
324 : :
325 : 1 : assert_int_equal(e1->timeout.tv_sec, timeout1.tv_sec); // unchanged
326 : 1 : assert_int_equal(e2->timeout.tv_sec, new_timeout2.tv_sec); // new value
327 : 1 : assert_int_equal(e3->timeout.tv_sec, timeout3.tv_sec); // unchanged
328 : 1 : assert_int_equal(e1->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout1.tv_sec);
329 : 1 : assert_int_equal(e2->deadline.tv_sec, fake_gettimeofday_tv_sec + new_timeout2.tv_sec);
330 : 1 : assert_int_equal(e3->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout3.tv_sec);
331 : 1 : }
332 : :
333 : : void
334 : 1 : test_scheduler_set_timeout_inf_and_deadline(void **state)
335 : : {
336 : : // Set timeout will update deadline to TV_INF if new timeout is TV_INF
337 : : scheduler_t s;
338 : 1 : scheduler_initialize(&s);
339 : :
340 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
341 : 1 : int fd = 1;
342 : 1 : struct timeval timeout = { .tv_sec = 996 };
343 : 1 : event_cb_t cb = &fake_event_cb;
344 : :
345 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 1, .tv_usec = 2};
346 : :
347 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
348 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
349 : :
350 : 1 : assert_int_equal(e->timeout.tv_sec, timeout.tv_sec);
351 : :
352 : 1 : struct timeval new_timeout = TV_INF;
353 : 1 : scheduler_event_set_timeout(&s, e->id, new_timeout);
354 : :
355 : 1 : assert_true(TV_IS_INF(e->timeout));
356 : 1 : assert_true(TV_IS_INF(e->deadline));
357 : 1 : }
358 : :
359 : : void
360 : 1 : test_scheduler_unregister_event_will_set_dead_field(void **state)
361 : : {
362 : : // Unregister event will set dead to 1
363 : : scheduler_t s;
364 : 1 : scheduler_initialize(&s);
365 : :
366 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
367 : 1 : int fd = 1;
368 : 1 : struct timeval timeout = { .tv_sec = 1 };
369 : 1 : event_cb_t cb = &fake_event_cb;
370 : :
371 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
372 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
373 : 1 : assert_int_not_equal(e->dead, 1);
374 : :
375 : 1 : scheduler_unregister_event(&s, e->id);
376 : 1 : assert_int_equal(e->dead, 1);
377 : 1 : }
378 : :
379 : : void
380 : 1 : test_scheduler_unregister_event_will_ignore_invalid_event(void **state)
381 : : {
382 : : // Unregister event will ignore invalid event id
383 : : scheduler_t s;
384 : 1 : scheduler_initialize(&s);
385 : :
386 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
387 : 1 : int fd = 1;
388 : 1 : struct timeval timeout = { .tv_sec = 1 };
389 : 1 : event_cb_t cb = &fake_event_cb;
390 : :
391 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
392 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
393 : 1 : assert_int_not_equal(e->dead, 1);
394 : :
395 : 1 : scheduler_unregister_event(&s, 0);
396 : 1 : assert_int_not_equal(e->dead, 1);
397 : 1 : }
398 : :
399 : : void
400 : 1 : test_scheduler_mask_event_will_set_masked_field(void **state)
401 : : {
402 : : // mask event will set masked to 1 or 0 depending on 3rd arg
403 : : scheduler_t s;
404 : 1 : scheduler_initialize(&s);
405 : :
406 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
407 : 1 : int fd = 1;
408 : 1 : struct timeval timeout = { .tv_sec = 1 };
409 : 1 : event_cb_t cb = &fake_event_cb;
410 : :
411 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
412 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
413 : 1 : assert_int_not_equal(e->masked, 1);
414 : :
415 : 1 : scheduler_mask_event(&s, e->id, 1);
416 : 1 : assert_int_equal(e->masked, 1);
417 : :
418 : 1 : scheduler_mask_event(&s, e->id, 0);
419 : 1 : assert_int_not_equal(e->masked, 1);
420 : 1 : }
421 : :
422 : : void
423 : 1 : test_scheduler_mask_event_will_accept_non_zero_value(void **state)
424 : : {
425 : : // mask event will accept any non-zero value and convert it to 1
426 : : scheduler_t s;
427 : 1 : scheduler_initialize(&s);
428 : :
429 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
430 : 1 : int fd = 1;
431 : 1 : struct timeval timeout = { .tv_sec = 1 };
432 : 1 : event_cb_t cb = &fake_event_cb;
433 : :
434 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
435 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
436 : 1 : assert_int_not_equal(e->masked, 1);
437 : :
438 : 1 : scheduler_mask_event(&s, e->id, 959);
439 : 1 : assert_int_equal(e->masked, 1);
440 : 1 : }
441 : :
442 : : void
443 : 1 : test_scheduler_mask_event_will_ignore_invalid_event_id(void **state)
444 : : {
445 : : // mask event will ignore invalid event id
446 : : scheduler_t s;
447 : 1 : scheduler_initialize(&s);
448 : :
449 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
450 : 1 : int fd = 1;
451 : 1 : struct timeval timeout = { .tv_sec = 1 };
452 : 1 : event_cb_t cb = &fake_event_cb;
453 : :
454 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
455 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
456 : 1 : assert_int_not_equal(e->masked, 1);
457 : :
458 : 1 : scheduler_mask_event(&s, 0, 1);
459 : 1 : assert_int_not_equal(e->masked, 1);
460 : 1 : }
461 : :
462 : : void
463 : 1 : test_scheduler_get_uuid(void **state)
464 : : {
465 : : scheduler_t s;
466 : 1 : scheduler_initialize(&s);
467 : :
468 : 1 : s.uuid = 1;
469 : 1 : const int new_uuid = scheduler_get_event_uuid(&s);
470 : 1 : assert_int_equal(new_uuid, 1);
471 : 1 : assert_int_equal(s.uuid, 2);
472 : 1 : }
473 : :
474 : : void
475 : 1 : test_scheduler_get_uuid_overflow(void **state)
476 : : {
477 : : // get uuid overflow
478 : : scheduler_t s;
479 : 1 : scheduler_initialize(&s);
480 : :
481 : 1 : s.uuid = INT_MAX;
482 : 1 : const int new_uuid = scheduler_get_event_uuid(&s);
483 : 1 : assert_int_equal(new_uuid, INT_MAX);
484 : 1 : assert_int_equal(s.uuid, INT_MIN);
485 : :
486 : 1 : const int new_uuid2 = scheduler_get_event_uuid(&s);
487 : 1 : assert_int_equal(new_uuid2, 1);
488 : 1 : assert_int_equal(s.uuid, 2);
489 : 1 : }
490 : :
491 : : void
492 : 1 : test_scheduler_get_uuid_overflow_fragmented(void **state)
493 : : {
494 : : // get uuid overflow fragmented
495 : : scheduler_t s;
496 : 1 : scheduler_initialize(&s);
497 : :
498 : : // Create a fragmented event list
499 : : // +---+---+---+---
500 : : // | 1 | 3 | |...
501 : : // +---+---+---+---
502 : 1 : s.uuid = 1;
503 : 1 : (void)scheduler_register_event(&s, SCHEDULER_POLL_TIMEOUT, 1,
504 : 1 : (struct timeval){}, &fake_event_cb, NULL);
505 : :
506 : 1 : s.uuid = 3;
507 : 1 : (void)scheduler_register_event(&s, SCHEDULER_POLL_TIMEOUT, 1,
508 : 1 : (struct timeval){}, &fake_event_cb, NULL);
509 : :
510 : : // After an overflow the next UUID should be 2
511 : : // because that is the next free event id
512 : 1 : s.uuid = INT_MIN;
513 : 1 : const int new_uuid1 = scheduler_get_event_uuid(&s);
514 : 1 : assert_int_equal(new_uuid1, 2);
515 : : // +---+---+---+---
516 : : // | 1 | 3 | 2 |...
517 : : // +---+---+---+---
518 : :
519 : : // The next UUID after that will be 4 because 3 is already used.
520 : 1 : const int new_uuid2 = scheduler_get_event_uuid(&s);
521 : 1 : assert_int_equal(new_uuid2, 4);
522 : : // +---+---+---+---+---
523 : : // | 1 | 3 | 2 | 4 |...
524 : : // +---+---+---+---+---
525 : 1 : }
526 : :
527 : : void
528 : 1 : test_scheduler_gc_will_remove_dead_events_from_list(void **state)
529 : : {
530 : : // gc will remove dead events from list
531 : : scheduler_t s;
532 : 1 : scheduler_initialize(&s);
533 : :
534 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
535 : 1 : int fd = 1;
536 : 1 : struct timeval timeout = { .tv_sec = 1 };
537 : 1 : event_cb_t cb = &fake_event_cb;
538 : :
539 : : // Create a 3 event list
540 : : // +---+ +---+ +---+
541 : : // | 1 |->| 2 |->| 3 |
542 : : // +---+ +---+ +---+
543 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
544 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
545 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
546 : 1 : const event_t* e1 = list_first_entry(&s.events, event_t, next);
547 : 2 : const event_t* e2 = list_next_entry(e1, next);
548 : 2 : const event_t* e3 = list_next_entry(e2, next);
549 : :
550 : : // Unregister event 2
551 : : // +---+ +----+ +---+
552 : : // | 1 |->|dead|->| 3 |
553 : : // +---+ +----+ +---+
554 : 1 : assert_int_equal(event_queue_length(&s), 3);
555 : 1 : scheduler_unregister_event(&s, e2->id);
556 : 1 : assert_int_equal(event_queue_length(&s), 3);
557 : :
558 : : // Call the GC to delete dead events
559 : : // +---+ +---+
560 : : // | 1 |->| 3 |
561 : : // +---+ +---+
562 : 1 : scheduler_gc_events(&s);
563 : 1 : assert_int_equal(event_queue_length(&s), 2);
564 : :
565 : : // event 1 is now linked to event 3
566 : 1 : assert_ptr_equal(list_next_entry(e1, next), e3);
567 : 1 : }
568 : :
569 : : void
570 : 1 : test_scheduler_check_timeouts(void **state)
571 : : {
572 : : // scheduler_check_timeouts
573 : : scheduler_t s;
574 : 1 : scheduler_initialize(&s);
575 : :
576 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
577 : 1 : int fd = 1;
578 : 1 : struct timeval timeout = {};
579 : 1 : event_cb_t cb = &fake_event_cb;
580 : :
581 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
582 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
583 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
584 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
585 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
586 : 1 : (void)scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
587 : :
588 : 1 : event_t* e1 = list_first_entry(&s.events, event_t, next);
589 : 2 : event_t* e2 = list_next_entry(e1, next);
590 : 2 : event_t* e3 = list_next_entry(e2, next);
591 : 2 : event_t* e4 = list_next_entry(e3, next);
592 : 2 : event_t* e5 = list_next_entry(e4, next);
593 : 2 : event_t* e6 = list_next_entry(e5, next);
594 : :
595 : : // Set current time to 2
596 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 2};
597 : :
598 : 1 : e1->dead = true; // 1: skip because dead
599 : 1 : e2->pending = SCHEDULER_POLL_TIMEOUT; // 2: skip because already pending
600 : 1 : e3->mode = SCHEDULER_POLL_READ_FD; // 3: skip because TIMEOUT mode
601 : 1 : e4->timeout = TV_INF; // 4: skip because timeout is INF
602 : 1 : e5->deadline.tv_sec = 3; // 5: skip because timeout not reached
603 : 1 : e6->deadline.tv_sec = 1; // 6: mark because timeout has passed
604 : :
605 : 1 : scheduler_check_timeouts(&s);
606 : 1 : assert_int_not_equal(e1->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
607 : 1 : assert_int_equal(e2->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
608 : 1 : assert_int_not_equal(e3->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
609 : 1 : assert_int_not_equal(e4->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
610 : 1 : assert_int_not_equal(e5->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
611 : 1 : assert_int_equal(e6->pending, SCHEDULER_POLL_TIMEOUT); // changed
612 : 1 : }
613 : :
614 : : void
615 : 1 : test_scheduler_callback(void **state)
616 : : {
617 : 1 : const int time_now1 = 911;
618 : 1 : const int time_now2 = 930;
619 : :
620 : : scheduler_t s;
621 : 1 : scheduler_initialize(&s);
622 : :
623 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
624 : 1 : int fd = 1;
625 : 1 : const struct timeval to = { .tv_sec = 964 };
626 : 1 : event_cb_spy_t event_cb_spy = {};
627 : :
628 : : // Update current time to time_now1
629 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = time_now1 };
630 : :
631 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
632 : 1 : event_t* event1 = list_first_entry(&s.events, event_t, next);
633 : :
634 : : // Event deadline is using time_now1
635 : 1 : assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now1);
636 : :
637 : 1 : const char test_mode = 9;
638 : :
639 : : // Check callback has not been called
640 : 1 : assert_false(event_cb_spy.was_called);
641 : 1 : assert_int_not_equal(event_cb_spy.mode, test_mode);
642 : 1 : assert_int_not_equal(event_cb_spy.id, event1->id);
643 : :
644 : : // Update current time to time_now2
645 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 };
646 : :
647 : 1 : scheduler_event_callback(event1, test_mode);
648 : :
649 : : // Check callback has been called
650 : 1 : assert_true(event_cb_spy.was_called);
651 : 1 : assert_int_equal(event_cb_spy.mode, test_mode);
652 : 1 : assert_int_equal(event_cb_spy.id, event1->id);
653 : :
654 : : // Event deadline has been updated using time_now2
655 : 1 : assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now2);
656 : 1 : }
657 : :
658 : : void
659 : 1 : test_scheduler_callback_ignores_masked_events(void **state)
660 : : {
661 : : // callback ignores masked events
662 : : scheduler_t s;
663 : 1 : scheduler_initialize(&s);
664 : :
665 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
666 : 1 : int fd = 1;
667 : 1 : const struct timeval to = {};
668 : 1 : event_cb_spy_t event_cb_spy = {};
669 : :
670 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
671 : 1 : event_t* event1 = list_first_entry(&s.events, event_t, next);
672 : :
673 : : // Mask the event here
674 : 1 : event1->masked = true;
675 : :
676 : 1 : const int test_mode = 1;
677 : 1 : scheduler_event_callback(event1, test_mode);
678 : :
679 : : // Check callback has not been called
680 : 1 : assert_false(event_cb_spy.was_called);
681 : 1 : assert_int_not_equal(event_cb_spy.mode, test_mode);
682 : 1 : assert_int_not_equal(event_cb_spy.id, event1->id);
683 : 1 : }
684 : :
685 : : void
686 : 1 : test_scheduler_run_events_run_callback_if_pending(void **state)
687 : : {
688 : : // scheduler_run_events will run callback if event pending
689 : : scheduler_t s;
690 : 1 : scheduler_initialize(&s);
691 : :
692 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
693 : 1 : int fd = 1;
694 : 1 : const struct timeval to = {};
695 : 1 : event_cb_spy_t event_cb_spy = {};
696 : :
697 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
698 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
699 : :
700 : : // Set event to pending
701 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
702 : :
703 : 1 : const int n_dispatched = scheduler_run_events(&s);
704 : :
705 : 1 : assert_int_equal(n_dispatched, 1);
706 : 1 : assert_true(event_cb_spy.was_called);
707 : 1 : }
708 : :
709 : : void
710 : 1 : test_scheduler_run_events_no_callback_if_not_pending(void **state)
711 : : {
712 : : // scheduler_run_events will ignore callback if event not pending
713 : : scheduler_t s;
714 : 1 : scheduler_initialize(&s);
715 : :
716 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
717 : 1 : int fd = 1;
718 : 1 : const struct timeval to = {};
719 : 1 : event_cb_spy_t event_cb_spy = {};
720 : :
721 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
722 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
723 : :
724 : 1 : event->pending = 0;
725 : :
726 : 1 : const int n_dispatched = scheduler_run_events(&s);
727 : :
728 : 1 : assert_int_equal(n_dispatched, 0);
729 : 1 : assert_false(event_cb_spy.was_called);
730 : 1 : }
731 : :
732 : : void
733 : 1 : test_scheduler_run_events_pending_mode_is_reset(void **state)
734 : : {
735 : : // scheduler_run_events pending mode is reset on the event but still passed into callback
736 : : scheduler_t s;
737 : 1 : scheduler_initialize(&s);
738 : :
739 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
740 : 1 : int fd = 1;
741 : 1 : const struct timeval to = {};
742 : 1 : event_cb_spy_t event_cb_spy = {};
743 : :
744 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
745 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
746 : :
747 : : // Set event to pending
748 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
749 : :
750 : 1 : (void)scheduler_run_events(&s);
751 : :
752 : : // The callback gets the original pending value
753 : 1 : assert_int_equal(event_cb_spy.mode, SCHEDULER_POLL_TIMEOUT);
754 : :
755 : : // Event pending flag is reset
756 : 1 : assert_int_not_equal(event->pending, SCHEDULER_POLL_TIMEOUT);
757 : 1 : }
758 : :
759 : : void
760 : 1 : test_scheduler_run_events_ignore_event_if_dead(void **state)
761 : : {
762 : : // scheduler_run_events will ignore callback if event is dead
763 : : scheduler_t s;
764 : 1 : scheduler_initialize(&s);
765 : :
766 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
767 : 1 : int fd = 1;
768 : 1 : const struct timeval to = {};
769 : 1 : event_cb_spy_t event_cb_spy = {};
770 : :
771 : 1 : (void)scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
772 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
773 : :
774 : : // Set event to pending
775 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
776 : :
777 : 1 : event->dead = true;
778 : :
779 : 1 : const int n_dispatched = scheduler_run_events(&s);
780 : :
781 : 1 : assert_int_equal(n_dispatched, 0);
782 : 1 : assert_false(event_cb_spy.was_called);
783 : 1 : }
784 : :
785 : : void
786 : 1 : test_scheduler_run_events_no_events(void **state)
787 : : {
788 : : // scheduler_run_events no events no problem
789 : : scheduler_t s;
790 : 1 : scheduler_initialize(&s);
791 : :
792 : 1 : const int n_dispatched = scheduler_run_events(&s);
793 : :
794 : 1 : assert_int_equal(n_dispatched, 0);
795 : 1 : }
796 : :
797 : : void
798 : 1 : test_scheduler_prepare_events_no_events(void **state)
799 : : {
800 : : // scheduler_prepare_events no events no problem
801 : : scheduler_t s;
802 : 1 : scheduler_initialize(&s);
803 : 1 : scheduler_prepare_events(&s);
804 : 1 : assert_int_equal(s.max_fd, -1);
805 : 1 : }
806 : :
807 : : void
808 : 1 : test_scheduler_prepare_events_masked_event_ignored(void **state)
809 : : {
810 : : // scheduler_prepare_events masked event is ignored
811 : : scheduler_t s;
812 : 1 : scheduler_initialize(&s);
813 : :
814 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
815 : 1 : const int fd = 1;
816 : 1 : const struct timeval to = {};
817 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
818 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
819 : :
820 : : // Mask the event here
821 : 1 : event->masked = true;
822 : :
823 : 1 : scheduler_prepare_events(&s);
824 : :
825 : 1 : assert_int_equal(s.max_fd, -1);
826 : 1 : }
827 : :
828 : : void
829 : 1 : test_scheduler_prepare_events_dead_event_ignored(void **state)
830 : : {
831 : : // scheduler_prepare_events dead event is ignored
832 : : scheduler_t s;
833 : 1 : scheduler_initialize(&s);
834 : :
835 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
836 : 1 : const int fd = 1;
837 : 1 : const struct timeval to = {};
838 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
839 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
840 : :
841 : : // Unalive event here
842 : 1 : event->dead = true;
843 : :
844 : 1 : scheduler_prepare_events(&s);
845 : :
846 : 1 : assert_int_equal(s.max_fd, -1);
847 : 1 : }
848 : :
849 : : void
850 : 1 : test_scheduler_add_read_event(void **state)
851 : : {
852 : : // scheduler_prepare_events add READ_FD
853 : : scheduler_t s;
854 : 1 : scheduler_initialize(&s);
855 : :
856 : 1 : const char md = SCHEDULER_POLL_READ_FD;
857 : 1 : const int test_fd = 991;
858 : 1 : const struct timeval to = {};
859 : 1 : (void)scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
860 : :
861 : 1 : scheduler_prepare_events(&s);
862 : :
863 : 1 : assert_int_equal(s.max_fd, test_fd);
864 : 1 : }
865 : :
866 : : void
867 : 1 : test_scheduler_read_event_with_invalid_fd(void **state)
868 : : {
869 : : // scheduler_prepare_events READ_FD event with invalid file descriptor is ignored
870 : : scheduler_t s;
871 : 1 : scheduler_initialize(&s);
872 : :
873 : 1 : const char md = SCHEDULER_POLL_READ_FD;
874 : 1 : const int fd = 1;
875 : 1 : const struct timeval to = {};
876 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
877 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
878 : :
879 : : // Invalid event
880 : 1 : event->fd = -2;
881 : :
882 : 1 : scheduler_prepare_events(&s);
883 : :
884 : 1 : assert_int_equal(s.max_fd, -1);
885 : 1 : }
886 : :
887 : : void
888 : 1 : test_scheduler_add_write_event(void **state)
889 : : {
890 : : // scheduler_prepare_events add WRITE_FD
891 : : scheduler_t s;
892 : 1 : scheduler_initialize(&s);
893 : :
894 : 1 : const char md = SCHEDULER_POLL_WRITE_FD;
895 : 1 : const int test_fd = 991;
896 : 1 : const struct timeval to = {};
897 : 1 : (void)scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
898 : :
899 : 1 : scheduler_prepare_events(&s);
900 : :
901 : 1 : assert_int_equal(s.max_fd, test_fd);
902 : 1 : }
903 : :
904 : : void
905 : 1 : test_scheduler_write_event_with_invalid_fd(void **state)
906 : : {
907 : : // scheduler_prepare_events WRITE_FD event with invalid file descriptor is ignored
908 : : scheduler_t s;
909 : 1 : scheduler_initialize(&s);
910 : :
911 : 1 : const char md = SCHEDULER_POLL_WRITE_FD;
912 : 1 : const int fd = 1;
913 : 1 : const struct timeval to = {};
914 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
915 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
916 : :
917 : : // Invalid event
918 : 1 : event->fd = -2;
919 : :
920 : 1 : scheduler_prepare_events(&s);
921 : :
922 : 1 : assert_int_equal(s.max_fd, -1);
923 : 1 : }
924 : :
925 : : void
926 : 1 : test_scheduler_add_except_event(void **state)
927 : : {
928 : : // scheduler_prepare_events add EXCEPT_FD
929 : : scheduler_t s;
930 : 1 : scheduler_initialize(&s);
931 : :
932 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
933 : 1 : const int test_fd = 991;
934 : 1 : const struct timeval to = {};
935 : 1 : (void)scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
936 : :
937 : 1 : scheduler_prepare_events(&s);
938 : :
939 : 1 : assert_int_equal(s.max_fd, test_fd);
940 : 1 : }
941 : :
942 : : void
943 : 1 : test_scheduler_except_event_with_invalid_fd(void **state)
944 : : {
945 : : // scheduler_prepare_events EXCEPT_FD event with invalid file descriptor is ignored
946 : : scheduler_t s;
947 : 1 : scheduler_initialize(&s);
948 : :
949 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
950 : 1 : const int fd = 1;
951 : 1 : const struct timeval to = {};
952 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
953 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
954 : :
955 : : // Invalid event
956 : 1 : event->fd = -2;
957 : :
958 : 1 : scheduler_prepare_events(&s);
959 : :
960 : 1 : assert_int_equal(s.max_fd, -1);
961 : 1 : }
962 : :
963 : : void
964 : 1 : test_scheduler_no_timeout_events_then_timeout_is_max(void **state)
965 : : {
966 : : // scheduler_prepare_events with no TIMEOUT events the timeout is MAX
967 : : scheduler_t s;
968 : 1 : scheduler_initialize(&s);
969 : 1 : s.max_timeout = TV_SECS(600); // FIXME
970 : :
971 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
972 : 1 : const int test_fd = 991;
973 : 1 : const struct timeval to = {};
974 : 1 : (void)scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
975 : :
976 : 1 : scheduler_prepare_events(&s);
977 : :
978 : : const struct timeval expected_tv = TV_SECS(600);
979 : 1 : assert_int_equal(s.timeout.tv_sec, expected_tv.tv_sec);
980 : 1 : }
981 : :
982 : : void
983 : 1 : test_scheduler_add_timeout_event(void **state)
984 : : {
985 : : // scheduler_prepare_events add TIMEOUT event
986 : : scheduler_t s;
987 : 1 : scheduler_initialize(&s);
988 : 1 : s.max_timeout = TV_SECS(600);
989 : :
990 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
991 : 1 : const int fd = 1;
992 : 1 : const struct timeval to = { .tv_sec = 10 };
993 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
994 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
995 : :
996 : 1 : const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0};
997 : 1 : fake_gettimeofday = time_now;
998 : :
999 : 1 : scheduler_prepare_events(&s);
1000 : :
1001 : : // New timeout value is time to the event deadline
1002 : 1 : assert_int_equal(s.timeout.tv_sec, to.tv_sec - time_now.tv_sec);
1003 : 1 : }
1004 : :
1005 : : void
1006 : 1 : test_scheduler_multiple_timeout_events_use_lowest_timeout(void **state)
1007 : : {
1008 : : // scheduler_prepare_events add multiple TIMEOUT events new timeout is lowest
1009 : : scheduler_t s;
1010 : 1 : scheduler_initialize(&s);
1011 : 1 : s.max_timeout = TV_SECS(600);
1012 : :
1013 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1014 : 1 : const int fd = 1;
1015 : 1 : const struct timeval to1 = { .tv_sec = 20 };
1016 : 1 : const struct timeval to2 = { .tv_sec = 10 };
1017 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1018 : 1 : (void)scheduler_register_event(&s, md, fd, to1, &fake_event_cb, NULL);
1019 : 1 : (void)scheduler_register_event(&s, md, fd, to2, &fake_event_cb, NULL);
1020 : :
1021 : 1 : const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0};
1022 : 1 : fake_gettimeofday = time_now;
1023 : :
1024 : 1 : scheduler_prepare_events(&s);
1025 : :
1026 : : // New timeout is based on the smaller event timeout value (to2).
1027 : 1 : assert_int_equal(s.timeout.tv_sec, to2.tv_sec - time_now.tv_sec);
1028 : 1 : }
1029 : :
1030 : : void
1031 : 1 : test_scheduler_timeout_event_is_instant_if_deadline_is_now(void **state)
1032 : : {
1033 : : // scheduler_prepare_events add TIMEOUT event timeout is zero if deadline is now
1034 : : scheduler_t s;
1035 : 1 : scheduler_initialize(&s);
1036 : 1 : s.max_timeout = TV_SECS(600);
1037 : :
1038 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1039 : 1 : const int fd = 1;
1040 : 1 : const struct timeval to = { .tv_sec = 10 };
1041 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1042 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1043 : :
1044 : : // Set the time now to the event timeout
1045 : 1 : fake_gettimeofday = to;
1046 : :
1047 : 1 : scheduler_prepare_events(&s);
1048 : :
1049 : : // New timeout is zero because deadline has already been reached.
1050 : 1 : assert_int_equal(s.timeout.tv_sec, 0);
1051 : 1 : }
1052 : :
1053 : : void
1054 : 1 : test_scheduler_multiple_timeout_events_dont_interfere(void **state)
1055 : : {
1056 : : // scheduler_prepare_events multiple timeout events don't clobber each other
1057 : : scheduler_t s;
1058 : 1 : scheduler_initialize(&s);
1059 : 1 : s.max_timeout = TV_SECS(600);
1060 : :
1061 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1062 : 1 : const int fd = 1;
1063 : 1 : const struct timeval to1 = { .tv_sec = 10 };
1064 : 1 : const struct timeval to2 = { .tv_sec = 20 };
1065 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1066 : 1 : (void)scheduler_register_event(&s, md, fd, to1, &fake_event_cb, NULL);
1067 : 1 : (void)scheduler_register_event(&s, md, fd, to2, &fake_event_cb, NULL);
1068 : :
1069 : : // Set the time now to the first event timeout
1070 : 1 : fake_gettimeofday = to1;
1071 : :
1072 : 1 : scheduler_prepare_events(&s);
1073 : :
1074 : : // Event though event2 still has 10 seconds left event1 is 0 therefor timeout is 0
1075 : 1 : assert_int_equal(s.timeout.tv_sec, 0);
1076 : 1 : }
1077 : :
1078 : : void
1079 : 1 : test_scheduler_timeout_event_ignored_if_no_timeout(void **state)
1080 : : {
1081 : : // scheduler_prepare_events add TIMEOUT event ignored if no timeout
1082 : : scheduler_t s;
1083 : 1 : scheduler_initialize(&s);
1084 : 1 : s.max_timeout = TV_SECS(600);
1085 : :
1086 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1087 : 1 : const int fd = 1;
1088 : 1 : const struct timeval to = TV_INF;
1089 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1090 : 1 : (void)scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1091 : :
1092 : 1 : scheduler_prepare_events(&s);
1093 : :
1094 : 1 : assert_int_equal(s.max_fd, -1);
1095 : 1 : }
1096 : :
1097 : : void
1098 : 1 : test_scheduler_with_no_events_will_timeout(void **state)
1099 : : {
1100 : : // Scheduler with no events will timeout
1101 : : scheduler_t s;
1102 : 1 : scheduler_initialize(&s);
1103 : :
1104 : 1 : const int ret = scheduler_wait_for_events(&s);
1105 : 1 : assert_int_equal(ret, 0);
1106 : 1 : }
1107 : :
1108 : : static int
1109 : 6 : checked_open(const char* filename, int flags)
1110 : : {
1111 : 6 : const int fd = open(filename, flags);
1112 [ - + ]: 6 : if (fd < 1) {
1113 : 0 : perror("open");
1114 : : }
1115 : 6 : return fd;
1116 : : }
1117 : :
1118 : : static int
1119 : 3 : checked_mkfifo(const char* pipe_name, int mode)
1120 : : {
1121 : 3 : const int r = mkfifo(pipe_name, 0666);
1122 [ - + ]: 3 : if (r != 0) {
1123 [ # # ]: 0 : if (errno == EEXIST) {
1124 : : return 0;
1125 : : } else {
1126 : 0 : perror("mkfifo");
1127 : : }
1128 : : }
1129 : 3 : return r;
1130 : : }
1131 : :
1132 : : /*
1133 : : * Test running a single SCHEDULER_POLL_READ_FD event to completion.
1134 : : * 1. Create a named pipe
1135 : : * 2. Create a SCHEDULER_POLL_READ_FD event for pipe
1136 : : * 3. Write data to the pipe
1137 : : * 4. Manually tick the scheduler
1138 : : * 5. Check that the event callback fired
1139 : : */
1140 : : void
1141 : 1 : test_scheduler_run_single_read_fd(void **state)
1142 : : {
1143 : : scheduler_t s;
1144 : 1 : scheduler_initialize(&s);
1145 : :
1146 : 1 : const char* pipe_name = "/tmp/bunnies";
1147 : 1 : assert_int_equal(checked_mkfifo(pipe_name, 0666), 0);
1148 : :
1149 : : // Must be non-blocking or open will block waiting for the writer
1150 : 1 : const int fd = checked_open(pipe_name, O_RDONLY | O_NONBLOCK);
1151 : 1 : assert_true(fd > 0);
1152 : :
1153 : 1 : const int fd_wr = checked_open(pipe_name, O_WRONLY);
1154 : 1 : assert_true(fd_wr > 0);
1155 : :
1156 : : /* Create a scheduler event for this fd.
1157 : : * The callback will be called when fd is ready for reading. */
1158 : 1 : event_cb_spy_t event_cb_spy = {};
1159 : : {
1160 : 1 : const char mode = SCHEDULER_POLL_READ_FD;
1161 : 1 : const struct timeval timeout = {};
1162 : 1 : const int ret = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1163 : 1 : assert_int_not_equal(ret, 0);
1164 : : }
1165 : :
1166 : : /* Tick 1 - nothing changed so should timeout with no callback */
1167 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1168 : 1 : assert_false(event_cb_spy.was_called);
1169 : :
1170 : : /* Write something to the pipe so now there will be data available to read */
1171 : : {
1172 : 1 : const char* test_string = "pancakes";
1173 : 1 : assert_int_equal(write(fd_wr, test_string, strlen(test_string)), strlen(test_string));
1174 : : }
1175 : :
1176 : : /* Tick 2 - fd has data, event callback should be called */
1177 : 1 : event_cb_spy.was_called = false;
1178 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1179 : 1 : assert_true(event_cb_spy.was_called);
1180 : :
1181 : 1 : close(fd);
1182 : 1 : close(fd_wr);
1183 : 1 : unlink(pipe_name);
1184 : 1 : }
1185 : :
1186 : : /*
1187 : : * Test running a single SCHEDULER_POLL_WRITE_FD event to completion.
1188 : : * 1. Create a named pipe
1189 : : * 2. Create a SCHEDULER_POLL_WRITE_FD event for pipe
1190 : : * 3. Manually tick the scheduler
1191 : : * 4. Check that the event callback fired
1192 : : * 5. Write data to the pipe until the internal buffer is full
1193 : : * 6. Manually tick the scheduler
1194 : : * 7. Check that the event callback did not fire because pipe is full and not
1195 : : * ready for another write
1196 : : */
1197 : : void
1198 : 1 : test_scheduler_run_single_write_fd(void **state)
1199 : : {
1200 : : scheduler_t s;
1201 : 1 : scheduler_initialize(&s);
1202 : :
1203 : 1 : const char* pipe_name = "/tmp/bunnies";
1204 : 1 : assert_int_equal(checked_mkfifo(pipe_name, 0666), 0);
1205 : :
1206 : : // Must be non-blocking or open will block waiting for the writer
1207 : 1 : const int fd_rd = checked_open(pipe_name, O_RDONLY | O_NONBLOCK);
1208 : 1 : assert_true(fd_rd > 0);
1209 : :
1210 : 1 : const int fd = checked_open(pipe_name, O_WRONLY);
1211 : 1 : assert_true(fd > 0);
1212 : :
1213 : : /* Create a scheduler event for this fd.
1214 : : * The callback will be called when fd is ready for writing. */
1215 : 1 : event_cb_spy_t event_cb_spy = {};
1216 : : {
1217 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1218 : 1 : const struct timeval timeout = {};
1219 : 1 : const int r = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1220 : 1 : assert_int_not_equal(r, 0);
1221 : : }
1222 : :
1223 : : /* Tick 1 - fd is ready for writing so event callback should be called */
1224 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1225 : 1 : assert_true(event_cb_spy.was_called);
1226 : :
1227 : : /* Get the pipe buffer size and fill it up */
1228 : 1 : const int pipesize = fcntl(fd, F_GETPIPE_SZ);
1229 : : {
1230 : : int i;
1231 [ + + ]: 65537 : for (i = 0; i < pipesize; ++i) {
1232 : 65536 : assert_int_equal(write(fd, "a", 1), 1);
1233 : : }
1234 : : }
1235 : : /* With the buffer full the next write would now block */
1236 : :
1237 : : /* We expect a timeout next tick so set a low value */
1238 : 1 : scheduler_set_max_timeout(&s, (struct timeval){ .tv_sec = 0, .tv_usec = 500 });
1239 : :
1240 : : /* Tick 2 - fd is not ready for writing so should timeout and callback is not called*/
1241 : 1 : event_cb_spy.was_called = false;
1242 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1243 : 1 : assert_false(event_cb_spy.was_called);
1244 : :
1245 : : /* Drain the buffer by reading all that was written */
1246 : : {
1247 : : char buf;
1248 : : int i;
1249 [ + + ]: 65537 : for (i = 0; i < pipesize; ++i) {
1250 : 65536 : assert_int_equal(read(fd_rd, &buf, 1), 1);
1251 : : }
1252 : : }
1253 : :
1254 : : /* Tick 3 - fd is ready again so callback should be called */
1255 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1256 : 1 : assert_true(event_cb_spy.was_called);
1257 : :
1258 : 1 : close(fd);
1259 : 1 : close(fd_rd);
1260 : 1 : unlink(pipe_name);
1261 : 1 : }
1262 : :
1263 : : #if 0
1264 : : void
1265 : : test_scheduler_run_single_except_fd(void **state)
1266 : : {
1267 : : No code uses SCHEDULER_POLL_EXCEPT_FD and it is not trivial to test.
1268 : :
1269 : : According to man pages these are what count as exceptional conditions:
1270 : : - There is out-of-band data on a TCP socket (see tcp(7)).
1271 : : - A pseudoterminal master in packet mode has seen a state change on the slave
1272 : : - A cgroup.events file has been modified (see cgroups(7)).
1273 : :
1274 : : None of which seem worth testing for here.
1275 : : }
1276 : : #endif
1277 : :
1278 : : /*
1279 : : * Test running a single SCHEDULER_POLL_READ_FD event but then cancelling it.
1280 : : * 1. Create a named pipe
1281 : : * 2. Create a SCHEDULER_POLL_READ_FD event for pipe
1282 : : * 3. Write data to the pipe
1283 : : * 4. Unreregister the event
1284 : : * 5. Manually tick the scheduler
1285 : : * 6. Check that the event callback did not fire
1286 : : */
1287 : : void
1288 : 1 : test_scheduler_run_single_dead_event(void **state)
1289 : : {
1290 : : scheduler_t s;
1291 : 1 : scheduler_initialize(&s);
1292 : :
1293 : 1 : const char* pipe_name = "/tmp/bunnies";
1294 : 1 : assert_int_equal(checked_mkfifo(pipe_name, 0666), 0);
1295 : :
1296 : : // Must be non-blocking or open will block waiting for the writer
1297 : 1 : const int fd = checked_open(pipe_name, O_RDONLY | O_NONBLOCK);
1298 : 1 : assert_true(fd > 0);
1299 : :
1300 : 1 : const int fd_wr = checked_open(pipe_name, O_WRONLY);
1301 : 1 : assert_true(fd_wr > 0);
1302 : :
1303 : : /* Create a scheduler event for this fd.
1304 : : * The callback will be called when fd is ready for reading. */
1305 : 1 : event_cb_spy_t event_cb_spy = {};
1306 : 1 : int event_id = -1;
1307 : : {
1308 : 1 : const char mode = SCHEDULER_POLL_READ_FD;
1309 : 1 : const struct timeval timeout = {};
1310 : 1 : event_id = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1311 : 1 : assert_true(event_id > 0);
1312 : : }
1313 : :
1314 : : /* Tick 1 - nothing changed so should timeout with no callback */
1315 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1316 : 1 : assert_false(event_cb_spy.was_called);
1317 : :
1318 : : /* Write something to the pipe so now there will be data available to read */
1319 : : {
1320 : 1 : const char* test_string = "pancakes";
1321 : 1 : assert_int_equal(write(fd_wr, test_string, strlen(test_string)), strlen(test_string));
1322 : : }
1323 : :
1324 : 1 : scheduler_unregister_event(&s, event_id);
1325 : :
1326 : : /* We expect a timeout next tick so set a low value */
1327 : 1 : scheduler_set_max_timeout(&s, (struct timeval){ .tv_sec = 0, .tv_usec = 500 });
1328 : :
1329 : : /* Tick 2 - fd has data, but event was unregistered so expect no callback */
1330 : 1 : event_cb_spy.was_called = false;
1331 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1332 : 1 : assert_false(event_cb_spy.was_called);
1333 : :
1334 : 1 : close(fd);
1335 : 1 : close(fd_wr);
1336 : 1 : unlink(pipe_name);
1337 : 1 : }
|