1//////////////////////////////////////////////////////////////////////////////
2// Copyright 2005-2006 Andreas Huber Doenni
3// Distributed under the Boost Software License, Version 1.0. (See accompany-
4// ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5//////////////////////////////////////////////////////////////////////////////
6
7
8
9#include <boost/statechart/state_machine.hpp>
10#include <boost/statechart/event.hpp>
11#include <boost/statechart/simple_state.hpp>
12#include <boost/statechart/transition.hpp>
13#include <boost/statechart/custom_reaction.hpp>
14
15#include <boost/mpl/list.hpp>
16
17#include <boost/test/test_tools.hpp>
18
19#include <set>
20#include <map>
21#include <string>
22
23#include <cstddef> // size_t
24
25
26
27namespace sc = boost::statechart;
28namespace mpl = boost::mpl;
29
30
31
32struct EvToC : sc::event< EvToC > {};
33struct EvToD : sc::event< EvToD > {};
34
35struct EvDiscardNever : sc::event< EvDiscardNever > {};
36struct EvDiscardInB : sc::event< EvDiscardInB > {};
37struct EvDiscardInD : sc::event< EvDiscardInD > {};
38
39struct EvTransit : sc::event< EvTransit > {};
40struct EvTransitWithAction : sc::event< EvTransitWithAction > {};
41struct EvDefer : sc::event< EvDefer > {};
42struct EvTerminate : sc::event< EvTerminate > {};
43
44
45struct A;
46struct CustomReactionTest : sc::state_machine< CustomReactionTest, A >
47{
48 public:
49 //////////////////////////////////////////////////////////////////////////
50 CustomReactionTest();
51
52 void Visited( const state_base_type & stt )
53 {
54 const StateNamesMap::const_iterator found =
55 stateNamesMap_.find( x: stt.dynamic_type() );
56 BOOST_REQUIRE( found != stateNamesMap_.end() );
57 visitedStates_.insert( x: found->second );
58 }
59
60 void ClearVisited()
61 {
62 visitedStates_.clear();
63 }
64
65 void AssertVisitedAll( const std::string & stateNames ) const
66 {
67 for ( std::string::const_iterator expectedName = stateNames.begin();
68 expectedName != stateNames.end(); ++expectedName )
69 {
70 BOOST_REQUIRE( visitedStates_.find(
71 std::string( 1, *expectedName ) ) != visitedStates_.end() );
72 }
73 }
74
75 void AssertVisitedOne( const std::string & stateNames ) const
76 {
77 std::size_t found = 0;
78
79 for ( std::string::const_iterator actualName = stateNames.begin();
80 actualName != stateNames.end(); ++actualName )
81 {
82 found = found + ( visitedStates_.find(
83 x: std::string( 1, *actualName ) ) != visitedStates_.end() ) ? 1 : 0;
84 }
85
86 BOOST_REQUIRE( found == 1 );
87 }
88
89 void TransitionAction( const EvTransitWithAction & ) {}
90
91 private:
92 //////////////////////////////////////////////////////////////////////////
93 typedef std::map< state_base_type::id_type, std::string > StateNamesMap;
94 typedef std::set< std::string > VisitedStates;
95
96 StateNamesMap stateNamesMap_;
97 VisitedStates visitedStates_;
98};
99
100struct B;
101struct A : sc::simple_state< A, CustomReactionTest, B >
102{
103 typedef mpl::list<
104 sc::custom_reaction< EvDiscardNever >,
105 sc::custom_reaction< EvDiscardInB >,
106 sc::custom_reaction< EvDiscardInD >,
107 sc::custom_reaction< EvDefer >,
108 sc::custom_reaction< EvTerminate >,
109 sc::custom_reaction< EvTransitWithAction >,
110 sc::custom_reaction< EvTransit >
111 > reactions;
112
113 sc::result react( const EvDiscardNever & )
114 {
115 outermost_context().Visited( stt: *this );
116 return forward_event();
117 }
118
119 sc::result react( const EvDiscardInB & )
120 {
121 BOOST_FAIL( "An event discarded in B must never reach A" );
122 return discard_event();
123 }
124
125 sc::result react( const EvDiscardInD & )
126 {
127 BOOST_FAIL( "An event discarded in D must never reach B" );
128 return discard_event();
129 }
130
131 // The following code is here just to make sure that calls to the transit<>,
132 // defer_event and terminate functions actually compile.
133 // Their functionality is tested extensively in TransitionTest,
134 // DeferralTest and TerminationTest with appropriate reactions. Internally,
135 // these reactions call exactly the same functions as the following custom
136 // reactions call.
137 sc::result react( const EvDefer & )
138 {
139 return defer_event();
140 }
141
142 sc::result react( const EvTerminate & )
143 {
144 return terminate();
145 }
146
147 sc::result react( const EvTransit & )
148 {
149 return transit< A >();
150 }
151
152 sc::result react( const EvTransitWithAction & evt )
153 {
154 return transit< A >( pTransitionAction: &CustomReactionTest::TransitionAction, evt );
155 }
156};
157
158 struct C;
159 struct B : sc::simple_state< B, A, C >
160 {
161 typedef mpl::list<
162 sc::custom_reaction< EvDiscardNever >,
163 sc::custom_reaction< EvDiscardInB >,
164 sc::custom_reaction< EvDiscardInD >
165 > reactions;
166
167 sc::result react( const EvDiscardNever & )
168 {
169 outermost_context().Visited( stt: *this );
170 return forward_event();
171 }
172
173 sc::result react( const EvDiscardInB & )
174 {
175 outermost_context().Visited( stt: *this );
176 return discard_event();
177 }
178
179 sc::result react( const EvDiscardInD & )
180 {
181 BOOST_FAIL( "An event discarded in D must never reach B" );
182 return discard_event();
183 }
184 };
185
186 struct E;
187 struct F;
188 struct D : sc::simple_state< D, B, mpl::list< E, F > >
189 {
190 typedef mpl::list<
191 sc::transition< EvToC, C >,
192 sc::custom_reaction< EvDiscardNever >,
193 sc::custom_reaction< EvDiscardInB >,
194 sc::custom_reaction< EvDiscardInD >
195 > reactions;
196
197 sc::result react( const EvDiscardNever & )
198 {
199 outermost_context().Visited( stt: *this );
200 return forward_event();
201 }
202
203 sc::result react( const EvDiscardInB & )
204 {
205 outermost_context().Visited( stt: *this );
206 return forward_event();
207 }
208
209 sc::result react( const EvDiscardInD & )
210 {
211 outermost_context().Visited( stt: *this );
212 return discard_event();
213 }
214 };
215
216 struct E : sc::simple_state< E, D::orthogonal< 0 > >
217 {
218 typedef mpl::list<
219 sc::custom_reaction< EvDiscardNever >,
220 sc::custom_reaction< EvDiscardInB >,
221 sc::custom_reaction< EvDiscardInD >
222 > reactions;
223
224 sc::result react( const EvDiscardNever & )
225 {
226 outermost_context().Visited( stt: *this );
227 return forward_event();
228 }
229
230 sc::result react( const EvDiscardInB & )
231 {
232 outermost_context().Visited( stt: *this );
233 return forward_event();
234 }
235
236 sc::result react( const EvDiscardInD & )
237 {
238 outermost_context().Visited( stt: *this );
239 return forward_event();
240 }
241 };
242
243 struct F : sc::simple_state< F, D::orthogonal< 1 > >
244 {
245 typedef mpl::list<
246 sc::custom_reaction< EvDiscardNever >,
247 sc::custom_reaction< EvDiscardInB >,
248 sc::custom_reaction< EvDiscardInD >
249 > reactions;
250
251 sc::result react( const EvDiscardNever & )
252 {
253 outermost_context().Visited( stt: *this );
254 return forward_event();
255 }
256
257 sc::result react( const EvDiscardInB & )
258 {
259 outermost_context().Visited( stt: *this );
260 return forward_event();
261 }
262
263 sc::result react( const EvDiscardInD & )
264 {
265 outermost_context().Visited( stt: *this );
266 return forward_event();
267 }
268 };
269
270 struct C : sc::simple_state< C, B >
271 {
272 typedef mpl::list<
273 sc::transition< EvToD, D >,
274 sc::custom_reaction< EvDiscardNever >,
275 sc::custom_reaction< EvDiscardInB >,
276 sc::custom_reaction< EvDiscardInD >
277 > reactions;
278
279 sc::result react( const EvDiscardNever & )
280 {
281 outermost_context().Visited( stt: *this );
282 return forward_event();
283 }
284
285 sc::result react( const EvDiscardInB & )
286 {
287 outermost_context().Visited( stt: *this );
288 return forward_event();
289 }
290
291 sc::result react( const EvDiscardInD & )
292 {
293 outermost_context().Visited( stt: *this );
294 return forward_event();
295 }
296 };
297
298CustomReactionTest::CustomReactionTest()
299{
300 // We're not using custom type information to make this test work even when
301 // BOOST_STATECHART_USE_NATIVE_RTTI is defined
302 stateNamesMap_[ A::static_type() ] = "A";
303 stateNamesMap_[ B::static_type() ] = "B";
304 stateNamesMap_[ C::static_type() ] = "C";
305 stateNamesMap_[ D::static_type() ] = "D";
306 stateNamesMap_[ E::static_type() ] = "E";
307 stateNamesMap_[ F::static_type() ] = "F";
308}
309
310
311struct X1;
312struct CustomReactionEventBaseTest : sc::state_machine< CustomReactionEventBaseTest, X1 >
313{
314 public:
315 CustomReactionEventBaseTest() : reactionCount_( 0 ) {}
316
317 void IncrementReactionCount()
318 {
319 ++reactionCount_;
320 }
321
322 unsigned int GetReactionCount() const
323 {
324 return reactionCount_;
325 }
326
327 private:
328 unsigned int reactionCount_;
329};
330
331struct X1 : sc::simple_state< X1, CustomReactionEventBaseTest >
332{
333 typedef sc::custom_reaction< sc::event_base > reactions;
334
335 sc::result react( const sc::event_base & )
336 {
337 outermost_context().IncrementReactionCount();
338 return discard_event();
339 }
340};
341
342
343int test_main( int, char* [] )
344{
345 CustomReactionTest machine;
346 machine.initiate();
347
348 machine.process_event( evt: EvDiscardNever() );
349 machine.AssertVisitedAll( stateNames: "ABC" );
350 machine.ClearVisited();
351
352 machine.process_event( evt: EvDiscardInB() );
353 machine.AssertVisitedAll( stateNames: "BC" );
354 machine.process_event( evt: EvToD() );
355 machine.ClearVisited();
356
357 machine.process_event( evt: EvDiscardNever() );
358 machine.AssertVisitedAll( stateNames: "ABDEF" );
359 machine.ClearVisited();
360
361 machine.process_event( evt: EvDiscardInD() );
362 machine.AssertVisitedAll( stateNames: "D" );
363 machine.AssertVisitedOne( stateNames: "EF" );
364 machine.ClearVisited();
365
366 machine.process_event( evt: EvDiscardInB() );
367 machine.AssertVisitedAll( stateNames: "BD" );
368 machine.AssertVisitedOne( stateNames: "EF" );
369 machine.ClearVisited();
370
371
372 CustomReactionEventBaseTest eventBaseMachine;
373 eventBaseMachine.initiate();
374 BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 0 );
375 eventBaseMachine.process_event( evt: EvToC() );
376 BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 1 );
377 eventBaseMachine.process_event( evt: EvToD() );
378 BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 2 );
379
380 return 0;
381}
382

source code of boost/libs/statechart/test/CustomReactionTest.cpp