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

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