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