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)
12 #include <boost/msm/back/state_machine.hpp>
13 #include <boost/msm/front/euml/euml.hpp>
15 #ifndef BOOST_MSM_NONSTANDALONE_TEST
16 #define BOOST_TEST_MODULE MyTest
18 #include <boost/test/unit_test.hpp>
21 using namespace boost::msm::front::euml
;
22 namespace msm
= boost::msm
;
26 // A "complicated" event type that carries some data.
33 BOOST_MSM_EUML_EVENT(play
)
34 BOOST_MSM_EUML_EVENT(end_pause
)
35 BOOST_MSM_EUML_EVENT(stop
)
36 BOOST_MSM_EUML_EVENT(pause
)
37 BOOST_MSM_EUML_EVENT(open_close
)
38 BOOST_MSM_EUML_EVENT(next_song
)
39 BOOST_MSM_EUML_EVENT(previous_song
)
40 BOOST_MSM_EUML_EVENT(region2_evt
)
41 // A "complicated" event type that carries some data.
42 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string
,cd_name
)
43 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum
,cd_type
)
44 BOOST_MSM_EUML_ATTRIBUTES((attributes_
<< cd_name
<< cd_type
), cd_detected_attributes
)
45 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected
,cd_detected_attributes
)
48 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter
)
49 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter
)
51 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Empty
)
52 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Open
)
53 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Stopped
)
54 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Paused
)
56 // Playing is now a state machine itself.
57 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter
)
58 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter
)
60 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Song1
)
61 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Song2
)
62 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Song3
)
63 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Region2State1
)
64 BOOST_MSM_EUML_STATE(( ++state_(entry_counter
),++state_(exit_counter
),attributes_
<< entry_counter
<< exit_counter
),Region2State2
)
66 // Playing has a transition table
67 BOOST_MSM_EUML_TRANSITION_TABLE((
68 // +------------------------------------------------------------------------------+
69 Song2
== Song1
+ next_song
,
70 Song1
== Song2
+ previous_song
[True_()] / ++fsm_(start_prev_song_guard_counter
) ,
71 Song3
== Song2
+ next_song
/ ++fsm_(start_next_song_counter
) ,
72 Song2
== Song3
+ previous_song
[True_()] / ++fsm_(start_prev_song_guard_counter
) ,
73 Region2State2
== Region2State1
+ region2_evt
74 // +------------------------------------------------------------------------------+
75 ),playing_transition_table
)
77 BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table
, //STT
78 init_
<< Song1
<< Region2State1
, // Init State
79 ++state_(entry_counter
), // Entry
80 ++state_(exit_counter
), // Exit
81 attributes_
<< entry_counter
<< exit_counter
82 << start_next_song_counter
83 << start_prev_song_guard_counter
// Attributes
86 typedef msm::back::state_machine
<Playing_
> Playing_type
;
87 Playing_type
const Playing
;
91 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter
)
92 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter
)
93 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter
)
94 BOOST_MSM_EUML_ACTION(No_Transition
)
96 template <class FSM
,class Event
>
97 void operator()(Event
const&,FSM
&,int)
99 BOOST_FAIL("no_transition called!");
102 BOOST_MSM_EUML_ACTION(good_disk_format
)
104 template <class FSM
,class EVT
,class SourceState
,class TargetState
>
105 bool operator()(EVT
const& evt
,FSM
&,SourceState
& ,TargetState
& )
107 if (evt
.get_attribute(cd_type
)!=DISK_CD
)
114 BOOST_MSM_EUML_TRANSITION_TABLE((
115 Playing
== Stopped
+ play
/ (++fsm_(start_playback_counter
),++fsm_(test_fct_counter
) ),
116 Playing
== Paused
+ end_pause
,
117 // +------------------------------------------------------------------------------+
118 Empty
== Open
+ open_close
/ ++fsm_(can_close_drawer_counter
),
119 // +------------------------------------------------------------------------------+
120 Open
== Empty
+ open_close
,
121 Open
== Paused
+ open_close
,
122 Open
== Stopped
+ open_close
,
123 Open
== Playing
+ open_close
,
124 // +------------------------------------------------------------------------------+
125 Paused
== Playing
+ pause
,
126 // +------------------------------------------------------------------------------+
127 Stopped
== Playing
+ stop
,
128 Stopped
== Paused
+ stop
,
129 Stopped
== Empty
+ cd_detected
[good_disk_format
||
130 (event_(cd_type
)==Int_
<DISK_CD
>())] / process_(play
) ,
131 Stopped
== Stopped
+ stop
132 // +------------------------------------------------------------------------------+
135 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table
, //STT
136 init_
<< Empty
, // Init State
139 attributes_
<< start_playback_counter
140 << can_close_drawer_counter
<< test_fct_counter
, // Attributes
141 configure_
<< no_configure_
, // configuration
142 No_Transition
// no_transition handler
146 typedef msm::back::state_machine
<player_
> player
;
148 // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" };
151 BOOST_AUTO_TEST_CASE( my_test
)
156 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Empty
)&>().get_attribute(entry_counter
) == 1,
157 "Empty entry not called correctly");
159 p
.process_event(open_close());
160 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 2,"Open should be active"); //Open
161 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Empty
)&>().get_attribute(exit_counter
) == 1,
162 "Empty exit not called correctly");
163 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Open
)&>().get_attribute(entry_counter
) == 1,
164 "Open entry not called correctly");
166 p
.process_event(open_close());
167 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 3,"Empty should be active"); //Empty
168 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Open
)&>().get_attribute(exit_counter
) == 1,
169 "Open exit not called correctly");
170 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Empty
)&>().get_attribute(entry_counter
) == 2,
171 "Empty entry not called correctly");
172 BOOST_CHECK_MESSAGE(p
.get_attribute(can_close_drawer_counter
) == 1,"guard not called correctly");
175 cd_detected("louie, louie",DISK_DVD
));
176 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 3,"Empty should be active"); //Empty
177 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Open
)&>().get_attribute(exit_counter
) == 1,
178 "Open exit not called correctly");
179 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Empty
)&>().get_attribute(entry_counter
) == 2,
180 "Empty entry not called correctly");
183 cd_detected("louie, louie",DISK_CD
));
184 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 4,"Playing should be active"); //Playing
185 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Empty
)&>().get_attribute(exit_counter
) == 2,
186 "Empty exit not called correctly");
187 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Stopped
)&>().get_attribute(entry_counter
) == 1,
188 "Stopped entry not called correctly");
189 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Stopped
)&>().get_attribute(exit_counter
) == 1,
190 "Stopped exit not called correctly");
191 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().get_attribute(entry_counter
) == 1,
192 "Playing entry not called correctly");
193 BOOST_CHECK_MESSAGE(p
.get_attribute(start_playback_counter
) == 1,"action not called correctly");
194 BOOST_CHECK_MESSAGE(p
.get_attribute(test_fct_counter
) == 1,"action not called correctly");
196 p
.process_event(next_song
);
197 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 4,"Playing should be active"); //Playing
198 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().current_state()[0] == 1,"Song2 should be active");
199 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().current_state()[1] == 3,"Region2State1 should be active");
201 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Region2State1
)&>().get_attribute(entry_counter
) == 1,
202 "Region2State1 entry not called correctly");
204 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song2
)&>().get_attribute(entry_counter
) == 1,
205 "Song2 entry not called correctly");
207 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song1
)&>().get_attribute(exit_counter
) == 1,
208 "Song1 exit not called correctly");
210 p
.get_state
<Playing_type
&>().get_attribute(start_next_song_counter
) == 0,
211 "submachine action not called correctly");
213 p
.process_event(next_song
);
214 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 4,"Playing should be active"); //Playing
215 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().current_state()[0] == 2,"Song3 should be active");
217 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song3
)&>().get_attribute(entry_counter
) == 1,
218 "Song3 entry not called correctly");
220 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song2
)&>().get_attribute(exit_counter
) == 1,
221 "Song2 exit not called correctly");
223 p
.get_state
<Playing_type
&>().get_attribute(start_next_song_counter
) == 1,
224 "submachine action not called correctly");
226 p
.process_event(previous_song
);
227 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 4,"Playing should be active"); //Playing
228 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().current_state()[0] == 1,"Song2 should be active");
230 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song2
)&>().get_attribute(entry_counter
) == 2,
231 "Song2 entry not called correctly");
233 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Song3
)&>().get_attribute(exit_counter
) == 1,
234 "Song3 exit not called correctly");
236 p
.get_state
<Playing_type
&>().get_attribute(start_prev_song_guard_counter
) == 1,
237 "submachine guard not called correctly");
239 p
.get_state
<Playing_type
&>().get_state
<BOOST_MSM_EUML_STATE_NAME(Region2State2
)&>().get_attribute(entry_counter
) == 0,
240 "Region2State2 entry not called correctly");
243 p
.process_event(pause());
244 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 1,"Paused should be active"); //Paused
245 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().get_attribute(exit_counter
) == 1,
246 "Playing exit not called correctly");
247 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Paused
)&>().get_attribute(entry_counter
) == 1,
248 "Paused entry not called correctly");
250 // go back to Playing
251 p
.process_event(end_pause());
252 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 4,"Playing should be active"); //Playing
253 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Paused
)&>().get_attribute(exit_counter
) == 1,
254 "Paused exit not called correctly");
255 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().get_attribute(entry_counter
) == 2,
256 "Playing entry not called correctly");
258 p
.process_event(pause());
259 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 1,"Paused should be active"); //Paused
260 BOOST_CHECK_MESSAGE(p
.get_state
<Playing_type
&>().get_attribute(exit_counter
) == 2,
261 "Playing exit not called correctly");
262 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Paused
)&>().get_attribute(entry_counter
) == 2,
263 "Paused entry not called correctly");
265 p
.process_event(stop());
266 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 0,"Stopped should be active"); //Stopped
267 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Paused
)&>().get_attribute(exit_counter
) == 2,
268 "Paused exit not called correctly");
269 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Stopped
)&>().get_attribute(entry_counter
) == 2,
270 "Stopped entry not called correctly");
272 p
.process_event(stop());
273 BOOST_CHECK_MESSAGE(p
.current_state()[0] == 0,"Stopped should be active"); //Stopped
274 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Stopped
)&>().get_attribute(exit_counter
) == 2,
275 "Stopped exit not called correctly");
276 BOOST_CHECK_MESSAGE(p
.get_state
<BOOST_MSM_EUML_STATE_NAME(Stopped
)&>().get_attribute(entry_counter
) == 3,
277 "Stopped entry not called correctly");