1 /*
2 * Copyright 2024 Glenn Andrews
3 * based on test_lib_hierarchical_smf.c
4 * Copyright 2021 The Chromium OS Authors
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/ztest.h>
10 #include <zephyr/smf.h>
11
12 /*
13 * Hierarchical Test Transition to self:
14 *
15 * This implements a hierarchical state machine using UML rules and demonstrates
16 * initial transitions, transitions to self (in PARENT_C) and preventing event
17 * propagation (in STATE_B)
18 *
19 * The order of entry, exit and run actions is given in the ordering of the test_value[] array.
20 */
21
22 #define TEST_OBJECT(o) ((struct test_object *)o)
23
24 #define SMF_RUN 5
25
26 /* Number of state transitions for each test: */
27 #define TEST_VALUE_NUM 22
28 #define TEST_PARENT_ENTRY_VALUE_NUM 1
29 #define TEST_PARENT_RUN_VALUE_NUM 8
30 #define TEST_PARENT_EXIT_VALUE_NUM 10
31 #define TEST_ENTRY_VALUE_NUM 2
32 #define TEST_RUN_VALUE_NUM 6
33 #define TEST_EXIT_VALUE_NUM 15
34
35 enum test_steps {
36 /* Initial Setup: Testing initial transitions */
37 ROOT_ENTRY = 0,
38 PARENT_AB_ENTRY,
39 STATE_A_ENTRY,
40
41 /* Run 0: normal state transition */
42 STATE_A_RUN,
43 STATE_A_EXIT,
44 STATE_B_ENTRY,
45
46 /* Run 1: Test preventing event propagation */
47 STATE_B_1ST_RUN,
48
49 /* Run 2: Normal state transition via parent */
50 STATE_B_2ND_RUN,
51 PARENT_AB_RUN,
52 STATE_B_EXIT,
53 PARENT_AB_EXIT,
54 PARENT_C_1ST_ENTRY,
55 STATE_C_1ST_ENTRY,
56
57 /* Run 3: PARENT_C executes transition to self */
58 STATE_C_1ST_RUN,
59 PARENT_C_RUN,
60 STATE_C_1ST_EXIT,
61 PARENT_C_1ST_EXIT,
62 PARENT_C_2ND_ENTRY,
63 STATE_C_2ND_ENTRY,
64
65 /* Run 4: Test transition from parent state */
66 STATE_C_2ND_RUN,
67 STATE_C_2ND_EXIT,
68 PARENT_C_2ND_EXIT,
69
70 /* End of run */
71 FINAL_VALUE,
72
73 /* Unused functions: Error checks if set */
74 ROOT_RUN,
75 ROOT_EXIT,
76 };
77
78 /*
79 * Note: Test values are taken before the appropriate test bit for that state is set i.e. if
80 * ROOT_ENTRY_BIT is BIT(0), test_value for root_entry() will be BIT_MASK(0) not BIT_MASK(1)
81 */
82 static uint32_t test_value[] = {
83 /* Initial Setup */
84 BIT_MASK(ROOT_ENTRY),
85 BIT_MASK(PARENT_AB_ENTRY),
86 BIT_MASK(STATE_A_ENTRY),
87 /* Run 0 */
88 BIT_MASK(STATE_A_RUN),
89 BIT_MASK(STATE_A_EXIT),
90 BIT_MASK(STATE_B_ENTRY),
91 /* Run 1 */
92 BIT_MASK(STATE_B_1ST_RUN),
93 /* Run 2 */
94 BIT_MASK(STATE_B_2ND_RUN),
95 BIT_MASK(PARENT_AB_RUN),
96 BIT_MASK(STATE_B_EXIT),
97 BIT_MASK(PARENT_AB_EXIT),
98 BIT_MASK(PARENT_C_1ST_ENTRY),
99 BIT_MASK(STATE_C_1ST_ENTRY),
100 /* Run 3 */
101 BIT_MASK(STATE_C_1ST_RUN),
102 BIT_MASK(PARENT_C_RUN),
103 BIT_MASK(STATE_C_1ST_EXIT),
104 BIT_MASK(PARENT_C_1ST_EXIT),
105 BIT_MASK(PARENT_C_2ND_ENTRY),
106 BIT_MASK(STATE_C_2ND_ENTRY),
107 /* Run 4 */
108 BIT_MASK(STATE_C_2ND_RUN),
109 BIT_MASK(STATE_C_2ND_EXIT),
110 BIT_MASK(PARENT_C_2ND_EXIT),
111 /* Post-run Check */
112 BIT_MASK(FINAL_VALUE),
113 };
114
115 /* Forward declaration of test_states */
116 static const struct smf_state test_states[];
117
118 /* List of all TypeC-level states */
119 enum test_state {
120 ROOT,
121 PARENT_AB,
122 PARENT_C,
123 STATE_A,
124 STATE_B,
125 STATE_C,
126 STATE_D,
127 };
128
129 enum terminate_action {
130 NONE,
131 PARENT_ENTRY,
132 PARENT_RUN,
133 PARENT_EXIT,
134 ENTRY,
135 RUN,
136 EXIT
137 };
138
139 #define B_ENTRY_FIRST_TIME BIT(0)
140 #define B_RUN_FIRST_TIME BIT(1)
141 #define PARENT_C_ENTRY_FIRST_TIME BIT(2)
142 #define C_RUN_FIRST_TIME BIT(3)
143 #define C_ENTRY_FIRST_TIME BIT(4)
144 #define C_EXIT_FIRST_TIME BIT(5)
145
146 #define FIRST_TIME_BITS \
147 (B_ENTRY_FIRST_TIME | B_RUN_FIRST_TIME | PARENT_C_ENTRY_FIRST_TIME | C_RUN_FIRST_TIME | \
148 C_ENTRY_FIRST_TIME | C_EXIT_FIRST_TIME)
149
150 static struct test_object {
151 struct smf_ctx ctx;
152 uint32_t transition_bits;
153 uint32_t tv_idx;
154 enum terminate_action terminate;
155 uint32_t first_time;
156 } test_obj;
157
root_entry(void * obj)158 static void root_entry(void *obj)
159 {
160 struct test_object *o = TEST_OBJECT(obj);
161
162 o->tv_idx = 0;
163
164 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root entry failed");
165
166 o->transition_bits |= BIT(ROOT_ENTRY);
167 }
168
root_run(void * obj)169 static enum smf_state_result root_run(void *obj)
170 {
171 struct test_object *o = TEST_OBJECT(obj);
172
173 o->tv_idx++;
174
175 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root run failed");
176
177 o->transition_bits |= BIT(ROOT_RUN);
178
179 /* Return to parent run state */
180 return SMF_EVENT_PROPAGATE;
181 }
182
root_exit(void * obj)183 static void root_exit(void *obj)
184 {
185 struct test_object *o = TEST_OBJECT(obj);
186
187 o->tv_idx++;
188
189 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root exit failed");
190 o->transition_bits |= BIT(ROOT_EXIT);
191 }
192
parent_ab_entry(void * obj)193 static void parent_ab_entry(void *obj)
194 {
195 struct test_object *o = TEST_OBJECT(obj);
196
197 o->tv_idx++;
198
199 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB entry failed");
200
201 if (o->terminate == PARENT_ENTRY) {
202 smf_set_terminate(obj, -1);
203 return;
204 }
205
206 o->transition_bits |= BIT(PARENT_AB_ENTRY);
207 }
208
parent_ab_run(void * obj)209 static enum smf_state_result parent_ab_run(void *obj)
210 {
211 struct test_object *o = TEST_OBJECT(obj);
212
213 o->tv_idx++;
214
215 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB run failed");
216
217 if (o->terminate == PARENT_RUN) {
218 smf_set_terminate(obj, -1);
219 return SMF_EVENT_PROPAGATE;
220 }
221
222 o->transition_bits |= BIT(PARENT_AB_RUN);
223
224 smf_set_state(SMF_CTX(obj), &test_states[STATE_C]);
225 return SMF_EVENT_HANDLED;
226 }
227
parent_ab_exit(void * obj)228 static void parent_ab_exit(void *obj)
229 {
230 struct test_object *o = TEST_OBJECT(obj);
231
232 o->tv_idx++;
233
234 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB exit failed");
235
236 if (o->terminate == PARENT_EXIT) {
237 smf_set_terminate(obj, -1);
238 return;
239 }
240
241 o->transition_bits |= BIT(PARENT_AB_EXIT);
242 }
243
parent_c_entry(void * obj)244 static void parent_c_entry(void *obj)
245 {
246 struct test_object *o = TEST_OBJECT(obj);
247
248 o->tv_idx++;
249
250 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C entry failed");
251 if (o->first_time & PARENT_C_ENTRY_FIRST_TIME) {
252 o->first_time &= ~PARENT_C_ENTRY_FIRST_TIME;
253 o->transition_bits |= BIT(PARENT_C_1ST_ENTRY);
254 } else {
255 o->transition_bits |= BIT(PARENT_C_2ND_ENTRY);
256 }
257 }
258
parent_c_run(void * obj)259 static enum smf_state_result parent_c_run(void *obj)
260 {
261 struct test_object *o = TEST_OBJECT(obj);
262
263 o->tv_idx++;
264
265 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C run failed");
266
267 o->transition_bits |= BIT(PARENT_C_RUN);
268
269 smf_set_state(SMF_CTX(obj), &test_states[PARENT_C]);
270
271 return SMF_EVENT_PROPAGATE;
272 }
273
parent_c_exit(void * obj)274 static void parent_c_exit(void *obj)
275 {
276 struct test_object *o = TEST_OBJECT(obj);
277
278 o->tv_idx++;
279
280 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C exit failed");
281
282 if (o->first_time & B_ENTRY_FIRST_TIME) {
283 o->first_time &= ~B_ENTRY_FIRST_TIME;
284 o->transition_bits |= BIT(PARENT_C_1ST_EXIT);
285 } else {
286 o->transition_bits |= BIT(PARENT_C_2ND_EXIT);
287 }
288 }
289
state_a_entry(void * obj)290 static void state_a_entry(void *obj)
291 {
292 struct test_object *o = TEST_OBJECT(obj);
293
294 o->tv_idx++;
295
296 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A entry failed");
297
298 if (o->terminate == ENTRY) {
299 smf_set_terminate(obj, -1);
300 return;
301 }
302
303 o->transition_bits |= BIT(STATE_A_ENTRY);
304 }
305
state_a_run(void * obj)306 static enum smf_state_result state_a_run(void *obj)
307 {
308 struct test_object *o = TEST_OBJECT(obj);
309
310 o->tv_idx++;
311
312 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A run failed");
313
314 o->transition_bits |= BIT(STATE_A_RUN);
315
316 smf_set_state(SMF_CTX(obj), &test_states[STATE_B]);
317 return SMF_EVENT_PROPAGATE;
318 }
319
state_a_exit(void * obj)320 static void state_a_exit(void *obj)
321 {
322 struct test_object *o = TEST_OBJECT(obj);
323
324 o->tv_idx++;
325
326 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A exit failed");
327 o->transition_bits |= BIT(STATE_A_EXIT);
328 }
329
state_b_entry(void * obj)330 static void state_b_entry(void *obj)
331 {
332 struct test_object *o = TEST_OBJECT(obj);
333
334 o->tv_idx++;
335
336 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B entry failed");
337
338 o->transition_bits |= BIT(STATE_B_ENTRY);
339 }
340
state_b_run(void * obj)341 static enum smf_state_result state_b_run(void *obj)
342 {
343 struct test_object *o = TEST_OBJECT(obj);
344
345 o->tv_idx++;
346
347 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B run failed");
348
349 if (o->terminate == RUN) {
350 smf_set_terminate(obj, -1);
351 return SMF_EVENT_PROPAGATE;
352 }
353
354 if (o->first_time & B_RUN_FIRST_TIME) {
355 o->first_time &= ~B_RUN_FIRST_TIME;
356 o->transition_bits |= BIT(STATE_B_1ST_RUN);
357 return SMF_EVENT_HANDLED;
358 } else {
359 o->transition_bits |= BIT(STATE_B_2ND_RUN);
360 /* bubble up to PARENT_AB */
361 }
362 return SMF_EVENT_PROPAGATE;
363 }
364
state_b_exit(void * obj)365 static void state_b_exit(void *obj)
366 {
367 struct test_object *o = TEST_OBJECT(obj);
368
369 o->tv_idx++;
370
371 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B exit failed");
372
373 o->transition_bits |= BIT(STATE_B_EXIT);
374 }
375
state_c_entry(void * obj)376 static void state_c_entry(void *obj)
377 {
378 struct test_object *o = TEST_OBJECT(obj);
379
380 o->tv_idx++;
381
382 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C entry failed");
383 if (o->first_time & C_ENTRY_FIRST_TIME) {
384 o->first_time &= ~C_ENTRY_FIRST_TIME;
385 o->transition_bits |= BIT(STATE_C_1ST_ENTRY);
386 } else {
387 o->transition_bits |= BIT(STATE_C_2ND_ENTRY);
388 }
389 }
390
state_c_run(void * obj)391 static enum smf_state_result state_c_run(void *obj)
392 {
393 struct test_object *o = TEST_OBJECT(obj);
394
395 o->tv_idx++;
396
397 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C run failed");
398
399 if (o->first_time & C_RUN_FIRST_TIME) {
400 o->first_time &= ~C_RUN_FIRST_TIME;
401 o->transition_bits |= BIT(STATE_C_1ST_RUN);
402 /* Do nothing, Let parent handle it */
403 } else {
404 o->transition_bits |= BIT(STATE_C_2ND_RUN);
405 smf_set_state(SMF_CTX(obj), &test_states[STATE_D]);
406 }
407 return SMF_EVENT_PROPAGATE;
408 }
409
state_c_exit(void * obj)410 static void state_c_exit(void *obj)
411 {
412 struct test_object *o = TEST_OBJECT(obj);
413
414 o->tv_idx++;
415
416 zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C exit failed");
417
418 if (o->terminate == EXIT) {
419 smf_set_terminate(obj, -1);
420 return;
421 }
422
423 if (o->first_time & C_EXIT_FIRST_TIME) {
424 o->first_time &= ~C_EXIT_FIRST_TIME;
425 o->transition_bits |= BIT(STATE_C_1ST_EXIT);
426 } else {
427 o->transition_bits |= BIT(STATE_C_2ND_EXIT);
428 }
429 }
430
state_d_entry(void * obj)431 static void state_d_entry(void *obj)
432 {
433 struct test_object *o = TEST_OBJECT(obj);
434
435 o->tv_idx++;
436 }
437
state_d_run(void * obj)438 static enum smf_state_result state_d_run(void *obj)
439 {
440 /* Do nothing */
441 return SMF_EVENT_PROPAGATE;
442 }
443
state_d_exit(void * obj)444 static void state_d_exit(void *obj)
445 {
446 /* Do nothing */
447 }
448
449 static const struct smf_state test_states[] = {
450 [ROOT] = SMF_CREATE_STATE(root_entry, root_run, root_exit, NULL, &test_states[PARENT_AB]),
451 [PARENT_AB] = SMF_CREATE_STATE(parent_ab_entry, parent_ab_run, parent_ab_exit,
452 &test_states[ROOT], &test_states[STATE_A]),
453 [PARENT_C] = SMF_CREATE_STATE(parent_c_entry, parent_c_run, parent_c_exit,
454 &test_states[ROOT], &test_states[STATE_C]),
455 [STATE_A] = SMF_CREATE_STATE(state_a_entry, state_a_run, state_a_exit,
456 &test_states[PARENT_AB], NULL),
457 [STATE_B] = SMF_CREATE_STATE(state_b_entry, state_b_run, state_b_exit,
458 &test_states[PARENT_AB], NULL),
459 [STATE_C] = SMF_CREATE_STATE(state_c_entry, state_c_run, state_c_exit,
460 &test_states[PARENT_C], NULL),
461 [STATE_D] = SMF_CREATE_STATE(state_d_entry, state_d_run, state_d_exit, &test_states[ROOT],
462 NULL),
463 };
464
ZTEST(smf_tests,test_smf_self_transition)465 ZTEST(smf_tests, test_smf_self_transition)
466 {
467 /* A) Test state transitions */
468
469 test_obj.transition_bits = 0;
470 test_obj.first_time = FIRST_TIME_BITS;
471 test_obj.terminate = NONE;
472 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
473
474 for (int i = 0; i < SMF_RUN; i++) {
475 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
476 break;
477 }
478 }
479
480 zassert_equal(TEST_VALUE_NUM, test_obj.tv_idx, "Incorrect test value index");
481 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
482 "Final state not reached");
483
484 /* B) Test termination in parent entry action */
485
486 test_obj.transition_bits = 0;
487 test_obj.first_time = FIRST_TIME_BITS;
488 test_obj.terminate = PARENT_ENTRY;
489 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
490
491 for (int i = 0; i < SMF_RUN; i++) {
492 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
493 break;
494 }
495 }
496
497 zassert_equal(TEST_PARENT_ENTRY_VALUE_NUM, test_obj.tv_idx,
498 "Incorrect test value index for parent entry termination");
499 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
500 "Final parent entry termination state not reached");
501
502 /* C) Test termination in parent run action */
503
504 test_obj.transition_bits = 0;
505 test_obj.first_time = FIRST_TIME_BITS;
506 test_obj.terminate = PARENT_RUN;
507 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
508
509 for (int i = 0; i < SMF_RUN; i++) {
510 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
511 break;
512 }
513 }
514
515 zassert_equal(TEST_PARENT_RUN_VALUE_NUM, test_obj.tv_idx,
516 "Incorrect test value index for parent run termination");
517 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
518 "Final parent run termination state not reached");
519
520 /* D) Test termination in parent exit action */
521
522 test_obj.transition_bits = 0;
523 test_obj.first_time = FIRST_TIME_BITS;
524 test_obj.terminate = PARENT_EXIT;
525 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
526
527 for (int i = 0; i < SMF_RUN; i++) {
528 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
529 break;
530 }
531 }
532
533 zassert_equal(TEST_PARENT_EXIT_VALUE_NUM, test_obj.tv_idx,
534 "Incorrect test value index for parent exit termination");
535 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
536 "Final parent exit termination state not reached");
537
538 /* E) Test termination in child entry action */
539
540 test_obj.transition_bits = 0;
541 test_obj.first_time = FIRST_TIME_BITS;
542 test_obj.terminate = ENTRY;
543 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
544
545 for (int i = 0; i < SMF_RUN; i++) {
546 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
547 break;
548 }
549 }
550
551 zassert_equal(TEST_ENTRY_VALUE_NUM, test_obj.tv_idx,
552 "Incorrect test value index for entry termination");
553 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
554 "Final entry termination state not reached");
555
556 /* F) Test termination in child run action */
557
558 test_obj.transition_bits = 0;
559 test_obj.first_time = FIRST_TIME_BITS;
560 test_obj.terminate = RUN;
561 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
562
563 for (int i = 0; i < SMF_RUN; i++) {
564 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
565 break;
566 }
567 }
568
569 zassert_equal(TEST_RUN_VALUE_NUM, test_obj.tv_idx,
570 "Incorrect test value index for run termination");
571 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
572 "Final run termination state not reached");
573
574 /* G) Test termination in child exit action */
575
576 test_obj.transition_bits = 0;
577 test_obj.first_time = FIRST_TIME_BITS;
578 test_obj.terminate = EXIT;
579 smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]);
580
581 for (int i = 0; i < SMF_RUN; i++) {
582 if (smf_run_state((struct smf_ctx *)&test_obj) < 0) {
583 break;
584 }
585 }
586
587 zassert_equal(TEST_EXIT_VALUE_NUM, test_obj.tv_idx,
588 "Incorrect test value index for exit termination");
589 zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
590 "Final exit termination state not reached");
591 }
592