blob: 507463d7af374e374b26fc1e327d1e5a4ed8af70 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// Copyright 2005-2008 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/test/test_tools.hpp>
#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 <stdexcept>
namespace sc = boost::statechart;
namespace mpl = boost::mpl;
struct EvCheckCtorArgs : sc::event< EvCheckCtorArgs >
{
public:
EvCheckCtorArgs( int expectedArgs ) : expectedArgs_( expectedArgs ) {}
const int expectedArgs_;
private:
// avoids C4512 (assignment operator could not be generated)
EvCheckCtorArgs & operator=( const EvCheckCtorArgs & );
};
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();
}
}
static int refArg1;
static int refArg2;
static int refArg3;
static int refArg4;
static int refArg5;
static int refArg6;
void Check(
sc::fifo_scheduler<> & scheduler,
const sc::fifo_scheduler<>::processor_handle & processor,
int ctorArgs )
{
refArg1 = 6;
refArg2 = 5;
refArg3 = 4;
refArg4 = 3;
refArg5 = 2;
refArg6 = 1;
// Make sure the processor has been created
RunScheduler( scheduler, 1UL );
refArg1 = refArg2 = refArg3 = refArg4 = refArg5 = refArg6 = 0;
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;
Check( scheduler, scheduler.create_processor< FifoSchedulerTest >(), 0 );
Check(
scheduler, scheduler.create_processor< FifoSchedulerTest >( 1 ), 1 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ) ),
6 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >( 1, 2 ),
12 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ), boost::cref( refArg2 ) ),
65 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3 ),
123 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ), boost::cref( refArg2 ),
boost::cref( refArg3 ) ),
654 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4 ),
1234 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ), boost::cref( refArg2 ),
boost::cref( refArg3 ), boost::cref( refArg4 ) ),
6543 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5 ),
12345 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ), boost::cref( refArg2 ),
boost::cref( refArg3 ), boost::cref( refArg4 ),
boost::cref( refArg5 ) ),
65432 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5, 6 ),
123456 );
Check(
scheduler,
scheduler.create_processor< FifoSchedulerTest >(
boost::cref( refArg1 ), boost::cref( refArg2 ),
boost::cref( refArg3 ), boost::cref( refArg4 ),
boost::cref( refArg5 ), boost::cref( refArg6 ) ),
654321 );
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;
}