////////////////////////////////////////////////////////////////////////////// // Copyright 2005-2006 Andreas Huber Doenni // Distributed under the Boost Software License, Version 1.0. (See accompany- // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include // size_t namespace sc = boost::statechart; namespace mpl = boost::mpl; struct EvToC : sc::event< EvToC > {}; struct EvToD : sc::event< EvToD > {}; struct EvDiscardNever : sc::event< EvDiscardNever > {}; struct EvDiscardInB : sc::event< EvDiscardInB > {}; struct EvDiscardInD : sc::event< EvDiscardInD > {}; struct EvTransit : sc::event< EvTransit > {}; struct EvTransitWithAction : sc::event< EvTransitWithAction > {}; struct EvDefer : sc::event< EvDefer > {}; struct EvTerminate : sc::event< EvTerminate > {}; struct A; struct CustomReactionTest : sc::state_machine< CustomReactionTest, A > { public: ////////////////////////////////////////////////////////////////////////// CustomReactionTest(); void Visited( const state_base_type & stt ) { const StateNamesMap::const_iterator found = stateNamesMap_.find( stt.dynamic_type() ); BOOST_REQUIRE( found != stateNamesMap_.end() ); visitedStates_.insert( found->second ); } void ClearVisited() { visitedStates_.clear(); } void AssertVisitedAll( const std::string & stateNames ) const { for ( std::string::const_iterator expectedName = stateNames.begin(); expectedName != stateNames.end(); ++expectedName ) { BOOST_REQUIRE( visitedStates_.find( std::string( 1, *expectedName ) ) != visitedStates_.end() ); } } void AssertVisitedOne( const std::string & stateNames ) const { std::size_t found = 0; for ( std::string::const_iterator actualName = stateNames.begin(); actualName != stateNames.end(); ++actualName ) { found = found + ( visitedStates_.find( std::string( 1, *actualName ) ) != visitedStates_.end() ) ? 1 : 0; } BOOST_REQUIRE( found == 1 ); } void TransitionAction( const EvTransitWithAction & ) {} private: ////////////////////////////////////////////////////////////////////////// typedef std::map< state_base_type::id_type, std::string > StateNamesMap; typedef std::set< std::string > VisitedStates; StateNamesMap stateNamesMap_; VisitedStates visitedStates_; }; struct B; struct A : sc::simple_state< A, CustomReactionTest, B > { typedef mpl::list< sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD >, sc::custom_reaction< EvDefer >, sc::custom_reaction< EvTerminate >, sc::custom_reaction< EvTransitWithAction >, sc::custom_reaction< EvTransit > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { BOOST_FAIL( "An event discarded in B must never reach A" ); return discard_event(); } sc::result react( const EvDiscardInD & ) { BOOST_FAIL( "An event discarded in D must never reach B" ); return discard_event(); } // The following code is here just to make sure that calls to the transit<>, // defer_event and terminate functions actually compile. // Their functionality is tested extensively in TransitionTest, // DeferralTest and TerminationTest with appropriate reactions. Internally, // these reactions call exactly the same functions as the following custom // reactions call. sc::result react( const EvDefer & ) { return defer_event(); } sc::result react( const EvTerminate & ) { return terminate(); } sc::result react( const EvTransit & ) { return transit< A >(); } sc::result react( const EvTransitWithAction & evt ) { return transit< A >( &CustomReactionTest::TransitionAction, evt ); } }; struct C; struct B : sc::simple_state< B, A, C > { typedef mpl::list< sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { outermost_context().Visited( *this ); return discard_event(); } sc::result react( const EvDiscardInD & ) { BOOST_FAIL( "An event discarded in D must never reach B" ); return discard_event(); } }; struct E; struct F; struct D : sc::simple_state< D, B, mpl::list< E, F > > { typedef mpl::list< sc::transition< EvToC, C >, sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInD & ) { outermost_context().Visited( *this ); return discard_event(); } }; struct E : sc::simple_state< E, D::orthogonal< 0 > > { typedef mpl::list< sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInD & ) { outermost_context().Visited( *this ); return forward_event(); } }; struct F : sc::simple_state< F, D::orthogonal< 1 > > { typedef mpl::list< sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInD & ) { outermost_context().Visited( *this ); return forward_event(); } }; struct C : sc::simple_state< C, B > { typedef mpl::list< sc::transition< EvToD, D >, sc::custom_reaction< EvDiscardNever >, sc::custom_reaction< EvDiscardInB >, sc::custom_reaction< EvDiscardInD > > reactions; sc::result react( const EvDiscardNever & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInB & ) { outermost_context().Visited( *this ); return forward_event(); } sc::result react( const EvDiscardInD & ) { outermost_context().Visited( *this ); return forward_event(); } }; CustomReactionTest::CustomReactionTest() { // We're not using custom type information to make this test work even when // BOOST_STATECHART_USE_NATIVE_RTTI is defined stateNamesMap_[ A::static_type() ] = "A"; stateNamesMap_[ B::static_type() ] = "B"; stateNamesMap_[ C::static_type() ] = "C"; stateNamesMap_[ D::static_type() ] = "D"; stateNamesMap_[ E::static_type() ] = "E"; stateNamesMap_[ F::static_type() ] = "F"; } struct X1; struct CustomReactionEventBaseTest : sc::state_machine< CustomReactionEventBaseTest, X1 > { public: CustomReactionEventBaseTest() : reactionCount_( 0 ) {} void IncrementReactionCount() { ++reactionCount_; } unsigned int GetReactionCount() const { return reactionCount_; } private: unsigned int reactionCount_; }; struct X1 : sc::simple_state< X1, CustomReactionEventBaseTest > { typedef sc::custom_reaction< sc::event_base > reactions; sc::result react( const sc::event_base & ) { outermost_context().IncrementReactionCount(); return discard_event(); } }; int test_main( int, char* [] ) { CustomReactionTest machine; machine.initiate(); machine.process_event( EvDiscardNever() ); machine.AssertVisitedAll( "ABC" ); machine.ClearVisited(); machine.process_event( EvDiscardInB() ); machine.AssertVisitedAll( "BC" ); machine.process_event( EvToD() ); machine.ClearVisited(); machine.process_event( EvDiscardNever() ); machine.AssertVisitedAll( "ABDEF" ); machine.ClearVisited(); machine.process_event( EvDiscardInD() ); machine.AssertVisitedAll( "D" ); machine.AssertVisitedOne( "EF" ); machine.ClearVisited(); machine.process_event( EvDiscardInB() ); machine.AssertVisitedAll( "BD" ); machine.AssertVisitedOne( "EF" ); machine.ClearVisited(); CustomReactionEventBaseTest eventBaseMachine; eventBaseMachine.initiate(); BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 0 ); eventBaseMachine.process_event( EvToC() ); BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 1 ); eventBaseMachine.process_event( EvToD() ); BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 2 ); return 0; }