• 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 #ifndef BOOST_MSM_NONSTANDALONE_TEST
17 #define BOOST_TEST_MODULE MyTest
18 #endif
19 #include <boost/test/unit_test.hpp>
20 
21 namespace msm = boost::msm;
22 namespace mpl = boost::mpl;
23 
24 namespace
25 {
26     // events
27     struct play {};
28     struct end_pause {};
29     struct stop {};
30     struct pause {};
31     struct open_close {};
32 
33     // A "complicated" event type that carries some data.
34     enum DiskTypeEnum
35     {
36         DISK_CD=0,
37         DISK_DVD=1
38     };
39     struct cd_detected
40     {
cd_detected__anon34264fed0111::cd_detected41         cd_detected(std::string name, DiskTypeEnum diskType)
42             : name(name),
43             disc_type(diskType)
44         {}
45 
46         std::string name;
47         DiskTypeEnum disc_type;
48     };
49 
50     // front-end: define the FSM structure
51     struct player_ : public msm::front::state_machine_def<player_>
52     {
53         unsigned int start_playback_counter;
54         unsigned int can_close_drawer_counter;
55 
player___anon34264fed0111::player_56         player_():
57         start_playback_counter(0),
58         can_close_drawer_counter(0)
59         {}
60 
61         // The list of FSM states
62         struct Empty : public msm::front::state<>
63         {
64             template <class Event,class FSM>
on_entry__anon34264fed0111::player_::Empty65             void on_entry(Event const&,FSM& ) {++entry_counter;}
66             template <class Event,class FSM>
on_exit__anon34264fed0111::player_::Empty67             void on_exit(Event const&,FSM& ) {++exit_counter;}
68             int entry_counter;
69             int exit_counter;
70         };
71         struct Open : public msm::front::state<>
72         {
73             template <class Event,class FSM>
on_entry__anon34264fed0111::player_::Open74             void on_entry(Event const&,FSM& ) {++entry_counter;}
75             template <class Event,class FSM>
on_exit__anon34264fed0111::player_::Open76             void on_exit(Event const&,FSM& ) {++exit_counter;}
77             int entry_counter;
78             int exit_counter;
79         };
80 
81         // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
82         struct Stopped : public msm::front::state<>
83         {
84             template <class Event,class FSM>
on_entry__anon34264fed0111::player_::Stopped85             void on_entry(Event const&,FSM& ) {++entry_counter;}
86             template <class Event,class FSM>
on_exit__anon34264fed0111::player_::Stopped87             void on_exit(Event const&,FSM& ) {++exit_counter;}
88             int entry_counter;
89             int exit_counter;
90         };
91 
92         struct Playing : public msm::front::state<>
93         {
94             template <class Event,class FSM>
on_entry__anon34264fed0111::player_::Playing95             void on_entry(Event const&,FSM& ) {++entry_counter;}
96             template <class Event,class FSM>
on_exit__anon34264fed0111::player_::Playing97             void on_exit(Event const&,FSM& ) {++exit_counter;}
98             int entry_counter;
99             int exit_counter;
100         };
101 
102         // state not defining any entry or exit
103         struct Paused : public msm::front::state<>
104         {
105             template <class Event,class FSM>
on_entry__anon34264fed0111::player_::Paused106             void on_entry(Event const&,FSM& ) {++entry_counter;}
107             template <class Event,class FSM>
on_exit__anon34264fed0111::player_::Paused108             void on_exit(Event const&,FSM& ) {++exit_counter;}
109             int entry_counter;
110             int exit_counter;
111         };
112 
113         // the initial state of the player SM. Must be defined
114         typedef Empty initial_state;
115 
116         // transition actions
start_playback__anon34264fed0111::player_117         void start_playback(play const&)       {++start_playback_counter; }
open_drawer__anon34264fed0111::player_118         void open_drawer(open_close const&)    {  }
store_cd_info__anon34264fed0111::player_119         void store_cd_info(cd_detected const&) {  }
stop_playback__anon34264fed0111::player_120         void stop_playback(stop const&)        {  }
pause_playback__anon34264fed0111::player_121         void pause_playback(pause const&)      {  }
resume_playback__anon34264fed0111::player_122         void resume_playback(end_pause const&)      {  }
stop_and_open__anon34264fed0111::player_123         void stop_and_open(open_close const&)  {  }
stopped_again__anon34264fed0111::player_124         void stopped_again(stop const&){}
125         // guard conditions
good_disk_format__anon34264fed0111::player_126         bool good_disk_format(cd_detected const& evt)
127         {
128             // to test a guard condition, let's say we understand only CDs, not DVD
129             if (evt.disc_type != DISK_CD)
130             {
131                 return false;
132             }
133             return true;
134         }
can_close_drawer__anon34264fed0111::player_135         bool can_close_drawer(open_close const&)
136         {
137             ++can_close_drawer_counter;
138             return true;
139         }
140 
141         typedef player_ p; // makes transition table cleaner
142 
143         // Transition table for player
144         struct transition_table : mpl::vector<
145             //    Start     Event         Next      Action               Guard
146             //  +---------+-------------+---------+---------------------+----------------------+
147           a_row < Stopped , play        , Playing , &p::start_playback                         >,
148           a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,
149            _row < Stopped , stop        , Stopped                                              >,
150             //  +---------+-------------+---------+---------------------+----------------------+
151           g_row < Open    , open_close  , Empty   ,                      &p::can_close_drawer  >,
152             //  +---------+-------------+---------+---------------------+----------------------+
153           a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,
154             row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,
155             //  +---------+-------------+---------+---------------------+----------------------+
156           a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
157           a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
158           a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
159             //  +---------+-------------+---------+---------------------+----------------------+
160           a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
161           a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
162           a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >
163             //  +---------+-------------+---------+---------------------+----------------------+
164         > {};
165         // Replaces the default no-transition response.
166         template <class FSM,class Event>
no_transition__anon34264fed0111::player_167         void no_transition(Event const& , FSM&,int)
168         {
169             BOOST_FAIL("no_transition called!");
170         }
171         // init counters
172         template <class Event,class FSM>
on_entry__anon34264fed0111::player_173         void on_entry(Event const&,FSM& fsm)
174         {
175             fsm.template get_state<player_::Stopped&>().entry_counter=0;
176             fsm.template get_state<player_::Stopped&>().exit_counter=0;
177             fsm.template get_state<player_::Open&>().entry_counter=0;
178             fsm.template get_state<player_::Open&>().exit_counter=0;
179             fsm.template get_state<player_::Empty&>().entry_counter=0;
180             fsm.template get_state<player_::Empty&>().exit_counter=0;
181             fsm.template get_state<player_::Playing&>().entry_counter=0;
182             fsm.template get_state<player_::Playing&>().exit_counter=0;
183             fsm.template get_state<player_::Paused&>().entry_counter=0;
184             fsm.template get_state<player_::Paused&>().exit_counter=0;
185         }
186 
187     };
188     // Pick a back-end
189     typedef msm::back::state_machine<player_> player;
190 
191 //    static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
192 
193 
BOOST_AUTO_TEST_CASE(my_test)194     BOOST_AUTO_TEST_CASE( my_test )
195     {
196         player p;
197 
198         p.start();
199         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
200 
201         p.process_event(open_close());
202         BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
203         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
204         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
205 
206         p.process_event(open_close());
207         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
208         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
209         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
210         BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
211 
212         p.process_event(
213             cd_detected("louie, louie",DISK_DVD));
214         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
215         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
216         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
217 
218         p.process_event(
219             cd_detected("louie, louie",DISK_CD));
220         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
221         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
222         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
223 
224         p.process_event(play());
225         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
226         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
227         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
228         BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
229 
230         p.process_event(pause());
231         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
232         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
233         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
234 
235         // go back to Playing
236         p.process_event(end_pause());
237         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
238         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
239         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
240 
241         p.process_event(pause());
242         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
243         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
244         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
245 
246         p.process_event(stop());
247         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
248         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
249         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
250 
251         p.process_event(stop());
252         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
253         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
254         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
255     }
256 }
257 
258