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_internal_with_puml |
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 | |
30 | |
31 | namespace |
32 | { |
33 | |
34 | |
35 | // front-end: define the FSM structure |
36 | struct player_ : public msm::front::state_machine_def<player_> |
37 | { |
38 | unsigned int start_playback_counter=0; |
39 | unsigned int can_close_drawer_counter=0; |
40 | unsigned int internal_action_counter=0; |
41 | unsigned int internal_guard_counter=0; |
42 | |
43 | BOOST_MSM_PUML_DECLARE_TABLE( |
44 | R"( |
45 | @startuml Player |
46 | skinparam linetype polyline |
47 | state Player{ |
48 | [*]-> Empty |
49 | Stopped -> Playing : play / start_playback |
50 | Stopped -> Open : open_close / open_drawer |
51 | Stopped -> Stopped : stop |
52 | |
53 | Open -> Empty : open_close [can_close_drawer] |
54 | |
55 | Empty --> Open : open_close / open_drawer |
56 | Empty ---> Stopped : cd_detected / store_cd_info [good_disk_format] |
57 | Empty -> Empty : -internal_evt / internal_action [internal_guard2] |
58 | Empty -> Empty : -to_ignore |
59 | Empty -> Empty : -cd_detected [internal_guard] |
60 | Empty -> Empty : -internal_evt / internal_action_fct [internal_guard_fct] |
61 | |
62 | Playing --> Stopped : stop / stop_playback |
63 | Playing -> Paused : pause / pause_playback |
64 | Playing --> Open : open_close / stop_and_open |
65 | |
66 | Paused -> Playing : end_pause / resume_playback |
67 | Paused --> Stopped : stop / stop_playback |
68 | Paused --> Open : open_close / stop_and_open |
69 | } |
70 | @enduml |
71 | )" |
72 | ) |
73 | |
74 | // Replaces the default no-transition response. |
75 | template <class FSM,class Event> |
76 | void no_transition(Event const&, FSM&,int) |
77 | { |
78 | BOOST_FAIL("no_transition called!" ); |
79 | } |
80 | }; |
81 | // Pick a back-end |
82 | typedef msm::back11::state_machine<player_> player; |
83 | |
84 | |
85 | BOOST_AUTO_TEST_CASE( back11_simple_internal_with_puml_test ) |
86 | { |
87 | player p; |
88 | |
89 | p.start(); |
90 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 1, "Empty entry not called correctly" ); |
91 | // internal events |
92 | p.process_event(Event<by_name(str: "to_ignore" )>{}); |
93 | p.process_event(Event<by_name(str: "internal_evt" )>{}); |
94 | BOOST_CHECK_MESSAGE(p.internal_action_counter == 1, "Internal action not called correctly" ); |
95 | BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1, "Internal guard not called correctly" ); |
96 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().empty_internal_action_counter == 0, "Empty internal action not called correctly" ); |
97 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().empty_internal_guard_counter == 1, "Empty internal guard not called correctly" ); |
98 | |
99 | p.process_event(Event<by_name(str: "open_close" )>{}); |
100 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active" ); //Open |
101 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().exit_counter == 1,"Empty exit not called correctly" ); |
102 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().entry_counter == 1,"Open entry not called correctly" ); |
103 | |
104 | p.process_event(Event<by_name(str: "open_close" )>{}); |
105 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active" ); //Empty |
106 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().exit_counter == 1,"Open exit not called correctly" ); |
107 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 2,"Empty entry not called correctly" ); |
108 | BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly" ); |
109 | |
110 | p.process_event(Event<by_name(str: "cd_detected" )>{"louie, louie" , DISK_DVD}); |
111 | |
112 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active" ); //Empty |
113 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open" )>&>().exit_counter == 1,"Open exit not called correctly" ); |
114 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().entry_counter == 2,"Empty entry not called correctly" ); |
115 | |
116 | p.process_event(Event<by_name(str: "cd_detected" )>{"louie, louie" , DISK_CD}); |
117 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0, "Stopped should be active" ); //Stopped |
118 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty" )>&>().exit_counter == 2,"Empty exit not called correctly" ); |
119 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 1,"Stopped entry not called correctly" ); |
120 | BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3, "Internal guard not called correctly" ); |
121 | |
122 | p.process_event(Event<by_name(str: "play" )>{}); |
123 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3, "Playing should be active" ); //Playing |
124 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().exit_counter == 1, "Stopped exit not called correctly" ); |
125 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing" )>&>().entry_counter == 1, "Playing entry not called correctly" ); |
126 | BOOST_CHECK_MESSAGE(p.start_playback_counter == 1, "action not called correctly" ); |
127 | |
128 | p.process_event(Event<by_name(str: "pause" )>{}); |
129 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4, "Paused should be active" ); //Paused |
130 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().exit_counter == 1, "Playing exit not called correctly" ); |
131 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().entry_counter == 1, "Paused entry not called correctly" ); |
132 | |
133 | // go back to Playing |
134 | p.process_event(Event<by_name(str: "end_pause" )>{}); |
135 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active" ); //Playing |
136 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().exit_counter == 1,"Paused exit not called correctly" ); |
137 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing" )>&>().entry_counter == 2,"Playing entry not called correctly" ); |
138 | |
139 | p.process_event(Event<by_name(str: "pause" )>{}); |
140 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active" ); //Paused |
141 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing" )>&>().exit_counter == 2,"Playing exit not called correctly" ); |
142 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().entry_counter == 2,"Paused entry not called correctly" ); |
143 | |
144 | p.process_event(Event<by_name(str: "stop" )>{}); |
145 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
146 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused" )>&>().exit_counter == 2,"Paused exit not called correctly" ); |
147 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 2,"Stopped entry not called correctly" ); |
148 | |
149 | p.process_event(Event<by_name(str: "stop" )>{}); |
150 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
151 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().exit_counter == 2,"Stopped exit not called correctly" ); |
152 | BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped" )>&>().entry_counter == 3,"Stopped entry not called correctly" ); |
153 | |
154 | } |
155 | } |
156 | |
157 | |