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 NextSong {}; 33 struct PreviousSong {}; 34 struct cd_detected 35 { cd_detected__anondaf65f3f0111::cd_detected36 cd_detected(std::string name) 37 : name(name) 38 {} 39 std::string name; 40 }; 41 42 // front-end: define the FSM structure 43 struct player_ : public msm::front::state_machine_def<player_> 44 { 45 unsigned int start_playback_counter; 46 unsigned int can_close_drawer_counter; 47 player___anondaf65f3f0111::player_48 player_(): 49 start_playback_counter(0), 50 can_close_drawer_counter(0) 51 {} 52 // The list of FSM states 53 struct Empty : public msm::front::state<> 54 { 55 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Empty56 void on_entry(Event const&,FSM& ) {++entry_counter;} 57 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Empty58 void on_exit(Event const&,FSM& ) {++exit_counter;} 59 int entry_counter; 60 int exit_counter; 61 }; 62 struct Open : public msm::front::state<> 63 { 64 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Open65 void on_entry(Event const&,FSM& ) {++entry_counter;} 66 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Open67 void on_exit(Event const&,FSM& ) {++exit_counter;} 68 int entry_counter; 69 int exit_counter; 70 }; 71 72 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing 73 struct Stopped : public msm::front::state<> 74 { 75 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Stopped76 void on_entry(Event const&,FSM& ) {++entry_counter;} 77 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Stopped78 void on_exit(Event const&,FSM& ) {++exit_counter;} 79 int entry_counter; 80 int exit_counter; 81 }; 82 83 struct Playing_ : public msm::front::state_machine_def<Playing_> 84 { 85 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Playing_86 void on_entry(Event const&,FSM& ) {++entry_counter;} 87 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Playing_88 void on_exit(Event const&,FSM& ) {++exit_counter;} 89 int entry_counter; 90 int exit_counter; 91 unsigned int start_next_song_counter; 92 unsigned int start_prev_song_guard_counter; 93 Playing___anondaf65f3f0111::player_::Playing_94 Playing_(): 95 start_next_song_counter(0), 96 start_prev_song_guard_counter(0) 97 {} 98 99 // The list of FSM states 100 struct Song1 : public msm::front::state<> 101 { 102 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Playing_::Song1103 void on_entry(Event const&,FSM& ) {++entry_counter;} 104 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Playing_::Song1105 void on_exit(Event const&,FSM& ) {++exit_counter;} 106 int entry_counter; 107 int exit_counter; 108 }; 109 struct Song2 : public msm::front::state<> 110 { 111 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Playing_::Song2112 void on_entry(Event const&,FSM& ) {++entry_counter;} 113 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Playing_::Song2114 void on_exit(Event const&,FSM& ) {++exit_counter;} 115 int entry_counter; 116 int exit_counter; 117 }; 118 struct Song3 : public msm::front::state<> 119 { 120 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Playing_::Song3121 void on_entry(Event const&,FSM& ) {++entry_counter;} 122 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Playing_::Song3123 void on_exit(Event const&,FSM& ) {++exit_counter;} 124 int entry_counter; 125 int exit_counter; 126 }; 127 // the initial state. Must be defined 128 typedef Song1 initial_state; 129 // transition actions start_next_song__anondaf65f3f0111::player_::Playing_130 void start_next_song(NextSong const&) {++start_next_song_counter; } start_prev_song__anondaf65f3f0111::player_::Playing_131 void start_prev_song(PreviousSong const&) { } 132 // guard conditions start_prev_song_guard__anondaf65f3f0111::player_::Playing_133 bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } 134 135 typedef Playing_ pl; // makes transition table cleaner 136 // Transition table for Playing 137 struct transition_table : mpl::vector4< 138 // Start Event Next Action Guard 139 // +---------+-------------+---------+---------------------+----------------------+ 140 _row < Song1 , NextSong , Song2 >, 141 row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, 142 a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, 143 g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> 144 // +---------+-------------+---------+---------------------+----------------------+ 145 > {}; 146 // Replaces the default no-transition response. 147 template <class FSM,class Event> no_transition__anondaf65f3f0111::player_::Playing_148 void no_transition(Event const&, FSM&,int) 149 { 150 BOOST_FAIL("no_transition called!"); 151 } 152 }; 153 // back-end 154 typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; 155 156 // state not defining any entry or exit 157 struct Paused : public msm::front::state<> 158 { 159 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_::Paused160 void on_entry(Event const&,FSM& ) {++entry_counter;} 161 template <class Event,class FSM> on_exit__anondaf65f3f0111::player_::Paused162 void on_exit(Event const&,FSM& ) {++exit_counter;} 163 int entry_counter; 164 int exit_counter; 165 }; 166 167 // the initial state of the player SM. Must be defined 168 typedef Empty initial_state; 169 170 // transition actions start_playback__anondaf65f3f0111::player_171 void start_playback(play const&) {++start_playback_counter; } open_drawer__anondaf65f3f0111::player_172 void open_drawer(open_close const&) { } store_cd_info__anondaf65f3f0111::player_173 void store_cd_info(cd_detected const&) { } stop_playback__anondaf65f3f0111::player_174 void stop_playback(stop const&) { } pause_playback__anondaf65f3f0111::player_175 void pause_playback(pause const&) { } resume_playback__anondaf65f3f0111::player_176 void resume_playback(end_pause const&) { } stop_and_open__anondaf65f3f0111::player_177 void stop_and_open(open_close const&) { } stopped_again__anondaf65f3f0111::player_178 void stopped_again(stop const&){} 179 //guards can_close_drawer__anondaf65f3f0111::player_180 bool can_close_drawer(open_close const&) 181 { 182 ++can_close_drawer_counter; 183 return true; 184 } 185 186 187 typedef player_ p; // makes transition table cleaner 188 189 // Transition table for player 190 struct transition_table : mpl::vector< 191 // Start Event Next Action Guard 192 // +---------+-------------+---------+---------------------+----------------------+ 193 a_row < Stopped , play , Playing , &p::start_playback >, 194 a_row < Stopped , open_close , Open , &p::open_drawer >, 195 _row < Stopped , stop , Stopped >, 196 // +---------+-------------+---------+---------------------+----------------------+ 197 g_row < Open , open_close , Empty , &p::can_close_drawer >, 198 // +---------+-------------+---------+---------------------+----------------------+ 199 a_row < Empty , open_close , Open , &p::open_drawer >, 200 a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, 201 // +---------+-------------+---------+---------------------+----------------------+ 202 a_row < Playing , stop , Stopped , &p::stop_playback >, 203 a_row < Playing , pause , Paused , &p::pause_playback >, 204 a_row < Playing , open_close , Open , &p::stop_and_open >, 205 // +---------+-------------+---------+---------------------+----------------------+ 206 a_row < Paused , end_pause , Playing , &p::resume_playback >, 207 a_row < Paused , stop , Stopped , &p::stop_playback >, 208 a_row < Paused , open_close , Open , &p::stop_and_open > 209 // +---------+-------------+---------+---------------------+----------------------+ 210 > {}; 211 // Replaces the default no-transition response. 212 template <class FSM,class Event> no_transition__anondaf65f3f0111::player_213 void no_transition(Event const& , FSM&,int ) 214 { 215 BOOST_FAIL("no_transition called!"); 216 } 217 // init counters 218 template <class Event,class FSM> on_entry__anondaf65f3f0111::player_219 void on_entry(Event const&,FSM& fsm) 220 { 221 fsm.template get_state<player_::Stopped&>().entry_counter=0; 222 fsm.template get_state<player_::Stopped&>().exit_counter=0; 223 fsm.template get_state<player_::Open&>().entry_counter=0; 224 fsm.template get_state<player_::Open&>().exit_counter=0; 225 fsm.template get_state<player_::Empty&>().entry_counter=0; 226 fsm.template get_state<player_::Empty&>().exit_counter=0; 227 fsm.template get_state<player_::Playing&>().entry_counter=0; 228 fsm.template get_state<player_::Playing&>().exit_counter=0; 229 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; 230 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; 231 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; 232 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; 233 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; 234 fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; 235 fsm.template get_state<player_::Paused&>().entry_counter=0; 236 fsm.template get_state<player_::Paused&>().exit_counter=0; 237 } 238 239 }; 240 // Pick a back-end 241 typedef msm::back::state_machine<player_> player; 242 243 // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; 244 245 BOOST_AUTO_TEST_CASE(my_test)246 BOOST_AUTO_TEST_CASE( my_test ) 247 { 248 player p; 249 250 p.start(); 251 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); 252 253 p.process_event(open_close()); 254 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open 255 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); 256 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); 257 258 p.process_event(open_close()); 259 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty 260 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); 261 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); 262 BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); 263 264 p.process_event(cd_detected("louie, louie")); 265 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 266 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); 267 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); 268 269 p.process_event(play()); 270 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 271 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); 272 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); 273 BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); 274 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); 275 BOOST_CHECK_MESSAGE( 276 p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, 277 "Song1 entry not called correctly"); 278 279 p.process_event(NextSong()); 280 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 281 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); 282 BOOST_CHECK_MESSAGE( 283 p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, 284 "Song2 entry not called correctly"); 285 BOOST_CHECK_MESSAGE( 286 p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, 287 "Song1 exit not called correctly"); 288 BOOST_CHECK_MESSAGE( 289 p.get_state<player_::Playing&>().start_next_song_counter == 0, 290 "submachine action not called correctly"); 291 292 p.process_event(NextSong()); 293 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 294 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); 295 BOOST_CHECK_MESSAGE( 296 p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, 297 "Song3 entry not called correctly"); 298 BOOST_CHECK_MESSAGE( 299 p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, 300 "Song2 exit not called correctly"); 301 BOOST_CHECK_MESSAGE( 302 p.get_state<player_::Playing&>().start_next_song_counter == 1, 303 "submachine action not called correctly"); 304 305 p.process_event(PreviousSong()); 306 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 307 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); 308 BOOST_CHECK_MESSAGE( 309 p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, 310 "Song2 entry not called correctly"); 311 BOOST_CHECK_MESSAGE( 312 p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, 313 "Song3 exit not called correctly"); 314 BOOST_CHECK_MESSAGE( 315 p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, 316 "submachine guard not called correctly"); 317 318 p.process_event(pause()); 319 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused 320 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); 321 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); 322 323 // go back to Playing 324 p.process_event(end_pause()); 325 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 326 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); 327 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); 328 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); 329 BOOST_CHECK_MESSAGE( 330 p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 3, 331 "Song2 entry not called correctly"); 332 333 p.process_event(pause()); 334 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused 335 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); 336 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); 337 338 p.process_event(stop()); 339 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 340 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); 341 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); 342 343 p.process_event(stop()); 344 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 345 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); 346 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); 347 348 p.process_event(play()); 349 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); 350 BOOST_CHECK_MESSAGE( 351 p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 2, 352 "Song1 entry not called correctly"); 353 354 } 355 } 356 357