c++unit-testingbooststate-machineboost-msm

How to reach directly any state in a state-machine written with Boost MSM


I am currently using the Boost MSM library to write a state machine and I want to write unit tests to check transitions between its states. For each unit test, I need to write repetitive lines of code to reach the state from which I want to start. Therefore I would like to know if their is a way to start the state machine in a given state instead of the starting state.

For example if I have a simple state machine like this that usually starts at StartingState, I would like to reach directly IdleState to do my test :


Solution

  • Boost.MSM doesn't directly support the functionality that you want.

    But you can control the initial state using initial_state inner type and preprocessor macro.

    Let's say your state machine is defined in sm1.hpp.

    sm1.hpp

    #include <iostream>
    #include <boost/msm/back/state_machine.hpp>
    
    #include <boost/msm/front/state_machine_def.hpp>
    #include <boost/msm/front/functor_row.hpp>
    
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    
    // ----- Events
    struct Event1 {};
    
    // Test helper code
    #if !defined(TEST_SM1_STATE)
    #define TEST_SM1_STATE StartingState
    #endif //!defined(TEST_SM1_STATE)
    
    // ----- State machine
    struct Sm1_:msmf::state_machine_def<Sm1_> {
        // States
        struct StartingState:msmf::state<> {
            // Entry action
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) {
                std::cout << "StartingState::on_entry()" << std::endl;
            }
        };
        struct IdleState:msmf::state<> {
            // Entry action
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) {
                std::cout << "IdleState::on_entry()" << std::endl;
            }
        };
        struct ErrorState:msmf::state<> {
            // Entry action
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) {
                std::cout << "ErrorState::on_entry()" << std::endl;
            }
        };
    
        // Set initial state
        using initial_state = TEST_SM1_STATE;
    
        // Transition table
        struct transition_table:mpl::vector<
            //          Start          Event   Next        Action      Guard
            msmf::Row < StartingState, Event1, IdleState,  msmf::none, msmf::none >,
            msmf::Row < IdleState,     Event1, ErrorState, msmf::none, msmf::none >
        > {};
    };
    
    // Pick a back-end
    typedef msm::back::state_machine<Sm1_> Sm1;
    

    test.cpp

    #define TEST_SM1_STATE IdleState
    #include "sm1.hpp"
    
    int main() {
        Sm1 sm1;
        sm1.start(); 
    }
    

    Demo: https://wandbox.org/permlink/dnLrAZ7fTJhg473q

    The key point is the following code:

    // Set initial state
    using initial_state = TEST_SM1_STATE;
    

    You can set any state as the initial state. Define initial state before including sm1.hpp like as follows:

    #define TEST_SM1_STATE IdleState
    #include "sm1.hpp"