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#include <boost/fusion/adapted/std_tuple.hpp>
12#include <boost/fusion/include/std_tuple.hpp>
13
14// back-end
15#include <boost/msm/back11/state_machine.hpp>
16//front-end
17#include <boost/msm/front/state_machine_def.hpp>
18// functors
19#include <boost/msm/front/functor_row.hpp>
20// for And_ operator
21#include <boost/msm/front/operator.hpp>
22#ifndef BOOST_MSM_NONSTANDALONE_TEST
23#define BOOST_TEST_MODULE back11_simple_with_functors_test
24#endif
25#include <boost/test/unit_test.hpp>
26
27using namespace std;
28namespace msm = boost::msm;
29namespace mpl = boost::mpl;
30using namespace msm::front;
31
32namespace
33{
34 // events
35 struct play {int cpt_ = 0;};
36 struct end_pause {int cpt_ = 0;};
37 struct stop {};
38 struct pause {};
39 struct open_close {};
40
41 // A "complicated" event type that carries some data.
42 enum DiskTypeEnum
43 {
44 DISK_CD=0,
45 DISK_DVD=1
46 };
47 struct cd_detected
48 {
49 cd_detected(std::string name, DiskTypeEnum diskType)
50 : name(name),
51 disc_type(diskType)
52 {}
53
54 std::string name;
55 DiskTypeEnum disc_type;
56 };
57
58 // front-end: define the FSM structure
59 struct player_ : public msm::front::state_machine_def<player_>
60 {
61 unsigned int start_playback_counter;
62 unsigned int can_close_drawer_counter;
63 unsigned int test_fct_counter;
64
65 player_():
66 start_playback_counter(0),
67 can_close_drawer_counter(0),
68 test_fct_counter(0)
69 {}
70
71 // The list of FSM states
72 struct Empty : 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 Open : 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
91 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
92 struct Stopped : public msm::front::state<>
93 {
94 template <class Event,class FSM>
95 void on_entry(Event const&,FSM& ) {++entry_counter;}
96 template <class Event,class FSM>
97 void on_exit(Event const&,FSM& ) {++exit_counter;}
98 int entry_counter;
99 int exit_counter;
100 };
101
102 struct Playing : public msm::front::state<>
103 {
104 template <class Event,class FSM>
105 void on_entry(Event const& e,FSM& )
106 {
107 ++entry_counter;
108 event_counter = e.cpt_;
109 }
110 template <class Event,class FSM>
111 void on_exit(Event const&,FSM& ) {++exit_counter;}
112 int entry_counter;
113 int exit_counter;
114 int event_counter=0;
115 };
116
117 struct Paused : public msm::front::state<>
118 {
119 template <class Event,class FSM>
120 void on_entry(Event const&,FSM& ) {++entry_counter;}
121 template <class Event,class FSM>
122 void on_exit(Event const&,FSM& ) {++exit_counter;}
123 int entry_counter;
124 int exit_counter;
125 };
126
127 // the initial state of the player SM. Must be defined
128 typedef Empty initial_state;
129
130 // transition actions
131 struct TestFct
132 {
133 template <class EVT,class FSM,class SourceState,class TargetState>
134 void operator()(EVT& e, FSM& fsm,SourceState& ,TargetState& )
135 {
136 ++e.cpt_;
137 ++fsm.test_fct_counter;
138 }
139 };
140 struct start_playback
141 {
142 template <class EVT,class FSM,class SourceState,class TargetState>
143 void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
144 {
145 ++fsm.start_playback_counter;
146 }
147 };
148 struct open_drawer
149 {
150 template <class EVT,class FSM,class SourceState,class TargetState>
151 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
152 {
153 }
154 };
155 struct close_drawer
156 {
157 template <class EVT,class FSM,class SourceState,class TargetState>
158 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
159 {
160 }
161 };
162 struct store_cd_info
163 {
164 template <class EVT,class FSM,class SourceState,class TargetState>
165 void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
166 {
167 fsm.process_event(play());
168 }
169 };
170 struct stop_playback
171 {
172 template <class EVT,class FSM,class SourceState,class TargetState>
173 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
174 {
175 }
176 };
177 struct pause_playback
178 {
179 template <class EVT,class FSM,class SourceState,class TargetState>
180 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
181 {
182 }
183 };
184 struct resume_playback
185 {
186 template <class EVT,class FSM,class SourceState,class TargetState>
187 void operator()(EVT& e,FSM& ,SourceState& ,TargetState& )
188 {
189 ++e.cpt_;
190 }
191 };
192 struct stop_and_open
193 {
194 template <class EVT,class FSM,class SourceState,class TargetState>
195 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
196 {
197 }
198 };
199 struct stopped_again
200 {
201 template <class EVT,class FSM,class SourceState,class TargetState>
202 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
203 {
204 }
205 };
206 // guard conditions
207 struct DummyGuard
208 {
209 template <class EVT,class FSM,class SourceState,class TargetState>
210 bool operator()(EVT const&,FSM&,SourceState&,TargetState&)
211 {
212 return true;
213 }
214 };
215 struct good_disk_format
216 {
217 template <class EVT,class FSM,class SourceState,class TargetState>
218 bool operator()(EVT& evt ,FSM&,SourceState& ,TargetState& )
219 {
220 // to test a guard condition, let's say we understand only CDs, not DVD
221 if (evt.disc_type != DISK_CD)
222 {
223 return false;
224 }
225 return true;
226 }
227 };
228 struct always_true
229 {
230 template <class EVT,class FSM,class SourceState,class TargetState>
231 bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& )
232 {
233 return true;
234 }
235 };
236 struct can_close_drawer
237 {
238 template <class EVT,class FSM,class SourceState,class TargetState>
239 bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
240 {
241 ++fsm.can_close_drawer_counter;
242 return true;
243 }
244 };
245
246 typedef player_ p; // makes transition table cleaner
247
248 // Transition table for player
249 struct transition_table : boost::fusion::vector<
250 // Start Event Next Action Guard
251 // +---------+-------------+---------+---------------------+----------------------+
252 Row < Stopped , play , Playing , ActionSequence_
253 <boost::fusion::vector<
254 TestFct,start_playback> >
255 , DummyGuard >,
256 Row < Stopped , open_close , Open , open_drawer , none >,
257 Row < Stopped , stop , Stopped , none , none >,
258 // +---------+-------------+---------+---------------------------+----------------------+
259 Row < Open , open_close , Empty , close_drawer , can_close_drawer >,
260 // +---------+-------------+---------+---------------------------+----------------------+
261 Row < Empty , open_close , Open , open_drawer , none >,
262 Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format,
263 always_true> >,
264 // +---------+-------------+---------+---------------------------+----------------------+
265 Row < Playing , stop , Stopped , stop_playback , none >,
266 Row < Playing , pause , Paused , pause_playback , none >,
267 Row < Playing , open_close , Open , stop_and_open , none >,
268 // +---------+-------------+---------+---------------------------+----------------------+
269 Row < Paused , end_pause , Playing , resume_playback , none >,
270 Row < Paused , stop , Stopped , stop_playback , none >,
271 Row < Paused , open_close , Open , stop_and_open , none >
272
273 // +---------+-------------+---------+---------------------+----------------------+
274 >/*>::type*/ {};
275 // Replaces the default no-transition response.
276 template <class FSM,class Event>
277 void no_transition(Event const&, FSM&,int)
278 {
279 BOOST_FAIL("no_transition called!");
280 }
281 // init counters
282 template <class Event,class FSM>
283 void on_entry(Event const&,FSM& fsm)
284 {
285 fsm.template get_state<player_::Stopped&>().entry_counter=0;
286 fsm.template get_state<player_::Stopped&>().exit_counter=0;
287 fsm.template get_state<player_::Open&>().entry_counter=0;
288 fsm.template get_state<player_::Open&>().exit_counter=0;
289 fsm.template get_state<player_::Empty&>().entry_counter=0;
290 fsm.template get_state<player_::Empty&>().exit_counter=0;
291 fsm.template get_state<player_::Playing&>().entry_counter=0;
292 fsm.template get_state<player_::Playing&>().exit_counter=0;
293 fsm.template get_state<player_::Paused&>().entry_counter=0;
294 fsm.template get_state<player_::Paused&>().exit_counter=0;
295 }
296
297 };
298 // Pick a back-end
299 typedef msm::back11::state_machine<player_> player;
300
301// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
302
303
304 BOOST_AUTO_TEST_CASE( back11_simple_with_functors_test )
305 {
306 player p;
307
308 p.start();
309 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
310
311 p.process_event(evt: open_close());
312 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
313 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
314 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
315
316 p.process_event(evt: open_close());
317 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
318 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
319 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
320 BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
321
322 p.process_event(
323 evt: cd_detected("louie, louie",DISK_DVD));
324 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
325 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
326 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
327
328 p.process_event(
329 evt: cd_detected("louie, louie",DISK_CD));
330 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
331 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
332 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
333 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
334 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
335 BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
336 BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly");
337
338 p.process_event(evt: pause());
339 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
340 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
341 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
342
343 // go back to Playing
344 p.process_event(evt: end_pause());
345 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
346 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
347 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
348 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().event_counter == 1,"Playing event counter incorrect");
349
350 p.process_event(evt: pause());
351 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
352 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
353 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
354
355 p.process_event(evt: stop());
356 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
357 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
358 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
359
360 p.process_event(evt: stop());
361 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
362 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
363 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
364
365 }
366}
367
368

source code of boost/libs/msm/test/Back11SimpleWithFunctors.cpp