• 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     struct internal_evt {};
33     struct to_ignore {};
34 
35     // A "complicated" event type that carries some data.
36     enum DiskTypeEnum
37     {
38         DISK_CD=0,
39         DISK_DVD=1
40     };
41     struct cd_detected
42     {
cd_detected__anonaa5d9ad40111::cd_detected43         cd_detected(std::string name, DiskTypeEnum diskType)
44             : name(name),
45             disc_type(diskType)
46         {}
47 
48         std::string name;
49         DiskTypeEnum disc_type;
50     };
51 
52     // front-end: define the FSM structure
53     struct player_ : public msm::front::state_machine_def<player_>
54     {
55         unsigned int start_playback_counter;
56         unsigned int can_close_drawer_counter;
57         unsigned int internal_action_counter;
58         unsigned int internal_guard_counter;
59 
player___anonaa5d9ad40111::player_60         player_():
61         start_playback_counter(0),
62         can_close_drawer_counter(0),
63         internal_action_counter(0),
64         internal_guard_counter(0)
65         {}
66 
67         // The list of FSM states
68         struct Empty : public msm::front::state<>
69         {
70             template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_::Empty71             void on_entry(Event const&,FSM& ) {++entry_counter;}
72             template <class Event,class FSM>
on_exit__anonaa5d9ad40111::player_::Empty73             void on_exit(Event const&,FSM& ) {++exit_counter;}
74             int entry_counter;
75             int exit_counter;
76         };
77         struct Open : public msm::front::state<>
78         {
79             template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_::Open80             void on_entry(Event const&,FSM& ) {++entry_counter;}
81             template <class Event,class FSM>
on_exit__anonaa5d9ad40111::player_::Open82             void on_exit(Event const&,FSM& ) {++exit_counter;}
83             int entry_counter;
84             int exit_counter;
85         };
86 
87         // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
88         struct Stopped : public msm::front::state<>
89         {
90             template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_::Stopped91             void on_entry(Event const&,FSM& ) {++entry_counter;}
92             template <class Event,class FSM>
on_exit__anonaa5d9ad40111::player_::Stopped93             void on_exit(Event const&,FSM& ) {++exit_counter;}
94             int entry_counter;
95             int exit_counter;
96         };
97 
98         struct Playing : public msm::front::state<>
99         {
100             template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_::Playing101             void on_entry(Event const&,FSM& ) {++entry_counter;}
102             template <class Event,class FSM>
on_exit__anonaa5d9ad40111::player_::Playing103             void on_exit(Event const&,FSM& ) {++exit_counter;}
104             int entry_counter;
105             int exit_counter;
106         };
107 
108         // state not defining any entry or exit
109         struct Paused : public msm::front::state<>
110         {
111             template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_::Paused112             void on_entry(Event const&,FSM& ) {++entry_counter;}
113             template <class Event,class FSM>
on_exit__anonaa5d9ad40111::player_::Paused114             void on_exit(Event const&,FSM& ) {++exit_counter;}
115             int entry_counter;
116             int exit_counter;
117         };
118 
119         // the initial state of the player SM. Must be defined
120         typedef Empty initial_state;
121 
122         // transition actions
start_playback__anonaa5d9ad40111::player_123         void start_playback(play const&)       {++start_playback_counter; }
open_drawer__anonaa5d9ad40111::player_124         void open_drawer(open_close const&)    {  }
store_cd_info__anonaa5d9ad40111::player_125         void store_cd_info(cd_detected const&) {  }
stop_playback__anonaa5d9ad40111::player_126         void stop_playback(stop const&)        {  }
pause_playback__anonaa5d9ad40111::player_127         void pause_playback(pause const&)      {  }
resume_playback__anonaa5d9ad40111::player_128         void resume_playback(end_pause const&)      {  }
stop_and_open__anonaa5d9ad40111::player_129         void stop_and_open(open_close const&)  {  }
stopped_again__anonaa5d9ad40111::player_130         void stopped_again(stop const&){}
internal_action__anonaa5d9ad40111::player_131         void internal_action(internal_evt const&){++internal_action_counter; }
internal_guard__anonaa5d9ad40111::player_132         bool internal_guard(cd_detected const&){++internal_guard_counter;return false;}
internal_guard2__anonaa5d9ad40111::player_133         bool internal_guard2(internal_evt const&){++internal_guard_counter;return true;}
134         // guard conditions
good_disk_format__anonaa5d9ad40111::player_135         bool good_disk_format(cd_detected const& evt)
136         {
137             // to test a guard condition, let's say we understand only CDs, not DVD
138             if (evt.disc_type != DISK_CD)
139             {
140                 return false;
141             }
142             return true;
143         }
can_close_drawer__anonaa5d9ad40111::player_144         bool can_close_drawer(open_close const&)
145         {
146             ++can_close_drawer_counter;
147             return true;
148         }
149 
150         typedef player_ p; // makes transition table cleaner
151 
152         // Transition table for player
153         struct transition_table : mpl::vector<
154             //    Start     Event         Next      Action               Guard
155             //  +---------+-------------+---------+---------------------+----------------------+
156           a_row < Stopped , play        , Playing , &p::start_playback                         >,
157           a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,
158            _row < Stopped , stop        , Stopped                                              >,
159             //  +---------+-------------+---------+---------------------+----------------------+
160           g_row < Open    , open_close  , Empty   ,                      &p::can_close_drawer  >,
161             //  +---------+-------------+---------+---------------------+----------------------+
162           a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,
163             row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,
164            irow < Empty   , internal_evt,           &p::internal_action ,&p::internal_guard2   >,
165           _irow < Empty   , to_ignore                                                          >,
166          g_irow < Empty   , cd_detected                                 ,&p::internal_guard    >,
167             //  +---------+-------------+---------+---------------------+----------------------+
168           a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
169           a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
170           a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
171             //  +---------+-------------+---------+---------------------+----------------------+
172           a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
173           a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
174           a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >
175             //  +---------+-------------+---------+---------------------+----------------------+
176         > {};
177         // Replaces the default no-transition response.
178         template <class FSM,class Event>
no_transition__anonaa5d9ad40111::player_179         void no_transition(Event const&, FSM&,int)
180         {
181             BOOST_FAIL("no_transition called!");
182         }
183         // init counters
184         template <class Event,class FSM>
on_entry__anonaa5d9ad40111::player_185         void on_entry(Event const&,FSM& fsm)
186         {
187             fsm.template get_state<player_::Stopped&>().entry_counter=0;
188             fsm.template get_state<player_::Stopped&>().exit_counter=0;
189             fsm.template get_state<player_::Open&>().entry_counter=0;
190             fsm.template get_state<player_::Open&>().exit_counter=0;
191             fsm.template get_state<player_::Empty&>().entry_counter=0;
192             fsm.template get_state<player_::Empty&>().exit_counter=0;
193             fsm.template get_state<player_::Playing&>().entry_counter=0;
194             fsm.template get_state<player_::Playing&>().exit_counter=0;
195             fsm.template get_state<player_::Paused&>().entry_counter=0;
196             fsm.template get_state<player_::Paused&>().exit_counter=0;
197         }
198 
199     };
200     // Pick a back-end
201     typedef msm::back::state_machine<player_> player;
202 
203 //    static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
204 
205 
BOOST_AUTO_TEST_CASE(my_test)206     BOOST_AUTO_TEST_CASE( my_test )
207     {
208         player p;
209 
210         p.start();
211         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
212         // internal events
213         p.process_event(to_ignore());
214         p.process_event(internal_evt());
215         BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly");
216         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly");
217 
218         p.process_event(open_close());
219         BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
220         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
221         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
222 
223         p.process_event(open_close());
224         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
225         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
226         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
227         BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
228 
229         p.process_event(
230             cd_detected("louie, louie",DISK_DVD));
231         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
232         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
233         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
234         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly");
235 
236         p.process_event(
237             cd_detected("louie, louie",DISK_CD));
238         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
239         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
240         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
241         BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly");
242 
243         p.process_event(play());
244         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
245         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
246         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
247         BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
248 
249         p.process_event(pause());
250         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
251         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
252         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
253 
254         // go back to Playing
255         p.process_event(end_pause());
256         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
257         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
258         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
259 
260         p.process_event(pause());
261         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
262         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
263         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
264 
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<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
268         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
269 
270         p.process_event(stop());
271         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
272         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
273         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
274     }
275 }
276 
277