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/eventfd.h>
42 : : #include <sys/time.h>
43 : :
44 : : #include "scheduler.c"
45 : :
46 : : struct timeval fake_gettimeofday;
47 : 114 : int __wrap_gettimeofday(struct timeval* tv, struct timezone* tz)
48 : : {
49 : 114 : *tv = fake_gettimeofday;
50 : 114 : return 0;
51 : : }
52 : :
53 : : typedef struct {
54 : : int was_called;
55 : : char mode;
56 : : event_id_t id;
57 : : } event_cb_spy_t;
58 : :
59 : 17 : void mock_event_cb(event_id_t id, char mode, void *private)
60 : : {
61 : 17 : event_cb_spy_t* out = (event_cb_spy_t*)private;
62 : 17 : out->was_called += 1;
63 : 17 : out->mode = mode;
64 : 17 : out->id = id;
65 : 17 : }
66 : :
67 : : static int
68 : : event_queue_length(const scheduler_t* s)
69 : : {
70 : 6 : int len = 0;
71 : : event_t *event;
72 [ + + ][ + + ]: 17 : scheduler_for_each_event(s, event){
[ + + ][ - + ]
[ + + ][ + + ]
73 : 11 : len += 1;
74 : : }
75 : : return len;
76 : : }
77 : :
78 : 0 : void fake_event_cb (event_id_t id, char mode, void *private) {}
79 : :
80 : 13 : int mock_fd_create()
81 : : {
82 : 13 : const int fd = eventfd(0, 0);
83 [ - + ]: 13 : if (fd < 1) {
84 : 0 : perror("eventfd");
85 : : }
86 : 13 : assert_true(fd > 0);
87 : 13 : return fd;
88 : : }
89 : :
90 : 3 : void mock_fd_set_readable(int fd)
91 : : {
92 : 3 : uint64_t b = 1;
93 [ - + ]: 3 : if (write(fd, &b, sizeof(b)) != sizeof(b)) {
94 : 0 : perror("write: ");
95 : 0 : assert_true(false);
96 : : }
97 : 3 : }
98 : :
99 : 1 : void mock_fd_set_unwritable(int fd)
100 : : {
101 : 1 : uint64_t b = 0xfffffffffffffffe;
102 [ - + ]: 1 : if (write(fd, &b, sizeof(b)) != sizeof(b)) {
103 : 0 : perror("write: ");
104 : 0 : assert_true(false);
105 : : }
106 : 1 : }
107 : :
108 : 1 : void mock_fd_set_writable(int fd)
109 : : {
110 : : uint64_t b;
111 [ - + ]: 1 : if (read(fd, &b, sizeof(b)) != sizeof(b)){
112 : 0 : perror("read");
113 : 0 : assert_true(false);
114 : : }
115 : 1 : }
116 : :
117 : : void
118 : 1 : test_scheduler_set_max_timeout(void **state)
119 : : {
120 : : scheduler_t s;
121 : 1 : s.max_timeout.tv_sec = 42;
122 : 1 : struct timeval timeout = {.tv_sec = 0 };
123 : : scheduler_set_max_timeout(&s, timeout);
124 : 1 : assert_int_equal(s.max_timeout.tv_sec, 0);
125 : 1 : }
126 : :
127 : : void
128 : 1 : test_scheduler_set_max_timeout_lower(void **state)
129 : : {
130 : : // Setting a new lower value will stick
131 : : scheduler_t s;
132 : 1 : s.max_timeout.tv_sec = 430;
133 : 1 : struct timeval timeout = {.tv_sec = 360 };
134 : : scheduler_set_max_timeout(&s, timeout);
135 : 1 : assert_int_equal(s.max_timeout.tv_sec, 360);
136 : 1 : }
137 : :
138 : : void
139 : 1 : test_scheduler_set_max_timeout_higher(void **state)
140 : : {
141 : : // Setting a new higher value will be ignored
142 : : scheduler_t s;
143 : 1 : s.max_timeout.tv_sec = 430;
144 : 1 : struct timeval timeout = {.tv_sec = 458 };
145 : : scheduler_set_max_timeout(&s, timeout);
146 : 1 : assert_int_equal(s.max_timeout.tv_sec, 430);
147 : 1 : }
148 : :
149 : : void
150 : 1 : test_scheduler_set_max_timeout_negative(void **state)
151 : : {
152 : : // Setting a new negative value will be ignored
153 : : scheduler_t s;
154 : 1 : s.max_timeout.tv_sec = 458;
155 : 1 : struct timeval timeout = {.tv_sec = -2 };
156 : : scheduler_set_max_timeout(&s, timeout);
157 : 1 : assert_int_equal(s.max_timeout.tv_sec, 458);
158 : 1 : }
159 : :
160 : : void
161 : 1 : test_scheduler_set_max_timeout_inf(void **state)
162 : : {
163 : : // Setting TV_INF will be ignored
164 : : scheduler_t s;
165 : 1 : s.max_timeout.tv_sec = 458;
166 : 1 : struct timeval timeout = TV_INF;
167 : : scheduler_set_max_timeout(&s, timeout);
168 : 1 : assert_int_equal(s.max_timeout.tv_sec, 458);
169 : 1 : }
170 : :
171 : : void
172 : 1 : test_scheduler_register_event_null_callback(void **state)
173 : : {
174 : : // Callback cannot be NULL
175 : : scheduler_t s;
176 : 1 : scheduler_initialize(&s);
177 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
178 : 1 : int fd = 0;
179 : 1 : struct timeval timeout = {};
180 : 1 : event_cb_t cb = NULL;
181 : 1 : void *private = NULL;
182 : :
183 : 1 : const int r = scheduler_register_event(&s, mode, fd, timeout, cb, private);
184 : 1 : assert_int_equal(r, -EINVAL);
185 : :
186 : : scheduler_unregister_event(&s, r);
187 : 1 : scheduler_gc_events(&s);
188 : 1 : }
189 : :
190 : : void
191 : 1 : test_scheduler_register_event_bad_mode(void **state)
192 : : {
193 : : // Bad mode
194 : : scheduler_t s;
195 : 1 : scheduler_initialize(&s);
196 : 1 : char mode = 0;
197 : 1 : int fd = 0;
198 : 1 : struct timeval timeout = {};
199 : 1 : event_cb_t cb = &fake_event_cb;
200 : 1 : void *private = NULL;
201 : :
202 : 1 : const int r = scheduler_register_event(&s, mode, fd, timeout, cb, private);
203 : 1 : assert_int_equal(r, -EINVAL);
204 : :
205 : : scheduler_unregister_event(&s, r);
206 : 1 : scheduler_gc_events(&s);
207 : 1 : }
208 : :
209 : : void
210 : 1 : test_scheduler_register_multiple_events(void **state)
211 : : {
212 : : scheduler_t s;
213 : 1 : scheduler_initialize(&s);
214 : :
215 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
216 : 1 : int fd = 0;
217 : 1 : struct timeval timeout = {};
218 : 1 : event_cb_t cb = &fake_event_cb;
219 : 1 : void *private = NULL;
220 : :
221 : : /* Event queue starts off empty */
222 : 1 : assert_int_equal(event_queue_length(&s), 0);
223 : :
224 : : /* Event queue starts now has one event */
225 : 1 : const int event_id1 = scheduler_register_event(&s, mode, fd, timeout, cb, private);
226 : 1 : assert_int_equal(event_queue_length(&s), 1);
227 : :
228 : : /* Event queue starts now has two events */
229 : 1 : const int event_id2 = scheduler_register_event(&s, mode, fd, timeout, cb, private);
230 : 1 : assert_int_equal(event_queue_length(&s), 2);
231 : :
232 : : /* The two event IDs we were given are different */
233 : 1 : assert_int_not_equal(event_id1, event_id2);
234 : :
235 : : scheduler_unregister_event(&s, event_id1);
236 : : scheduler_unregister_event(&s, event_id2);
237 : 1 : scheduler_gc_events(&s);
238 : 1 : }
239 : :
240 : : void
241 : 1 : test_scheduler_register_event_populates_event(void **state)
242 : : {
243 : : /* scheduler_register_event will fill out all fields in event */
244 : : scheduler_t s;
245 : 1 : scheduler_initialize(&s);
246 : :
247 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
248 : 1 : int fd = 458;
249 : 1 : struct timeval timeout = { .tv_sec = 575, .tv_usec = 599 };
250 : 1 : event_cb_t cb = &fake_event_cb;
251 : 1 : int private = 430;
252 : :
253 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 2, .tv_usec = 3};
254 : 1 : const int event_id1 = scheduler_register_event(&s, mode, fd, timeout, cb, &private);
255 : :
256 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
257 : :
258 : 1 : assert_int_equal(e->id, event_id1);
259 : 1 : assert_int_equal(e->mode, mode);
260 : 1 : assert_int_equal(e->fd, fd);
261 : 1 : assert_int_equal(e->timeout.tv_sec, timeout.tv_sec);
262 : 1 : assert_int_equal(e->timeout.tv_usec, timeout.tv_usec);
263 : 1 : assert_ptr_equal(e->cb, &fake_event_cb);
264 : 1 : assert_ptr_equal(e->private, &private);
265 : 1 : assert_ptr_equal(e->masked, 0);
266 : :
267 : 1 : assert_int_equal(e->deadline.tv_sec, fake_gettimeofday.tv_sec + timeout.tv_sec);
268 : 1 : assert_int_equal(e->deadline.tv_usec, fake_gettimeofday.tv_usec + timeout.tv_usec);
269 : :
270 : : scheduler_unregister_event(&s, event_id1);
271 : 1 : scheduler_gc_events(&s);
272 : 1 : }
273 : :
274 : : void
275 : 1 : test_scheduler_set_timeout_inf(void **state)
276 : : {
277 : : /* If timeout is TV_INF then deadline is TV_INF */
278 : : scheduler_t s;
279 : 1 : scheduler_initialize(&s);
280 : :
281 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
282 : 1 : int fd = 458;
283 : 1 : struct timeval timeout = TV_INF;
284 : 1 : event_cb_t cb = &fake_event_cb;
285 : :
286 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
287 : :
288 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
289 : :
290 : 1 : assert_true(TV_IS_INF(e->timeout));
291 : :
292 : : scheduler_unregister_event(&s, id);
293 : 1 : scheduler_gc_events(&s);
294 : 1 : }
295 : :
296 : : void
297 : 1 : test_scheduler_set_timeout_invalid_event(void **state)
298 : : {
299 : : // Invalid event ID will return EINVAL
300 : : scheduler_t sched;
301 : 1 : event_id_t event_id = 0;
302 : : struct timeval timeo;
303 : 1 : const int r = scheduler_event_set_timeout(&sched, event_id, timeo);
304 : 1 : assert_int_equal(r, -EINVAL);
305 : 1 : }
306 : :
307 : : void
308 : 1 : test_scheduler_set_timeout_on_non_polled_event(void **state)
309 : : {
310 : : // Set timeout on none polled event returns EINVAL
311 : : scheduler_t s;
312 : 1 : scheduler_initialize(&s);
313 : :
314 : 1 : char mode = SCHEDULER_POLL_READ_FD; // Not a POLL_TIMEOUT event
315 : 1 : const int fd = mock_fd_create();
316 : 1 : struct timeval timeout = { .tv_sec = 996 };
317 : 1 : event_cb_t cb = &fake_event_cb;
318 : :
319 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
320 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
321 : 1 : const int r = scheduler_event_set_timeout(&s, e->id, (struct timeval){});
322 : 1 : assert_int_equal(r, -EINVAL);
323 : :
324 : 1 : close(fd);
325 : : scheduler_unregister_event(&s, id);
326 : 1 : scheduler_gc_events(&s);
327 : 1 : }
328 : :
329 : : void
330 : 1 : test_scheduler_set_timeout_missing_event(void **state)
331 : : {
332 : : // Set timeout returns ENOENT if event is not in list
333 : : scheduler_t s;
334 : 1 : scheduler_initialize(&s);
335 : :
336 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
337 : 1 : int fd = 1;
338 : 1 : struct timeval timeout = { .tv_sec = 996 };
339 : 1 : event_cb_t cb = &fake_event_cb;
340 : :
341 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
342 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
343 : 1 : const int r = scheduler_event_set_timeout(&s, e->id+1, (struct timeval){});
344 : 1 : assert_int_equal(r, -ENOENT);
345 : :
346 : : scheduler_unregister_event(&s, id);
347 : 1 : scheduler_gc_events(&s);
348 : 1 : }
349 : :
350 : : void
351 : 1 : test_scheduler_set_timeout(void **state)
352 : : {
353 : : // Set timeout will update timeout and deadline for affected event
354 : : scheduler_t s;
355 : 1 : scheduler_initialize(&s);
356 : :
357 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
358 : 1 : int fd = 1;
359 : 1 : struct timeval timeout1 = { .tv_sec = 996 };
360 : 1 : struct timeval timeout2 = { .tv_sec = 993 };
361 : 1 : struct timeval timeout3 = { .tv_sec = 964 };
362 : 1 : event_cb_t cb = &fake_event_cb;
363 : :
364 : 1 : int fake_gettimeofday_tv_sec = 1;
365 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = fake_gettimeofday_tv_sec, .tv_usec = 0};
366 : :
367 : 1 : const int id1 = scheduler_register_event(&s, mode, fd, timeout1, cb, NULL);
368 : 1 : const int id2 = scheduler_register_event(&s, mode, fd, timeout2, cb, NULL);
369 : 1 : const int id3 = scheduler_register_event(&s, mode, fd, timeout3, cb, NULL);
370 : 1 : const event_t* e1 = list_first_entry(&s.events, event_t, next);
371 : 1 : const event_t* e2 = list_next_entry(e1, next);
372 : 1 : const event_t* e3 = list_next_entry(e2, next);
373 : :
374 : 1 : assert_int_equal(e1->timeout.tv_sec, timeout1.tv_sec);
375 : 1 : assert_int_equal(e2->timeout.tv_sec, timeout2.tv_sec);
376 : 1 : assert_int_equal(e3->timeout.tv_sec, timeout3.tv_sec);
377 : 1 : assert_int_equal(e1->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout1.tv_sec);
378 : 1 : assert_int_equal(e2->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout2.tv_sec);
379 : 1 : assert_int_equal(e3->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout3.tv_sec);
380 : :
381 : 1 : struct timeval new_timeout2 = { .tv_sec = 911 };
382 : 1 : scheduler_event_set_timeout(&s, e2->id, new_timeout2);
383 : :
384 : 1 : assert_int_equal(e1->timeout.tv_sec, timeout1.tv_sec); // unchanged
385 : 1 : assert_int_equal(e2->timeout.tv_sec, new_timeout2.tv_sec); // new value
386 : 1 : assert_int_equal(e3->timeout.tv_sec, timeout3.tv_sec); // unchanged
387 : 1 : assert_int_equal(e1->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout1.tv_sec);
388 : 1 : assert_int_equal(e2->deadline.tv_sec, fake_gettimeofday_tv_sec + new_timeout2.tv_sec);
389 : 1 : assert_int_equal(e3->deadline.tv_sec, fake_gettimeofday_tv_sec + timeout3.tv_sec);
390 : :
391 : : scheduler_unregister_event(&s, id1);
392 : : scheduler_unregister_event(&s, id2);
393 : : scheduler_unregister_event(&s, id3);
394 : 1 : scheduler_gc_events(&s);
395 : 1 : }
396 : :
397 : : void
398 : 1 : test_scheduler_set_timeout_inf_and_deadline(void **state)
399 : : {
400 : : // Set timeout will update deadline to TV_INF if new timeout is TV_INF
401 : : scheduler_t s;
402 : 1 : scheduler_initialize(&s);
403 : :
404 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
405 : 1 : int fd = 1;
406 : 1 : struct timeval timeout = { .tv_sec = 996 };
407 : 1 : event_cb_t cb = &fake_event_cb;
408 : :
409 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 1, .tv_usec = 2};
410 : :
411 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
412 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
413 : :
414 : 1 : assert_int_equal(e->timeout.tv_sec, timeout.tv_sec);
415 : :
416 : 1 : struct timeval new_timeout = TV_INF;
417 : 1 : scheduler_event_set_timeout(&s, e->id, new_timeout);
418 : :
419 : 1 : assert_true(TV_IS_INF(e->timeout));
420 : 1 : assert_true(TV_IS_INF(e->deadline));
421 : :
422 : : scheduler_unregister_event(&s, id);
423 : 1 : scheduler_gc_events(&s);
424 : 1 : }
425 : :
426 : : void
427 : 1 : test_scheduler_unregister_event_will_set_dead_field(void **state)
428 : : {
429 : : // Unregister event will set dead to 1
430 : : scheduler_t s;
431 : 1 : scheduler_initialize(&s);
432 : :
433 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
434 : 1 : int fd = 1;
435 : 1 : struct timeval timeout = { .tv_sec = 1 };
436 : 1 : event_cb_t cb = &fake_event_cb;
437 : :
438 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
439 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
440 : 1 : assert_int_not_equal(e->dead, 1);
441 : :
442 : 1 : scheduler_unregister_event(&s, e->id);
443 : 1 : assert_int_equal(e->dead, 1);
444 : :
445 : : scheduler_unregister_event(&s, id);
446 : 1 : scheduler_gc_events(&s);
447 : 1 : }
448 : :
449 : : void
450 : 1 : test_scheduler_unregister_event_will_ignore_invalid_event(void **state)
451 : : {
452 : : // Unregister event will ignore invalid event id
453 : : scheduler_t s;
454 : 1 : scheduler_initialize(&s);
455 : :
456 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
457 : 1 : int fd = 1;
458 : 1 : struct timeval timeout = { .tv_sec = 1 };
459 : 1 : event_cb_t cb = &fake_event_cb;
460 : :
461 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
462 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
463 : 1 : assert_int_not_equal(e->dead, 1);
464 : :
465 : : scheduler_unregister_event(&s, 0);
466 : 1 : assert_int_not_equal(e->dead, 1);
467 : :
468 : : scheduler_unregister_event(&s, id);
469 : 1 : scheduler_gc_events(&s);
470 : 1 : }
471 : :
472 : : void
473 : 1 : test_scheduler_mask_event_will_set_masked_field(void **state)
474 : : {
475 : : // mask event will set masked to 1 or 0 depending on 3rd arg
476 : : scheduler_t s;
477 : 1 : scheduler_initialize(&s);
478 : :
479 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
480 : 1 : int fd = 1;
481 : 1 : struct timeval timeout = { .tv_sec = 1 };
482 : 1 : event_cb_t cb = &fake_event_cb;
483 : :
484 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
485 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
486 : 1 : assert_int_not_equal(e->masked, 1);
487 : :
488 : 1 : scheduler_mask_event(&s, e->id, 1);
489 : 1 : assert_int_equal(e->masked, 1);
490 : :
491 : 1 : scheduler_mask_event(&s, e->id, 0);
492 : 1 : assert_int_not_equal(e->masked, 1);
493 : :
494 : : scheduler_unregister_event(&s, id);
495 : 1 : scheduler_gc_events(&s);
496 : 1 : }
497 : :
498 : : void
499 : 1 : test_scheduler_mask_event_will_accept_non_zero_value(void **state)
500 : : {
501 : : // mask event will accept any non-zero value and convert it to 1
502 : : scheduler_t s;
503 : 1 : scheduler_initialize(&s);
504 : :
505 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
506 : 1 : int fd = 1;
507 : 1 : struct timeval timeout = { .tv_sec = 1 };
508 : 1 : event_cb_t cb = &fake_event_cb;
509 : :
510 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
511 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
512 : 1 : assert_int_not_equal(e->masked, 1);
513 : :
514 : 1 : scheduler_mask_event(&s, e->id, 959);
515 : 1 : assert_int_equal(e->masked, 1);
516 : :
517 : : scheduler_unregister_event(&s, id);
518 : 1 : scheduler_gc_events(&s);
519 : 1 : }
520 : :
521 : : void
522 : 1 : test_scheduler_mask_event_will_ignore_invalid_event_id(void **state)
523 : : {
524 : : // mask event will ignore invalid event id
525 : : scheduler_t s;
526 : 1 : scheduler_initialize(&s);
527 : :
528 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
529 : 1 : int fd = 1;
530 : 1 : struct timeval timeout = { .tv_sec = 1 };
531 : 1 : event_cb_t cb = &fake_event_cb;
532 : :
533 : 1 : const int id = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
534 : 1 : const event_t* e = list_first_entry(&s.events, event_t, next);
535 : 1 : assert_int_not_equal(e->masked, 1);
536 : :
537 : : scheduler_mask_event(&s, 0, 1);
538 : 1 : assert_int_not_equal(e->masked, 1);
539 : :
540 : : scheduler_unregister_event(&s, id);
541 : 1 : scheduler_gc_events(&s);
542 : 1 : }
543 : :
544 : : void
545 : 1 : test_scheduler_get_uuid(void **state)
546 : : {
547 : : scheduler_t s;
548 : 1 : scheduler_initialize(&s);
549 : :
550 : 1 : s.uuid = 1;
551 : 1 : const event_id_t new_uuid = scheduler_get_event_uuid(&s);
552 : 1 : assert_int_equal(new_uuid, 1);
553 : 1 : assert_int_equal(s.uuid, 2);
554 : 1 : }
555 : :
556 : : void
557 : 1 : test_scheduler_get_uuid_overflow(void **state)
558 : : {
559 : : // get uuid overflow
560 : : scheduler_t s;
561 : 1 : scheduler_initialize(&s);
562 : :
563 : 1 : s.uuid = INT_MAX;
564 : 1 : const event_id_t new_uuid = scheduler_get_event_uuid(&s);
565 : 1 : assert_int_equal(new_uuid, INT_MAX);
566 : 1 : assert_int_equal(s.uuid, 1);
567 : :
568 : 1 : const event_id_t new_uuid2 = scheduler_get_event_uuid(&s);
569 : 1 : assert_int_equal(new_uuid2, 1);
570 : 1 : assert_int_equal(s.uuid, 2);
571 : 1 : }
572 : :
573 : : void
574 : 1 : test_scheduler_get_uuid_overflow_fragmented(void **state)
575 : : {
576 : : // get uuid overflow fragmented
577 : : scheduler_t s;
578 : 1 : scheduler_initialize(&s);
579 : :
580 : : // Create a fragmented event list
581 : : // +---+---+---+---
582 : : // | 1 | 3 | |...
583 : : // +---+---+---+---
584 : 1 : s.uuid = 1;
585 : 1 : const int id1 = scheduler_register_event(&s, SCHEDULER_POLL_TIMEOUT, 1,
586 : 1 : (struct timeval){}, &fake_event_cb, NULL);
587 : :
588 : 1 : s.uuid = 3;
589 : 1 : const int id2 = scheduler_register_event(&s, SCHEDULER_POLL_TIMEOUT, 1,
590 : 1 : (struct timeval){}, &fake_event_cb, NULL);
591 : :
592 : : // After an overflow the next UUID should be 2
593 : : // because that is the next free event id
594 : 1 : s.uuid = INT_MIN;
595 : 1 : const event_id_t new_uuid1 = scheduler_get_event_uuid(&s);
596 : 1 : assert_int_equal(new_uuid1, 2);
597 : : // +---+---+---+---
598 : : // | 1 | 3 | 2 |...
599 : : // +---+---+---+---
600 : :
601 : : // The next UUID after that will be 4 because 3 is already used.
602 : 1 : const event_id_t new_uuid2 = scheduler_get_event_uuid(&s);
603 : 1 : assert_int_equal(new_uuid2, 4);
604 : : // +---+---+---+---+---
605 : : // | 1 | 3 | 2 | 4 |...
606 : : // +---+---+---+---+---
607 : :
608 : : scheduler_unregister_event(&s, id1);
609 : : scheduler_unregister_event(&s, id2);
610 : 1 : scheduler_gc_events(&s);
611 : 1 : }
612 : :
613 : : void
614 : 1 : test_scheduler_gc_will_remove_dead_events_from_list(void **state)
615 : : {
616 : : // gc will remove dead events from list
617 : : scheduler_t s;
618 : 1 : scheduler_initialize(&s);
619 : :
620 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
621 : 1 : int fd = 1;
622 : 1 : struct timeval timeout = { .tv_sec = 1 };
623 : 1 : event_cb_t cb = &fake_event_cb;
624 : :
625 : : // Create a 3 event list
626 : : // +---+ +---+ +---+
627 : : // | 1 |->| 2 |->| 3 |
628 : : // +---+ +---+ +---+
629 : 1 : const int id1 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
630 : 1 : const int id2 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
631 : 1 : const int id3 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
632 : 1 : const event_t* e1 = list_first_entry(&s.events, event_t, next);
633 : 2 : const event_t* e2 = list_next_entry(e1, next);
634 : 1 : const event_t* e3 = list_next_entry(e2, next);
635 : :
636 : : // Unregister event 2
637 : : // +---+ +----+ +---+
638 : : // | 1 |->|dead|->| 3 |
639 : : // +---+ +----+ +---+
640 : 1 : assert_int_equal(event_queue_length(&s), 3);
641 : 1 : scheduler_unregister_event(&s, e2->id);
642 : 1 : assert_int_equal(event_queue_length(&s), 3);
643 : :
644 : : // Call the GC to delete dead events
645 : : // +---+ +---+
646 : : // | 1 |->| 3 |
647 : : // +---+ +---+
648 : 1 : scheduler_gc_events(&s);
649 : 1 : assert_int_equal(event_queue_length(&s), 2);
650 : :
651 : : // event 1 is now linked to event 3
652 : 1 : assert_ptr_equal(list_next_entry(e1, next), e3);
653 : :
654 : : scheduler_unregister_event(&s, id1);
655 : : scheduler_unregister_event(&s, id2);
656 : : scheduler_unregister_event(&s, id3);
657 : 1 : scheduler_gc_events(&s);
658 : 1 : }
659 : :
660 : : void
661 : 1 : test_scheduler_check_timeouts(void **state)
662 : : {
663 : : // scheduler_check_timeouts
664 : : scheduler_t s;
665 : 1 : scheduler_initialize(&s);
666 : :
667 : 1 : char mode = SCHEDULER_POLL_TIMEOUT;
668 : 1 : int fd = 1;
669 : 1 : struct timeval timeout = {};
670 : 1 : event_cb_t cb = &fake_event_cb;
671 : :
672 : 1 : const int id1 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
673 : 1 : const int id2 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
674 : 1 : const int id3 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
675 : 1 : const int id4 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
676 : 1 : const int id5 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
677 : 1 : const int id6 = scheduler_register_event(&s, mode, fd, timeout, cb, NULL);
678 : :
679 : 1 : event_t* e1 = list_first_entry(&s.events, event_t, next);
680 : 1 : event_t* e2 = list_next_entry(e1, next);
681 : 1 : event_t* e3 = list_next_entry(e2, next);
682 : 1 : event_t* e4 = list_next_entry(e3, next);
683 : 1 : event_t* e5 = list_next_entry(e4, next);
684 : 1 : event_t* e6 = list_next_entry(e5, next);
685 : :
686 : : // Set current time to 2
687 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 2};
688 : :
689 : 1 : e1->dead = true; // 1: skip because dead
690 : 1 : e2->pending = SCHEDULER_POLL_TIMEOUT; // 2: skip because already pending
691 : 1 : e3->mode = SCHEDULER_POLL_READ_FD; // 3: skip because TIMEOUT mode
692 : 1 : e4->timeout = TV_INF; // 4: skip because timeout is INF
693 : 1 : e5->deadline.tv_sec = 3; // 5: skip because timeout not reached
694 : 1 : e6->deadline.tv_sec = 1; // 6: mark because timeout has passed
695 : :
696 : 1 : scheduler_check_timeouts(&s);
697 : 1 : assert_int_not_equal(e1->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
698 : 1 : assert_int_equal(e2->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
699 : 1 : assert_int_not_equal(e3->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
700 : 1 : assert_int_not_equal(e4->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
701 : 1 : assert_int_not_equal(e5->pending, SCHEDULER_POLL_TIMEOUT); // unchanged
702 : 1 : assert_int_equal(e6->pending, SCHEDULER_POLL_TIMEOUT); // changed
703 : :
704 : : scheduler_unregister_event(&s, id1);
705 : : scheduler_unregister_event(&s, id2);
706 : : scheduler_unregister_event(&s, id3);
707 : : scheduler_unregister_event(&s, id4);
708 : : scheduler_unregister_event(&s, id5);
709 : : scheduler_unregister_event(&s, id6);
710 : 1 : scheduler_gc_events(&s);
711 : 1 : }
712 : :
713 : : void
714 : 1 : test_scheduler_callback(void **state)
715 : : {
716 : 1 : const int time_now1 = 911;
717 : 1 : const int time_now2 = 930;
718 : :
719 : : scheduler_t s;
720 : 1 : scheduler_initialize(&s);
721 : :
722 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
723 : 1 : int fd = 1;
724 : 1 : const struct timeval to = { .tv_sec = 964 };
725 : 1 : event_cb_spy_t event_cb_spy = {};
726 : :
727 : : // Update current time to time_now1
728 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = time_now1 };
729 : :
730 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
731 : 1 : event_t* event1 = list_first_entry(&s.events, event_t, next);
732 : :
733 : : // Event deadline is using time_now1
734 : 1 : assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now1);
735 : :
736 : 1 : const char test_mode = 9;
737 : :
738 : : // Check callback has not been called
739 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
740 : 1 : assert_int_not_equal(event_cb_spy.mode, test_mode);
741 : 1 : assert_int_not_equal(event_cb_spy.id, event1->id);
742 : :
743 : : // Update current time to time_now2
744 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 };
745 : :
746 : 1 : scheduler_event_callback(event1, test_mode);
747 : :
748 : : // Check callback has been called
749 : 1 : assert_int_equal(event_cb_spy.was_called, 1);
750 : 1 : assert_int_equal(event_cb_spy.mode, test_mode);
751 : 1 : assert_int_equal(event_cb_spy.id, event1->id);
752 : :
753 : : // Event deadline has been updated using time_now2
754 : 1 : assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now2);
755 : :
756 : : scheduler_unregister_event(&s, id);
757 : 1 : scheduler_gc_events(&s);
758 : 1 : }
759 : :
760 : : void
761 : 1 : test_scheduler_callback_ignores_masked_events(void **state)
762 : : {
763 : : // callback ignores masked events
764 : : scheduler_t s;
765 : 1 : scheduler_initialize(&s);
766 : :
767 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
768 : 1 : int fd = 1;
769 : 1 : const struct timeval to = {};
770 : 1 : event_cb_spy_t event_cb_spy = {};
771 : :
772 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
773 : 1 : event_t* event1 = list_first_entry(&s.events, event_t, next);
774 : :
775 : : // Mask the event here
776 : 1 : event1->masked = true;
777 : :
778 : 1 : const int test_mode = 1;
779 : 1 : scheduler_event_callback(event1, test_mode);
780 : :
781 : : // Check callback has not been called
782 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
783 : 1 : assert_int_not_equal(event_cb_spy.mode, test_mode);
784 : 1 : assert_int_not_equal(event_cb_spy.id, event1->id);
785 : :
786 : : scheduler_unregister_event(&s, id);
787 : 1 : scheduler_gc_events(&s);
788 : 1 : }
789 : :
790 : : void
791 : 1 : test_scheduler_run_events_run_callback_if_pending(void **state)
792 : : {
793 : : // scheduler_run_events will run callback if event pending
794 : : scheduler_t s;
795 : 1 : scheduler_initialize(&s);
796 : :
797 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
798 : 1 : int fd = 1;
799 : 1 : const struct timeval to = {};
800 : 1 : event_cb_spy_t event_cb_spy = {};
801 : :
802 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
803 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
804 : :
805 : : // Set event to pending
806 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
807 : :
808 : 1 : const int n_dispatched = scheduler_run_events(&s);
809 : :
810 : 1 : assert_int_equal(n_dispatched, 1);
811 : 1 : assert_int_equal(event_cb_spy.was_called, 1);
812 : :
813 : : scheduler_unregister_event(&s, id);
814 : 1 : scheduler_gc_events(&s);
815 : 1 : }
816 : :
817 : : void
818 : 1 : test_scheduler_run_events_no_callback_if_not_pending(void **state)
819 : : {
820 : : // scheduler_run_events will ignore callback if event not pending
821 : : scheduler_t s;
822 : 1 : scheduler_initialize(&s);
823 : :
824 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
825 : 1 : int fd = 1;
826 : 1 : const struct timeval to = {};
827 : 1 : event_cb_spy_t event_cb_spy = {};
828 : :
829 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
830 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
831 : :
832 : 1 : event->pending = 0;
833 : :
834 : 1 : const int n_dispatched = scheduler_run_events(&s);
835 : :
836 : 1 : assert_int_equal(n_dispatched, 0);
837 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
838 : :
839 : : scheduler_unregister_event(&s, id);
840 : 1 : scheduler_gc_events(&s);
841 : 1 : }
842 : :
843 : : void
844 : 1 : test_scheduler_run_events_pending_mode_is_reset(void **state)
845 : : {
846 : : // scheduler_run_events pending mode is reset on the event but still passed into callback
847 : : scheduler_t s;
848 : 1 : scheduler_initialize(&s);
849 : :
850 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
851 : 1 : int fd = 1;
852 : 1 : const struct timeval to = {};
853 : 1 : event_cb_spy_t event_cb_spy = {};
854 : :
855 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
856 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
857 : :
858 : : // Set event to pending
859 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
860 : :
861 : 1 : (void)scheduler_run_events(&s);
862 : :
863 : : // The callback gets the original pending value
864 : 1 : assert_int_equal(event_cb_spy.mode, SCHEDULER_POLL_TIMEOUT);
865 : :
866 : : // Event pending flag is reset
867 : 1 : assert_int_not_equal(event->pending, SCHEDULER_POLL_TIMEOUT);
868 : :
869 : : scheduler_unregister_event(&s, id);
870 : 1 : scheduler_gc_events(&s);
871 : 1 : }
872 : :
873 : : void
874 : 1 : test_scheduler_run_events_ignore_event_if_dead(void **state)
875 : : {
876 : : // scheduler_run_events will ignore callback if event is dead
877 : : scheduler_t s;
878 : 1 : scheduler_initialize(&s);
879 : :
880 : 1 : char md = SCHEDULER_POLL_TIMEOUT;
881 : 1 : int fd = 1;
882 : 1 : const struct timeval to = {};
883 : 1 : event_cb_spy_t event_cb_spy = {};
884 : :
885 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &mock_event_cb, &event_cb_spy);
886 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
887 : :
888 : : // Set event to pending
889 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
890 : :
891 : 1 : event->dead = true;
892 : :
893 : 1 : const int n_dispatched = scheduler_run_events(&s);
894 : :
895 : 1 : assert_int_equal(n_dispatched, 0);
896 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
897 : :
898 : : scheduler_unregister_event(&s, id);
899 : 1 : scheduler_gc_events(&s);
900 : 1 : }
901 : :
902 : : void
903 : 1 : test_scheduler_run_events_no_events(void **state)
904 : : {
905 : : // scheduler_run_events no events no problem
906 : : scheduler_t s;
907 : 1 : scheduler_initialize(&s);
908 : :
909 : 1 : const int n_dispatched = scheduler_run_events(&s);
910 : :
911 : 1 : assert_int_equal(n_dispatched, 0);
912 : 1 : }
913 : :
914 : : void
915 : 1 : test_scheduler_prepare_events_no_events(void **state)
916 : : {
917 : : // scheduler_prepare_events no events no problem
918 : : scheduler_t s;
919 : 1 : scheduler_initialize(&s);
920 : 1 : scheduler_prepare_events(&s);
921 : 1 : assert_int_equal(s.max_fd, -1);
922 : 1 : }
923 : :
924 : : void
925 : 1 : test_scheduler_prepare_events_masked_event_ignored(void **state)
926 : : {
927 : : // scheduler_prepare_events masked event is ignored
928 : : scheduler_t s;
929 : 1 : scheduler_initialize(&s);
930 : :
931 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
932 : 1 : const int fd = 1;
933 : 1 : const struct timeval to = {};
934 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
935 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
936 : :
937 : : // Mask the event here
938 : 1 : event->masked = true;
939 : :
940 : 1 : scheduler_prepare_events(&s);
941 : :
942 : 1 : assert_int_equal(s.max_fd, -1);
943 : :
944 : : scheduler_unregister_event(&s, id);
945 : 1 : scheduler_gc_events(&s);
946 : 1 : }
947 : :
948 : : void
949 : 1 : test_scheduler_prepare_events_dead_event_ignored(void **state)
950 : : {
951 : : // scheduler_prepare_events dead event is ignored
952 : : scheduler_t s;
953 : 1 : scheduler_initialize(&s);
954 : :
955 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
956 : 1 : const int fd = 1;
957 : 1 : const struct timeval to = {};
958 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
959 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
960 : :
961 : : // Unalive event here
962 : 1 : event->dead = true;
963 : :
964 : 1 : scheduler_prepare_events(&s);
965 : :
966 : 1 : assert_int_equal(s.max_fd, -1);
967 : :
968 : : scheduler_unregister_event(&s, id);
969 : 1 : scheduler_gc_events(&s);
970 : 1 : }
971 : :
972 : : void
973 : 1 : test_scheduler_add_read_event(void **state)
974 : : {
975 : : // scheduler_prepare_events add READ_FD
976 : : scheduler_t s;
977 : 1 : scheduler_initialize(&s);
978 : :
979 : 1 : const char md = SCHEDULER_POLL_READ_FD;
980 : 1 : const int test_fd = 991;
981 : 1 : const struct timeval to = {};
982 : 1 : const int id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
983 : :
984 : 1 : scheduler_prepare_events(&s);
985 : :
986 : 1 : assert_int_equal(s.max_fd, test_fd);
987 : :
988 : : scheduler_unregister_event(&s, id);
989 : 1 : scheduler_gc_events(&s);
990 : 1 : }
991 : :
992 : : void
993 : 1 : test_scheduler_read_event_with_invalid_fd(void **state)
994 : : {
995 : : // scheduler_prepare_events READ_FD event with invalid file descriptor is ignored
996 : : scheduler_t s;
997 : 1 : scheduler_initialize(&s);
998 : :
999 : 1 : const char md = SCHEDULER_POLL_READ_FD;
1000 : 1 : const int fd = mock_fd_create();
1001 : 1 : const struct timeval to = {};
1002 : 1 : const int event_id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1003 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
1004 : :
1005 : : // Invalid event
1006 : 1 : event->fd = -2;
1007 : :
1008 : 1 : scheduler_prepare_events(&s);
1009 : :
1010 : 1 : assert_int_equal(s.max_fd, -1);
1011 : :
1012 : 1 : close(fd);
1013 : : scheduler_unregister_event(&s, event_id);
1014 : 1 : scheduler_gc_events(&s);
1015 : 1 : }
1016 : :
1017 : : void
1018 : 1 : test_scheduler_add_write_event(void **state)
1019 : : {
1020 : : // scheduler_prepare_events add WRITE_FD
1021 : : scheduler_t s;
1022 : 1 : scheduler_initialize(&s);
1023 : :
1024 : 1 : const char md = SCHEDULER_POLL_WRITE_FD;
1025 : 1 : const int test_fd = 991;
1026 : 1 : const struct timeval to = {};
1027 : 1 : const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
1028 : :
1029 : 1 : scheduler_prepare_events(&s);
1030 : :
1031 : 1 : assert_int_equal(s.max_fd, test_fd);
1032 : :
1033 : : scheduler_unregister_event(&s, event_id);
1034 : 1 : scheduler_gc_events(&s);
1035 : 1 : }
1036 : :
1037 : : void
1038 : 1 : test_scheduler_write_event_with_invalid_fd(void **state)
1039 : : {
1040 : : // scheduler_prepare_events WRITE_FD event with invalid file descriptor is ignored
1041 : : scheduler_t s;
1042 : 1 : scheduler_initialize(&s);
1043 : :
1044 : 1 : const char md = SCHEDULER_POLL_WRITE_FD;
1045 : 1 : const int fd = mock_fd_create();
1046 : 1 : const struct timeval to = {};
1047 : 1 : const int event_id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1048 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
1049 : :
1050 : : // Invalid event
1051 : 1 : event->fd = -2;
1052 : :
1053 : 1 : scheduler_prepare_events(&s);
1054 : :
1055 : 1 : assert_int_equal(s.max_fd, -1);
1056 : :
1057 : 1 : close(fd);
1058 : : scheduler_unregister_event(&s, event_id);
1059 : 1 : scheduler_gc_events(&s);
1060 : 1 : }
1061 : :
1062 : : void
1063 : 1 : test_scheduler_add_except_event(void **state)
1064 : : {
1065 : : // scheduler_prepare_events add EXCEPT_FD
1066 : : scheduler_t s;
1067 : 1 : scheduler_initialize(&s);
1068 : :
1069 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
1070 : 1 : const int test_fd = 991;
1071 : 1 : const struct timeval to = {};
1072 : 1 : const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
1073 : :
1074 : 1 : scheduler_prepare_events(&s);
1075 : :
1076 : 1 : assert_int_equal(s.max_fd, test_fd);
1077 : :
1078 : : scheduler_unregister_event(&s, event_id);
1079 : 1 : scheduler_gc_events(&s);
1080 : 1 : }
1081 : :
1082 : : void
1083 : 1 : test_scheduler_except_event_with_invalid_fd(void **state)
1084 : : {
1085 : : // scheduler_prepare_events EXCEPT_FD event with invalid file descriptor is ignored
1086 : : scheduler_t s;
1087 : 1 : scheduler_initialize(&s);
1088 : :
1089 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
1090 : 1 : const int fd = mock_fd_create();
1091 : 1 : const struct timeval to = {};
1092 : 1 : const int event_id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1093 : 1 : event_t* event = list_first_entry(&s.events, event_t, next);
1094 : :
1095 : : // Invalid event
1096 : 1 : event->fd = -2;
1097 : :
1098 : 1 : scheduler_prepare_events(&s);
1099 : :
1100 : 1 : assert_int_equal(s.max_fd, -1);
1101 : 1 : close(fd);
1102 : : scheduler_unregister_event(&s, event_id);
1103 : 1 : scheduler_gc_events(&s);
1104 : 1 : }
1105 : :
1106 : : void
1107 : 1 : test_scheduler_no_timeout_events_then_timeout_is_max(void **state)
1108 : : {
1109 : : // scheduler_prepare_events with no TIMEOUT events the timeout is MAX
1110 : : scheduler_t s;
1111 : 1 : scheduler_initialize(&s);
1112 : 1 : s.max_timeout = TV_SECS(600); // FIXME
1113 : :
1114 : 1 : const char md = SCHEDULER_POLL_EXCEPT_FD;
1115 : 1 : const int test_fd = mock_fd_create();
1116 : 1 : const struct timeval to = {};
1117 : 1 : const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL);
1118 : :
1119 : 1 : scheduler_prepare_events(&s);
1120 : :
1121 : 1 : const struct timeval expected_tv = TV_SECS(600);
1122 : 1 : assert_int_equal(s.timeout.tv_sec, expected_tv.tv_sec);
1123 : :
1124 : 1 : close(test_fd);
1125 : : scheduler_unregister_event(&s, event_id);
1126 : 1 : scheduler_gc_events(&s);
1127 : 1 : }
1128 : :
1129 : : void
1130 : 1 : test_scheduler_add_timeout_event(void **state)
1131 : : {
1132 : : // scheduler_prepare_events add TIMEOUT event
1133 : : scheduler_t s;
1134 : 1 : scheduler_initialize(&s);
1135 : 1 : s.max_timeout = TV_SECS(600);
1136 : :
1137 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1138 : 1 : const int fd = 1;
1139 : 1 : const struct timeval to = { .tv_sec = 10 };
1140 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1141 : 1 : const int event_id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1142 : :
1143 : 1 : const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0};
1144 : 1 : fake_gettimeofday = time_now;
1145 : :
1146 : 1 : scheduler_prepare_events(&s);
1147 : :
1148 : : // New timeout value is time to the event deadline
1149 : 1 : assert_int_equal(s.timeout.tv_sec, to.tv_sec - time_now.tv_sec);
1150 : :
1151 : : scheduler_unregister_event(&s, event_id);
1152 : 1 : scheduler_gc_events(&s);
1153 : 1 : }
1154 : :
1155 : : void
1156 : 1 : test_scheduler_multiple_timeout_events_use_lowest_timeout(void **state)
1157 : : {
1158 : : // scheduler_prepare_events add multiple TIMEOUT events new timeout is lowest
1159 : : scheduler_t s;
1160 : 1 : scheduler_initialize(&s);
1161 : 1 : s.max_timeout = TV_SECS(600);
1162 : :
1163 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1164 : 1 : const int fd = 1;
1165 : 1 : const struct timeval to1 = { .tv_sec = 20 };
1166 : 1 : const struct timeval to2 = { .tv_sec = 10 };
1167 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1168 : 1 : const int id1 = scheduler_register_event(&s, md, fd, to1, &fake_event_cb, NULL);
1169 : 1 : const int id2 = scheduler_register_event(&s, md, fd, to2, &fake_event_cb, NULL);
1170 : :
1171 : 1 : const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0};
1172 : 1 : fake_gettimeofday = time_now;
1173 : :
1174 : 1 : scheduler_prepare_events(&s);
1175 : :
1176 : : // New timeout is based on the smaller event timeout value (to2).
1177 : 1 : assert_int_equal(s.timeout.tv_sec, to2.tv_sec - time_now.tv_sec);
1178 : :
1179 : : scheduler_unregister_event(&s, id1);
1180 : : scheduler_unregister_event(&s, id2);
1181 : 1 : scheduler_gc_events(&s);
1182 : 1 : }
1183 : :
1184 : : void
1185 : 1 : test_scheduler_timeout_event_is_instant_if_deadline_is_now(void **state)
1186 : : {
1187 : : // scheduler_prepare_events add TIMEOUT event timeout is zero if deadline is now
1188 : : scheduler_t s;
1189 : 1 : scheduler_initialize(&s);
1190 : 1 : s.max_timeout = TV_SECS(600);
1191 : :
1192 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1193 : 1 : const int fd = 1;
1194 : 1 : const struct timeval to = { .tv_sec = 10 };
1195 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1196 : 1 : const int event_id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1197 : :
1198 : : // Set the time now to the event timeout
1199 : 1 : fake_gettimeofday = to;
1200 : :
1201 : 1 : scheduler_prepare_events(&s);
1202 : :
1203 : : // New timeout is zero because deadline has already been reached.
1204 : 1 : assert_int_equal(s.timeout.tv_sec, 0);
1205 : :
1206 : : scheduler_unregister_event(&s, event_id);
1207 : 1 : scheduler_gc_events(&s);
1208 : 1 : }
1209 : :
1210 : : void
1211 : 1 : test_scheduler_multiple_timeout_events_dont_interfere(void **state)
1212 : : {
1213 : : // scheduler_prepare_events multiple timeout events don't clobber each other
1214 : : scheduler_t s;
1215 : 1 : scheduler_initialize(&s);
1216 : 1 : s.max_timeout = TV_SECS(600);
1217 : :
1218 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1219 : 1 : const int fd = 1;
1220 : 1 : const struct timeval to1 = { .tv_sec = 10 };
1221 : 1 : const struct timeval to2 = { .tv_sec = 20 };
1222 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1223 : 1 : const int id1 = scheduler_register_event(&s, md, fd, to1, &fake_event_cb, NULL);
1224 : 1 : const int id2 = scheduler_register_event(&s, md, fd, to2, &fake_event_cb, NULL);
1225 : :
1226 : : // Set the time now to the first event timeout
1227 : 1 : fake_gettimeofday = to1;
1228 : :
1229 : 1 : scheduler_prepare_events(&s);
1230 : :
1231 : : // Event though event2 still has 10 seconds left event1 is 0 therefor timeout is 0
1232 : 1 : assert_int_equal(s.timeout.tv_sec, 0);
1233 : :
1234 : : scheduler_unregister_event(&s, id1);
1235 : : scheduler_unregister_event(&s, id2);
1236 : 1 : scheduler_gc_events(&s);
1237 : 1 : }
1238 : :
1239 : : void
1240 : 1 : test_scheduler_timeout_event_ignored_if_no_timeout(void **state)
1241 : : {
1242 : : // scheduler_prepare_events add TIMEOUT event ignored if no timeout
1243 : : scheduler_t s;
1244 : 1 : scheduler_initialize(&s);
1245 : 1 : s.max_timeout = TV_SECS(600);
1246 : :
1247 : 1 : const char md = SCHEDULER_POLL_TIMEOUT;
1248 : 1 : const int fd = 1;
1249 : 1 : const struct timeval to = TV_INF;
1250 : 1 : fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0};
1251 : 1 : const int id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL);
1252 : :
1253 : 1 : scheduler_prepare_events(&s);
1254 : :
1255 : 1 : assert_int_equal(s.max_fd, -1);
1256 : : scheduler_unregister_event(&s, id);
1257 : 1 : scheduler_gc_events(&s);
1258 : 1 : }
1259 : :
1260 : : void
1261 : 1 : test_scheduler_with_no_events_will_timeout(void **state)
1262 : : {
1263 : : // Scheduler with no events will timeout
1264 : : scheduler_t s;
1265 : 1 : scheduler_initialize(&s);
1266 : :
1267 : 1 : const int ret = scheduler_wait_for_events(&s);
1268 : 1 : assert_int_equal(ret, 0);
1269 : 1 : }
1270 : :
1271 : : /*
1272 : : * Test running a single SCHEDULER_POLL_READ_FD event to completion.
1273 : : */
1274 : : void
1275 : 1 : test_scheduler_run_single_read_fd(void **state)
1276 : : {
1277 : : scheduler_t s;
1278 : 1 : scheduler_initialize(&s);
1279 : :
1280 : 1 : const int fd = mock_fd_create();
1281 : :
1282 : : /* Create a scheduler event for this fd.
1283 : : * The callback will be called when fd is ready for reading. */
1284 : 1 : event_cb_spy_t event_cb_spy = {};
1285 : : int event_id;
1286 : : {
1287 : 1 : const char mode = SCHEDULER_POLL_READ_FD;
1288 : 1 : const struct timeval timeout = {};
1289 : 1 : event_id = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1290 : 1 : assert_int_not_equal(event_id, 0);
1291 : : }
1292 : :
1293 : : /* Tick 1 - nothing changed so should timeout with no callback */
1294 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1295 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
1296 : :
1297 : 1 : mock_fd_set_readable(fd);
1298 : :
1299 : : /* Tick 2 - fd has data, event callback should be called */
1300 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1301 : 1 : assert_int_equal(event_cb_spy.was_called, 1);
1302 : :
1303 : 1 : close(fd);
1304 : : scheduler_unregister_event(&s, event_id);
1305 : 1 : scheduler_gc_events(&s);
1306 : 1 : }
1307 : :
1308 : : /*
1309 : : * Test running a single SCHEDULER_POLL_WRITE_FD event to completion.
1310 : : * 1. Create a mock fd
1311 : : * 2. Create a SCHEDULER_POLL_WRITE_FD event for pipe
1312 : : * 3. Manually tick the scheduler
1313 : : * 4. Check that the event callback fired
1314 : : * 5. Set the mock fd to unwriteable
1315 : : * 6. Manually tick the scheduler
1316 : : * 7. Check that the event callback did not fire
1317 : : */
1318 : : void
1319 : 1 : test_scheduler_run_single_write_fd(void **state)
1320 : : {
1321 : : scheduler_t s;
1322 : 1 : scheduler_initialize(&s);
1323 : :
1324 : 1 : const int fd = mock_fd_create();
1325 : :
1326 : : /* Create a scheduler event for this fd.
1327 : : * The callback will be called when fd is ready for writing. */
1328 : 1 : event_cb_spy_t event_cb_spy = {};
1329 : : int event_id;
1330 : : {
1331 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1332 : 1 : const struct timeval timeout = {};
1333 : 1 : event_id = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1334 : 1 : assert_int_not_equal(event_id, 0);
1335 : : }
1336 : :
1337 : : /* Tick 1 - fd is ready for writing so event callback should be called */
1338 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1339 : 1 : assert_int_equal(event_cb_spy.was_called, 1);
1340 : :
1341 : 1 : mock_fd_set_unwritable(fd);
1342 : :
1343 : : /* We expect a timeout next tick so set a low value */
1344 : 1 : scheduler_set_max_timeout(&s, (struct timeval){ .tv_sec = 0, .tv_usec = 500 });
1345 : :
1346 : : /* Tick 2 - fd is not ready for writing so should timeout and callback is not called*/
1347 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1348 : 1 : assert_int_equal(event_cb_spy.was_called, 1);
1349 : :
1350 : 1 : mock_fd_set_writable(fd);
1351 : :
1352 : : /* Tick 3 - fd is ready again so callback should be called */
1353 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1354 : 1 : assert_int_equal(event_cb_spy.was_called, 2);
1355 : :
1356 : 1 : close(fd);
1357 : : scheduler_unregister_event(&s, event_id);
1358 : 1 : scheduler_gc_events(&s);
1359 : 1 : }
1360 : :
1361 : : #if 0
1362 : : void
1363 : : test_scheduler_run_single_except_fd(void **state)
1364 : : {
1365 : : No code uses SCHEDULER_POLL_EXCEPT_FD and it is not trivial to test.
1366 : :
1367 : : According to man pages these are what count as exceptional conditions:
1368 : : - There is out-of-band data on a TCP socket (see tcp(7)).
1369 : : - A pseudoterminal master in packet mode has seen a state change on the slave
1370 : : - A cgroup.events file has been modified (see cgroups(7)).
1371 : :
1372 : : None of which seem worth testing for here.
1373 : : }
1374 : : #endif
1375 : :
1376 : : /*
1377 : : * Test running a single SCHEDULER_POLL_READ_FD event but then cancelling it.
1378 : : * 1. Create a mock fd
1379 : : * 2. Create a SCHEDULER_POLL_READ_FD event for pipe
1380 : : * 3. Make the mock fd readable
1381 : : * 4. Unreregister the event
1382 : : * 5. Manually tick the scheduler
1383 : : * 6. Check that the event callback did not fire
1384 : : */
1385 : : void
1386 : 1 : test_scheduler_run_single_dead_event(void **state)
1387 : : {
1388 : : scheduler_t s;
1389 : 1 : scheduler_initialize(&s);
1390 : :
1391 : 1 : const int fd = mock_fd_create();
1392 : :
1393 : : /* Create a scheduler event for this fd.
1394 : : * The callback will be called when fd is ready for reading. */
1395 : 1 : event_cb_spy_t event_cb_spy = {};
1396 : 1 : int event_id = -1;
1397 : : {
1398 : 1 : const char mode = SCHEDULER_POLL_READ_FD;
1399 : 1 : const struct timeval timeout = {};
1400 : 1 : event_id = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy);
1401 : 1 : assert_true(event_id > 0);
1402 : : }
1403 : :
1404 : : /* Tick 1 - nothing changed so should timeout with no callback */
1405 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1406 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
1407 : :
1408 : 1 : mock_fd_set_readable(fd);
1409 : :
1410 : 1 : const event_t* e1 = list_first_entry(&s.events, event_t, next);
1411 : : scheduler_unregister_event(&s, event_id);
1412 : : /* Event struct still exists but is marked as dead */
1413 : 1 : assert_int_equal(e1->id, event_id);
1414 : 1 : assert_int_equal(e1->dead, 1);
1415 : :
1416 : : /* We expect a timeout next tick so set a low value */
1417 : 1 : scheduler_set_max_timeout(&s, (struct timeval){ .tv_sec = 0, .tv_usec = 500 });
1418 : :
1419 : : /* Tick 2 - fd has data, but event was unregistered so expect no callback */
1420 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1421 : 1 : assert_int_equal(event_cb_spy.was_called, 0);
1422 : :
1423 : : /* The garbage collector has now removed the dead event from the list */
1424 : 1 : const event_t* e2 = list_first_entry(&s.events, event_t, next);
1425 : : /* The head of the list is now an empty placeholder. */
1426 : 1 : assert_int_equal(e2->id, 0);
1427 : 1 : assert_ptr_not_equal(e2, e1); /* The dead event is gone. */
1428 : :
1429 : 1 : close(fd);
1430 : : scheduler_unregister_event(&s, event_id);
1431 : 1 : scheduler_gc_events(&s);
1432 : 1 : }
1433 : :
1434 : : /* Create two events with the same fd but different callbacks.
1435 : : * Only the first registered callback should fire.
1436 : : * If we unregister the first event then the second callback should fire next.
1437 : : *
1438 : : * This seems to be more of an accident of how the earlier select version of
1439 : : * the scheduler was written rather than a concious design decision.
1440 : : *
1441 : : * It is important to preserve this behaviour because some parts of blktap will
1442 : : * accidentally register multiple events with the same fd but then break if the
1443 : : * callback is called more than once.
1444 : : */
1445 : : void
1446 : 1 : test_scheduler_run_duplicate_fds_are_handled_once(void **state)
1447 : : {
1448 : : scheduler_t s;
1449 : 1 : scheduler_initialize(&s);
1450 : :
1451 : 1 : const int fd = mock_fd_create();
1452 : :
1453 : 1 : event_cb_spy_t event_cb_spy1 = {};
1454 : 1 : event_cb_spy_t event_cb_spy2 = {};
1455 : :
1456 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1457 : 1 : const struct timeval timeout = {};
1458 : :
1459 : : /* Create a scheduler event for this fd. */
1460 : 1 : const int event_id1 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy1);
1461 : 1 : assert_int_not_equal(event_id1, 0);
1462 : :
1463 : : /* Register same fd and callback again */
1464 : 1 : const int event_id2 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy2);
1465 : 1 : assert_int_not_equal(event_id2, 0);
1466 : :
1467 : : /* The scheduler regards them as two separate events */
1468 : 1 : assert_int_not_equal(event_id1, event_id2);
1469 : :
1470 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1471 : :
1472 : : /* Only first callback should have fired */
1473 : 1 : assert_int_equal(event_cb_spy1.was_called, 1);
1474 : 1 : assert_int_equal(event_cb_spy2.was_called, 0);
1475 : :
1476 : : scheduler_unregister_event(&s, event_id1);
1477 : :
1478 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1479 : :
1480 : : /* Now the second callback should have fired instead */
1481 : 1 : assert_int_equal(event_cb_spy1.was_called, 1);
1482 : 1 : assert_int_equal(event_cb_spy2.was_called, 1);
1483 : :
1484 : 1 : close(fd);
1485 : : scheduler_unregister_event(&s, event_id1);
1486 : : scheduler_unregister_event(&s, event_id2);
1487 : 1 : scheduler_gc_events(&s);
1488 : 1 : }
1489 : :
1490 : : /* Register two events with different fds but the same callback.
1491 : : * The callback should be called twice when both fds are ready.
1492 : : */
1493 : : void
1494 : 1 : test_scheduler_run_with_duplicate_callbacks(void **state)
1495 : : {
1496 : : scheduler_t s;
1497 : 1 : scheduler_initialize(&s);
1498 : :
1499 : 1 : const int fd1 = mock_fd_create();
1500 : 1 : const int fd2 = mock_fd_create();
1501 : :
1502 : 1 : event_cb_spy_t event_cb_spy = {};
1503 : :
1504 : : int event_id1, event_id2;
1505 : : {
1506 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1507 : 1 : const struct timeval timeout = {};
1508 : :
1509 : : /* Create a scheduler event for fd1. */
1510 : 1 : event_id1 = scheduler_register_event(&s, mode, fd1, timeout, &mock_event_cb, &event_cb_spy);
1511 : 1 : assert_int_not_equal(event_id1, 0);
1512 : :
1513 : : /* Create a scheduler event for fd2 but same callback */
1514 : 1 : event_id2 = scheduler_register_event(&s, mode, fd2, timeout, &mock_event_cb, &event_cb_spy);
1515 : 1 : assert_int_not_equal(event_id2, 0);
1516 : : }
1517 : :
1518 : : /* Tick the scheduler */
1519 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1520 : :
1521 : : /* Callback is called twice because both events had a different fd */
1522 : 1 : assert_int_equal(event_cb_spy.was_called, 2);
1523 : :
1524 : 1 : close(fd1);
1525 : 1 : close(fd2);
1526 : : scheduler_unregister_event(&s, event_id1);
1527 : : scheduler_unregister_event(&s, event_id2);
1528 : 1 : scheduler_gc_events(&s);
1529 : 1 : }
1530 : :
1531 : : void
1532 : 1 : test_scheduler_run_read_and_write_fd(void **state)
1533 : : {
1534 : : scheduler_t s;
1535 : 1 : scheduler_initialize(&s);
1536 : :
1537 : 1 : const int fd = mock_fd_create();
1538 : :
1539 : 1 : event_cb_spy_t event_cb_spy1 = {};
1540 : 1 : event_cb_spy_t event_cb_spy2 = {};
1541 : :
1542 : : int event_id1, event_id2;
1543 : : {
1544 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1545 : 1 : const struct timeval timeout = {};
1546 : :
1547 : : /* Create a write event for fd. */
1548 : 1 : event_id1 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy1);
1549 : 1 : assert_int_not_equal(event_id1, 0);
1550 : : }
1551 : :
1552 : : {
1553 : 1 : const char mode = SCHEDULER_POLL_READ_FD;
1554 : 1 : const struct timeval timeout = {};
1555 : :
1556 : : /* Create a read event for fd1. */
1557 : 1 : event_id2 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy2);
1558 : 1 : assert_int_not_equal(event_id2, 0);
1559 : : }
1560 : :
1561 : : /* Make the fd read and writable */
1562 : 1 : mock_fd_set_readable(fd);
1563 : :
1564 : : /* Tick the scheduler */
1565 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1566 : :
1567 : : /* Both read and write callbacks were called */
1568 : 1 : assert_int_equal(event_cb_spy1.was_called, 1);
1569 : 1 : assert_int_equal(event_cb_spy2.was_called, 1);
1570 : :
1571 : : /* Tick the scheduler again*/
1572 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1573 : :
1574 : : /* Both read and write callbacks were called again */
1575 : 1 : assert_int_equal(event_cb_spy1.was_called, 2);
1576 : 1 : assert_int_equal(event_cb_spy2.was_called, 2);
1577 : :
1578 : : /* Unregister the READ event */
1579 : : scheduler_unregister_event(&s, event_id2);
1580 : :
1581 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1582 : :
1583 : : /* Only the WRITE event callback is called */
1584 : 1 : assert_int_equal(event_cb_spy1.was_called, 3);
1585 : 1 : assert_int_equal(event_cb_spy2.was_called, 2);
1586 : :
1587 : 1 : close(fd);
1588 : : scheduler_unregister_event(&s, event_id1);
1589 : : scheduler_unregister_event(&s, event_id2);
1590 : 1 : scheduler_gc_events(&s);
1591 : 1 : }
1592 : :
1593 : : void
1594 : 1 : test_scheduler_run_deleted_duplicate_event(void **state)
1595 : : {
1596 : : scheduler_t s;
1597 : 1 : scheduler_initialize(&s);
1598 : :
1599 : 1 : const int fd = mock_fd_create();
1600 : :
1601 : 1 : event_cb_spy_t event_cb_spy1 = {};
1602 : 1 : event_cb_spy_t event_cb_spy2 = {};
1603 : :
1604 : : int event_id1, event_id2;
1605 : : {
1606 : 1 : const char mode = SCHEDULER_POLL_WRITE_FD;
1607 : 1 : const struct timeval timeout = {};
1608 : :
1609 : : /* Create two identical events with the same fd. */
1610 : 1 : event_id1 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy1);
1611 : 1 : assert_int_not_equal(event_id1, 0);
1612 : :
1613 : 1 : event_id2 = scheduler_register_event(&s, mode, fd, timeout, &mock_event_cb, &event_cb_spy2);
1614 : 1 : assert_int_not_equal(event_id2, 0);
1615 : : }
1616 : :
1617 : : /* Tick the scheduler */
1618 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1619 : :
1620 : : /* Only the callback registered first should fire */
1621 : 1 : assert_int_equal(event_cb_spy1.was_called, 1);
1622 : 1 : assert_int_equal(event_cb_spy2.was_called, 0);
1623 : :
1624 : : /* Unregister the first event */
1625 : : scheduler_unregister_event(&s, event_id1);
1626 : :
1627 : : /* Tick the scheduler again*/
1628 : 1 : assert_int_equal(scheduler_wait_for_events(&s), 0);
1629 : :
1630 : : /* This time the second callback should fire */
1631 : 1 : assert_int_equal(event_cb_spy1.was_called, 1);
1632 : 1 : assert_int_equal(event_cb_spy2.was_called, 1);
1633 : :
1634 : 1 : close(fd);
1635 : :
1636 : : scheduler_unregister_event(&s, event_id1);
1637 : : scheduler_unregister_event(&s, event_id2);
1638 : 1 : scheduler_gc_events(&s);
1639 : 1 : }
|