/**
 * @file TestQueue.cpp
 * @date Jul 30, 2008
 * @author Ronald Kluth
 *
 * @brief Tests for ODEMx class Queue
 */

#include "TestSynchronization.h"
#include "../TestBase/TestBase.h"

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

	/**
	 * @struct QueueFixture
	 * @brief Helper struct providing set-up/tear-down of Queue tests
	 *
	 * @copydetails EventFixture
	 */
	struct QueueFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		Queue queue;
		SuiteBase::ProcessTest process1;
		SuiteBase::ProcessTest process2;
		SuiteBase::ProcessTest process3;
		data::TypeInfo type;

		QueueFixture()
		:	sim( "QueueTestSim" ),
			log( TestLogConsumer::create() ),
			queue( sim, "QueueTest" ),
			process1( sim, "QueueTestProcess1" ),
			process2( sim, "QueueTestProcess2" ),
			process3( sim, "QueueTestProcess3" ),
			type( typeid(Queue) )
			{
				sim.addConsumer( log );
			}
	};

	/**
	 * @test odemx::Queue construction
	 *
	 * Expected effects:
	 * @li the simulation (statistics manager) is set correctly
	 * @li the label is set correctly
	 * @li the statistics are initialized
	 * @li the length of the queue is \c 0
	 */
	TEST_FIXTURE( QueueFixture, ConstructionDestruction )
	{
		data::Label label = "QueueTestConstructionUserSim";
		{
			Queue queue2( sim, label );
			CHECK( log->getTraceRecord( "create", type ) );
			CHECK( log->getStatisticsRecord( "update", "length", type ) );
			CHECK_EQUAL( label, queue2.getLabel() );
			CHECK_EQUAL( (std::size_t) 0, queue2.getLength() );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );
	}

	/**
	 * @test odemx::Queue::inSort(Process*,bool)
	 *
	 * Expected function call effects:
	 * @li all process pointers are added to the queue
	 * @li in FIFO mode, the process is appended to the list
	 * @li in LIFO mode, the process is prepended to the list
	 * @li the processes are in the expected order
	 */
	TEST_FIXTURE( QueueFixture, InSort )
	{
		queue.inSort( &process2 );
		CHECK_EQUAL( (std::size_t) 1, queue.getLength() );
		CHECK( log->getTraceRecord( "insert", type ) );
		CHECK( log->getStatisticsRecord( "update", "length", type ) );

		bool fifo = true; // is the default
		queue.inSort( &process3, fifo );
		CHECK_EQUAL( (std::size_t) 2, queue.getLength() );

		fifo = false;
		queue.inSort( &process1, fifo );
		CHECK_EQUAL( (std::size_t) 3, queue.getLength() );

		CHECK_EQUAL( &process1, queue.getTop() ); queue.popQueue();
		CHECK_EQUAL( &process2, queue.getTop() ); queue.popQueue();
		CHECK_EQUAL( &process3, queue.getTop() ); queue.popQueue();
		CHECK( queue.isEmpty() );
	}

	/**
	 * @test odemx::Queue::remove(Process*)
	 *
	 * Expected function call effects:
	 * @li the specified Process is removed from the queue
	 * @li the statistics are updated
	 * @li the order of the other processes is not affected
	 */
	TEST_FIXTURE( QueueFixture, Remove )
	{
		queue.inSort( &process1 );
		queue.inSort( &process2 );
		queue.inSort( &process3 );

		CHECK_EQUAL( (std::size_t) 3, queue.getLength() );
		queue.remove( &process2 );
		CHECK( log->getTraceRecord( "remove", type ) );
		CHECK( log->getStatisticsRecord( "update", "length", type ) );

		// 0 means the process is not in the queue
		CHECK_EQUAL( (std::size_t) 0, queue.getPosition( &process2 ) );
		CHECK_EQUAL( (std::size_t) 2, queue.getLength() );

		CHECK_EQUAL( &process1, queue.getTop() ); queue.popQueue();
		CHECK_EQUAL( &process3, queue.getTop() ); queue.popQueue();
		CHECK( queue.isEmpty() );
	}

	/**
	 * @test odemx::Queue::popQueue()
	 *
	 * Expected function call effects:
	 * @li the first Process is removed from the queue
	 * @li the statistics are updated
	 * @li the order of the other processes is not affected
	 */
	TEST_FIXTURE( QueueFixture, PopQueue )
	{
		queue.popQueue();
		CHECK( log->getWarningRecord( "Queue::popQueue(): called on empty queue", type ) );

		queue.inSort( &process1 );
		queue.inSort( &process2 );
		queue.inSort( &process3 );

		base::Process* removed = queue.getTop();
		CHECK_EQUAL( (std::size_t) 3, queue.getLength() );
		queue.popQueue();
		CHECK( log->getTraceRecord( "pop queue", type ) );
		CHECK( log->getStatisticsRecord( "update", "length", type ) );

		// 0 means the process is not in the queue
		CHECK_EQUAL( (std::size_t) 0, queue.getPosition( removed ) );
		CHECK_EQUAL( (std::size_t) 2, queue.getLength() );

		CHECK_EQUAL( &process2, queue.getTop() ); queue.popQueue();
		CHECK_EQUAL( &process3, queue.getTop() ); queue.popQueue();
		CHECK( queue.isEmpty() );
	}

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