statechart/test/FifoSchedulerTest.cpp

262 lines
7.3 KiB
C++

//////////////////////////////////////////////////////////////////////////////
// (c) Copyright Andreas Huber Doenni 2005
// 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/asynchronous_state_machine.hpp>
#include <boost/statechart/fifo_scheduler.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/termination.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/mpl/list.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/test/test_tools.hpp>
#include <stdexcept>
namespace sc = boost::statechart;
namespace mpl = boost::mpl;
struct EvCheckCtorArgs : sc::event< EvCheckCtorArgs >
{
EvCheckCtorArgs( int expectedArgs ) : expectedArgs_( expectedArgs ) {}
const int expectedArgs_;
};
struct EvTerminate : sc::event< EvTerminate > {};
struct EvFail : sc::event< EvFail > {};
struct Initial;
struct FifoSchedulerTest :
sc::asynchronous_state_machine< FifoSchedulerTest, Initial >
{
public:
//////////////////////////////////////////////////////////////////////////
FifoSchedulerTest( my_context ctx ) :
my_base( ctx ),
ctorArgs_( 0 )
{
}
FifoSchedulerTest( my_context ctx, int arg1 ) :
my_base( ctx ),
ctorArgs_( arg1 )
{
}
FifoSchedulerTest( my_context ctx, int arg1, int arg2 ) :
my_base( ctx ),
ctorArgs_( arg1 * 10 + arg2 )
{
}
FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3 ) :
my_base( ctx ),
ctorArgs_( ( arg1 * 10 + arg2 ) * 10 + arg3 )
{
}
FifoSchedulerTest(
my_context ctx,
int arg1, int arg2, int arg3, int arg4
) :
my_base( ctx ),
ctorArgs_( ( ( arg1 * 10 + arg2 ) * 10 + arg3 ) * 10 + arg4 )
{
}
FifoSchedulerTest(
my_context ctx,
int arg1, int arg2, int arg3, int arg4, int arg5
) :
my_base( ctx ),
ctorArgs_( ( ( ( arg1 * 10 + arg2 ) * 10 +
arg3 ) * 10 + arg4 ) * 10 + arg5 )
{
}
FifoSchedulerTest(
my_context ctx,
int arg1, int arg2, int arg3, int arg4, int arg5, int arg6
) :
my_base( ctx ),
ctorArgs_( ( ( ( ( arg1 * 10 + arg2 ) * 10 +
arg3 ) * 10 + arg4 ) * 10 + arg5 ) * 10 + arg6 )
{
}
int CtorArgs()
{
return ctorArgs_;
}
private:
//////////////////////////////////////////////////////////////////////////
const int ctorArgs_;
};
boost::intrusive_ptr< const sc::event_base > MakeEvent(
const sc::event_base * pEvent )
{
return boost::intrusive_ptr< const sc::event_base >( pEvent );
}
struct Initial : sc::simple_state< Initial, FifoSchedulerTest >
{
typedef mpl::list<
sc::custom_reaction< EvCheckCtorArgs >,
sc::termination< EvTerminate >,
sc::custom_reaction< EvFail >
> reactions;
sc::result react( const EvCheckCtorArgs & ev )
{
BOOST_REQUIRE( ev.expectedArgs_ == outermost_context().CtorArgs() );
outermost_context_type & machine = outermost_context();
machine.my_scheduler().queue_event(
machine.my_handle(), MakeEvent( new EvTerminate() ) );
return discard_event();
}
sc::result react( const EvFail & )
{
BOOST_FAIL( "State machine is unexpectedly still running." );
return discard_event();
}
};
struct UnexpectedEventCount : public std::runtime_error
{
UnexpectedEventCount() : std::runtime_error( "" ) {}
};
void RunScheduler(
sc::fifo_scheduler<> & scheduler, unsigned long expectedEventCount )
{
// Workaround: For some reason MSVC has a problem with BOOST_REQUIRE here
// (C1055: compiler limit: out of keys)
if ( scheduler() != expectedEventCount )
{
throw UnexpectedEventCount();
}
}
void Check(
sc::fifo_scheduler<> & scheduler,
const sc::fifo_scheduler<>::processor_handle & processor,
int ctorArgs )
{
// Make sure the processor has been created
RunScheduler( scheduler, 1UL );
scheduler.initiate_processor( processor );
// This event triggers the queueing of another event, which itself
// terminates the machine ...
scheduler.queue_event(
processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
// ... that's why 3 instead of two events must have been processed
RunScheduler( scheduler, 3UL );
// Since the machine has been terminated, this event will be ignored
scheduler.queue_event( processor, MakeEvent( new EvFail() ) );
RunScheduler( scheduler, 1UL );
// Check that we can reinitiate the machine
scheduler.initiate_processor( processor );
scheduler.queue_event(
processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
RunScheduler( scheduler, 3UL );
// Check that we are terminated again
scheduler.queue_event( processor, MakeEvent( new EvFail() ) );
RunScheduler( scheduler, 1UL );
scheduler.destroy_processor( processor );
// The following will simply be ignored because the processor has already
// be destroyed
scheduler.initiate_processor( processor );
scheduler.queue_event(
processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
RunScheduler( scheduler, 3UL );
}
void SetToTrue( bool & value )
{
value = true;
}
int test_main( int, char* [] )
{
try
{
sc::fifo_scheduler<> scheduler;
const sc::fifo_scheduler<>::processor_handle processor0 =
scheduler.create_processor< FifoSchedulerTest >();
Check( scheduler, processor0, 0 );
const sc::fifo_scheduler<>::processor_handle processor1 =
scheduler.create_processor< FifoSchedulerTest >( 1 );
Check( scheduler, processor1, 1 );
const sc::fifo_scheduler<>::processor_handle processor2 =
scheduler.create_processor< FifoSchedulerTest >( 1, 2 );
Check( scheduler, processor2, 12 );
const sc::fifo_scheduler<>::processor_handle processor3 =
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3 );
Check( scheduler, processor3, 123 );
const sc::fifo_scheduler<>::processor_handle processor4 =
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4 );
Check( scheduler, processor4, 1234 );
const sc::fifo_scheduler<>::processor_handle processor5 =
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5 );
Check( scheduler, processor5, 12345 );
const sc::fifo_scheduler<>::processor_handle processor6 =
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5, 6 );
Check( scheduler, processor6, 123456 );
RunScheduler( scheduler, 0UL );
bool workItem1Processed = false;
scheduler.queue_work_item(
boost::bind( &SetToTrue, boost::ref( workItem1Processed ) ) );
RunScheduler( scheduler, 1UL );
BOOST_REQUIRE( workItem1Processed );
scheduler.terminate();
RunScheduler( scheduler, 1UL );
BOOST_REQUIRE( scheduler.terminated() );
RunScheduler( scheduler, 0UL );
bool workItem2Processed = false;
scheduler.queue_work_item(
boost::bind( &SetToTrue, boost::ref( workItem2Processed ) ) );
// After being terminated, a call to operator() must not process any more
// events
RunScheduler( scheduler, 0UL );
BOOST_REQUIRE( !workItem2Processed );
}
catch ( const UnexpectedEventCount & )
{
BOOST_FAIL( "Unexpected event count." );
}
return 0;
}