statechart/test/CustomReactionTest.cpp
2006-12-03 15:10:26 +00:00

382 lines
9.9 KiB
C++

//////////////////////////////////////////////////////////////////////////////
// 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 <boost/statechart/state_machine.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/mpl/list.hpp>
#include <boost/test/test_tools.hpp>
#include <set>
#include <map>
#include <string>
#include <cstddef> // 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;
}