blob: 0f29a2e4514925bf3d3933e0249353bb03da2767 [file] [log] [blame]
Harald Welte136e7372016-05-29 10:53:17 +09001#include <stdlib.h>
2#include <stdio.h>
3#include <stdarg.h>
4#include <unistd.h>
5#include <string.h>
Neels Hofmeyr975ee6b2018-04-09 00:42:12 +02006#include <errno.h>
Harald Welte136e7372016-05-29 10:53:17 +09007
8#include <osmocom/core/utils.h>
9#include <osmocom/core/select.h>
10#include <osmocom/core/logging.h>
11#include <osmocom/core/fsm.h>
Harald Welte31c0fef2017-04-16 17:26:30 +020012#include <osmocom/ctrl/control_if.h>
Harald Welte136e7372016-05-29 10:53:17 +090013
14enum {
15 DMAIN,
16};
17
18static void *g_ctx;
19
Neels Hofmeyr975ee6b2018-04-09 00:42:12 +020020static int safe_strcmp(const char *a, const char *b)
21{
22 if (!a || !b)
23 return a == b ? 0 : 1;
24 return strcmp(a, b);
25}
Harald Welte136e7372016-05-29 10:53:17 +090026
27enum test_fsm_states {
28 ST_NULL = 0,
29 ST_ONE,
30 ST_TWO,
31};
32
33enum test_fsm_evt {
34 EV_A,
35 EV_B,
36};
37
Stefan Sperling888dc7d2018-02-26 19:17:02 +010038static const struct value_string test_fsm_event_names[] = {
39 OSMO_VALUE_STRING(EV_A),
40 OSMO_VALUE_STRING(EV_B),
41 { 0, NULL }
42};
43
Harald Welte136e7372016-05-29 10:53:17 +090044static void test_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
45{
46 switch (event) {
47 case EV_A:
48 OSMO_ASSERT(data == (void *) 23);
49 osmo_fsm_inst_state_chg(fi, ST_ONE, 0, 0);
50 break;
51 default:
52 OSMO_ASSERT(0);
53 break;
54 }
55}
56
57static void test_fsm_one(struct osmo_fsm_inst *fi, uint32_t event, void *data)
58{
59 switch (event) {
60 case EV_B:
61 OSMO_ASSERT(data == (void *) 42);
62 osmo_fsm_inst_state_chg(fi,ST_TWO, 1, 2342);
63 break;
64 default:
65 OSMO_ASSERT(0);
66 break;
67 }
68}
69
Neels Hofmeyrd8f175c2018-04-09 00:42:22 +020070static bool main_loop_run = true;
71
Neels Hofmeyrcba8eb92016-12-23 04:32:09 +010072static int test_fsm_tmr_cb(struct osmo_fsm_inst *fi)
Harald Welte136e7372016-05-29 10:53:17 +090073{
74 OSMO_ASSERT(fi->T == 2342);
75 OSMO_ASSERT(fi->state == ST_TWO);
76 LOGP(DMAIN, LOGL_INFO, "Timer\n");
77
Neels Hofmeyrd8f175c2018-04-09 00:42:22 +020078 main_loop_run = false;
79
80 return 0;
Harald Welte136e7372016-05-29 10:53:17 +090081}
82
83static struct osmo_fsm_state test_fsm_states[] = {
84 [ST_NULL] = {
85 .in_event_mask = (1 << EV_A),
86 .out_state_mask = (1 << ST_ONE),
87 .name = "NULL",
88 .action = test_fsm_null,
89 },
90 [ST_ONE]= {
91 .in_event_mask = (1 << EV_B),
92 .out_state_mask = (1 << ST_TWO),
93 .name = "ONE",
94 .action= test_fsm_one,
95 },
96 [ST_TWO]= {
97 .in_event_mask = 0,
98 .name = "TWO",
99 .action = NULL,
100 },
101};
102
103static struct osmo_fsm fsm = {
Harald Welte31c0fef2017-04-16 17:26:30 +0200104 .name = "Test_FSM",
Harald Welte136e7372016-05-29 10:53:17 +0900105 .states = test_fsm_states,
106 .num_states = ARRAY_SIZE(test_fsm_states),
107 .log_subsys = DMAIN,
Stefan Sperling888dc7d2018-02-26 19:17:02 +0100108 .event_names = test_fsm_event_names,
Harald Welte136e7372016-05-29 10:53:17 +0900109};
110
Harald Welte31c0fef2017-04-16 17:26:30 +0200111static struct ctrl_handle *g_ctrl;
112
113static struct ctrl_cmd *exec_ctrl_cmd(const char *cmdstr)
114{
115 struct ctrl_cmd *cmd;
Vadim Yanitskiy35b54d12017-05-14 20:38:44 +0300116
117 cmd = ctrl_cmd_exec_from_string(g_ctrl, cmdstr);
Harald Welte31c0fef2017-04-16 17:26:30 +0200118 OSMO_ASSERT(cmd);
Vadim Yanitskiy35b54d12017-05-14 20:38:44 +0300119
Harald Welte31c0fef2017-04-16 17:26:30 +0200120 return cmd;
121}
122
123static void assert_cmd_reply(const char *cmdstr, const char *expres)
124{
125 struct ctrl_cmd *cmd;
126
127 cmd = exec_ctrl_cmd(cmdstr);
Neels Hofmeyr975ee6b2018-04-09 00:42:12 +0200128 if (safe_strcmp(cmd->reply, expres)) {
Harald Welte31c0fef2017-04-16 17:26:30 +0200129 fprintf(stderr, "Reply '%s' doesn't match expected '%s'\n", cmd->reply, expres);
130 OSMO_ASSERT(0);
131 }
132 talloc_free(cmd);
133}
134
Max3de97e12016-11-02 10:37:58 +0100135static struct osmo_fsm_inst *foo(void)
Harald Welte136e7372016-05-29 10:53:17 +0900136{
137 struct osmo_fsm_inst *fi;
Harald Welte31c0fef2017-04-16 17:26:30 +0200138 struct ctrl_cmd *cmd;
Harald Welte136e7372016-05-29 10:53:17 +0900139
140 LOGP(DMAIN, LOGL_INFO, "Checking FSM allocation\n");
Harald Welte4585e672017-04-16 17:23:56 +0200141 fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, "my_id");
Harald Welte136e7372016-05-29 10:53:17 +0900142 OSMO_ASSERT(fi);
143 OSMO_ASSERT(fi->fsm == &fsm);
144 OSMO_ASSERT(!strncmp(osmo_fsm_inst_name(fi), fsm.name, strlen(fsm.name)));
145 OSMO_ASSERT(fi->state == ST_NULL);
146 OSMO_ASSERT(fi->log_level == LOGL_DEBUG);
Harald Welte31c0fef2017-04-16 17:26:30 +0200147 assert_cmd_reply("GET 1 fsm.Test_FSM.id.my_id.state", "NULL");
148 assert_cmd_reply("GET 1 fsm.Test_FSM.id.my_id.timer", "0,0,0");
Harald Welte136e7372016-05-29 10:53:17 +0900149
150 /* Try invalid state transition */
151 osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42);
152 OSMO_ASSERT(fi->state == ST_NULL);
Harald Welte31c0fef2017-04-16 17:26:30 +0200153 assert_cmd_reply("GET 1 fsm.Test_FSM.id.my_id.state", "NULL");
154
Harald Welte136e7372016-05-29 10:53:17 +0900155
156 /* Legitimate state transition */
157 osmo_fsm_inst_dispatch(fi, EV_A, (void *) 23);
158 OSMO_ASSERT(fi->state == ST_ONE);
Harald Welte31c0fef2017-04-16 17:26:30 +0200159 assert_cmd_reply("GET 1 fsm.Test_FSM.id.my_id.state", "ONE");
Harald Welte136e7372016-05-29 10:53:17 +0900160
161 /* Legitimate transition with timer */
162 fsm.timer_cb = test_fsm_tmr_cb;
163 osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42);
164 OSMO_ASSERT(fi->state == ST_TWO);
Harald Welte31c0fef2017-04-16 17:26:30 +0200165 assert_cmd_reply("GET 1 fsm.Test_FSM.id.my_id.state", "TWO");
Harald Welte136e7372016-05-29 10:53:17 +0900166
Harald Welte31c0fef2017-04-16 17:26:30 +0200167 cmd = exec_ctrl_cmd("GET 2 fsm.Test_FSM.id.my_id.dump");
168 const char *exp = "'Test_FSM(my_id)','my_id','DEBUG','TWO',2342,timeout_sec=";
169 OSMO_ASSERT(!strncmp(cmd->reply, exp, strlen(exp)));
170 talloc_free(cmd);
Harald Welte136e7372016-05-29 10:53:17 +0900171
Max3de97e12016-11-02 10:37:58 +0100172 return fi;
Harald Welte136e7372016-05-29 10:53:17 +0900173}
174
Neels Hofmeyr975ee6b2018-04-09 00:42:12 +0200175static void test_id_api()
176{
177 struct osmo_fsm_inst *fi;
178
179 fprintf(stderr, "\n--- %s()\n", __func__);
180
181/* Assert the instance has this name and can be looked up by it */
182#define assert_name(expected_name) \
183do { \
184 const char *name = osmo_fsm_inst_name(fi); \
185 fprintf(stderr, " osmo_fsm_inst_name() == %s\n", osmo_quote_str(name, -1)); \
186 if (safe_strcmp(name, expected_name)) { \
187 fprintf(stderr, " ERROR: expected %s\n", osmo_quote_str(expected_name, -1)); \
188 OSMO_ASSERT(false); \
189 } \
190 OSMO_ASSERT(osmo_fsm_inst_find_by_name(&fsm, expected_name) == fi); \
191 fprintf(stderr, " osmo_fsm_inst_find_by_name(%s) == fi\n", osmo_quote_str(expected_name, -1)); \
192} while(0)
193
194/* Assert the instance can be looked up by this id string */
195#define assert_id(expected_id) \
196do { \
197 OSMO_ASSERT(osmo_fsm_inst_find_by_id(&fsm, expected_id) == fi); \
198 fprintf(stderr, " osmo_fsm_inst_find_by_id(%s) == fi\n", osmo_quote_str(expected_id, -1)); \
199} while(0)
200
201/* Update the id, assert the proper rc, and expect a resulting fsm inst name + lookup */
202#define test_id(new_id, expect_rc, expect_name_suffix) do { \
203 int rc; \
204 fprintf(stderr, "osmo_fsm_inst_update_id(%s)\n", osmo_quote_str(new_id, -1)); \
205 rc = osmo_fsm_inst_update_id(fi, new_id); \
206 fprintf(stderr, " rc == %d", rc); \
207 if (rc == (expect_rc)) \
208 fprintf(stderr, ", ok\n"); \
209 else { \
210 fprintf(stderr, ", ERROR: expected rc == %d\n", expect_rc); \
211 OSMO_ASSERT(rc == expect_rc); \
212 } \
213 assert_name("Test_FSM" expect_name_suffix); \
214 }while (0)
215
216/* Successfully set a new id, along with name and id lookup assertions */
217#define change_id(new_id) \
218 test_id(new_id, 0, "(" new_id ")"); \
219 assert_id(new_id)
220
221 /* allocate FSM instance without id, there should be a name without id */
222 fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
223 OSMO_ASSERT(fi);
224 /* CURRENT BUG: here I want to just do
225 assert_name("Test_FSM");
226 * but when allocated with a NULL id, the fsm's name remains unset. Hence: */
227 {
228 const char *expected_name = "Test_FSM";
229 const char *name = osmo_fsm_inst_name(fi);
230 fprintf(stderr, " osmo_fsm_inst_name() == %s\n", osmo_quote_str(name, -1));
231 if (safe_strcmp(name, expected_name)) {
232 fprintf(stderr, " ERROR: expected %s\n", osmo_quote_str(expected_name, -1));
233 OSMO_ASSERT(false);
234 }
235 OSMO_ASSERT(osmo_fsm_inst_find_by_name(&fsm, "Test_FSM") == NULL); /* <- ERROR */
236 fprintf(stderr, " osmo_fsm_inst_find_by_name(%s) == NULL\n", osmo_quote_str(expected_name, -1));
237 }
238
239 change_id("my_id");
240 change_id("another_id");
241
242 test_id(NULL, 0, "");
243 /* clear already cleared id */
244 test_id(NULL, 0, "");
245
246 change_id("arbitrary_id");
247
248 /* clear id by empty string doesn't work */
249 test_id("", -EINVAL, "(arbitrary_id)");
250
251 test_id("invalid.id", -EINVAL, "(arbitrary_id)");
252
253 fprintf(stderr, "\n--- %s() done\n\n", __func__);
254
255 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
256}
257
Harald Welte136e7372016-05-29 10:53:17 +0900258static const struct log_info_cat default_categories[] = {
259 [DMAIN] = {
260 .name = "DMAIN",
261 .description = "Main",
262 .enabled = 1, .loglevel = LOGL_DEBUG,
263 },
264};
265
266static const struct log_info log_info = {
267 .cat = default_categories,
268 .num_cat = ARRAY_SIZE(default_categories),
269};
270
271int main(int argc, char **argv)
272{
273 struct log_target *stderr_target;
Max3de97e12016-11-02 10:37:58 +0100274 struct osmo_fsm_inst *finst;
Harald Welte136e7372016-05-29 10:53:17 +0900275
276 osmo_fsm_log_addr(false);
277
278 log_init(&log_info, NULL);
279 stderr_target = log_target_create_stderr();
280 log_add_target(stderr_target);
281 log_set_print_filename(stderr_target, 0);
Harald Welte31c0fef2017-04-16 17:26:30 +0200282 g_ctrl = ctrl_handle_alloc(NULL, NULL, NULL);
Harald Welte136e7372016-05-29 10:53:17 +0900283
Harald Welte4585e672017-04-16 17:23:56 +0200284 g_ctx = NULL;
285 OSMO_ASSERT(osmo_fsm_find_by_name(fsm.name) == NULL);
286 osmo_fsm_register(&fsm);
287 OSMO_ASSERT(osmo_fsm_find_by_name(fsm.name) == &fsm);
288
289 OSMO_ASSERT(osmo_fsm_inst_find_by_name(&fsm, "my_id") == NULL);
Max3de97e12016-11-02 10:37:58 +0100290 finst = foo();
Harald Welte136e7372016-05-29 10:53:17 +0900291
Neels Hofmeyrd8f175c2018-04-09 00:42:22 +0200292 while (main_loop_run) {
Harald Welte136e7372016-05-29 10:53:17 +0900293 osmo_select_main(0);
294 }
Max3de97e12016-11-02 10:37:58 +0100295 osmo_fsm_inst_free(finst);
Neels Hofmeyr975ee6b2018-04-09 00:42:12 +0200296
297 test_id_api();
298
Max8b25a3f2016-11-01 11:02:17 +0100299 osmo_fsm_unregister(&fsm);
Harald Welte136e7372016-05-29 10:53:17 +0900300 exit(0);
301}