f8e471e541
[SVN r36255]
382 lines
9.9 KiB
C++
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;
|
|
}
|