26#include <freeradius-devel/util/machine.h>
31typedef enum :
unsigned int {
48#define CONN_ROW(_from, ...) FR_MACHINE_ROW(conn_state_t, _from, __VA_ARGS__)
108 .valid = &conn_valid,
127 log_add(
"watch requests transition");
128 TEST_CHECK(conn_machine_transition(m, to) == FR_MACHINE_QUEUED);
131static bool log_matches(
char const *
const expected[],
size_t expected_num)
142 for (i = 0; i < expected_num; i++) {
144 TEST_MSG(
"event %zu: logged \"%s\", expected \"%s\"", i,
event_log[i], expected[i]);
149#define LOG_MATCHES(...) \
150 log_matches((char const * const []){ __VA_ARGS__ }, VA_NARG(__VA_ARGS__))
159 TEST_CASE(
"init sets the state and prev, and runs nothing");
169 conn_machine_fini(&m);
178 TEST_CASE(
"the table allows listed transitions");
182 TEST_CASE(
"the table rejects unlisted transitions, self transitions and out of range states");
187 TEST_CASE(
"a terminal state has no exits");
192 conn_machine_fini(&m);
203 conn_machine_watch_add_any(&m, ctx,
CONN_CONNECTED, FR_MACHINE_POST_ENTER,
watch_log,
false,
"post CONNECTED");
205 TEST_CASE(
"INIT -> CONNECTING runs the entry function");
210 TEST_CASE(
"CONNECTING -> CONNECTED runs exit, pre watchers, enter, post watchers, in that order");
214 LOG_MATCHES(
"enter CONNECTING",
"exit CONNECTING",
"pre CONNECTED",
"enter CONNECTED",
"post CONNECTED");
216 conn_machine_fini(&m);
229 TEST_CASE(
"an unlisted transition is rejected and runs nothing");
235 conn_machine_fini(&m);
247 conn_machine_watch_add_any(&m, ctx,
CONN_CONNECTED, FR_MACHINE_POST_ENTER,
watch_log,
false,
"post CONNECTED");
250 TEST_CASE(
"a failed entry function reports failure, leaves the machine in the new state, and skips post watchers");
253 LOG_MATCHES(
"exit CONNECTING",
"pre CONNECTED",
"enter CONNECTED");
255 conn_machine_fini(&m);
267 "edge CONNECTING->FAILED");
269 "edge CONNECTED->FAILED");
271 "exit CONNECTING (watch)");
272 conn_machine_watch_add_any(&m, ctx,
CONN_FAILED, FR_MACHINE_POST_ENTER,
watch_log,
false,
"any->FAILED");
274 TEST_CASE(
"INIT -> CONNECTING matches no watcher");
278 TEST_CASE(
"CONNECTING -> FAILED matches the edge, the exit wildcard and the entry wildcard, in registration order");
280 LOG_MATCHES(
"enter CONNECTING",
"exit CONNECTING",
"edge CONNECTING->FAILED",
"exit CONNECTING (watch)",
"any->FAILED");
282 conn_machine_fini(&m);
290 fr_machine_watch_entry_t *persistent;
298 TEST_CASE(
"both watchers run on the first entry");
300 LOG_MATCHES(
"enter CONNECTING",
"oneshot",
"persistent");
302 TEST_CASE(
"only the persistent watcher runs on the second entry");
307 LOG_MATCHES(
"exit CONNECTING",
"enter CONNECTING",
"persistent");
309 TEST_CASE(
"a disabled watcher is skipped, and runs again once enabled");
311 fr_machine_watch_disable(persistent);
315 LOG_MATCHES(
"exit CONNECTING",
"enter CONNECTING");
318 fr_machine_watch_enable(persistent);
322 LOG_MATCHES(
"exit CONNECTING",
"enter CONNECTING",
"persistent");
324 TEST_CASE(
"freeing the handle removes the watcher");
330 LOG_MATCHES(
"exit CONNECTING",
"enter CONNECTING");
332 conn_machine_fini(&m);
342 fr_machine_watch_entry_t **entry_p = uctx;
353 fr_machine_watch_entry_t *self = NULL, *sibling = NULL;
358 TEST_CASE(
"a watcher that frees itself does not break the walk");
362 LOG_MATCHES(
"enter CONNECTING",
"freeing an entry",
"after self");
365 TEST_CASE(
"a watcher that frees the next sibling skips it and continues");
374 LOG_MATCHES(
"enter CONNECTING",
"after self",
"freeing an entry",
"after sibling");
377 conn_machine_fini(&m);
391 conn_machine_watch_add_any(&m, ctx,
CONN_CONNECTED, FR_MACHINE_POST_ENTER,
watch_log,
false,
"post CONNECTED");
393 TEST_CASE(
"a transition requested by a watcher runs after the current one completes");
397 LOG_MATCHES(
"enter CONNECTING",
"watch requests transition",
"exit CONNECTING",
"enter CONNECTED",
"post CONNECTED");
399 TEST_CASE(
"two requests from one callback run in order");
408 LOG_MATCHES(
"enter CONNECTING",
"watch requests transition",
"watch requests transition",
409 "exit CONNECTING",
"enter CONNECTED",
"post CONNECTED");
411 conn_machine_fini(&m);
424 TEST_CASE(
"a transition while paused is queued");
425 conn_machine_pause(&m);
426 conn_machine_pause(&m);
431 TEST_CASE(
"the first resume of two does not run the queue");
432 conn_machine_resume(&m);
436 TEST_CASE(
"the last resume runs the queue");
437 conn_machine_resume(&m);
439 LOG_MATCHES(
"enter CONNECTING",
"post CONNECTING");
441 conn_machine_fini(&m);
453 TEST_CASE(
"a queued transition the table no longer allows is dropped");
454 conn_machine_pause(&m);
457 conn_machine_resume(&m);
461 conn_machine_fini(&m);
473 conn_machine_watch_add_any(&m, watch_ctx,
CONN_CONNECTING, FR_MACHINE_POST_ENTER,
watch_log,
false,
"from freed ctx");
476 TEST_CASE(
"freeing the talloc context of a watcher removes it");
481 TEST_CASE(
"fini frees the remaining watchers, and the context they were parented to is then empty");
482 conn_machine_fini(&m);
#define TEST_ASSERT(cond)
Common initialisation for acutest binaries.
char const * fr_machine_state_name(fr_machine_t *m, int state)
Get the name of a particular state.
fr_machine_func_t enter
run this when entering the state
static bool log_matches(char const *const expected[], size_t expected_num)
#define CONN_ROW(_from,...)
static fr_machine_def_t const conn_def
static int conn_enter_connected(UNUSED fr_machine_t *m, UNUSED void *uctx)
static void log_reset(void)
FR_MACHINE_VALID(conn, CONN_MAX, CONN_ROW(CONN_INIT, CONN_CONNECTING, CONN_CLOSED), CONN_ROW(CONN_CONNECTING, CONN_CONNECTED, CONN_FAILED, CONN_CLOSED), CONN_ROW(CONN_CONNECTED, CONN_FAILED, CONN_CLOSED),)
static void test_table(void)
static void test_enter_failed(void)
static void conn_exit_connecting(UNUSED fr_machine_t *m, UNUSED void *uctx)
static int conn_enter_connecting(UNUSED fr_machine_t *m, UNUSED void *uctx)
static void test_transition_from_watcher(void)
static void test_pause_resume(void)
static void watch_transition(conn_machine_t *m, UNUSED conn_state_t prev, UNUSED conn_state_t state, void *uctx)
static void test_watch_oneshot_and_disable(void)
static char const * event_log[LOG_MAX]
static void test_watch_free_during_walk(void)
static fr_machine_state_t const conn_states[CONN_MAX]
static void test_transition_invalid(void)
static void log_add(char const *event)
static void test_init(void)
static fr_table_num_indexed_t const conn_state_names[]
static void test_watch_ctx_free(void)
static void watch_free_entry(UNUSED conn_machine_t *m, UNUSED conn_state_t prev, UNUSED conn_state_t state, void *uctx)
static void test_queue_revalidated(void)
static void test_watch_edges(void)
static void test_transition_order(void)
static void watch_log(UNUSED conn_machine_t *m, UNUSED conn_state_t prev, UNUSED conn_state_t state, void *uctx)
#define FR_TABLE_INDEXED_ENTRY(_v)
Build a single fr_table_num_indexed_t entry from an enum identifier.
An element in a table indexed by numeric value.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.