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 | |
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 | using namespace boost::msm::front; |
24 | |
25 | namespace |
26 | { |
27 | // events |
28 | struct event1 {}; |
29 | |
30 | |
31 | // front-end: define the FSM structure |
32 | struct my_machine_ : public msm::front::state_machine_def<my_machine_> |
33 | { |
34 | unsigned int state2_to_state3_counter; |
35 | unsigned int state3_to_state4_counter; |
36 | unsigned int always_true_counter; |
37 | unsigned int always_false_counter; |
38 | |
39 | my_machine_(): |
40 | state2_to_state3_counter(0), |
41 | state3_to_state4_counter(0), |
42 | always_true_counter(0), |
43 | always_false_counter(0) |
44 | {} |
45 | |
46 | // The list of FSM states |
47 | struct State1 : public msm::front::state<> |
48 | { |
49 | template <class Event,class FSM> |
50 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
51 | template <class Event,class FSM> |
52 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
53 | int entry_counter; |
54 | int exit_counter; |
55 | }; |
56 | struct State2 : public msm::front::state<> |
57 | { |
58 | template <class Event,class FSM> |
59 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
60 | template <class Event,class FSM> |
61 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
62 | int entry_counter; |
63 | int exit_counter; |
64 | }; |
65 | |
66 | struct State3 : public msm::front::state<> |
67 | { |
68 | template <class Event,class FSM> |
69 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
70 | template <class Event,class FSM> |
71 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
72 | int entry_counter; |
73 | int exit_counter; |
74 | }; |
75 | |
76 | struct State4 : public msm::front::state<> |
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 | }; |
85 | |
86 | // the initial state of the player SM. Must be defined |
87 | typedef State1 initial_state; |
88 | |
89 | // transition actions |
90 | void State2ToState3(none const&) { ++state2_to_state3_counter; } |
91 | void State3ToState4(none const&) { ++state3_to_state4_counter; } |
92 | // guard conditions |
93 | bool always_true(none const& ) |
94 | { |
95 | ++always_true_counter; |
96 | return true; |
97 | } |
98 | bool always_false(none const& ) |
99 | { |
100 | ++always_false_counter; |
101 | return false; |
102 | } |
103 | |
104 | typedef my_machine_ p; // makes transition table cleaner |
105 | |
106 | // Transition table for player |
107 | struct transition_table : mpl::vector< |
108 | // Start Event Next Action Guard |
109 | // +---------+-------------+---------+---------------------+----------------------+ |
110 | _row < State1 , none , State2 >, |
111 | a_row < State2 , none , State3 , &p::State2ToState3 >, |
112 | // +---------+-------------+---------+---------------------+----------------------+ |
113 | row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, |
114 | g_row < State3 , none , State4 , &p::always_false >, |
115 | _row < State4 , event1 , State1 > |
116 | // +---------+-------------+---------+---------------------+----------------------+ |
117 | > {}; |
118 | // Replaces the default no-transition response. |
119 | template <class FSM,class Event> |
120 | void no_transition(Event const&, FSM&,int) |
121 | { |
122 | BOOST_FAIL("no_transition called!" ); |
123 | } |
124 | // init counters |
125 | template <class Event,class FSM> |
126 | void on_entry(Event const&,FSM& fsm) |
127 | { |
128 | fsm.template get_state<my_machine_::State1&>().entry_counter=0; |
129 | fsm.template get_state<my_machine_::State1&>().exit_counter=0; |
130 | fsm.template get_state<my_machine_::State2&>().entry_counter=0; |
131 | fsm.template get_state<my_machine_::State2&>().exit_counter=0; |
132 | fsm.template get_state<my_machine_::State3&>().entry_counter=0; |
133 | fsm.template get_state<my_machine_::State3&>().exit_counter=0; |
134 | fsm.template get_state<my_machine_::State4&>().entry_counter=0; |
135 | fsm.template get_state<my_machine_::State4&>().exit_counter=0; |
136 | } |
137 | }; |
138 | // Pick a back-end |
139 | typedef msm::back::state_machine<my_machine_> my_machine; |
140 | |
141 | //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; |
142 | |
143 | |
144 | BOOST_AUTO_TEST_CASE( anonymous_test ) |
145 | { |
146 | my_machine p; |
147 | |
148 | // needed to start the highest-level SM. This will call on_entry and mark the start of the SM |
149 | // in this case it will also immediately trigger all anonymous transitions |
150 | p.start(); |
151 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active" ); //State4 |
152 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 1,"State1 exit not called correctly" ); |
153 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 1,"State1 entry not called correctly" ); |
154 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 1,"State2 exit not called correctly" ); |
155 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 1,"State2 entry not called correctly" ); |
156 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 1,"State3 exit not called correctly" ); |
157 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 1,"State3 entry not called correctly" ); |
158 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 1,"State4 entry not called correctly" ); |
159 | BOOST_CHECK_MESSAGE(p.always_true_counter == 1,"guard not called correctly" ); |
160 | BOOST_CHECK_MESSAGE(p.always_false_counter == 1,"guard not called correctly" ); |
161 | BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 1,"action not called correctly" ); |
162 | BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 1,"action not called correctly" ); |
163 | |
164 | |
165 | // this event will bring us back to the initial state and thus, a new "loop" will be started |
166 | p.process_event(evt: event1()); |
167 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active" ); //State4 |
168 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 2,"State1 exit not called correctly" ); |
169 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 2,"State1 entry not called correctly" ); |
170 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 2,"State2 exit not called correctly" ); |
171 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 2,"State2 entry not called correctly" ); |
172 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 2,"State3 exit not called correctly" ); |
173 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 2,"State3 entry not called correctly" ); |
174 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 2,"State4 entry not called correctly" ); |
175 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().exit_counter == 1,"State4 exit not called correctly" ); |
176 | BOOST_CHECK_MESSAGE(p.always_true_counter == 2,"guard not called correctly" ); |
177 | BOOST_CHECK_MESSAGE(p.always_false_counter == 2,"guard not called correctly" ); |
178 | BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 2,"action not called correctly" ); |
179 | BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 2,"action not called correctly" ); |
180 | |
181 | } |
182 | } |
183 | |
184 | |