/**
 * @file TestScheduler.cpp
 * @date May 2, 2010
 * @author ron
 *
 * @brief Tests for ODEMx class Scheduler
 */

#include "TestBase.h"

/// @cond DOXYGEN_SKIP
SUITE( Base )
{
/// @endcond

	/**
	 * @brief Helper struct providing set-up/tear-down of Scheduler tests
	 *
	 * @copydoc SchedulerFixture
	 */
	struct SchedulerFixture
	{
		SchedulerFixture():
			sim( "SchedulerTestSim" ),
			log( TestLogConsumer::create() ),
			scheduler( sim, "SchedulerTest" ),
			event( sim, "SchedulerTestEvent" ),
			process( sim, "SchedulerTestProcess" ),
			type( typeid(Scheduler) ),
			execListType( typeid(ExecutionList) )
			{
				sim.addConsumer( log );
			}

		SimulationTest sim; ///< a user-defined simulation
		TestLogConsumer::Ptr log; ///< log consumer to check sim records
		Scheduler scheduler; ///< a Scheduler object
		EventTest event; ///< an Event object
		ProcessTest process; ///< a Process object
		data::TypeInfo type;
		data::TypeInfo execListType;
	};

	/**
	 * @test odemx::Scheduler construction and destruction
	 *
	 * Tests correct Event member initialization:
	 * @li Simulation context
	 * @li Label
	 * @li observation calls of \c onCreate() and \c onDestroy()
	 *
	 * @note One object uses the constructor for DefaultSimulation, while
	 * the other one uses a given Simulation object.
	 */
	TEST_FIXTURE( SchedulerFixture, ConstructionDestruction )
	{
		data::Label label = "SchedulerTestConstructionDestruction";
		{
			Scheduler sched( sim, label );
			CHECK_EQUAL( &sim, &sched.getSimulation() );
			CHECK_EQUAL( label, sched.getLabel() );
			CHECK( log->getTraceRecord( "create", type ) );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );
	}

	/**
	 * @test odemx::base::Scheduler::addSched()
	 *
	 * Expected function call effects:
	 * @li the given Event object is added to the execution list
	 */
	TEST_FIXTURE( SchedulerFixture, AddSched )
	{
		scheduler.addSched( &event );
		CHECK( log->getTraceRecord( "add", execListType ) );
		CHECK( ! scheduler.getExecutionList().isEmpty() );
	}

	/**
	 * @test odemx::base::Scheduler::insertSched()
	 *
	 * Expected function call effects:
	 * @li the given Event object is inserted in the execution list
	 */
	TEST_FIXTURE( SchedulerFixture, InsertSched )
	{
		scheduler.insertSched( &event );
		CHECK( log->getTraceRecord( "insert", execListType ) );
		CHECK( ! scheduler.getExecutionList().isEmpty() );
	}

	/**
	 * @test odemx::base::Scheduler::insertSchedBefore()
	 *
	 * Expected function call effects:
	 * @li the given Event object is added to the execution list
	 */
	TEST_FIXTURE( SchedulerFixture, InsertSchedBefore )
	{
		scheduler.addSched( &process );
		scheduler.insertSchedBefore( &event, &process );
		CHECK( log->getTraceRecord( "insert before", execListType ) );
	}

	/**
	 * @test odemx::base::Scheduler::insertSchedAfter()
	 *
	 * Expected function call effects:
	 * @li the given Event object is added to the execution list
	 */
	TEST_FIXTURE( SchedulerFixture, InsertSchedAfter )
	{
		scheduler.addSched( &process );
		scheduler.insertSchedAfter( &event, &process );
		CHECK( log->getTraceRecord( "insert after", execListType ) );
	}

	/**
	 * @test odemx::base::Scheduler::removeSched()
	 *
	 * Expected function call effects:
	 * @li the given Event object is added to the execution list
	 */
	TEST_FIXTURE( SchedulerFixture, RemoveSched )
	{
		scheduler.addSched( &event );
		CHECK( ! scheduler.getExecutionList().isEmpty() );

		scheduler.removeSched( &event );
		CHECK( log->getTraceRecord( "remove", execListType ) );
		CHECK( scheduler.getExecutionList().isEmpty() );
	}

	/**
	 * @test odemx::base::Scheduler::run()
	 *
	 * Expected function call effects:
	 * @li the scheduled Event and Process are executed
	 * @li the call can be observed
	 */
	TEST_FIXTURE( SchedulerFixture, Run )
	{
		// we need to use the simulation in order to use its scheduler
		event.schedule();
		process.activate();
		sim.run();
		CHECK( event.executed );
		CHECK( process.executed );
		CHECK( log->getTraceRecord( "run", type ) );
		CHECK( log->getTraceRecord( "execute event", type ) );
		CHECK( log->getTraceRecord( "execute process", type ) );
		CHECK( sim.getScheduler().getExecutionList().isEmpty() );
	}

	/**
	 * @test odemx::base::Scheduler::runUntil()
	 *
	 * Expected function call effects:
	 * @li the scheduled Event and Process are executed
	 * @li the call can be observed
	 */
	TEST_FIXTURE( SchedulerFixture, RunUntil )
	{
		// we need to use the simulation in order to use its scheduler
		event.schedule();
		process.activateAt( 4 );
		sim.runUntil( 3 );
		CHECK( event.executed );
		CHECK( ! process.executed );
		CHECK( log->getTraceRecord( "run until", type ) );
		CHECK_EQUAL( (base::SimTime) 3, sim.getTime() );
		sim.runUntil( 4 );
		CHECK( process.executed );
	}

	/**
	 * @test odemx::base::Scheduler::step()
	 *
	 * Expected function call effects:
	 * @li the scheduled Event and Process are executed
	 * @li the call can be observed
	 */
	TEST_FIXTURE( SchedulerFixture, Step )
	{
		// we need to use the simulation in order to use its scheduler
		event.schedule();
		process.activate();
		sim.step();
		CHECK( process.executed );
		CHECK( ! event.executed );
		CHECK( log->getTraceRecord( "step", type ) );

		sim.step();
		CHECK( event.executed );
		CHECK( sim.getScheduler().getExecutionList().isEmpty() );
	}

/// @cond DOXYGEN_SKIP
}
/// @endcond


