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__anon7dcbd3670111::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___anon7dcbd3670111::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__anon7dcbd3670111::player_::Empty77 void on_entry(Event const&,FSM& ) {++entry_counter;} 78 template <class Event,class FSM> on_exit__anon7dcbd3670111::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__anon7dcbd3670111::player_::Open86 void on_entry(Event const&,FSM& ) {++entry_counter;} 87 template <class Event,class FSM> on_exit__anon7dcbd3670111::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__anon7dcbd3670111::player_::Stopped97 void on_entry(Event const&,FSM& ) {++entry_counter;} 98 template <class Event,class FSM> on_exit__anon7dcbd3670111::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__anon7dcbd3670111::player_::Playing107 void on_entry(Event const&,FSM& ) {++entry_counter;} 108 template <class Event,class FSM> on_exit__anon7dcbd3670111::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__anon7dcbd3670111::player_::Paused118 void on_entry(Event const&,FSM& ) {++entry_counter;} 119 template <class Event,class FSM> on_exit__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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 ()__anon7dcbd3670111::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__anon7dcbd3670111::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__anon7dcbd3670111::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