1 | // Copyright 2010 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 | // back-end |
12 | #include <boost/msm/back/state_machine.hpp> |
13 | //front-end |
14 | #include <boost/msm/front/state_machine_def.hpp> |
15 | #ifndef BOOST_MSM_NONSTANDALONE_TEST |
16 | #define BOOST_TEST_MODULE MyTest |
17 | #endif |
18 | #include <boost/test/unit_test.hpp> |
19 | |
20 | namespace msm = boost::msm; |
21 | namespace mpl = boost::mpl; |
22 | |
23 | namespace |
24 | { |
25 | // events |
26 | struct event1 {}; |
27 | struct event2 {}; |
28 | struct event3 {}; |
29 | struct event4 {}; |
30 | struct event5 {}; |
31 | struct event6 |
32 | { |
33 | event6(){} |
34 | template <class Event> |
35 | event6(Event const&){} |
36 | }; |
37 | // front-end: define the FSM structure |
38 | struct Fsm_ : public msm::front::state_machine_def<Fsm_> |
39 | { |
40 | // The list of FSM states |
41 | struct State1 : public msm::front::state<> |
42 | { |
43 | template <class Event,class FSM> |
44 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
45 | template <class Event,class FSM> |
46 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
47 | int entry_counter; |
48 | int exit_counter; |
49 | }; |
50 | struct State2 : public msm::front::state<> |
51 | { |
52 | template <class Event,class FSM> |
53 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
54 | template <class Event,class FSM> |
55 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
56 | int entry_counter; |
57 | int exit_counter; |
58 | }; |
59 | struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> |
60 | { |
61 | typedef msm::back::state_machine<SubFsm2_> SubFsm2; |
62 | |
63 | unsigned int entry_action_counter; |
64 | |
65 | template <class Event,class FSM> |
66 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
67 | template <class Event,class FSM> |
68 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
69 | int entry_counter; |
70 | int exit_counter; |
71 | |
72 | struct SubState1 : public msm::front::state<> |
73 | { |
74 | template <class Event,class FSM> |
75 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
76 | template <class Event,class FSM> |
77 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
78 | int entry_counter; |
79 | int exit_counter; |
80 | }; |
81 | struct SubState1b : public msm::front::state<> |
82 | { |
83 | template <class Event,class FSM> |
84 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
85 | template <class Event,class FSM> |
86 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
87 | int entry_counter; |
88 | int exit_counter; |
89 | }; |
90 | struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0> |
91 | { |
92 | template <class Event,class FSM> |
93 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
94 | template <class Event,class FSM> |
95 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
96 | int entry_counter; |
97 | int exit_counter; |
98 | }; |
99 | struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1> |
100 | { |
101 | template <class Event,class FSM> |
102 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
103 | template <class Event,class FSM> |
104 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
105 | int entry_counter; |
106 | int exit_counter; |
107 | }; |
108 | // test with a pseudo entry |
109 | struct PseudoEntry1 : public msm::front::entry_pseudo_state<0> |
110 | { |
111 | template <class Event,class FSM> |
112 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
113 | template <class Event,class FSM> |
114 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
115 | int entry_counter; |
116 | int exit_counter; |
117 | }; |
118 | struct SubState3 : public msm::front::state<> |
119 | { |
120 | template <class Event,class FSM> |
121 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
122 | template <class Event,class FSM> |
123 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
124 | int entry_counter; |
125 | int exit_counter; |
126 | }; |
127 | struct PseudoExit1 : public msm::front::exit_pseudo_state<event6> |
128 | { |
129 | template <class Event,class FSM> |
130 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
131 | template <class Event,class FSM> |
132 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
133 | int entry_counter; |
134 | int exit_counter; |
135 | }; |
136 | // action methods |
137 | void entry_action(event4 const&) |
138 | { |
139 | ++entry_action_counter; |
140 | } |
141 | // the initial state. Must be defined |
142 | typedef mpl::vector<SubState1,SubState1b> initial_state; |
143 | |
144 | typedef mpl::vector<SubState2b> explicit_creation; |
145 | |
146 | // Transition table for SubFsm2 |
147 | struct transition_table : mpl::vector< |
148 | // Start Event Next Action Guard |
149 | // +--------------+-------------+------------+------------------------+----------------------+ |
150 | a_row < PseudoEntry1 , event4 , SubState3 ,&SubFsm2_::entry_action >, |
151 | _row < SubState2 , event6 , SubState1 >, |
152 | _row < SubState3 , event5 , PseudoExit1 > |
153 | // +--------------+-------------+------------+------------------------+----------------------+ |
154 | > {}; |
155 | // Replaces the default no-transition response. |
156 | template <class FSM,class Event> |
157 | void no_transition(Event const& , FSM&,int) |
158 | { |
159 | BOOST_FAIL("no_transition called!" ); |
160 | } |
161 | }; |
162 | typedef msm::back::state_machine<SubFsm2_> SubFsm2; |
163 | |
164 | // the initial state of the player SM. Must be defined |
165 | typedef State1 initial_state; |
166 | |
167 | // transition actions |
168 | // guard conditions |
169 | |
170 | // Transition table for Fsm |
171 | struct transition_table : mpl::vector< |
172 | // Start Event Next Action Guard |
173 | // +---------------------+--------+------------------------------------+-------+--------+ |
174 | _row < State1 , event1 , SubFsm2 >, |
175 | _row < State1 , event2 , SubFsm2::direct<SubFsm2_::SubState2> >, |
176 | _row < State1 , event3 , mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, |
177 | SubFsm2::direct<SubFsm2_::SubState2b> > >, |
178 | _row < State1 , event4 , SubFsm2::entry_pt |
179 | <SubFsm2_::PseudoEntry1> >, |
180 | // +---------------------+--------+------------------------------------+-------+--------+ |
181 | _row < SubFsm2 , event1 , State1 >, |
182 | _row < SubFsm2::exit_pt |
183 | <SubFsm2_::PseudoExit1>, event6 , State2 > |
184 | // +---------------------+--------+------------------------------------+-------+--------+ |
185 | > {}; |
186 | |
187 | // Replaces the default no-transition response. |
188 | template <class FSM,class Event> |
189 | void no_transition(Event const& , FSM&,int ) |
190 | { |
191 | BOOST_FAIL("no_transition called!" ); |
192 | } |
193 | // init counters |
194 | template <class Event,class FSM> |
195 | void on_entry(Event const&,FSM& fsm) |
196 | { |
197 | fsm.template get_state<Fsm_::State1&>().entry_counter=0; |
198 | fsm.template get_state<Fsm_::State1&>().exit_counter=0; |
199 | fsm.template get_state<Fsm_::State2&>().entry_counter=0; |
200 | fsm.template get_state<Fsm_::State2&>().exit_counter=0; |
201 | fsm.template get_state<Fsm_::SubFsm2&>().entry_counter=0; |
202 | fsm.template get_state<Fsm_::SubFsm2&>().exit_counter=0; |
203 | fsm.template get_state<Fsm_::SubFsm2&>().entry_action_counter=0; |
204 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().entry_counter=0; |
205 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().exit_counter=0; |
206 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().entry_counter=0; |
207 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().exit_counter=0; |
208 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().entry_counter=0; |
209 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().exit_counter=0; |
210 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().entry_counter=0; |
211 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().exit_counter=0; |
212 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().entry_counter=0; |
213 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().exit_counter=0; |
214 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().entry_counter=0; |
215 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().exit_counter=0; |
216 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().entry_counter=0; |
217 | fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().exit_counter=0; |
218 | |
219 | } |
220 | }; |
221 | typedef msm::back::state_machine<Fsm_> Fsm; |
222 | // static char const* const state_names[] = { "State1", "SubFsm2","State2" }; |
223 | |
224 | |
225 | BOOST_AUTO_TEST_CASE( entries_test ) |
226 | { |
227 | Fsm p; |
228 | |
229 | p.start(); |
230 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 1,"State1 entry not called correctly" ); |
231 | |
232 | p.process_event(evt: event1()); |
233 | p.process_event(evt: event1()); |
234 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active" ); |
235 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 1,"State1 exit not called correctly" ); |
236 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 2,"State1 entry not called correctly" ); |
237 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 1,"SubFsm2 exit not called correctly" ); |
238 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 1,"SubFsm2 entry not called correctly" ); |
239 | |
240 | p.process_event(evt: event2()); |
241 | p.process_event(evt: event6()); |
242 | p.process_event(evt: event1()); |
243 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active" ); |
244 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 2,"State1 exit not called correctly" ); |
245 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 3,"State1 entry not called correctly" ); |
246 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 2,"SubFsm2 exit not called correctly" ); |
247 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 2,"SubFsm2 entry not called correctly" ); |
248 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 1, |
249 | "SubFsm2::SubState2 entry not called correctly" ); |
250 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 1, |
251 | "SubFsm2::SubState2 exit not called correctly" ); |
252 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().entry_counter == 2, |
253 | "SubFsm2::SubState1 entry not called correctly" ); |
254 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().exit_counter == 2, |
255 | "SubFsm2::SubState1 exit not called correctly" ); |
256 | |
257 | p.process_event(evt: event3()); |
258 | p.process_event(evt: event1()); |
259 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active" ); |
260 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 3,"State1 exit not called correctly" ); |
261 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 4,"State1 entry not called correctly" ); |
262 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 3,"SubFsm2 exit not called correctly" ); |
263 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 3,"SubFsm2 entry not called correctly" ); |
264 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 2, |
265 | "SubFsm2::SubState2 entry not called correctly" ); |
266 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 2, |
267 | "SubFsm2::SubState2 exit not called correctly" ); |
268 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().entry_counter == 1, |
269 | "SubFsm2::SubState2b entry not called correctly" ); |
270 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().exit_counter == 1, |
271 | "SubFsm2::SubState2b exit not called correctly" ); |
272 | |
273 | p.process_event(evt: event4()); |
274 | p.process_event(evt: event5()); |
275 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"State2 should be active" ); |
276 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 4,"State1 exit not called correctly" ); |
277 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State2&>().entry_counter == 1,"State2 entry not called correctly" ); |
278 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 4,"SubFsm2 exit not called correctly" ); |
279 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 4,"SubFsm2 entry not called correctly" ); |
280 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().entry_counter == 1, |
281 | "SubFsm2::PseudoEntry1 entry not called correctly" ); |
282 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().exit_counter == 1, |
283 | "SubFsm2::PseudoEntry1 exit not called correctly" ); |
284 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().entry_counter == 1, |
285 | "SubFsm2::SubState3 entry not called correctly" ); |
286 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().exit_counter == 1, |
287 | "SubFsm2::SubState3 exit not called correctly" ); |
288 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().entry_counter == 1, |
289 | "SubFsm2::PseudoExit1 entry not called correctly" ); |
290 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().exit_counter == 1, |
291 | "SubFsm2::PseudoExit1 exit not called correctly" ); |
292 | BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_action_counter == 1,"Action not called correctly" ); |
293 | |
294 | } |
295 | } |
296 | |
297 | |