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 //////////////////////////////////////////////////////////////////////////////
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>
15 #include <boost/mpl/list.hpp>
17 #include <boost/test/test_tools.hpp>
23 #include <cstddef> // size_t
27 namespace sc
= boost::statechart
;
28 namespace mpl
= boost::mpl
;
32 struct EvToC
: sc::event
< EvToC
> {};
33 struct EvToD
: sc::event
< EvToD
> {};
35 struct EvDiscardNever
: sc::event
< EvDiscardNever
> {};
36 struct EvDiscardInB
: sc::event
< EvDiscardInB
> {};
37 struct EvDiscardInD
: sc::event
< EvDiscardInD
> {};
39 struct EvTransit
: sc::event
< EvTransit
> {};
40 struct EvTransitWithAction
: sc::event
< EvTransitWithAction
> {};
41 struct EvDefer
: sc::event
< EvDefer
> {};
42 struct EvTerminate
: sc::event
< EvTerminate
> {};
46 struct CustomReactionTest
: sc::state_machine
< CustomReactionTest
, A
>
49 //////////////////////////////////////////////////////////////////////////
52 void Visited( const state_base_type
& stt
)
54 const StateNamesMap::const_iterator found
=
55 stateNamesMap_
.find( stt
.dynamic_type() );
56 BOOST_REQUIRE( found
!= stateNamesMap_
.end() );
57 visitedStates_
.insert( found
->second
);
62 visitedStates_
.clear();
65 void AssertVisitedAll( const std::string
& stateNames
) const
67 for ( std::string::const_iterator expectedName
= stateNames
.begin();
68 expectedName
!= stateNames
.end(); ++expectedName
)
70 BOOST_REQUIRE( visitedStates_
.find(
71 std::string( 1, *expectedName
) ) != visitedStates_
.end() );
75 void AssertVisitedOne( const std::string
& stateNames
) const
77 std::size_t found
= 0;
79 for ( std::string::const_iterator actualName
= stateNames
.begin();
80 actualName
!= stateNames
.end(); ++actualName
)
82 found
= found
+ ( visitedStates_
.find(
83 std::string( 1, *actualName
) ) != visitedStates_
.end() ) ? 1 : 0;
86 BOOST_REQUIRE( found
== 1 );
89 void TransitionAction( const EvTransitWithAction
& ) {}
92 //////////////////////////////////////////////////////////////////////////
93 typedef std::map
< state_base_type::id_type
, std::string
> StateNamesMap
;
94 typedef std::set
< std::string
> VisitedStates
;
96 StateNamesMap stateNamesMap_
;
97 VisitedStates visitedStates_
;
101 struct A
: sc::simple_state
< A
, CustomReactionTest
, B
>
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
>
113 sc::result
react( const EvDiscardNever
& )
115 outermost_context().Visited( *this );
116 return forward_event();
119 sc::result
react( const EvDiscardInB
& )
121 BOOST_FAIL( "An event discarded in B must never reach A" );
122 return discard_event();
125 sc::result
react( const EvDiscardInD
& )
127 BOOST_FAIL( "An event discarded in D must never reach B" );
128 return discard_event();
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
137 sc::result
react( const EvDefer
& )
139 return defer_event();
142 sc::result
react( const EvTerminate
& )
147 sc::result
react( const EvTransit
& )
149 return transit
< A
>();
152 sc::result
react( const EvTransitWithAction
& evt
)
154 return transit
< A
>( &CustomReactionTest::TransitionAction
, evt
);
159 struct B
: sc::simple_state
< B
, A
, C
>
162 sc::custom_reaction
< EvDiscardNever
>,
163 sc::custom_reaction
< EvDiscardInB
>,
164 sc::custom_reaction
< EvDiscardInD
>
167 sc::result
react( const EvDiscardNever
& )
169 outermost_context().Visited( *this );
170 return forward_event();
173 sc::result
react( const EvDiscardInB
& )
175 outermost_context().Visited( *this );
176 return discard_event();
179 sc::result
react( const EvDiscardInD
& )
181 BOOST_FAIL( "An event discarded in D must never reach B" );
182 return discard_event();
188 struct D
: sc::simple_state
< D
, B
, mpl::list
< E
, F
> >
191 sc::transition
< EvToC
, C
>,
192 sc::custom_reaction
< EvDiscardNever
>,
193 sc::custom_reaction
< EvDiscardInB
>,
194 sc::custom_reaction
< EvDiscardInD
>
197 sc::result
react( const EvDiscardNever
& )
199 outermost_context().Visited( *this );
200 return forward_event();
203 sc::result
react( const EvDiscardInB
& )
205 outermost_context().Visited( *this );
206 return forward_event();
209 sc::result
react( const EvDiscardInD
& )
211 outermost_context().Visited( *this );
212 return discard_event();
216 struct E
: sc::simple_state
< E
, D::orthogonal
< 0 > >
219 sc::custom_reaction
< EvDiscardNever
>,
220 sc::custom_reaction
< EvDiscardInB
>,
221 sc::custom_reaction
< EvDiscardInD
>
224 sc::result
react( const EvDiscardNever
& )
226 outermost_context().Visited( *this );
227 return forward_event();
230 sc::result
react( const EvDiscardInB
& )
232 outermost_context().Visited( *this );
233 return forward_event();
236 sc::result
react( const EvDiscardInD
& )
238 outermost_context().Visited( *this );
239 return forward_event();
243 struct F
: sc::simple_state
< F
, D::orthogonal
< 1 > >
246 sc::custom_reaction
< EvDiscardNever
>,
247 sc::custom_reaction
< EvDiscardInB
>,
248 sc::custom_reaction
< EvDiscardInD
>
251 sc::result
react( const EvDiscardNever
& )
253 outermost_context().Visited( *this );
254 return forward_event();
257 sc::result
react( const EvDiscardInB
& )
259 outermost_context().Visited( *this );
260 return forward_event();
263 sc::result
react( const EvDiscardInD
& )
265 outermost_context().Visited( *this );
266 return forward_event();
270 struct C
: sc::simple_state
< C
, B
>
273 sc::transition
< EvToD
, D
>,
274 sc::custom_reaction
< EvDiscardNever
>,
275 sc::custom_reaction
< EvDiscardInB
>,
276 sc::custom_reaction
< EvDiscardInD
>
279 sc::result
react( const EvDiscardNever
& )
281 outermost_context().Visited( *this );
282 return forward_event();
285 sc::result
react( const EvDiscardInB
& )
287 outermost_context().Visited( *this );
288 return forward_event();
291 sc::result
react( const EvDiscardInD
& )
293 outermost_context().Visited( *this );
294 return forward_event();
298 CustomReactionTest::CustomReactionTest()
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";
312 struct CustomReactionEventBaseTest
: sc::state_machine
< CustomReactionEventBaseTest
, X1
>
315 CustomReactionEventBaseTest() : reactionCount_( 0 ) {}
317 void IncrementReactionCount()
322 unsigned int GetReactionCount() const
324 return reactionCount_
;
328 unsigned int reactionCount_
;
331 struct X1
: sc::simple_state
< X1
, CustomReactionEventBaseTest
>
333 typedef sc::custom_reaction
< sc::event_base
> reactions
;
335 sc::result
react( const sc::event_base
& )
337 outermost_context().IncrementReactionCount();
338 return discard_event();
343 int test_main( int, char* [] )
345 CustomReactionTest machine
;
348 machine
.process_event( EvDiscardNever() );
349 machine
.AssertVisitedAll( "ABC" );
350 machine
.ClearVisited();
352 machine
.process_event( EvDiscardInB() );
353 machine
.AssertVisitedAll( "BC" );
354 machine
.process_event( EvToD() );
355 machine
.ClearVisited();
357 machine
.process_event( EvDiscardNever() );
358 machine
.AssertVisitedAll( "ABDEF" );
359 machine
.ClearVisited();
361 machine
.process_event( EvDiscardInD() );
362 machine
.AssertVisitedAll( "D" );
363 machine
.AssertVisitedOne( "EF" );
364 machine
.ClearVisited();
366 machine
.process_event( EvDiscardInB() );
367 machine
.AssertVisitedAll( "BD" );
368 machine
.AssertVisitedOne( "EF" );
369 machine
.ClearVisited();
372 CustomReactionEventBaseTest eventBaseMachine
;
373 eventBaseMachine
.initiate();
374 BOOST_REQUIRE( eventBaseMachine
.GetReactionCount() == 0 );
375 eventBaseMachine
.process_event( EvToC() );
376 BOOST_REQUIRE( eventBaseMachine
.GetReactionCount() == 1 );
377 eventBaseMachine
.process_event( EvToD() );
378 BOOST_REQUIRE( eventBaseMachine
.GetReactionCount() == 2 );