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/msm/back/state_machine.hpp>
12#include <boost/msm/front/euml/euml.hpp>
13#ifndef BOOST_MSM_NONSTANDALONE_TEST
14#define BOOST_TEST_MODULE MySimpleInternalEumlTest
15#endif
16#include <boost/test/unit_test.hpp>
17
18using namespace std;
19using namespace boost::msm::front::euml;
20namespace msm = boost::msm;
21
22namespace
23{
24 // A "complicated" event type that carries some data.
25 enum DiskTypeEnum
26 {
27 DISK_CD=0,
28 DISK_DVD=1
29 };
30 // events
31 BOOST_MSM_EUML_EVENT(play)
32 BOOST_MSM_EUML_EVENT(end_pause)
33 BOOST_MSM_EUML_EVENT(stop)
34 BOOST_MSM_EUML_EVENT(pause)
35 BOOST_MSM_EUML_EVENT(open_close)
36 BOOST_MSM_EUML_EVENT(internal_evt)
37 BOOST_MSM_EUML_EVENT(to_ignore)
38 // A "complicated" event type that carries some data.
39 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name)
40 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)
41 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes)
42 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes)
43
44 //states
45 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter)
46 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter)
47
48 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open)
49 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped)
50 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing)
51 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused)
52
53 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_guard_counter)
54 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter)
55
56 BOOST_MSM_EUML_ACTION(internal_guard_fct)
57 {
58 template <class FSM,class EVT,class SourceState,class TargetState>
59 bool operator()(EVT const&, FSM& ,SourceState& src,TargetState& )
60 {
61 ++src.get_attribute(empty_internal_guard_counter);
62 return false;
63 }
64 };
65 BOOST_MSM_EUML_DECLARE_STATE((++state_(entry_counter),++state_(exit_counter),
66 attributes_ << entry_counter << exit_counter
67 << empty_internal_guard_counter << empty_internal_action_counter),Empty_def)
68 // derive to be able to add an internal transition table
69 struct Empty_impl : public Empty_def
70 {
71 Empty_impl(){}
72 BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE((
73 internal_evt [internal_guard_fct] / ++source_(empty_internal_action_counter)
74 ))
75 };
76 // declare an instance for the stt as we are manually declaring a state
77 Empty_impl const Empty;
78
79 //fsm
80 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter)
81 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter)
82 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_action_counter)
83 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_guard_counter)
84 BOOST_MSM_EUML_ACTION(No_Transition)
85 {
86 template <class FSM,class Event>
87 void operator()(Event const&,FSM&,int)
88 {
89 BOOST_FAIL("no_transition called!");
90 }
91 };
92 BOOST_MSM_EUML_ACTION(good_disk_format)
93 {
94 template <class FSM,class EVT,class SourceState,class TargetState>
95 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
96 {
97 if (evt.get_attribute(cd_type)!=DISK_CD)
98 {
99 return false;
100 }
101 return true;
102 }
103 };
104 BOOST_MSM_EUML_ACTION(internal_guard)
105 {
106 template <class FSM,class EVT,class SourceState,class TargetState>
107 bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& )
108 {
109 ++fsm.get_attribute(internal_guard_counter);
110 return false;
111 }
112 };
113 BOOST_MSM_EUML_ACTION(internal_guard2)
114 {
115 template <class FSM,class EVT,class SourceState,class TargetState>
116 bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& )
117 {
118 ++fsm.get_attribute(internal_guard_counter);
119 return true;
120 }
121 };
122 BOOST_MSM_EUML_TRANSITION_TABLE((
123 Playing == Stopped + play / ++fsm_(start_playback_counter) ,
124 Playing == Paused + end_pause ,
125 // +------------------------------------------------------------------------------+
126 Empty == Open + open_close / ++fsm_(can_close_drawer_counter),
127 Empty + to_ignore ,
128 Empty + internal_evt [internal_guard2] / ++fsm_(internal_action_counter) ,
129 Empty + cd_detected [internal_guard] ,
130 // +------------------------------------------------------------------------------+
131 Open == Empty + open_close ,
132 Open == Paused + open_close ,
133 Open == Stopped + open_close ,
134 Open == Playing + open_close ,
135 // +------------------------------------------------------------------------------+
136 Paused == Playing + pause ,
137 // +------------------------------------------------------------------------------+
138 Stopped == Playing + stop ,
139 Stopped == Paused + stop ,
140 Stopped == Empty + cd_detected [good_disk_format] ,
141 Stopped == Stopped + stop
142 // +------------------------------------------------------------------------------+
143 ),transition_table)
144
145 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
146 init_ << Empty, // Init State
147 no_action, // Entry
148 no_action, // Exit
149 attributes_ << start_playback_counter << can_close_drawer_counter
150 << internal_action_counter << internal_guard_counter, // Attributes
151 configure_ << no_configure_, // configuration
152 No_Transition // no_transition handler
153 ),
154 player_) //fsm name
155
156 typedef msm::back::state_machine<player_> player;
157
158// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" };
159
160
161 BOOST_AUTO_TEST_CASE( simple_internal_euml_test )
162 {
163 player p;
164
165 p.start();
166 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1,
167 "Empty entry not called correctly");
168
169 // internal events
170 p.process_event(evt: to_ignore);
171 p.process_event(evt: internal_evt);
172 BOOST_CHECK_MESSAGE(p.get_attribute(internal_action_counter) == 1,"Internal action not called correctly");
173 BOOST_CHECK_MESSAGE(p.get_attribute(internal_guard_counter) == 1,"Internal guard not called correctly");
174 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_action_counter) == 0,
175 "Empty internal action not called correctly");
176 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_guard_counter) == 1,
177 "Empty internal guard not called correctly");
178
179 p.process_event(evt: open_close());
180 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open
181 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 1,
182 "Empty exit not called correctly");
183 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1,
184 "Open entry not called correctly");
185
186 p.process_event(evt: open_close());
187 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
188 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1,
189 "Open exit not called correctly");
190 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2,
191 "Empty entry not called correctly");
192 BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly");
193
194 p.process_event(
195 evt: cd_detected("louie, louie",DISK_DVD));
196 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
197 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1,
198 "Open exit not called correctly");
199 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2,
200 "Empty entry not called correctly");
201
202 p.process_event(
203 evt: cd_detected("louie, louie",DISK_CD));
204 p.process_event(evt: play);
205 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
206 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 2,
207 "Empty exit not called correctly");
208 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1,
209 "Stopped entry not called correctly");
210 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1,
211 "Stopped exit not called correctly");
212 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1,
213 "Playing entry not called correctly");
214 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly");
215
216 p.process_event(evt: pause());
217 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
218 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1,
219 "Playing exit not called correctly");
220 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1,
221 "Paused entry not called correctly");
222
223 // go back to Playing
224 p.process_event(evt: end_pause());
225 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
226 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1,
227 "Paused exit not called correctly");
228 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2,
229 "Playing entry not called correctly");
230
231 p.process_event(evt: pause());
232 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
233 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2,
234 "Playing exit not called correctly");
235 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2,
236 "Paused entry not called correctly");
237
238 p.process_event(evt: stop());
239 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
240 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2,
241 "Paused exit not called correctly");
242 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2,
243 "Stopped entry not called correctly");
244
245 p.process_event(evt: stop());
246 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
247 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2,
248 "Stopped exit not called correctly");
249 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3,
250 "Stopped entry not called correctly");
251 }
252}
253
254

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