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 | // back-end |
12 | #include <boost/msm/back/state_machine.hpp> |
13 | //front-end |
14 | #include <boost/msm/front/state_machine_def.hpp> |
15 | #include <boost/msm/front/functor_row.hpp> |
16 | |
17 | #include <boost/test/unit_test.hpp> |
18 | |
19 | namespace msm = boost::msm; |
20 | namespace mpl = boost::mpl; |
21 | using namespace boost::msm::front; |
22 | |
23 | namespace |
24 | { |
25 | // events |
26 | struct event1 {}; |
27 | struct event2 {}; |
28 | struct event3 {}; |
29 | struct eventd {}; |
30 | |
31 | |
32 | // front-end: define the FSM structure |
33 | struct player_ : public msm::front::state_machine_def<player_> |
34 | { |
35 | // The list of FSM states |
36 | struct State11 : public msm::front::state<> |
37 | { |
38 | template <class Event,class FSM> |
39 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
40 | template <class Event,class FSM> |
41 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
42 | int entry_counter; |
43 | int exit_counter; |
44 | }; |
45 | struct State12 : public msm::front::state<> |
46 | { |
47 | typedef mpl::vector<eventd> deferred_events; |
48 | template <class Event,class FSM> |
49 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
50 | template <class Event,class FSM> |
51 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
52 | int entry_counter; |
53 | int exit_counter; |
54 | }; |
55 | struct State13 : public msm::front::state<> |
56 | { |
57 | template <class Event,class FSM> |
58 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
59 | template <class Event,class FSM> |
60 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
61 | int entry_counter; |
62 | int exit_counter; |
63 | }; |
64 | struct State21 : public msm::front::state<> |
65 | { |
66 | template <class Event,class FSM> |
67 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
68 | template <class Event,class FSM> |
69 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
70 | int entry_counter; |
71 | int exit_counter; |
72 | }; |
73 | struct State22 : public msm::front::state<> |
74 | { |
75 | template <class Event,class FSM> |
76 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
77 | template <class Event,class FSM> |
78 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
79 | int entry_counter; |
80 | int exit_counter; |
81 | }; |
82 | // the initial state of the player SM. Must be defined |
83 | typedef mpl::vector<State11,State21> initial_state; |
84 | |
85 | |
86 | // Transition table for player |
87 | struct transition_table : mpl::vector< |
88 | // Start Event Next Action Guard |
89 | // +---------+-------------+---------+---------------------+----------------------+ |
90 | Row < State11 , event1 , State12 >, |
91 | Row < State12 , event2 , State13 >, |
92 | |
93 | Row < State21 , event3 , State22 >, |
94 | Row < State22 , eventd , State21 > |
95 | // +---------+-------------+---------+---------------------+----------------------+ |
96 | > {}; |
97 | |
98 | // Replaces the default no-transition response. |
99 | template <class FSM,class Event> |
100 | void no_transition(Event const& , FSM&,int ) |
101 | { |
102 | BOOST_FAIL("no_transition called!" ); |
103 | } |
104 | // init counters |
105 | template <class Event,class FSM> |
106 | void on_entry(Event const&,FSM& fsm) |
107 | { |
108 | fsm.template get_state<player_::State11&>().entry_counter=0; |
109 | fsm.template get_state<player_::State11&>().exit_counter=0; |
110 | fsm.template get_state<player_::State12&>().entry_counter=0; |
111 | fsm.template get_state<player_::State12&>().exit_counter=0; |
112 | fsm.template get_state<player_::State13&>().entry_counter=0; |
113 | fsm.template get_state<player_::State13&>().exit_counter=0; |
114 | fsm.template get_state<player_::State21&>().entry_counter=0; |
115 | fsm.template get_state<player_::State22&>().exit_counter=0; |
116 | } |
117 | }; |
118 | // Pick a back-end |
119 | typedef msm::back::state_machine<player_> player; |
120 | |
121 | BOOST_AUTO_TEST_CASE( TestDeferIn2Regions ) |
122 | { |
123 | player p; |
124 | // needed to start the highest-level SM. This will call on_entry and mark the start of the SM |
125 | p.start(); |
126 | |
127 | p.process_event(evt: event1()); |
128 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active" ); |
129 | BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active" ); |
130 | BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly" ); |
131 | BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly" ); |
132 | BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly" ); |
133 | |
134 | // deferred |
135 | p.process_event(evt: eventd()); |
136 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active" ); |
137 | BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active" ); |
138 | BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly" ); |
139 | BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly" ); |
140 | BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly" ); |
141 | BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().exit_counter == 0,"State12 exit not called correctly" ); |
142 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 0,"State21 exit not called correctly" ); |
143 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 1,"State21 entry not called correctly" ); |
144 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 0,"State22 exit not called correctly" ); |
145 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 0,"State22 entry not called correctly" ); |
146 | |
147 | p.process_event(evt: event3()); |
148 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active" ); |
149 | BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active" ); |
150 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 1,"State21 exit not called correctly" ); |
151 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 2,"State21 entry not called correctly" ); |
152 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 1,"State22 exit not called correctly" ); |
153 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 1,"State22 entry not called correctly" ); |
154 | BOOST_CHECK_MESSAGE(p.get_deferred_queue().size() == 1,"Deferred queue should have one element" ); |
155 | p.clear_deferred_queue(); |
156 | |
157 | p.process_event(evt: event2()); |
158 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"State13 should be active" ); |
159 | BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active" ); |
160 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 1,"State21 exit not called correctly" ); |
161 | BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 2,"State21 entry not called correctly" ); |
162 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 1,"State22 exit not called correctly" ); |
163 | BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 1,"State22 entry not called correctly" ); |
164 | BOOST_CHECK_MESSAGE(p.get_deferred_queue().size() == 0,"Deferred queue should have no element" ); |
165 | } |
166 | } |
167 | |
168 | |
169 | |