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

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