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