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