| 1 | // Copyright 2024 Christophe Henry |
| 2 | // henry UNDERSCORE christophe AT hotmail DOT com |
| 3 | // This is an extended version of the state machine available in the boost::mpl library |
| 4 | // Distributed under the same license as the original. |
| 5 | // Copyright for the original version: |
| 6 | // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed |
| 7 | // under the Boost Software License, Version 1.0. (See accompanying |
| 8 | // file LICENSE_1_0.txt or copy at |
| 9 | // http://www.boost.org/LICENSE_1_0.txt) |
| 10 | |
| 11 | |
| 12 | // back-end |
| 13 | #include <boost/msm/back11/state_machine.hpp> |
| 14 | //front-end |
| 15 | #include <boost/msm/front/state_machine_def.hpp> |
| 16 | #include <boost/msm/front/puml/puml.hpp> |
| 17 | #include <PumlCommon.hpp> |
| 18 | |
| 19 | #ifndef BOOST_MSM_NONSTANDALONE_TEST |
| 20 | #define BOOST_TEST_MODULE back11_simple_with_puml_test |
| 21 | #endif |
| 22 | #include <boost/test/unit_test.hpp> |
| 23 | |
| 24 | using namespace std; |
| 25 | namespace msm = boost::msm; |
| 26 | using namespace msm::front; |
| 27 | using namespace msm::front::puml; |
| 28 | |
| 29 | namespace |
| 30 | { |
| 31 | |
| 32 | |
| 33 | // front-end: define the FSM structure |
| 34 | struct player_ : public msm::front::state_machine_def<player_> |
| 35 | { |
| 36 | unsigned int start_playback_counter=0; |
| 37 | unsigned int can_close_drawer_counter=0; |
| 38 | unsigned int test_fct_counter=0; |
| 39 | BOOST_MSM_PUML_DECLARE_TABLE( |
| 40 | R"( |
| 41 | @startuml Player |
| 42 | skinparam linetype polyline |
| 43 | state Player{ |
| 44 | [*]-> Empty |
| 45 | Stopped -> Playing2 : play / TestFct,start_playback [DummyGuard] |
| 46 | Stopped -> Open : open_close / open_drawer |
| 47 | Stopped -> Stopped : stop |
| 48 | |
| 49 | Open -> Empty : open_close / close_drawer [can_close_drawer] |
| 50 | Empty --> Open : open_close / open_drawer |
| 51 | Empty ---> Stopped : cd_detected / store_cd_info2 [good_disk_format && always_true] |
| 52 | Playing2 --> Stopped : stop / stop_playback |
| 53 | Playing2 -> Paused : pause / pause_playback |
| 54 | Playing2 --> Open : open_close / stop_and_open |
| 55 | Paused -> Playing2 : end_pause / resume_playback2 |
| 56 | Paused --> Stopped : stop / stop_playback |
| 57 | Paused --> Open : open_close / stop_and_open |
| 58 | } |
| 59 | @enduml |
| 60 | )" |
| 61 | ) |
| 62 | |
| 63 | // Replaces the default no-transition response. |
| 64 | template <class FSM,class Event> |
| 65 | void no_transition(Event const&, FSM&,int) |
| 66 | { |
| 67 | BOOST_FAIL("no_transition called!" ); |
| 68 | } |
| 69 | }; |
| 70 | // Pick a back-end |
| 71 | typedef msm::back11::state_machine<player_> player; |
| 72 | |
| 73 | |
| 74 | BOOST_AUTO_TEST_CASE( back11_simple_with_puml_test ) |
| 75 | { |
| 76 | player p; |
| 77 | |
| 78 | p.start(); |
| 79 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 1, "Empty entry not called correctly" ); |
| 80 | |
| 81 | p.process_event(Event<by_name(str: "open_close" )>{}); |
| 82 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active" ); //Open |
| 83 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().exit_counter == 1,"Empty exit not called correctly" ); |
| 84 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().entry_counter == 1,"Open entry not called correctly" ); |
| 85 | |
| 86 | p.process_event(Event<by_name(str: "open_close" )>{}); |
| 87 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active" ); //Empty |
| 88 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().exit_counter == 1,"Open exit not called correctly" ); |
| 89 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 2,"Empty entry not called correctly" ); |
| 90 | BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly" ); |
| 91 | |
| 92 | p.process_event(Event<by_name(str: "cd_detected" )>{"louie, louie" , DISK_DVD}); |
| 93 | |
| 94 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active" ); //Empty |
| 95 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().exit_counter == 1,"Open exit not called correctly" ); |
| 96 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 2,"Empty entry not called correctly" ); |
| 97 | |
| 98 | p.process_event(Event<by_name(str: "cd_detected" )>{"louie, louie" , DISK_CD}); |
| 99 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing2 should be active" ); //Playing2 |
| 100 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().exit_counter == 2,"Empty exit not called correctly" ); |
| 101 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 1,"Stopped entry not called correctly" ); |
| 102 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().exit_counter == 1,"Stopped exit not called correctly" ); |
| 103 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing2" )>&>().entry_counter == 1,"Playing2 entry not called correctly" ); |
| 104 | BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly" ); |
| 105 | BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly" ); |
| 106 | |
| 107 | p.process_event(Event<by_name(str: "pause" )>{}); |
| 108 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active" ); //Paused |
| 109 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing2" )>&>().exit_counter == 1,"Playing2 exit not called correctly" ); |
| 110 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().entry_counter == 1,"Paused entry not called correctly" ); |
| 111 | |
| 112 | // go back to Playing2 |
| 113 | p.process_event(Event<by_name(str: "end_pause" )>{}); |
| 114 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing2 should be active" ); //Playing2 |
| 115 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().exit_counter == 1,"Paused exit not called correctly" ); |
| 116 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing2" )>&>().entry_counter == 2,"Playing2 entry not called correctly" ); |
| 117 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing2" )>&>().event_counter == 1,"Playing2 event counter incorrect" ); |
| 118 | |
| 119 | p.process_event(Event<by_name(str: "pause" )>{}); |
| 120 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active" ); //Paused |
| 121 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing2" )>&>().exit_counter == 2,"Playing2 exit not called correctly" ); |
| 122 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().entry_counter == 2,"Paused entry not called correctly" ); |
| 123 | |
| 124 | p.process_event(Event<by_name(str: "stop" )>{}); |
| 125 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
| 126 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().exit_counter == 2,"Paused exit not called correctly" ); |
| 127 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 2,"Stopped entry not called correctly" ); |
| 128 | |
| 129 | p.process_event(Event<by_name(str: "stop" )>{}); |
| 130 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
| 131 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().exit_counter == 2,"Stopped exit not called correctly" ); |
| 132 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 3,"Stopped entry not called correctly" ); |
| 133 | |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | |