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 | |