• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <boost/msm/front/functor_row.hpp>
17 #include <boost/msm/front/euml/common.hpp>
18 
19 #ifndef BOOST_MSM_NONSTANDALONE_TEST
20 #define BOOST_TEST_MODULE MyTest
21 #endif
22 #include <boost/test/unit_test.hpp>
23 
24 using namespace std;
25 namespace msm = boost::msm;
26 using namespace msm::front;
27 namespace mpl = boost::mpl;
28 
29 namespace
30 {
31     // events
32     struct play {};
33     struct end_pause {};
34     struct stop {};
35     struct pause {};
36     struct open_close {};
37     struct internal_evt {};
38     struct to_ignore {};
39 
40     // A "complicated" event type that carries some data.
41     enum DiskTypeEnum
42     {
43         DISK_CD=0,
44         DISK_DVD=1
45     };
46     struct cd_detected
47     {
cd_detected__anon6e83b8490111::cd_detected48         cd_detected(std::string name, DiskTypeEnum diskType)
49             : name(name),
50             disc_type(diskType)
51         {}
52 
53         std::string name;
54         DiskTypeEnum disc_type;
55     };
56 
57     // front-end: define the FSM structure
58     struct player_ : public msm::front::state_machine_def<player_>
59     {
60         unsigned int start_playback_counter;
61         unsigned int can_close_drawer_counter;
62         unsigned int internal_action_counter;
63         unsigned int internal_guard_counter;
64 
player___anon6e83b8490111::player_65         player_():
66         start_playback_counter(0),
67         can_close_drawer_counter(0),
68         internal_action_counter(0),
69         internal_guard_counter(0)
70         {}
71 
72         // The list of FSM states
73         struct Empty : public msm::front::state<>
74         {
75             template <class Event,class FSM>
on_entry__anon6e83b8490111::player_::Empty76             void on_entry(Event const&,FSM& ) {++entry_counter;}
77             template <class Event,class FSM>
on_exit__anon6e83b8490111::player_::Empty78             void on_exit(Event const&,FSM& ) {++exit_counter;}
79             int entry_counter;
80             int exit_counter;
81             unsigned int empty_internal_guard_counter;
82             unsigned int empty_internal_action_counter;
83             struct internal_guard_fct
84             {
85                 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anon6e83b8490111::player_::Empty::internal_guard_fct86                 bool operator()(EVT const& ,FSM&,SourceState& src,TargetState& )
87                 {
88                     ++src.empty_internal_guard_counter;
89                     return false;
90                 }
91             };
92             struct internal_action_fct
93             {
94                 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anon6e83b8490111::player_::Empty::internal_action_fct95                 void operator()(EVT const& ,FSM& ,SourceState& src,TargetState& )
96                 {
97                     ++src.empty_internal_action_counter;
98                 }
99             };
100             // Transition table for Empty
101             struct internal_transition_table : mpl::vector<
102                 //    Start     Event         Next      Action               Guard
103            Internal <           internal_evt          , internal_action_fct ,internal_guard_fct    >
104                 //  +---------+-------------+---------+---------------------+----------------------+
105             > {};
106         };
107         struct Open : public msm::front::state<>
108         {
109             template <class Event,class FSM>
on_entry__anon6e83b8490111::player_::Open110             void on_entry(Event const&,FSM& ) {++entry_counter;}
111             template <class Event,class FSM>
on_exit__anon6e83b8490111::player_::Open112             void on_exit(Event const&,FSM& ) {++exit_counter;}
113             int entry_counter;
114             int exit_counter;
115         };
116 
117         // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
118         struct Stopped : public msm::front::state<>
119         {
120             template <class Event,class FSM>
on_entry__anon6e83b8490111::player_::Stopped121             void on_entry(Event const&,FSM& ) {++entry_counter;}
122             template <class Event,class FSM>
on_exit__anon6e83b8490111::player_::Stopped123             void on_exit(Event const&,FSM& ) {++exit_counter;}
124             int entry_counter;
125             int exit_counter;
126         };
127 
128         struct Playing : public msm::front::state<>
129         {
130             template <class Event,class FSM>
on_entry__anon6e83b8490111::player_::Playing131             void on_entry(Event const&,FSM& ) {++entry_counter;}
132             template <class Event,class FSM>
on_exit__anon6e83b8490111::player_::Playing133             void on_exit(Event const&,FSM& ) {++exit_counter;}
134             int entry_counter;
135             int exit_counter;
136         };
137 
138         // state not defining any entry or exit
139         struct Paused : public msm::front::state<>
140         {
141             template <class Event,class FSM>
on_entry__anon6e83b8490111::player_::Paused142             void on_entry(Event const&,FSM& ) {++entry_counter;}
143             template <class Event,class FSM>
on_exit__anon6e83b8490111::player_::Paused144             void on_exit(Event const&,FSM& ) {++exit_counter;}
145             int entry_counter;
146             int exit_counter;
147         };
148 
149         // the initial state of the player SM. Must be defined
150         typedef Empty initial_state;
151 
152         // transition actions
start_playback__anon6e83b8490111::player_153         void start_playback(play const&)       {++start_playback_counter; }
open_drawer__anon6e83b8490111::player_154         void open_drawer(open_close const&)    {  }
store_cd_info__anon6e83b8490111::player_155         void store_cd_info(cd_detected const&) {  }
stop_playback__anon6e83b8490111::player_156         void stop_playback(stop const&)        {  }
pause_playback__anon6e83b8490111::player_157         void pause_playback(pause const&)      {  }
resume_playback__anon6e83b8490111::player_158         void resume_playback(end_pause const&)      {  }
stop_and_open__anon6e83b8490111::player_159         void stop_and_open(open_close const&)  {  }
stopped_again__anon6e83b8490111::player_160         void stopped_again(stop const&){}
161         struct internal_action
162         {
163             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anon6e83b8490111::player_::internal_action164             void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
165             {
166                 ++fsm.internal_action_counter;
167             }
168         };
169         struct internal_guard
170         {
171             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anon6e83b8490111::player_::internal_guard172             bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
173             {
174                 ++fsm.internal_guard_counter;
175                 return false;
176             }
177         };
178         struct internal_guard2
179         {
180             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anon6e83b8490111::player_::internal_guard2181             bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
182             {
183                 ++fsm.internal_guard_counter;
184                 return true;
185             }
186         };
187         // guard conditions
good_disk_format__anon6e83b8490111::player_188         bool good_disk_format(cd_detected const& evt)
189         {
190             // to test a guard condition, let's say we understand only CDs, not DVD
191             if (evt.disc_type != DISK_CD)
192             {
193                 return false;
194             }
195             return true;
196         }
can_close_drawer__anon6e83b8490111::player_197         bool can_close_drawer(open_close const&)
198         {
199             ++can_close_drawer_counter;
200             return true;
201         }
202 
203         typedef player_ p; // makes transition table cleaner
204 
205         // Transition table for player
206         struct transition_table : mpl::vector<
207             //    Start     Event         Next      Action               Guard
208             //  +---------+-------------+---------+---------------------+----------------------+
209           a_row < Stopped , play        , Playing , &p::start_playback                         >,
210           a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,
211            _row < Stopped , stop        , Stopped                                              >,
212             //  +---------+-------------+---------+---------------------+----------------------+
213           g_row < Open    , open_close  , Empty   ,                      &p::can_close_drawer  >,
214             //  +---------+-------------+---------+---------------------+----------------------+
215           a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,
216             row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,
217             Row < Empty   , internal_evt, none    , internal_action     ,internal_guard2       >,
218             Row < Empty   , to_ignore   , none    , none                , none                 >,
219             Row < Empty   , cd_detected , none    , none                ,internal_guard        >,
220             //  +---------+-------------+---------+---------------------+----------------------+
221           a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
222           a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
223           a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
224             //  +---------+-------------+---------+---------------------+----------------------+
225           a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
226           a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
227           a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >
228             //  +---------+-------------+---------+---------------------+----------------------+
229         > {};
230         // Replaces the default no-transition response.
231         template <class FSM,class Event>
no_transition__anon6e83b8490111::player_232         void no_transition(Event const&, FSM&,int)
233         {
234             BOOST_FAIL("no_transition called!");
235         }
236         // init counters
237         template <class Event,class FSM>
on_entry__anon6e83b8490111::player_238         void on_entry(Event const&,FSM& fsm)
239         {
240             fsm.template get_state<player_::Stopped&>().entry_counter=0;
241             fsm.template get_state<player_::Stopped&>().exit_counter=0;
242             fsm.template get_state<player_::Open&>().entry_counter=0;
243             fsm.template get_state<player_::Open&>().exit_counter=0;
244             fsm.template get_state<player_::Empty&>().entry_counter=0;
245             fsm.template get_state<player_::Empty&>().exit_counter=0;
246             fsm.template get_state<player_::Empty&>().empty_internal_guard_counter=0;
247             fsm.template get_state<player_::Empty&>().empty_internal_action_counter=0;
248             fsm.template get_state<player_::Playing&>().entry_counter=0;
249             fsm.template get_state<player_::Playing&>().exit_counter=0;
250             fsm.template get_state<player_::Paused&>().entry_counter=0;
251             fsm.template get_state<player_::Paused&>().exit_counter=0;
252         }
253 
254     };
255     // Pick a back-end
256     typedef msm::back::state_machine<player_> player;
257 
258 //    static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
259 
260 
BOOST_AUTO_TEST_CASE(my_test)261     BOOST_AUTO_TEST_CASE( my_test )
262     {
263         player p;
264 
265         p.start();
266         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
267         // internal events
268         p.process_event(to_ignore());
269         p.process_event(internal_evt());
270         BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly");
271         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly");
272         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_action_counter == 0,"Empty internal action not called correctly");
273         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_guard_counter == 1,"Empty internal guard not called correctly");
274 
275         p.process_event(open_close());
276         BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
277         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
278         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
279 
280         p.process_event(open_close());
281         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
282         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
283         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
284         BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
285 
286         p.process_event(
287             cd_detected("louie, louie",DISK_DVD));
288         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
289         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
290         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
291         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly");
292 
293         p.process_event(
294             cd_detected("louie, louie",DISK_CD));
295         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
296         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
297         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
298         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly");
299 
300         p.process_event(play());
301         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
302         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
303         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
304         BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
305 
306         p.process_event(pause());
307         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
308         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
309         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
310 
311         // go back to Playing
312         p.process_event(end_pause());
313         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
314         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
315         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
316 
317         p.process_event(pause());
318         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
319         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
320         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
321 
322         p.process_event(stop());
323         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
324         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
325         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
326 
327         p.process_event(stop());
328         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
329         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
330         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
331     }
332 }
333 
334