diff --git a/doc/releases/migration-guide-3.7.rst b/doc/releases/migration-guide-3.7.rst index 4c1fccd2e3a..cc9a8b1bce7 100644 --- a/doc/releases/migration-guide-3.7.rst +++ b/doc/releases/migration-guide-3.7.rst @@ -493,6 +493,14 @@ State Machine Framework now independent of the values of :kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` and :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION`. If the additional arguments are not used, they have to be set to ``NULL``. (:github:`71250`) +* SMF now follows a more UML-like transition flow when the transition source is a parent of the + state called by :c:func:`smf_run_state`. Exit actions up to (but not including) the Least Common + Ancestor of the transition source and target state will be executed, as will entry actions from + (but not including) the LCA down to the target state. (:github:`71675`) +* Previously, calling :c:func:`smf_set_state` with a ``new_state`` set to NULL would execute all + exit actions from the current state to the topmost parent, with the expectation the topmost exit + action would terminate the state machine. Passing ``NULL`` is now not allowed. Instead create a + 'terminate' state at the top level, and call :c:func:`smf_set_terminate` from its entry action. ZBus ==== diff --git a/doc/releases/release-notes-3.7.rst b/doc/releases/release-notes-3.7.rst index b460b1c13bd..aff99963d78 100644 --- a/doc/releases/release-notes-3.7.rst +++ b/doc/releases/release-notes-3.7.rst @@ -375,6 +375,9 @@ Libraries / Subsystems * State Machine Framework * The :c:macro:`SMF_CREATE_STATE` macro now always takes 5 arguments. + * Transition sources that are parents of the state that was run now choose the correct Least + Common Ancestor for executing Exit and Entry Actions. + * Passing ``NULL`` to :c:func:`smf_set_state` is now not allowed. * Storage diff --git a/doc/services/smf/index.rst b/doc/services/smf/index.rst index 169ee507ec4..9b5ee62af76 100644 --- a/doc/services/smf/index.rst +++ b/doc/services/smf/index.rst @@ -39,7 +39,7 @@ By default, a state can have no ancestor states, resulting in a flat state machine. But to enable the creation of a hierarchical state machine, the :kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` option must be enabled. -By default, the hierarchical state machine does not support initial transitions +By default, the hierarchical state machines do not support initial transitions to child states on entering a superstate. To enable them the :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` option must be enabled. @@ -87,31 +87,29 @@ from parent state S0 to child state S2:: }; To set the initial state, the :c:func:`smf_set_initial` function should be -called. It has the following prototype: -``void smf_set_initial(smf_ctx *ctx, smf_state *state)`` +called. To transition from one state to another, the :c:func:`smf_set_state` -function is used and it has the following prototype: -``void smf_set_state(smf_ctx *ctx, smf_state *state)`` +function is used. .. note:: If :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` is not set, :c:func:`smf_set_initial` and :c:func:`smf_set_state` function should not be passed a parent state as the parent state does not know which child state to transition to. Transitioning to a parent state is OK if an initial transition to a child state is defined. A well-formed - HSM will have initial transitions defined for all parent states. + HSM should have initial transitions defined for all parent states. -.. note:: While the state machine is running, smf_set_state should only be - called from the Entry and Run functions. Calling smf_set_state from the - Exit functions doesn't make sense and will generate a warning. +.. note:: While the state machine is running, :c:func:`smf_set_state` should + only be called from the Entry or Run function. Calling + :c:func:`smf_set_state` from Exit functions will generate a warning in the + log and no transition will occur. State Machine Execution ======================= To run the state machine, the :c:func:`smf_run_state` function should be called in some application dependent way. An application should cease calling -smf_run_state if it returns a non-zero value. The function has the following -prototype: ``int32_t smf_run_state(smf_ctx *ctx)`` +smf_run_state if it returns a non-zero value. Preventing Parent Run Actions ============================= @@ -124,13 +122,38 @@ State Machine Termination ========================= To terminate the state machine, the :c:func:`smf_set_terminate` function -should be called. It can be called from the entry, run, or exit action. The -function takes a non-zero user defined value that's returned by the -:c:func:`smf_run_state` function. The function has the following prototype: -``void smf_set_terminate(smf_ctx *ctx, int32_t val)`` +should be called. It can be called from the entry, run, or exit actions. The +function takes a non-zero user defined value that will be returned by the +:c:func:`smf_run_state` function. + +UML State Machines +================== + +SMF follows UML hierarchical state machine rules for transitions i.e., the +entry and exit actions of the least common ancestor are not executed on +transition, unless said transition is a transition to self. + +The UML Specification for StateMachines may be found in chapter 14 of the UML +specification available here: https://www.omg.org/spec/UML/ + +SMF breaks from UML rules in: + +1. Executing the actions associated with the transition within the context + of the source state, rather than after the exit actions are performed. +2. Only allowing external transitions to self, not to sub-states. A transition + from a superstate to a child state is treated as a local transition. +3. Prohibiting transitions using :c:func:`smf_set_state` in exit actions. + +SMF also does not provide any pseudostates except the Initial Pseudostate. +Terminate pseudostates can be modelled by calling :c:func:`smf_set_terminate` +from the entry action of a 'terminate' state. Orthogonal regions are modelled +by calling :c:func:`smf_run_state` for each region. + +State Machine Examples +====================== Flat State Machine Example -========================== +************************** This example turns the following state diagram into code using the SMF, where the initial state is S0. @@ -232,7 +255,7 @@ Code:: } Hierarchical State Machine Example -================================== +********************************** This example turns the following state diagram into code using the SMF, where S0 and S1 share a parent state and S0 is the initial state. @@ -342,17 +365,14 @@ When designing hierarchical state machines, the following should be considered: re-execute the ancestor\'s entry action or execute the exit action. For example, the parent_entry function is not called when transitioning from S0 to S1, nor is the parent_exit function called. - - Ancestor exit actions are executed after the sibling exit actions. For - example, the s1_exit function is called before the parent_exit function - is called. + - Ancestor exit actions are executed after the exit action of the current + state. For example, the s1_exit function is called before the parent_exit + function is called. - The parent_run function only executes if the child_run function does not call either :c:func:`smf_set_state` or :c:func:`smf_set_handled`. - - Transitions to self in super-states containing sub-states are not supported. - Transitions to self from the most-nested child state are supported and will - call the exit and entry function of the child state correctly. Event Driven State Machine Example -================================== +********************************** Events are not explicitly part of the State Machine Framework but an event driven state machine can be implemented using Zephyr :ref:`events`. @@ -499,47 +519,55 @@ Code:: } } -Hierarchical State Machine Example With Initial Transitions -=========================================================== +State Machine Example With Initial Transitions And Transition To Self +********************************************************************* + +:zephyr_file:`tests/lib/smf/src/test_lib_self_transition_smf.c` defines a state +machine for testing the initial transitions and transitions to self in a parent +state. The statechart for this test is below. -:zephyr_file:`tests/lib/smf/src/test_lib_initial_transitions_smf.c` defines -a state machine for testing initial transitions and :c:func:`smf_set_handled`. -The statechart for this test is below. .. graphviz:: - :caption: Test state machine for initial transitions and ``smf_set_handled`` + :caption: Test state machine for UML State Transitions digraph smf_hierarchical_initial { compound=true; node [style = rounded]; - smf_set_initial [shape=plaintext]; + "smf_set_initial()" [shape=plaintext fontname=Courier]; ab_init_state [shape = point]; STATE_A [shape = box]; STATE_B [shape = box]; STATE_C [shape = box]; STATE_D [shape = box]; + DC[shape=point height=0 width=0 label=<>] - subgraph cluster_ab { - label = "PARENT_AB"; + subgraph cluster_root { + label = "ROOT"; style = rounded; - ab_init_state -> STATE_A; - STATE_A -> STATE_B; + + subgraph cluster_ab { + label = "PARENT_AB"; + style = rounded; + ab_init_state -> STATE_A; + STATE_A -> STATE_B; + } + + subgraph cluster_c { + label = "PARENT_C"; + style = rounded; + STATE_B -> STATE_C [ltail=cluster_ab] + } + + STATE_C -> DC [ltail=cluster_c, dir=none]; + DC -> STATE_C [lhead=cluster_c]; + STATE_C -> STATE_D } - subgraph cluster_c { - label = "PARENT_C"; - style = rounded; - STATE_C -> STATE_C - } - - smf_set_initial -> STATE_A [lhead=cluster_ab] - STATE_B -> STATE_C - STATE_C -> STATE_D + "smf_set_initial()" -> STATE_A [lhead=cluster_ab] } - API Reference -************* +============= .. doxygengroup:: smf diff --git a/include/zephyr/smf.h b/include/zephyr/smf.h index ba019127264..5b5f303630d 100644 --- a/include/zephyr/smf.h +++ b/include/zephyr/smf.h @@ -18,26 +18,27 @@ /** * @brief State Machine Framework API * @defgroup smf State Machine Framework API + * @version 0.1.0 * @ingroup os_services * @{ */ /** - * @brief Macro to create a hierarchical state. + * @brief Macro to create a hierarchical state with initial transitions. * - * @param _entry State entry function - * @param _run State run function - * @param _exit State exit function + * @param _entry State entry function or NULL + * @param _run State run function or NULL + * @param _exit State exit function or NULL * @param _parent State parent object or NULL * @param _initial State initial transition object or NULL */ -#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ -{ \ - .entry = _entry, \ - .run = _run, \ - .exit = _exit, \ - IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,)) \ - IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,)) \ +#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ +{ \ + .entry = _entry, \ + .run = _run, \ + .exit = _exit, \ + IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,)) \ + IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,)) \ } /** @@ -72,6 +73,7 @@ struct smf_state { const state_execution run; /** Optional method that will be run when this state exists */ const state_execution exit; +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT /** * Optional parent state that contains common entry/run/exit * implementation among various child states. @@ -79,8 +81,8 @@ struct smf_state { * run: Parent function executes AFTER child function. * exit: Parent function executes AFTER child function. * - * Note: When transitioning between two child states with a shared parent, - * that parent's exit and entry functions do not execute. + * Note: When transitioning between two child states with a shared + * parent, that parent's exit and entry functions do not execute. */ const struct smf_state *parent; @@ -89,7 +91,8 @@ struct smf_state { * Optional initial transition state. NULL for leaf states. */ const struct smf_state *initial; -#endif +#endif /* CONFIG_SMF_INITIAL_TRANSITION */ +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ }; /** Defines the current context of the state machine. */ @@ -98,6 +101,11 @@ struct smf_ctx { const struct smf_state *current; /** Previous state the state machine executed */ const struct smf_state *previous; + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + /** Currently executing state (which may be a parent) */ + const struct smf_state *executing; +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ /** * This value is set by the set_terminate function and * should terminate the state machine when its set to a @@ -122,8 +130,8 @@ void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state); /** * @brief Changes a state machines state. This handles exiting the previous - * state and entering the target state. A common parent state will not - * exited nor be re-entered. + * state and entering the target state. For HSMs the entry and exit + * actions of the Least Common Ancestor will not be run. * * @param ctx State machine context * @param new_state State to transition to (NULL is valid and exits all states) diff --git a/lib/smf/smf.c b/lib/smf/smf.c index ecc10740744..fb0c95c574f 100644 --- a/lib/smf/smf.c +++ b/lib/smf/smf.c @@ -9,24 +9,22 @@ #include LOG_MODULE_REGISTER(smf); -/* - * Private structure (to this file) used to track state machine context. - * The structure is not used directly, but instead to cast the "internal" - * member of the smf_ctx structure. +/** + * @brief Private structure (to this file) used to track state machine context. + * The structure is not used directly, but instead to cast the "internal" + * member of the smf_ctx structure. */ struct internal_ctx { - bool new_state : 1; - bool terminate : 1; - bool exit : 1; - bool handled : 1; + bool new_state: 1; + bool terminate: 1; + bool is_exit: 1; + bool handled: 1; }; -static bool share_paren(const struct smf_state *test_state, - const struct smf_state *target_state) +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT +static bool share_paren(const struct smf_state *test_state, const struct smf_state *target_state) { - for (const struct smf_state *state = test_state; - state != NULL; - state = state->parent) { + for (const struct smf_state *state = test_state; state != NULL; state = state->parent) { if (target_state == state) { return true; } @@ -35,21 +33,10 @@ static bool share_paren(const struct smf_state *test_state, return false; } -static bool last_state_share_paren(struct smf_ctx *const ctx, - const struct smf_state *state) -{ - /* Get parent state of previous state */ - if (!ctx->previous) { - return false; - } - - return share_paren(ctx->previous->parent, state); -} - static const struct smf_state *get_child_of(const struct smf_state *states, const struct smf_state *parent) { - for (const struct smf_state *tmp = states; ; tmp = tmp->parent) { + for (const struct smf_state *tmp = states;; tmp = tmp->parent) { if (tmp->parent == parent) { return tmp; } @@ -68,22 +55,55 @@ static const struct smf_state *get_last_of(const struct smf_state *states) } /** - * @brief Execute all ancestor entry actions + * @brief Find the Least Common Ancestor (LCA) of two states + * + * @param source transition source + * @param dest transition destination + * @return LCA state, or NULL if states have no LCA. + */ +static const struct smf_state *get_lca_of(const struct smf_state *source, + const struct smf_state *dest) +{ + for (const struct smf_state *ancestor = source->parent; ancestor != NULL; + ancestor = ancestor->parent) { + if (ancestor == dest) { + return ancestor->parent; + } else if (share_paren(dest, ancestor)) { + return ancestor; + } + } + + return NULL; +} + +/** + * @brief Executes all entry actions from the direct child of topmost to the new state * * @param ctx State machine context - * @param target The entry actions of this target's ancestors are executed + * @param new_state State we are transitioning to + * @param topmost State we are entering from. Its entry action is not executed * @return true if the state machine should terminate, else false */ -__unused static bool smf_execute_ancestor_entry_actions( - struct smf_ctx *const ctx, const struct smf_state *target) +static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx, + const struct smf_state *new_state, + const struct smf_state *topmost) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; - for (const struct smf_state *to_execute = get_last_of(target); - to_execute != NULL && to_execute != target; - to_execute = get_child_of(target, to_execute)) { - /* Execute parent state's entry */ - if (!last_state_share_paren(ctx, to_execute) && to_execute->entry) { + if (new_state == topmost) { + /* There are no child states, so do nothing */ + return false; + } + + for (const struct smf_state *to_execute = get_child_of(new_state, topmost); + to_execute != NULL && to_execute != new_state; + to_execute = get_child_of(new_state, to_execute)) { + /* Execute every entry action EXCEPT that of the topmost state */ + if (to_execute->entry) { + /* Keep track of the executing entry action in case it calls + * smf_set_State() + */ + ctx->executing = to_execute; to_execute->entry(ctx); /* No need to continue if terminate was set */ @@ -93,6 +113,16 @@ __unused static bool smf_execute_ancestor_entry_actions( } } + /* and execute the new state entry action */ + if (new_state->entry) { + new_state->entry(ctx); + + /* No need to continue if terminate was set */ + if (internal->terminate) { + return true; + } + } + return false; } @@ -103,9 +133,9 @@ __unused static bool smf_execute_ancestor_entry_actions( * @param target The run actions of this target's ancestors are executed * @return true if the state machine should terminate, else false */ -__unused static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) +static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; /* Execute all run actions in reverse order */ /* Return if the current state switched states */ @@ -126,9 +156,10 @@ __unused static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) } /* Try to run parent run actions */ - for (const struct smf_state *tmp_state = ctx->current->parent; - tmp_state != NULL; + for (const struct smf_state *tmp_state = ctx->current->parent; tmp_state != NULL; tmp_state = tmp_state->parent) { + /* Keep track of where we are in case an ancestor calls smf_set_state() */ + ctx->executing = tmp_state; /* Execute parent run action */ if (tmp_state->run) { tmp_state->run(ctx); @@ -156,39 +187,35 @@ __unused static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) } /** - * @brief Execute all ancestor exit actions + * @brief Executes all exit actions from ctx->current to the direct child of topmost * * @param ctx State machine context - * @param target The exit actions of this target's ancestors are executed + * @param topmost State we are exiting to. Its exit action is not executed * @return true if the state machine should terminate, else false */ -__unused static bool smf_execute_ancestor_exit_actions( - struct smf_ctx *const ctx, const struct smf_state *target) +static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct smf_state *topmost) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; - /* Execute all parent exit actions in reverse order */ + for (const struct smf_state *to_execute = ctx->current; to_execute != topmost; + to_execute = to_execute->parent) { + if (to_execute->exit) { + to_execute->exit(ctx); - for (const struct smf_state *tmp_state = ctx->current->parent; - tmp_state != NULL; - tmp_state = tmp_state->parent) { - if ((target == NULL || !share_paren(target->parent, tmp_state)) && - tmp_state->exit) { - tmp_state->exit(ctx); - - /* No need to continue if terminate was set */ + /* No need to continue if terminate was set in the exit action */ if (internal->terminate) { return true; } } } + return false; } +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state) { - struct internal_ctx * const internal = (void *) &ctx->internal; - + struct internal_ctx *const internal = (void *)&ctx->internal; #ifdef CONFIG_SMF_INITIAL_TRANSITION /* @@ -199,98 +226,150 @@ void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state) init_state = init_state->initial; } #endif - internal->exit = false; + + internal->is_exit = false; internal->terminate = false; ctx->current = init_state; ctx->previous = NULL; ctx->terminate_val = 0; - if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) { - internal->new_state = false; +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + ctx->executing = init_state; + const struct smf_state *topmost = get_last_of(init_state); - if (smf_execute_ancestor_entry_actions(ctx, init_state)) { + /* Execute topmost state entry action, since smf_execute_all_entry_actions() + * doesn't + */ + if (topmost->entry) { + topmost->entry(ctx); + if (internal->terminate) { + /* No need to continue if terminate was set */ return; } } - /* Now execute the initial state's entry action */ + if (smf_execute_all_entry_actions(ctx, init_state, topmost)) { + /* No need to continue if terminate was set */ + return; + } +#else + /* execute entry action if it exists */ if (init_state->entry) { init_state->entry(ctx); } +#endif } -void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *target) +void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *new_state) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; - /* - * It does not make sense to call set_state in an exit phase of a state - * since we are already in a transition; we would always ignore the - * intended state to transition into. - */ - if (internal->exit) { - LOG_WRN("Calling %s from exit action", __func__); + if (new_state == NULL) { + LOG_ERR("new_state cannot be NULL"); return; } - internal->exit = true; + /* + * It does not make sense to call smf_set_state in an exit phase of a state + * since we are already in a transition; we would always ignore the + * intended state to transition into. + */ + if (internal->is_exit) { + LOG_ERR("Calling %s from exit action", __func__); + return; + } - /* Execute the current states exit action */ - if (ctx->current->exit) { - ctx->current->exit(ctx); +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + const struct smf_state *topmost; - /* - * No need to continue if terminate was set in the - * exit action - */ + if (share_paren(ctx->executing, new_state)) { + /* new state is a parent of where we are now*/ + topmost = new_state; + } else if (share_paren(new_state, ctx->executing)) { + /* we are a parent of the new state */ + topmost = ctx->executing; + } else { + /* not directly related, find LCA */ + topmost = get_lca_of(ctx->executing, new_state); + } + + internal->is_exit = true; + internal->new_state = true; + + /* call all exit actions up to (but not including) the topmost */ + if (smf_execute_all_exit_actions(ctx, topmost)) { + /* No need to continue if terminate was set in the exit action */ + return; + } + + /* if self-transition, call the exit action */ + if ((ctx->executing == new_state) && (new_state->exit)) { + new_state->exit(ctx); + + /* No need to continue if terminate was set in the exit action */ if (internal->terminate) { return; } } - if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) { - internal->new_state = true; + internal->is_exit = false; - if (smf_execute_ancestor_exit_actions(ctx, target)) { + /* if self transition, call the entry action */ + if ((ctx->executing == new_state) && (new_state->entry)) { + new_state->entry(ctx); + + /* No need to continue if terminate was set in the entry action */ + if (internal->terminate) { return; } } - - internal->exit = false; - #ifdef CONFIG_SMF_INITIAL_TRANSITION /* * The final target will be the deepest leaf state that * the target contains. Set that as the real target. */ - while (target->initial) { - target = target->initial; + while (new_state->initial) { + new_state = new_state->initial; } #endif /* update the state variables */ ctx->previous = ctx->current; - ctx->current = target; + ctx->current = new_state; - if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) { - if (smf_execute_ancestor_entry_actions(ctx, target)) { + /* call all entry actions (except those of topmost) */ + if (smf_execute_all_entry_actions(ctx, new_state, topmost)) { + /* No need to continue if terminate was set in the entry action */ + return; + } +#else + /* Flat state machines have a very simple transition: */ + if (ctx->current->exit) { + internal->is_exit = true; + ctx->current->exit(ctx); + /* No need to continue if terminate was set in the exit action */ + if (internal->terminate) { + return; + } + internal->is_exit = false; + } + /* update the state variables */ + ctx->previous = ctx->current; + ctx->current = new_state; + + if (ctx->current->entry) { + ctx->current->entry(ctx); + /* No need to continue if terminate was set in the entry action */ + if (internal->terminate) { return; } } - - /* Now execute the target entry action */ - if (ctx->current->entry) { - ctx->current->entry(ctx); - /* - * If terminate was set, it will be handled in the - * smf_run_state function - */ - } +#endif } void smf_set_terminate(struct smf_ctx *ctx, int32_t val) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; internal->terminate = true; ctx->terminate_val = val; @@ -305,22 +384,25 @@ void smf_set_handled(struct smf_ctx *ctx) int32_t smf_run_state(struct smf_ctx *const ctx) { - struct internal_ctx * const internal = (void *) &ctx->internal; + struct internal_ctx *const internal = (void *)&ctx->internal; /* No need to continue if terminate was set */ if (internal->terminate) { return ctx->terminate_val; } +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + ctx->executing = ctx->current; +#endif + if (ctx->current->run) { ctx->current->run(ctx); } - if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) { - if (smf_execute_ancestor_run_actions(ctx)) { - return ctx->terminate_val; - } +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + if (smf_execute_ancestor_run_actions(ctx)) { + return ctx->terminate_val; } - +#endif return 0; } diff --git a/tests/lib/smf/CMakeLists.txt b/tests/lib/smf/CMakeLists.txt index 08844e75aa6..3e793f98442 100644 --- a/tests/lib/smf/CMakeLists.txt +++ b/tests/lib/smf/CMakeLists.txt @@ -7,7 +7,7 @@ project(smf) target_sources(app PRIVATE src/main.c) if(CONFIG_SMF_INITIAL_TRANSITION) - target_sources(app PRIVATE src/test_lib_initial_transitions_smf.c) + target_sources(app PRIVATE src/test_lib_self_transition_smf.c) elseif(CONFIG_SMF_ANCESTOR_SUPPORT) target_sources(app PRIVATE src/test_lib_hierarchical_smf.c src/test_lib_hierarchical_5_ancestor_smf.c) diff --git a/tests/lib/smf/src/test_lib_initial_transitions_smf.c b/tests/lib/smf/src/test_lib_self_transition_smf.c similarity index 51% rename from tests/lib/smf/src/test_lib_initial_transitions_smf.c rename to tests/lib/smf/src/test_lib_self_transition_smf.c index 2e00dc2d553..c78e6fa04a5 100644 --- a/tests/lib/smf/src/test_lib_initial_transitions_smf.c +++ b/tests/lib/smf/src/test_lib_self_transition_smf.c @@ -1,6 +1,7 @@ /* * Copyright 2024 Glenn Andrews * based on test_lib_hierarchical_smf.c + * Copyright 2021 The Chromium OS Authors * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,82 +10,100 @@ #include /* - * Hierarchical Test Transition: + * Hierarchical Test Transition to self: * - * PARENT_AB_ENTRY --> A_ENTRY --> A_RUN --> PARENT_AB_RUN ---| - * | - * |----------------------------------------------------------| - * | - * |--> A_EXIT --> B_ENTRY --> B_RUN --> B_EXIT --------------| - * | - * |----------------------------------------------------------| - * | - * |--> PARENT_AB_EXIT --> PARENT_C_ENTRY --> C_ENTRY --------| - * | - * |----------------------------------------------------------| - * | - * |--> C_RUN(#1) --> C_RUN(#2) --> C_EXIT --> PARENT_C_RUN --| - * | - * |----------------------------------------------------------| - * | - * |--> PARENT_C_EXIT + * This implements a hierarchical state machine using UML rules and demonstrates + * initial transitions, transitions to self (in PARENT_C) and smf_set_handled (in STATE_B) + * + * The order of entry, exit and run actions is given in the ordering of the test_value[] array. */ #define TEST_OBJECT(o) ((struct test_object *)o) -#define SMF_RUN 4 +#define SMF_RUN 5 -/* Initial Setup */ -#define PARENT_AB_ENTRY_BIT (1 << 0) -#define STATE_A_ENTRY_BIT (1 << 1) +/* Initial Setup: Testing initial transitions */ +#define ROOT_ENTRY_BIT BIT(0) +#define PARENT_AB_ENTRY_BIT BIT(1) +#define STATE_A_ENTRY_BIT BIT(2) -/* Run 0 */ -#define STATE_A_RUN_BIT (1 << 2) -#define PARENT_AB_RUN_BIT (1 << 3) -#define STATE_A_EXIT_BIT (1 << 4) -#define STATE_B_ENTRY_BIT (1 << 5) +/* Run 0: normal state transition */ +#define STATE_A_RUN_BIT BIT(3) +#define STATE_A_EXIT_BIT BIT(4) +#define STATE_B_ENTRY_BIT BIT(5) -/* Run 1 */ -#define STATE_B_RUN_BIT (1 << 6) -#define STATE_B_EXIT_BIT (1 << 7) -#define PARENT_AB_EXIT_BIT (1 << 8) -#define PARENT_C_ENTRY_BIT (1 << 9) -#define STATE_C_ENTRY_BIT (1 << 10) +/* Run 1: Test smf_set_handled() */ +#define STATE_B_1ST_RUN_BIT BIT(6) -/* Run 2 */ -#define STATE_C_1ST_RUN_BIT (1 << 11) +/* Run 2: Normal state transition via parent */ +#define STATE_B_2ND_RUN_BIT BIT(7) +#define PARENT_AB_RUN_BIT BIT(8) +#define STATE_B_EXIT_BIT BIT(9) +#define PARENT_AB_EXIT_BIT BIT(10) +#define PARENT_C_1ST_ENTRY_BIT BIT(11) +#define STATE_C_1ST_ENTRY_BIT BIT(12) -/* Run 3 */ -#define STATE_C_2ND_RUN_BIT (1 << 12) -#define PARENT_C_RUN_BIT (1 << 13) -#define STATE_C_EXIT_BIT (1 << 14) -#define PARENT_C_EXIT_BIT (1 << 15) +/* Run 3: PARENT_C executes transition to self */ +#define STATE_C_1ST_RUN_BIT BIT(13) +#define PARENT_C_RUN_BIT BIT(14) +#define STATE_C_1ST_EXIT_BIT BIT(15) +#define PARENT_C_1ST_EXIT_BIT BIT(16) +#define PARENT_C_2ND_ENTRY_BIT BIT(17) +#define STATE_C_2ND_ENTRY_BIT BIT(18) -#define TEST_PARENT_ENTRY_VALUE_NUM 0 -#define TEST_PARENT_RUN_VALUE_NUM 3 -#define TEST_PARENT_EXIT_VALUE_NUM 8 -#define TEST_ENTRY_VALUE_NUM 1 +/* Run 4: Test transition from parent state */ +#define STATE_C_2ND_RUN_BIT BIT(19) +#define STATE_C_2ND_EXIT_BIT BIT(20) +#define PARENT_C_2ND_EXIT_BIT BIT(21) + +/* Unused functions: Error checks if set */ +#define ROOT_RUN_BIT BIT(22) +#define ROOT_EXIT_BIT BIT(23) + +/* Number of state transitions for each test: */ +#define TEST_VALUE_NUM 22 +#define TEST_PARENT_ENTRY_VALUE_NUM 1 +#define TEST_PARENT_RUN_VALUE_NUM 8 +#define TEST_PARENT_EXIT_VALUE_NUM 10 +#define TEST_ENTRY_VALUE_NUM 2 #define TEST_RUN_VALUE_NUM 6 -#define TEST_EXIT_VALUE_NUM 14 -#define TEST_VALUE_NUM 16 +#define TEST_EXIT_VALUE_NUM 15 + +/* + * Note: Test values are taken before the appropriate test bit for that state is set i.e. if + * ROOT_ENTRY_BIT is BIT(0), test_value for root_entry() will be BIT_MASK(0) not BIT_MASK(1) + */ static uint32_t test_value[] = { - 0x00, /* PARENT_AB_ENTRY */ - 0x01, /* STATE_A_ENTRY */ - 0x03, /* STATE_A_RUN */ - 0x07, /* PARENT_AB_RUN */ - 0x0f, /* STATE_A_EXIT */ - 0x1f, /* STATE_B_ENTRY */ - 0x3f, /* STATE_B_RUN */ - 0x7f, /* STATE_B_EXIT */ - 0xff, /* PARENT_AB_EXIT */ - 0x1ff, /* PARENT_C_ENTRY */ - 0x3ff, /* STATE_C_ENTRY */ - 0x7ff, /* STATE_C_1ST_RUN */ - 0xfff, /* STATE_C_2ND_RUN */ - 0x1fff, /* STATE_C_EXIT */ - 0x3fff, /* PARENT_C_RUN */ - 0x7fff, /* PARENT_C_EXIT */ - 0xffff, /* FINAL VALUE */ + /* Initial Setup */ + BIT_MASK(0), /* ROOT_ENTRY_BIT */ + BIT_MASK(1), /* PARENT_AB_ENTRY_BIT */ + BIT_MASK(2), /* STATE_A_ENTRY_BIT */ + /* Run 0 */ + BIT_MASK(3), /* STATE_A_RUN_BIT */ + BIT_MASK(4), /* STATE_A_EXIT_BIT */ + BIT_MASK(5), /* STATE_B_ENTRY_BIT */ + /* Run 1 */ + BIT_MASK(6), /* STATE_B_1ST_RUN_BIT */ + /* Run 2 */ + BIT_MASK(7), /* STATE_B_2ND_RUN_BIT */ + BIT_MASK(8), /* PARENT_AB_RUN_BIT */ + BIT_MASK(9), /* STATE_B_EXIT_BIT */ + BIT_MASK(10), /* PARENT_AB_EXIT_BIT */ + BIT_MASK(11), /* PARENT_C_1ST_ENTRY_BIT */ + BIT_MASK(12), /* STATE_C_1ST_ENTRY_BIT */ + /* Run 3 */ + BIT_MASK(13), /* STATE_C_1ST_RUN_BIT */ + BIT_MASK(14), /* PARENT_C_RUN_BIT */ + BIT_MASK(15), /* STATE_C_1ST_EXIT_BIT */ + BIT_MASK(16), /* PARENT_C_1ST_EXIT_BIT */ + BIT_MASK(17), /* PARENT_C_2ND_ENTRY_BIT */ + BIT_MASK(18), /* STATE_C_2ND_ENTRY_BIT */ + /* Run 4 */ + BIT_MASK(19), /* STATE_C_2ND_RUN_BIT */ + BIT_MASK(20), /* STATE_C_2ND_EXIT_BIT */ + BIT_MASK(21), /* PARENT_C_2ND_EXIT_BIT */ + /* Post-run Check */ + BIT_MASK(22), /* FINAL_VALUE */ }; /* Forward declaration of test_states */ @@ -92,6 +111,7 @@ static const struct smf_state test_states[]; /* List of all TypeC-level states */ enum test_state { + ROOT, PARENT_AB, PARENT_C, STATE_A, @@ -110,6 +130,17 @@ enum terminate_action { EXIT }; +#define B_ENTRY_FIRST_TIME BIT(0) +#define B_RUN_FIRST_TIME BIT(1) +#define PARENT_C_ENTRY_FIRST_TIME BIT(2) +#define C_RUN_FIRST_TIME BIT(3) +#define C_ENTRY_FIRST_TIME BIT(4) +#define C_EXIT_FIRST_TIME BIT(5) + +#define FIRST_TIME_BITS \ + (B_ENTRY_FIRST_TIME | B_RUN_FIRST_TIME | PARENT_C_ENTRY_FIRST_TIME | C_RUN_FIRST_TIME | \ + C_ENTRY_FIRST_TIME | C_EXIT_FIRST_TIME) + static struct test_object { struct smf_ctx ctx; uint32_t transition_bits; @@ -118,14 +149,47 @@ static struct test_object { uint32_t first_time; } test_obj; -static void parent_ab_entry(void *obj) +static void root_entry(void *obj) { struct test_object *o = TEST_OBJECT(obj); o->tv_idx = 0; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test Parent AB entry failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root entry failed"); + + o->transition_bits |= ROOT_ENTRY_BIT; +} + +static void root_run(void *obj) +{ + struct test_object *o = TEST_OBJECT(obj); + + o->tv_idx++; + + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root run failed"); + + o->transition_bits |= ROOT_RUN_BIT; + + /* Return to parent run state */ +} + +static void root_exit(void *obj) +{ + struct test_object *o = TEST_OBJECT(obj); + + o->tv_idx++; + + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Root exit failed"); + o->transition_bits |= ROOT_EXIT_BIT; +} + +static void parent_ab_entry(void *obj) +{ + struct test_object *o = TEST_OBJECT(obj); + + o->tv_idx++; + + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB entry failed"); if (o->terminate == PARENT_ENTRY) { smf_set_terminate(obj, -1); @@ -141,8 +205,7 @@ static void parent_ab_run(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test Parent AB run failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB run failed"); if (o->terminate == PARENT_RUN) { smf_set_terminate(obj, -1); @@ -151,7 +214,7 @@ static void parent_ab_run(void *obj) o->transition_bits |= PARENT_AB_RUN_BIT; - smf_set_state(SMF_CTX(obj), &test_states[STATE_B]); + smf_set_state(SMF_CTX(obj), &test_states[STATE_C]); } static void parent_ab_exit(void *obj) @@ -160,8 +223,7 @@ static void parent_ab_exit(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test Parent AB exit failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent AB exit failed"); if (o->terminate == PARENT_EXIT) { smf_set_terminate(obj, -1); @@ -177,9 +239,13 @@ static void parent_c_entry(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test Parent C entry failed"); - o->transition_bits |= PARENT_C_ENTRY_BIT; + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C entry failed"); + if (o->first_time & PARENT_C_ENTRY_FIRST_TIME) { + o->first_time &= ~PARENT_C_ENTRY_FIRST_TIME; + o->transition_bits |= PARENT_C_1ST_ENTRY_BIT; + } else { + o->transition_bits |= PARENT_C_2ND_ENTRY_BIT; + } } static void parent_c_run(void *obj) @@ -188,14 +254,11 @@ static void parent_c_run(void *obj) o->tv_idx++; - if (o->first_time) { - /* This state should not be reached */ - zassert_true(0, "Test Parent C run failed"); - } else { - o->transition_bits |= PARENT_C_RUN_BIT; + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C run failed"); - smf_set_state(SMF_CTX(obj), &test_states[STATE_D]); - } + o->transition_bits |= PARENT_C_RUN_BIT; + + smf_set_state(SMF_CTX(obj), &test_states[PARENT_C]); } static void parent_c_exit(void *obj) @@ -204,9 +267,14 @@ static void parent_c_exit(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test Parent C exit failed"); - o->transition_bits |= PARENT_C_EXIT_BIT; + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test Parent C exit failed"); + + if (o->first_time & B_ENTRY_FIRST_TIME) { + o->first_time &= ~B_ENTRY_FIRST_TIME; + o->transition_bits |= PARENT_C_1ST_EXIT_BIT; + } else { + o->transition_bits |= PARENT_C_2ND_EXIT_BIT; + } } static void state_a_entry(void *obj) @@ -215,8 +283,7 @@ static void state_a_entry(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State A entry failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A entry failed"); if (o->terminate == ENTRY) { smf_set_terminate(obj, -1); @@ -232,12 +299,11 @@ static void state_a_run(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State A run failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A run failed"); o->transition_bits |= STATE_A_RUN_BIT; - /* Return to parent run state */ + smf_set_state(SMF_CTX(obj), &test_states[STATE_B]); } static void state_a_exit(void *obj) @@ -246,8 +312,7 @@ static void state_a_exit(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State A exit failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A exit failed"); o->transition_bits |= STATE_A_EXIT_BIT; } @@ -257,8 +322,8 @@ static void state_b_entry(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State B entry failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B entry failed"); + o->transition_bits |= STATE_B_ENTRY_BIT; } @@ -268,17 +333,21 @@ static void state_b_run(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State B run failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B run failed"); if (o->terminate == RUN) { smf_set_terminate(obj, -1); return; } - o->transition_bits |= STATE_B_RUN_BIT; - - smf_set_state(SMF_CTX(obj), &test_states[STATE_C]); + if (o->first_time & B_RUN_FIRST_TIME) { + o->first_time &= ~B_RUN_FIRST_TIME; + o->transition_bits |= STATE_B_1ST_RUN_BIT; + smf_set_handled(SMF_CTX(obj)); + } else { + o->transition_bits |= STATE_B_2ND_RUN_BIT; + /* bubble up to PARENT_AB */ + } } static void state_b_exit(void *obj) @@ -287,8 +356,8 @@ static void state_b_exit(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State B exit failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B exit failed"); + o->transition_bits |= STATE_B_EXIT_BIT; } @@ -298,9 +367,13 @@ static void state_c_entry(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State C entry failed"); - o->transition_bits |= STATE_C_ENTRY_BIT; + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C entry failed"); + if (o->first_time & C_ENTRY_FIRST_TIME) { + o->first_time &= ~C_ENTRY_FIRST_TIME; + o->transition_bits |= STATE_C_1ST_ENTRY_BIT; + } else { + o->transition_bits |= STATE_C_2ND_ENTRY_BIT; + } } static void state_c_run(void *obj) @@ -309,16 +382,15 @@ static void state_c_run(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State C run failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C run failed"); - if (o->first_time) { - o->first_time = false; + if (o->first_time & C_RUN_FIRST_TIME) { + o->first_time &= ~C_RUN_FIRST_TIME; o->transition_bits |= STATE_C_1ST_RUN_BIT; - smf_set_handled(SMF_CTX(obj)); - } else { /* Do nothing, Let parent handle it */ + } else { o->transition_bits |= STATE_C_2ND_RUN_BIT; + smf_set_state(SMF_CTX(obj), &test_states[STATE_D]); } } @@ -328,15 +400,19 @@ static void state_c_exit(void *obj) o->tv_idx++; - zassert_equal(o->transition_bits, test_value[o->tv_idx], - "Test State C exit failed"); + zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C exit failed"); if (o->terminate == EXIT) { smf_set_terminate(obj, -1); return; } - o->transition_bits |= STATE_C_EXIT_BIT; + if (o->first_time & C_EXIT_FIRST_TIME) { + o->first_time &= ~C_EXIT_FIRST_TIME; + o->transition_bits |= STATE_C_1ST_EXIT_BIT; + } else { + o->transition_bits |= STATE_C_2ND_EXIT_BIT; + } } static void state_d_entry(void *obj) @@ -357,26 +433,27 @@ static void state_d_exit(void *obj) } static const struct smf_state test_states[] = { - [PARENT_AB] = SMF_CREATE_STATE(parent_ab_entry, parent_ab_run, - parent_ab_exit, NULL, &test_states[STATE_A]), - [PARENT_C] = SMF_CREATE_STATE(parent_c_entry, parent_c_run, - parent_c_exit, NULL, &test_states[STATE_C]), + [ROOT] = SMF_CREATE_STATE(root_entry, root_run, root_exit, NULL, &test_states[PARENT_AB]), + [PARENT_AB] = SMF_CREATE_STATE(parent_ab_entry, parent_ab_run, parent_ab_exit, + &test_states[ROOT], &test_states[STATE_A]), + [PARENT_C] = SMF_CREATE_STATE(parent_c_entry, parent_c_run, parent_c_exit, + &test_states[ROOT], &test_states[STATE_C]), [STATE_A] = SMF_CREATE_STATE(state_a_entry, state_a_run, state_a_exit, &test_states[PARENT_AB], NULL), [STATE_B] = SMF_CREATE_STATE(state_b_entry, state_b_run, state_b_exit, &test_states[PARENT_AB], NULL), [STATE_C] = SMF_CREATE_STATE(state_c_entry, state_c_run, state_c_exit, &test_states[PARENT_C], NULL), - [STATE_D] = SMF_CREATE_STATE(state_d_entry, state_d_run, state_d_exit, - NULL, NULL), + [STATE_D] = SMF_CREATE_STATE(state_d_entry, state_d_run, state_d_exit, &test_states[ROOT], + NULL), }; -ZTEST(smf_tests, test_smf_initial_transitions) +ZTEST(smf_tests, test_smf_self_transition) { /* A) Test state transitions */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = NONE; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -386,15 +463,14 @@ ZTEST(smf_tests, test_smf_initial_transitions) } } - zassert_equal(TEST_VALUE_NUM, test_obj.tv_idx, - "Incorrect test value index"); + zassert_equal(TEST_VALUE_NUM, test_obj.tv_idx, "Incorrect test value index"); zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx], "Final state not reached"); /* B) Test termination in parent entry action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = PARENT_ENTRY; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -412,7 +488,7 @@ ZTEST(smf_tests, test_smf_initial_transitions) /* C) Test termination in parent run action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = PARENT_RUN; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -430,7 +506,7 @@ ZTEST(smf_tests, test_smf_initial_transitions) /* D) Test termination in parent exit action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = PARENT_EXIT; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -448,7 +524,7 @@ ZTEST(smf_tests, test_smf_initial_transitions) /* E) Test termination in child entry action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = ENTRY; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -466,7 +542,7 @@ ZTEST(smf_tests, test_smf_initial_transitions) /* F) Test termination in child run action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = RUN; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); @@ -484,7 +560,7 @@ ZTEST(smf_tests, test_smf_initial_transitions) /* G) Test termination in child exit action */ test_obj.transition_bits = 0; - test_obj.first_time = 1; + test_obj.first_time = FIRST_TIME_BITS; test_obj.terminate = EXIT; smf_set_initial((struct smf_ctx *)&test_obj, &test_states[PARENT_AB]); diff --git a/tests/lib/smf/src/test_lib_smf.h b/tests/lib/smf/src/test_lib_smf.h index 6296352fb73..032dec06f79 100644 --- a/tests/lib/smf/src/test_lib_smf.h +++ b/tests/lib/smf/src/test_lib_smf.h @@ -10,6 +10,6 @@ void test_smf_flat(void); void test_smf_hierarchical(void); void test_smf_hierarchical_5_ancestors(void); -void test_smf_initial_transitions(void); +void test_smf_self_transition(void); #endif /* ZEPHYR_TEST_LIB_SMF_H_ */