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