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