History.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. #include <iostream>
  11. // back-end
  12. #include <boost/msm/back/state_machine.hpp>
  13. //front-end
  14. #include <boost/msm/front/state_machine_def.hpp>
  15. #ifndef BOOST_MSM_NONSTANDALONE_TEST
  16. #define BOOST_TEST_MODULE MyTest
  17. #endif
  18. #include <boost/test/unit_test.hpp>
  19. namespace msm = boost::msm;
  20. namespace mpl = boost::mpl;
  21. namespace
  22. {
  23. // events
  24. struct play {};
  25. struct end_pause {};
  26. struct stop {};
  27. struct pause {};
  28. struct open_close {};
  29. struct NextSong {};
  30. struct PreviousSong {};
  31. struct cd_detected
  32. {
  33. cd_detected(std::string name)
  34. : name(name)
  35. {}
  36. std::string name;
  37. };
  38. // front-end: define the FSM structure
  39. struct player_ : public msm::front::state_machine_def<player_>
  40. {
  41. unsigned int start_playback_counter;
  42. unsigned int can_close_drawer_counter;
  43. player_():
  44. start_playback_counter(0),
  45. can_close_drawer_counter(0)
  46. {}
  47. // The list of FSM states
  48. struct Empty : public msm::front::state<>
  49. {
  50. template <class Event,class FSM>
  51. void on_entry(Event const&,FSM& ) {++entry_counter;}
  52. template <class Event,class FSM>
  53. void on_exit(Event const&,FSM& ) {++exit_counter;}
  54. int entry_counter;
  55. int exit_counter;
  56. };
  57. struct Open : public msm::front::state<>
  58. {
  59. template <class Event,class FSM>
  60. void on_entry(Event const&,FSM& ) {++entry_counter;}
  61. template <class Event,class FSM>
  62. void on_exit(Event const&,FSM& ) {++exit_counter;}
  63. int entry_counter;
  64. int exit_counter;
  65. };
  66. // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
  67. struct Stopped : public msm::front::state<>
  68. {
  69. template <class Event,class FSM>
  70. void on_entry(Event const&,FSM& ) {++entry_counter;}
  71. template <class Event,class FSM>
  72. void on_exit(Event const&,FSM& ) {++exit_counter;}
  73. int entry_counter;
  74. int exit_counter;
  75. };
  76. struct Playing_ : public msm::front::state_machine_def<Playing_>
  77. {
  78. template <class Event,class FSM>
  79. void on_entry(Event const&,FSM& ) {++entry_counter;}
  80. template <class Event,class FSM>
  81. void on_exit(Event const&,FSM& ) {++exit_counter;}
  82. int entry_counter;
  83. int exit_counter;
  84. unsigned int start_next_song_counter;
  85. unsigned int start_prev_song_guard_counter;
  86. Playing_():
  87. start_next_song_counter(0),
  88. start_prev_song_guard_counter(0)
  89. {}
  90. // The list of FSM states
  91. struct Song1 : public msm::front::state<>
  92. {
  93. template <class Event,class FSM>
  94. void on_entry(Event const&,FSM& ) {++entry_counter;}
  95. template <class Event,class FSM>
  96. void on_exit(Event const&,FSM& ) {++exit_counter;}
  97. int entry_counter;
  98. int exit_counter;
  99. };
  100. struct Song2 : public msm::front::state<>
  101. {
  102. template <class Event,class FSM>
  103. void on_entry(Event const&,FSM& ) {++entry_counter;}
  104. template <class Event,class FSM>
  105. void on_exit(Event const&,FSM& ) {++exit_counter;}
  106. int entry_counter;
  107. int exit_counter;
  108. };
  109. struct Song3 : public msm::front::state<>
  110. {
  111. template <class Event,class FSM>
  112. void on_entry(Event const&,FSM& ) {++entry_counter;}
  113. template <class Event,class FSM>
  114. void on_exit(Event const&,FSM& ) {++exit_counter;}
  115. int entry_counter;
  116. int exit_counter;
  117. };
  118. // the initial state. Must be defined
  119. typedef Song1 initial_state;
  120. // transition actions
  121. void start_next_song(NextSong const&) {++start_next_song_counter; }
  122. void start_prev_song(PreviousSong const&) { }
  123. // guard conditions
  124. bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; }
  125. typedef Playing_ pl; // makes transition table cleaner
  126. // Transition table for Playing
  127. struct transition_table : mpl::vector4<
  128. // Start Event Next Action Guard
  129. // +---------+-------------+---------+---------------------+----------------------+
  130. _row < Song1 , NextSong , Song2 >,
  131. row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>,
  132. a_row < Song2 , NextSong , Song3 , &pl::start_next_song >,
  133. g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard>
  134. // +---------+-------------+---------+---------------------+----------------------+
  135. > {};
  136. // Replaces the default no-transition response.
  137. template <class FSM,class Event>
  138. void no_transition(Event const&, FSM&,int)
  139. {
  140. BOOST_FAIL("no_transition called!");
  141. }
  142. };
  143. // back-end
  144. typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
  145. // state not defining any entry or exit
  146. struct Paused : public msm::front::state<>
  147. {
  148. template <class Event,class FSM>
  149. void on_entry(Event const&,FSM& ) {++entry_counter;}
  150. template <class Event,class FSM>
  151. void on_exit(Event const&,FSM& ) {++exit_counter;}
  152. int entry_counter;
  153. int exit_counter;
  154. };
  155. // the initial state of the player SM. Must be defined
  156. typedef Empty initial_state;
  157. // transition actions
  158. void start_playback(play const&) {++start_playback_counter; }
  159. void open_drawer(open_close const&) { }
  160. void store_cd_info(cd_detected const&) { }
  161. void stop_playback(stop const&) { }
  162. void pause_playback(pause const&) { }
  163. void resume_playback(end_pause const&) { }
  164. void stop_and_open(open_close const&) { }
  165. void stopped_again(stop const&){}
  166. //guards
  167. bool can_close_drawer(open_close const&)
  168. {
  169. ++can_close_drawer_counter;
  170. return true;
  171. }
  172. typedef player_ p; // makes transition table cleaner
  173. // Transition table for player
  174. struct transition_table : mpl::vector<
  175. // Start Event Next Action Guard
  176. // +---------+-------------+---------+---------------------+----------------------+
  177. a_row < Stopped , play , Playing , &p::start_playback >,
  178. a_row < Stopped , open_close , Open , &p::open_drawer >,
  179. _row < Stopped , stop , Stopped >,
  180. // +---------+-------------+---------+---------------------+----------------------+
  181. g_row < Open , open_close , Empty , &p::can_close_drawer >,
  182. // +---------+-------------+---------+---------------------+----------------------+
  183. a_row < Empty , open_close , Open , &p::open_drawer >,
  184. a_row < Empty , cd_detected , Stopped , &p::store_cd_info >,
  185. // +---------+-------------+---------+---------------------+----------------------+
  186. a_row < Playing , stop , Stopped , &p::stop_playback >,
  187. a_row < Playing , pause , Paused , &p::pause_playback >,
  188. a_row < Playing , open_close , Open , &p::stop_and_open >,
  189. // +---------+-------------+---------+---------------------+----------------------+
  190. a_row < Paused , end_pause , Playing , &p::resume_playback >,
  191. a_row < Paused , stop , Stopped , &p::stop_playback >,
  192. a_row < Paused , open_close , Open , &p::stop_and_open >
  193. // +---------+-------------+---------+---------------------+----------------------+
  194. > {};
  195. // Replaces the default no-transition response.
  196. template <class FSM,class Event>
  197. void no_transition(Event const& , FSM&,int )
  198. {
  199. BOOST_FAIL("no_transition called!");
  200. }
  201. // init counters
  202. template <class Event,class FSM>
  203. void on_entry(Event const&,FSM& fsm)
  204. {
  205. fsm.template get_state<player_::Stopped&>().entry_counter=0;
  206. fsm.template get_state<player_::Stopped&>().exit_counter=0;
  207. fsm.template get_state<player_::Open&>().entry_counter=0;
  208. fsm.template get_state<player_::Open&>().exit_counter=0;
  209. fsm.template get_state<player_::Empty&>().entry_counter=0;
  210. fsm.template get_state<player_::Empty&>().exit_counter=0;
  211. fsm.template get_state<player_::Playing&>().entry_counter=0;
  212. fsm.template get_state<player_::Playing&>().exit_counter=0;
  213. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0;
  214. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0;
  215. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0;
  216. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0;
  217. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0;
  218. fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0;
  219. fsm.template get_state<player_::Paused&>().entry_counter=0;
  220. fsm.template get_state<player_::Paused&>().exit_counter=0;
  221. }
  222. };
  223. // Pick a back-end
  224. typedef msm::back::state_machine<player_> player;
  225. // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
  226. BOOST_AUTO_TEST_CASE( my_test )
  227. {
  228. player p;
  229. p.start();
  230. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
  231. p.process_event(open_close());
  232. BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
  233. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
  234. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
  235. p.process_event(open_close());
  236. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
  237. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
  238. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
  239. BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
  240. p.process_event(cd_detected("louie, louie"));
  241. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  242. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
  243. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
  244. p.process_event(play());
  245. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  246. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
  247. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
  248. BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
  249. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active");
  250. BOOST_CHECK_MESSAGE(
  251. p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1,
  252. "Song1 entry not called correctly");
  253. p.process_event(NextSong());
  254. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  255. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active");
  256. BOOST_CHECK_MESSAGE(
  257. p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1,
  258. "Song2 entry not called correctly");
  259. BOOST_CHECK_MESSAGE(
  260. p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1,
  261. "Song1 exit not called correctly");
  262. BOOST_CHECK_MESSAGE(
  263. p.get_state<player_::Playing&>().start_next_song_counter == 0,
  264. "submachine action not called correctly");
  265. p.process_event(NextSong());
  266. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  267. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active");
  268. BOOST_CHECK_MESSAGE(
  269. p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1,
  270. "Song3 entry not called correctly");
  271. BOOST_CHECK_MESSAGE(
  272. p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1,
  273. "Song2 exit not called correctly");
  274. BOOST_CHECK_MESSAGE(
  275. p.get_state<player_::Playing&>().start_next_song_counter == 1,
  276. "submachine action not called correctly");
  277. p.process_event(PreviousSong());
  278. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  279. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active");
  280. BOOST_CHECK_MESSAGE(
  281. p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2,
  282. "Song2 entry not called correctly");
  283. BOOST_CHECK_MESSAGE(
  284. p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1,
  285. "Song3 exit not called correctly");
  286. BOOST_CHECK_MESSAGE(
  287. p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1,
  288. "submachine guard not called correctly");
  289. p.process_event(pause());
  290. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  291. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
  292. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
  293. // go back to Playing
  294. p.process_event(end_pause());
  295. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  296. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
  297. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
  298. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active");
  299. BOOST_CHECK_MESSAGE(
  300. p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 3,
  301. "Song2 entry not called correctly");
  302. p.process_event(pause());
  303. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  304. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
  305. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
  306. p.process_event(stop());
  307. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  308. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
  309. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
  310. p.process_event(stop());
  311. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  312. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
  313. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
  314. p.process_event(play());
  315. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active");
  316. BOOST_CHECK_MESSAGE(
  317. p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 2,
  318. "Song1 entry not called correctly");
  319. }
  320. }