blob: 220be1bdf3ace70890a96c0e8743027593a60717 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// 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/termination.hpp>
#include <boost/mpl/list.hpp>
#include <boost/test/test_tools.hpp>
#include <set>
#include <map>
#include <string>
namespace sc = boost::statechart;
namespace mpl = boost::mpl;
struct EvTerminateA : sc::event< EvTerminateA > {};
struct EvTerminateB : sc::event< EvTerminateB > {};
struct EvTerminateC : sc::event< EvTerminateC > {};
struct EvTerminateD : sc::event< EvTerminateD > {};
struct EvTerminateE : sc::event< EvTerminateE > {};
struct EvTerminateF : sc::event< EvTerminateF > {};
struct EvTerminateG : sc::event< EvTerminateG > {};
struct A;
struct TerminationTest : sc::state_machine< TerminationTest, A >
{
public:
//////////////////////////////////////////////////////////////////////////
TerminationTest();
void AssertInState( const std::string & stateNames ) const
{
stateNamesCache_.clear();
for ( state_iterator currentState = state_begin();
currentState != state_end(); ++currentState )
{
AddName( currentState->dynamic_type() );
const state_base_type * outerState = currentState->outer_state_ptr();
while ( outerState != 0 )
{
AddName( outerState->dynamic_type() );
outerState = outerState->outer_state_ptr();
}
}
std::string::const_iterator expectedName = stateNames.begin();
BOOST_REQUIRE( stateNames.size() == stateNamesCache_.size() );
for ( StateNamesCache::const_iterator actualName =
stateNamesCache_.begin(); actualName != stateNamesCache_.end();
++actualName, ++expectedName )
{
BOOST_REQUIRE( ( *actualName )[ 0 ] == *expectedName );
}
}
private:
//////////////////////////////////////////////////////////////////////////
void AddName( state_base_type::id_type stateType ) const
{
const StateNamesMap::const_iterator found =
stateNamesMap_.find( stateType );
BOOST_REQUIRE( found != stateNamesMap_.end() );
stateNamesCache_.insert( found->second );
}
typedef std::map< state_base_type::id_type, std::string > StateNamesMap;
typedef std::set< std::string > StateNamesCache;
StateNamesMap stateNamesMap_;
mutable StateNamesCache stateNamesCache_;
};
template<
class MostDerived,
class Context,
class InnerInitial = mpl::list<> >
struct MyState : sc::simple_state< MostDerived, Context, InnerInitial >
{
public:
MyState() : exitCalled_( false ) {}
~MyState()
{
// BOOST_REQUIRE throws an exception when the test fails. If the state
// is destructed as part of a stack unwind, abort() is called what is
// presumably also detected by the test monitor.
BOOST_REQUIRE( exitCalled_ );
}
void exit()
{
exitCalled_ = true;
}
private:
bool exitCalled_;
};
struct B;
struct C;
struct A : MyState< A, TerminationTest, mpl::list< B, C > >
{
typedef sc::termination< EvTerminateA > reactions;
};
struct B : MyState< B, A::orthogonal< 0 > >
{
typedef sc::termination< EvTerminateB > reactions;
};
struct D;
struct E;
struct C : MyState< C, A::orthogonal< 1 >, mpl::list< D, E > >
{
typedef sc::termination< EvTerminateC > reactions;
};
struct D : MyState< D, C::orthogonal< 0 > >
{
typedef sc::termination< EvTerminateD > reactions;
};
struct F;
struct G;
struct E : MyState< E, C::orthogonal< 1 >, mpl::list< F, G > >
{
typedef sc::termination< EvTerminateE > reactions;
};
struct F : MyState< F, E::orthogonal< 0 > >
{
typedef sc::termination< EvTerminateF > reactions;
};
struct G : MyState< G, E::orthogonal< 1 > >
{
typedef sc::termination< EvTerminateG > reactions;
};
TerminationTest::TerminationTest()
{
// 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";
stateNamesMap_[ G::static_type() ] = "G";
}
struct X;
struct TerminationEventBaseTest :
sc::state_machine< TerminationEventBaseTest, X > {};
struct X : sc::simple_state< X, TerminationEventBaseTest >
{
typedef sc::termination< sc::event_base > reactions;
};
int test_main( int, char* [] )
{
TerminationTest machine;
machine.AssertInState( "" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateE() );
machine.AssertInState( "ABCD" );
machine.process_event( EvTerminateE() );
machine.AssertInState( "ABCD" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateC() );
machine.AssertInState( "AB" );
machine.process_event( EvTerminateC() );
machine.AssertInState( "AB" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateA() );
machine.AssertInState( "" );
machine.process_event( EvTerminateA() );
machine.AssertInState( "" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateG() );
machine.AssertInState( "ABCDEF" );
machine.process_event( EvTerminateG() );
machine.AssertInState( "ABCDEF" );
machine.process_event( EvTerminateF() );
machine.AssertInState( "ABCD" );
machine.process_event( EvTerminateF() );
machine.AssertInState( "ABCD" );
machine.process_event( EvTerminateD() );
machine.AssertInState( "AB" );
machine.process_event( EvTerminateD() );
machine.AssertInState( "AB" );
machine.process_event( EvTerminateB() );
machine.AssertInState( "" );
machine.process_event( EvTerminateB() );
machine.AssertInState( "" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateB() );
machine.AssertInState( "ACDEFG" );
machine.process_event( EvTerminateB() );
machine.AssertInState( "ACDEFG" );
machine.process_event( EvTerminateD() );
machine.AssertInState( "ACEFG" );
machine.process_event( EvTerminateD() );
machine.AssertInState( "ACEFG" );
machine.process_event( EvTerminateF() );
machine.AssertInState( "ACEG" );
machine.process_event( EvTerminateF() );
machine.AssertInState( "ACEG" );
machine.process_event( EvTerminateG() );
machine.AssertInState( "" );
machine.process_event( EvTerminateG() );
machine.AssertInState( "" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.process_event( EvTerminateE() );
machine.AssertInState( "ABCD" );
machine.process_event( EvTerminateE() );
machine.AssertInState( "ABCD" );
machine.process_event( EvTerminateC() );
machine.AssertInState( "AB" );
machine.process_event( EvTerminateC() );
machine.AssertInState( "AB" );
machine.process_event( EvTerminateA() );
machine.AssertInState( "" );
machine.process_event( EvTerminateA() );
machine.AssertInState( "" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.initiate();
machine.AssertInState( "ABCDEFG" );
machine.terminate();
machine.AssertInState( "" );
machine.terminate();
machine.AssertInState( "" );
TerminationEventBaseTest eventBaseMachine;
eventBaseMachine.initiate();
BOOST_REQUIRE( !eventBaseMachine.terminated() );
eventBaseMachine.process_event( EvTerminateA() );
BOOST_REQUIRE( eventBaseMachine.terminated() );
eventBaseMachine.initiate();
BOOST_REQUIRE( !eventBaseMachine.terminated() );
eventBaseMachine.process_event( EvTerminateB() );
BOOST_REQUIRE( eventBaseMachine.terminated() );
return 0;
}