The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
timer_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for timer lists
18 *
19 * @file src/lib/util/test//timer_tests.c
20 *
21 * @copyright 2025 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 */
23#include "acutest.h"
24#include"acutest_helpers.h"
25#include <freeradius-devel/util/time.h>
26#include <freeradius-devel/util/timer.h>
27
28/** Defines an artificial time source for a test
29 *
30 * Defines _name + _time() and _name + _set() functions.
31 */
32#define TIME_SOURCE(_name) \
33 static fr_time_t _name##_timer = fr_time_wrap(0); \
34 static fr_time_t _name##_time(void) \
35 { \
36 return _name##_timer; \
37 } \
38 static void _name##_set(fr_time_t t) \
39 { \
40 _name##_timer = t; \
41 }
42
43TIME_SOURCE(basic)
44
45/** Verifies time passed in is not 0, that tl is not NULL, and writes true to uctx (must be a bool)
46 *
47 */
48static void timer_cb(fr_timer_list_t *tl, fr_time_t now, void *uctx)
49{
50 bool *fired = (bool *)uctx;
51
52 TEST_ASSERT(tl != NULL);
54
55 *fired = true;
56}
57
59{
60 fr_time_t now;
61 fr_timer_t *event1 = NULL, *event1a = NULL, *event2 = NULL, *event3 = NULL, *event4 = NULL, *event5 = NULL, *event6 = NULL;
62 bool event1_fired = false, event1a_fired = false, event2_fired = false, event3_fired = false, event4_fired = false, event5_fired = false, event6_fired = false;
63 int ret;
64
65 /*
66 * Should fire together
67 */
68 ret = fr_timer_in(NULL, tl, &event1, fr_time_delta_from_sec(1), true, timer_cb, &event1_fired);
69 TEST_CHECK(ret == 0);
70
71 ret = fr_timer_in(NULL, tl, &event1a, fr_time_delta_from_sec(1), true, timer_cb, &event1a_fired);
72 TEST_CHECK(ret == 0);
73
74 ret = fr_timer_in(NULL, tl, &event2, fr_time_delta_from_sec(2), true, timer_cb, &event2_fired);
75 TEST_CHECK(ret == 0);
76
77 ret = fr_timer_in(NULL, tl, &event3, fr_time_delta_from_sec(3), true, timer_cb, &event3_fired);
78 TEST_CHECK(ret == 0);
79
80 /*
81 * Will be disarmed before it fires
82 */
83 ret = fr_timer_in(NULL, tl, &event4, fr_time_delta_from_sec(3), true, timer_cb, &event4_fired);
84 TEST_CHECK(ret == 0);
85
86 /*
87 * Will be delete before it fires
88 */
89 ret = fr_timer_in(NULL, tl, &event5, fr_time_delta_from_sec(4), true, timer_cb, &event5_fired);
90 TEST_CHECK(ret == 0);
91
92 ret = fr_timer_in(NULL, tl, &event6, fr_time_delta_from_sec(4), true, timer_cb, &event6_fired);
93 TEST_CHECK(ret == 0);
94
95 /*
96 * No events should have fired yet
97 */
99
100 now = fr_time_from_sec(1);
101
102 /*
103 * First batch of events
104 */
105 TEST_CHECK(fr_timer_list_run(tl, &now) == 2);
106 TEST_CHECK(event1_fired == true);
107 TEST_CHECK(event1a_fired == true);
108 TEST_CHECK(event2_fired == false);
109 TEST_CHECK(event1 == NULL);
110 TEST_CHECK(event1a == NULL);
111
112 /*
113 * Second batch of events (single event)
114 */
115 TEST_CHECK(fr_timer_list_run(tl, &now) == 1);
116 TEST_CHECK(event2 == NULL);
117 TEST_CHECK(event2_fired == true);
118 TEST_CHECK(event3_fired == false);
119 TEST_CHECK(event4_fired == false);
120
121 /*
122 * Now disarm event 4, so it doesn't fire
123 */
124 TEST_CHECK(fr_timer_disarm(event4) == 0);
125
126 now = fr_time_from_sec(3);
127 TEST_CHECK(fr_timer_list_run(tl, &now) == 1);
128
129 TEST_CHECK(event3 == NULL);
130 TEST_CHECK(event4 != NULL);
131 TEST_CHECK(event3_fired == true);
132 TEST_CHECK(event4_fired == false);
133
134 /*
135 * Now free event 5, so it doesn't fire
136 */
137 TEST_CHECK(fr_timer_delete(&event5) == 0);
138
139 now = fr_time_from_sec(4);
140 TEST_CHECK(fr_timer_list_run(tl, &now) == 1);
141 TEST_CHECK(event5_fired == false);
142 TEST_CHECK(event6_fired == true);
143 TEST_CHECK(event5 == NULL);
144 TEST_CHECK(event6 == NULL);
145
146 /*
147 * Re-arm event 4
148 */
149 now = fr_time_from_sec(4);
150 ret = fr_timer_at(NULL, tl, &event4, fr_time_from_sec(3), false, timer_cb, &event4_fired);
151 TEST_CHECK(ret == 0);
152 TEST_CHECK(fr_timer_list_run(tl, &now) == 1);
153 TEST_CHECK(event4_fired == true);
154 TEST_CHECK(event4 != NULL);
155
156 talloc_free(event4); /* This needs to be freed before its parent stack memory goes out of scope */
157}
158
159static void lst_basic_test(void)
160{
161 fr_timer_list_t *tl;
162
163 tl = fr_timer_list_lst_alloc(NULL, NULL);
164 TEST_ASSERT(tl != NULL);
165
166 fr_timer_list_set_time_func(tl, basic_time);
167
169
170 talloc_free(tl);
171}
172
173typedef struct {
174 bool *fired;
177
178static void timer_cb_deferred(fr_timer_list_t *tl, fr_time_t now, void *uctx)
179{
180 deferred_uctx_t *ctx = (deferred_uctx_t *)uctx;
181
182 TEST_CHECK(fr_timer_at(NULL, tl, &ctx->event, now, true, timer_cb, ctx->fired) == 0);
184
185 TEST_CHECK(fr_timer_list_run(tl, &now) == 0); /* Event won't run immediately because we're in a callback */
186}
187
189{
190 fr_time_t now;
191 fr_timer_t *event1 = NULL;
192 bool deferred_event_fired = false;
193
194 deferred_uctx_t ctx = { .fired = &deferred_event_fired, .event = NULL };
195
196 fr_timer_list_set_time_func(tl, basic_time);
197
198 now = fr_time_from_sec(1);
199 TEST_CHECK(fr_timer_at(NULL, tl, &event1, fr_time_from_sec(1), true, timer_cb_deferred, &ctx) == 0);
200
201 /*
202 * The inner fr_timer_list_run call moves the event from the deferred
203 * list into the lst, where it's immediately executed, which is why
204 * we get 2 events firing here.
205 */
206 TEST_CHECK(fr_timer_list_run(tl, &now) == 2);
207 TEST_CHECK(deferred_event_fired == true);
208
209 now = fr_time_from_sec(1);
210}
211
212static void ordered_basic_test(void)
213{
214 fr_timer_list_t *tl;
215
216 tl = fr_timer_list_ordered_alloc(NULL, NULL);
217 TEST_ASSERT(tl != NULL);
218
219 fr_timer_list_set_time_func(tl, basic_time);
220
222
223 talloc_free(tl);
224}
225
226static void lst_deferred_test(void)
227{
228 fr_timer_list_t *tl;
229
230 tl = fr_timer_list_lst_alloc(NULL, NULL);
231 TEST_ASSERT(tl != NULL);
232
234
235 talloc_free(tl);
236}
237
238static void ordered_deferred_test(void)
239{
240 fr_timer_list_t *tl;
241
242 tl = fr_timer_list_ordered_alloc(NULL, NULL);
243 TEST_ASSERT(tl != NULL);
244
246
247 talloc_free(tl);
248}
249
251{
252 fr_timer_list_t *tl;
253 fr_timer_t *event1 = NULL, *event2 = NULL;
254 bool event1_fired = false, event2_fired = false;
255 int ret;
256
257 tl = fr_timer_list_ordered_alloc(NULL, NULL);
258 TEST_ASSERT(tl != NULL);
259
260 fr_timer_list_set_time_func(tl, basic_time);
261
262 ret = fr_timer_in(NULL, tl, &event1, fr_time_delta_from_sec(5), true, timer_cb, &event1_fired);
263 TEST_CHECK(ret == 0);
264
265 /*
266 * Should fail (wrong order)
267 */
268 ret = fr_timer_in(NULL, tl, &event2, fr_time_delta_from_sec(1), true, timer_cb, &event2_fired);
269 TEST_CHECK(ret == -1);
270
271 talloc_free(tl);
272}
273
274static void nested_test(fr_timer_list_t *tl_outer, fr_timer_list_t *tl_inner)
275{
276 fr_timer_t *event1_inner = NULL, *event2_inner = NULL;
277 bool event1_inner_fired = false, event2_inner_fired = false;
278 fr_time_t now;
279
280 int ret;
281
282 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 0);
283
284 /*
285 * Should insert a single event into the outer list
286 */
287 ret = fr_timer_in(NULL, tl_inner, &event1_inner, fr_time_delta_from_sec(1), true, timer_cb, &event1_inner_fired);
288 TEST_CHECK(ret == 0);
289
290 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 1);
291 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 1);
292
293 /*
294 * Disable the event, the outer event count should drop to 0
295 */
296 TEST_CHECK(fr_timer_disarm(event1_inner) == 0);
297 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 0);
298 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 0);
299
300 /*
301 * Re-Enable the event
302 */
303 ret = fr_timer_in(NULL, tl_inner, &event1_inner, fr_time_delta_from_sec(1), true, timer_cb, &event1_inner_fired);
304 TEST_CHECK(ret == 0);
305
306 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 1);
307 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 1);
308
309 ret = fr_timer_in(NULL, tl_inner, &event2_inner, fr_time_delta_from_sec(1), true, timer_cb, &event2_inner_fired);
310 TEST_CHECK(ret == 0);
311
312 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 1);
313 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 2);
314
315 now = fr_time_from_sec(1);
316
317 /*
318 * One event should fire, which should run all the events in the inner list
319 */
320 TEST_CHECK(fr_timer_list_run(tl_outer, &now) == 1);
321 TEST_CHECK(event1_inner_fired == true);
322 TEST_CHECK(event2_inner_fired == true);
323
324 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 0);
325 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 0);
326}
327
328static void lst_nested(void)
329{
330 fr_timer_list_t *tl_outer, *tl_inner;
331 fr_timer_t *event1_inner = NULL;
332 int ret;
333
334 tl_outer = fr_timer_list_lst_alloc(NULL, NULL);
335 TEST_ASSERT(tl_outer != NULL);
336
337 tl_inner = fr_timer_list_lst_alloc(tl_outer, tl_outer);
338 TEST_ASSERT(tl_inner != NULL);
339
340 fr_timer_list_set_time_func(tl_outer, basic_time);
341 fr_timer_list_set_time_func(tl_inner, basic_time);
342
343 nested_test(tl_outer, tl_inner);
344
345 ret = fr_timer_in(NULL, tl_inner, &event1_inner, fr_time_delta_from_sec(1), true, timer_cb, NULL);
346 TEST_CHECK(ret == 0);
347
348 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 1);
349 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 1);
350
351 talloc_free(tl_inner);
352
353 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 0);
354 TEST_CHECK(event1_inner == NULL);
355
356 talloc_free(tl_outer);
357}
358
359static void ordered_nested(void)
360{
361 fr_timer_list_t *tl_outer, *tl_inner;
362 fr_timer_t *event1_inner = NULL;
363 int ret;
364
365 tl_outer = fr_timer_list_ordered_alloc(NULL, NULL);
366 TEST_ASSERT(tl_outer != NULL);
367
368 tl_inner = fr_timer_list_ordered_alloc(tl_outer, tl_outer);
369 TEST_ASSERT(tl_inner != NULL);
370
371 fr_timer_list_set_time_func(tl_outer, basic_time);
372 fr_timer_list_set_time_func(tl_inner, basic_time);
373
374 nested_test(tl_outer, tl_inner);
375
376 ret = fr_timer_in(NULL, tl_inner, &event1_inner, fr_time_delta_from_sec(1), true, timer_cb, NULL);
377 TEST_CHECK(ret == 0);
378
379 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 1);
380 TEST_CHECK(fr_timer_list_num_events(tl_inner) == 1);
381
382 talloc_free(tl_inner);
383
384 TEST_CHECK(fr_timer_list_num_events(tl_outer) == 0);
385 TEST_CHECK(event1_inner == NULL);
386
387 talloc_free(tl_outer);
388}
389
391 { "lst_basic", lst_basic_test },
392 { "ordered_basic", ordered_basic_test },
393 { "lst_deferred", lst_deferred_test },
394 { "ordered_deferred", ordered_deferred_test },
395 { "ordered_bad_inserts", ordered_bad_inserts_test },
396 { "lst_nested", lst_nested },
397 { "ordered_nested", ordered_nested },
399};
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_ASSERT(cond)
Definition acutest.h:110
#define TEST_TERMINATOR
Definition acutest.h:64
talloc_free(hp)
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_wrap(_time)
Definition time.h:145
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition time.h:858
#define fr_time_gt(_a, _b)
Definition time.h:237
"server local" time.
Definition time.h:69
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
Definition timer.c:939
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
Definition timer.c:1149
fr_timer_list_t * fr_timer_list_ordered_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new sorted event timer list.
Definition timer.c:1291
fr_timer_list_t * fr_timer_list_lst_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new lst based timer list.
Definition timer.c:1263
int fr_timer_disarm(fr_timer_t *ev)
Remove an event from the event list, but don't free the memory.
Definition timer.c:656
void fr_timer_list_set_time_func(fr_timer_list_t *tl, fr_event_time_source_t func)
Override event list time source.
Definition timer.c:1200
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
Definition timer.c:693
An event timer list.
Definition timer.c:50
A timer event.
Definition timer.c:84
#define fr_timer_in(...)
Definition timer.h:87
#define fr_timer_at(...)
Definition timer.h:81
TEST_LIST
static void ordered_nested(void)
fr_timer_t * event
static void lst_nested(void)
static void ordered_basic_test(void)
static void nested_test(fr_timer_list_t *tl_outer, fr_timer_list_t *tl_inner)
#define TIME_SOURCE(_name)
Defines an artificial time source for a test.
Definition timer_tests.c:32
static void deferred_timer_list_tests(fr_timer_list_t *tl)
static void lst_basic_test(void)
static void ordered_deferred_test(void)
static void lst_deferred_test(void)
static void timer_cb_deferred(fr_timer_list_t *tl, fr_time_t now, void *uctx)
static void basic_timer_list_tests(fr_timer_list_t *tl)
Definition timer_tests.c:58
static void timer_cb(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Verifies time passed in is not 0, that tl is not NULL, and writes true to uctx (must be a bool)
Definition timer_tests.c:48
static void ordered_bad_inserts_test(void)
int nonnull(2, 5))