/**
 * @file TestPortHead.cpp
 * @date Jul 27, 2008
 * @author Ronald Kluth
 *
 * @brief Tests for ODEMx class template PortHeadT
 */

#include "TestSynchronization.h"
#include "../TestBase/TestBase.h"
#include <odemx/data/output/OStreamWriter.h>

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

	/// make PortTestElement objects streamable
	std::ostream& operator<< ( std::ostream& stream, const PortTestElement& e )
	{
		return stream << e.id;
	}

	/// compare PortTestElement objects
	bool operator== ( const PortTestElement& e1, const PortTestElement& e2 )
	{
		return e1.id == e2.id;
	}

	/**
	 * @struct PortHeadTFixture
	 * @brief Helper struct providing set-up/tear-down of PortHeadT tests
	 *
	 * @copydetails EventFixture
	 */
	struct PortHeadTFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		PortHeadTTestObserver observer;
		data::TypeInfo type;
		data::TypeInfo portType;
		data::TypeInfo tailType;

		PortHeadTFixture()
		:	sim( "PortHeadTTestSim" ),
			log( TestLogConsumer::create() ),
			observer( false ),
			type( typeid( PortHeadT< PortTestElement >) ),
			portType( typeid(PortT< PortTestElement >) ),
			tailType( typeid(PortTailT< PortTestElement >) )
			{
				sim.addConsumer( log );
			}
	};

	/**
	 * @test odemx::PortHeadT construction and destruction
	 *
	 * Expected effects:
	 * @li label is set correctly
	 * @li mode is set correctly
	 * @li capacity is set correctly
	 * @li the Memory type is set correctly
	 * @li observer is set correctly
	 * @li construction and destruction can be observed
	 * @li attempt to create a 0-size port causes an error message and
	 * leads to the creation of a port with default capacity
	 */
	TEST_FIXTURE( PortHeadTFixture, ConstructionDestruction )
	{
		observer.history.clear();

		data::Label label = "PortHeadTTestConstruction4";
		{
			PortHeadT< PortTestElement >::Ptr port4 = PortHeadT< PortTestElement >::create(
					sim, label, ERROR_MODE, 1000, &observer );
			CHECK_EQUAL( label + " (head)", port4->getLabel() );
			CHECK_EQUAL( ERROR_MODE, port4->getMode() );
			CHECK_EQUAL( (std::size_t) 1000, port4->getMaxCapacity() );
			CHECK_EQUAL( &observer, port4->data::Observable< PortHeadTObserver< PortTestElement > >::getObservers().front() );
			CHECK( log->getTraceRecord( "create", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "element type", portType ) );
			CHECK( log->getStatisticsRecord( "parameter", "maximum capacity", portType ) );
			CHECK( observer.observed( "onCreate", port4->getLabel() ) );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );

		PortHeadT< PortTestElement >::Ptr zeroPort1 = PortHeadT< PortTestElement >::create(
				sim, label, ZERO_MODE, 0, &observer );
		CHECK( log->getErrorRecord( "PortT(): attempt to create Port with invalid capacity, using default", portType ) );
		CHECK_EQUAL( (std::size_t) 10000, zeroPort1->getMaxCapacity() );
	}

	/**
	 * @test odemx::PortHeadT::get() with ZERO_MODE
	 *
	 * Expected function call effects:
	 * @li empty port returns a null pointer without warning or error
	 * @li when not empty, the port returns the first element
	 * and removes it from the buffer
	 */
	TEST_FIXTURE( PortHeadTFixture, ZeroModeGet )
	{
		PortHeadT< int* >::Ptr head = PortHeadT< int* >::create( sim, "PortHeadTTestZeroModeGet", ZERO_MODE, 100 );
		CHECK( 0 == head->get().get() );

		// get old number of elements and put some into the port
		const std::list< int* >& elementList = head->getBuffer();
		int* element1 = new int( 1 );
		int* element2 = new int( 2 );
		head->getTail()->put( element1 );
		head->getTail()->put( element2 );
		std::size_t elementCount = elementList.size();

		// get the first element
		std::auto_ptr< int* > elementPtr = head->get();
		CHECK( 0 != elementPtr.get() );
		CHECK_EQUAL( element1, *elementPtr );
		CHECK_EQUAL( elementCount - 1, elementList.size() );
		CHECK( std::find( elementList.begin(), elementList.end(), element2 ) != elementList.end() );
	}

	/**
	 * @test odemx::PortHeadT::get() with ERROR_MODE
	 *
	 * Expected function call effects:
	 * @li empty port returns a null pointer and sends an error message
	 * to \c errorStream()
	 * @li when not empty, the port returns the first element
	 * and removes it from the buffer
	 */
	TEST_FIXTURE( PortHeadTFixture, ErrorModeGet )
	{
		PortHeadT< string >::Ptr head = PortHeadT< string >::create( sim, "PortHeadTTestErrorModeGet", ERROR_MODE, 100 );
		CHECK( 0 == head->get().get() );
		CHECK( log->getErrorRecord( "PortHeadT::get(): called on empty port", typeid(PortHeadT< string >) ) );

		// get old number of elements and put some into the port
		const std::list< string >& elementList = head->getBuffer();
		string element1( "1" );
		string element2( "2" );
		head->getTail()->put( element1 );
		head->getTail()->put( element2 );
		std::size_t elementCount = elementList.size();

		// get the first element
		std::auto_ptr< string > elementPtr = head->get();
		CHECK( 0 != elementPtr.get() );
		CHECK_EQUAL( element1, *elementPtr );
		CHECK_EQUAL( elementCount - 1, elementList.size() );
		CHECK( find( elementList.begin(), elementList.end(), element2 ) != elementList.end() );
	}

	/**
	 * @test odemx::PortHeadT::get() with WAITING_MODE
	 *
	 * Expected function call effects:
	 * @li calling on empty port from environment produces an error
	 * @li calling on empty port from event produces an error
	 * @li interrupt of a waiting Process causes return of a null pointer
	 * @li a waiting Process is alerted and gets the first element
	 * as soon as one becomes available
	 * @li the port returns the first element and removes it from the buffer
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, WaitModeGet )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestWaitModeGet", WAITING_MODE, 100, &observer );

		CHECK( 0 == head->get().get() );
		CHECK( log->getErrorRecord( "PortHeadT::get(): port in waiting mode called from environment", type ) );

		// call from Event
		PortHeadTTestGetEvent getterEvent( sim, "PortHeadTTestWaitGetFailEvent", head );
		getterEvent.schedule();
		sim.step();
		CHECK( getterEvent.received.get() == 0 );
		CHECK( log->getErrorRecord( "PortHeadT::get(): waiting mode not allowed for events", type ) );

		// call from Process, which gets interrupted
		PortHeadTTestGetProcess getterProcess1( sim, "PortHeadTTestWaitGetProcess", head );
		getterProcess1.activate();
		sim.step();
		CHECK_EQUAL( base::Process::IDLE, getterProcess1.getProcessState() );

		// interrupt the Process
		getterProcess1.interrupt();
		CHECK( getterProcess1.isScheduled() );
		sim.step();
		// 0 means that the auto_ptr was 0
		CHECK( 0 == getterProcess1.received.id );

		// successful call from Process
		PortHeadTTestGetProcess getterProcess2( sim, "PortHeadTTestWaitGetProcess", head );
		getterProcess2.activate();
		sim.step();

		// get old number of elements and put some into the port
		const std::list< PortTestElement >& elementList = head->getBuffer();
		// make an element available
		PortTestElement element1 = { 1 };
		head->getTail()->put( element1 );
		PortTestElement element2 = { 2 };
		head->getTail()->put( element2 );
		std::size_t elementCount = elementList.size();

		// let the Process resume and get the first element
		sim.step();
		CHECK_EQUAL( element1, getterProcess2.received );
		CHECK_EQUAL( elementCount - 1, elementList.size() );
		CHECK( std::find( elementList.begin(), elementList.end(), element2 ) != elementList.end() );
		CHECK( log->getTraceRecord( "get", type ) );
		CHECK( observer.observed( "onGet", head->getLabel() ) );
		CHECK( log->getStatisticsRecord( "count", "get calls", portType ) );
		CHECK( log->getStatisticsRecord( "update", "buffer size", portType ) );
	}

	/**
	 * @test odemx::PortHeadT::get( ElementCondition sel ) with WAITING_MODE
	 *
	 * Expected function call effects:
	 * @li calling on empty port from environment produces an error
	 * @li calling on empty port from event produces an error
	 * @li interrupt of a waiting Process causes return of a null pointer
	 * @li a waiting Process is alerted and gets the element matching the criterion 
   *     as soon as one becomes available
	 * @li the port returns the matching element and removes it from the buffer
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, WaitModeGetCriterion )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestWaitModeGet", WAITING_MODE, 100, &observer );

		CHECK( 0 == head->get().get());
		CHECK( log->getErrorRecord( "PortHeadT::get(): port in waiting mode called from environment", type ) );

		// call from Event
		PortHeadTTestGetEvent getterEvent( sim, "PortHeadTTestWaitGetCriterionFailEvent", head );
		getterEvent.schedule();
		sim.step();
		CHECK( getterEvent.received.get() == 0 );
		CHECK( log->getErrorRecord( "PortHeadT::get(): waiting mode not allowed for events", type ) );

		///// call from Process, which gets interrupted
		PortHeadTTestGetCriterionProcess getterProcess1( sim, "PortHeadTTestWaitGetCriterionProcess", head );
		getterProcess1.activate();
		sim.step();
		CHECK_EQUAL( base::Process::IDLE, getterProcess1.getProcessState() );

		// interrupt the Process
		getterProcess1.interrupt();
		CHECK( getterProcess1.isScheduled() );
		sim.step();
		// 0 means that the auto_ptr was 0
		CHECK( 0 == getterProcess1.received.id );

		// successful call from Process
		PortHeadTTestGetCriterionProcess getterProcess2( sim, "PortHeadTTestWaitGetCriterionProcess", head );
		getterProcess2.activate();
		sim.step();

		// get old number of elements and put some into the port
		const std::list< PortTestElement >& elementList = head->getBuffer();
		// make an element available
		PortTestElement element1 = { 0 };
		head->getTail()->put( element1 );

    sim.step ();

		PortTestElement element2 = { 1 };
		head->getTail()->put( element2 );

		std::size_t elementCount = elementList.size();

		// let the Process resume and get the last element, as that fits the criterion
    sim.step ();

		CHECK_EQUAL( element2, getterProcess2.received );
		CHECK_EQUAL( elementCount - 1, elementList.size() );

    // test if the other elements are still in the queue
		CHECK( std::find( elementList.begin(), elementList.end(), element1 ) != elementList.end() );

		CHECK( log->getTraceRecord( "get", type ) );
		CHECK( observer.observed( "onGet", head->getLabel() ) );
		CHECK( log->getStatisticsRecord( "count", "get calls", portType ) );
		CHECK( log->getStatisticsRecord( "update", "buffer size", portType ) );
	}

	/**
	 * @test odemx::PortHeadT::get() PortTailT alert call
	 *
	 * Expected function call effects:
	 * @li Processes waiting at the PortTail of a full Port are alerted as
	 * soon as space becomes available
	 * @li the new element of the waiting Process is successfully put into
	 * the buffer
	 */
	TEST_FIXTURE( PortHeadTFixture, GetAlertTail )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestGetAlertTail", WAITING_MODE, 1, &observer );
		PortTestElement element1 = { 1 };
		PortTailT< PortTestElement >::Ptr tail = head->getTail();
		tail->put( element1 );

		unsigned int elementId = 9;
		PortTailTTestPutProcess putProcess( sim, "PortHeadTTestGetAlertTailPutter", tail, elementId );
		putProcess.activate();
		sim.step();
		CHECK_EQUAL( base::Process::IDLE, putProcess.getProcessState() );

		// taking one element from the port should alert the waiting putProcess
		head->get();
		CHECK( putProcess.isAlerted() );
		sim.step();
		// check if the new element was put into the port
		CHECK_EQUAL( elementId, head->getBuffer().back().id );
		CHECK( log->getTraceRecord( "alert", tailType ) );
	}

	/**
	 * @test odemx::PortHeadT::get( ElementCondition ) PortTailT alert call
	 *
	 * Expected function call effects:
	 * @li Processes waiting at the PortTail of a full Port are alerted as
	 * soon as space becomes available
	 * @li the new element of the waiting Process is successfully put into
	 * the buffer
	 */
	TEST_FIXTURE( PortHeadTFixture, GetCriterionAlertTail )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestGetAlertTail", WAITING_MODE, 1, &observer );
		PortTestElement element1 = { 1 };
		PortTailT< PortTestElement >::Ptr tail = head->getTail();
		tail->put( element1 );

		unsigned int elementId = 1;
		PortTailTTestPutProcess putProcess( sim, "PortHeadTTestGetAlertTailPutter", tail, elementId );
		putProcess.activate();
		sim.step();
		CHECK_EQUAL( base::Process::IDLE, putProcess.getProcessState() );

		// taking one element from the port should alert the waiting putProcess
		PortHeadTTestGetProcess getterProcess1( sim, "PortHeadTTestWaitGetCriterionProcess", head );
    getterProcess1.activate ();
		sim.step();
		CHECK( putProcess.isAlerted() );

    // putProcess should put it's element into the port in the next step
		sim.step();
		// check if the new element was put into the port
		CHECK_EQUAL( elementId, head->getBuffer().back().id );
		CHECK( log->getTraceRecord( "alert", tailType ) );
	}

	/**
	 * @test odemx::PortHeadT::unGet(ElementType)
	 *
	 * Expected function call effects:
	 * @li successful call returns true
	 * @li the element is added to the front of the buffer
	 * @li the call can be observed
	 * @li if the port is full and in WAITING_MODE or ERROR_MODE, it sends
	 * an error message to \c errorStream() and returns false
	 * @li in ZERO_MODE unsuccessful calls only return false without error
	 */
	TEST_FIXTURE( PortHeadTFixture, UnGet )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestUnGet", WAITING_MODE, 2, &observer );
		PortTestElement element1 = { 1 };
		PortTestElement element2 = { 2 };
		PortTestElement element3 = { 3 };
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		tail->put( element1 );

		bool returnValue = head->unGet( element2 );
		CHECK_EQUAL( true, returnValue );
		CHECK_EQUAL( element1, head->getBuffer().back() );
		CHECK_EQUAL( element2, head->getBuffer().front() );
		CHECK( observer.observed( "onUnGet", head->getLabel(), toString( element2 ) ) );

		returnValue = head->unGet( element3 );
		CHECK_EQUAL( false, returnValue );
		CHECK( log->getErrorRecord( "PortHeadT::unGet(): called on full port", type ) );

		head->setMode( ERROR_MODE );
		returnValue = head->unGet( element3 );
		CHECK_EQUAL( false, returnValue );
		CHECK( log->getErrorRecord( "PortHeadT::unGet(): called on full port", type ) );

		head->setMode( ZERO_MODE );
		returnValue = head->unGet( element3 );
		CHECK_EQUAL( false, returnValue );
	}

	/**
	 * @test odemx::PortHeadT::getTail()
	 *
	 * Expected function call effects:
	 * @li mode and maximum capacity values of head and tail are the same
	 */
	TEST_FIXTURE( PortHeadTFixture, GetTail )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTestGetTail", WAITING_MODE, 1 );
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		CHECK_EQUAL( tail->getMaxCapacity(), head->getMaxCapacity() );
		CHECK_EQUAL( tail->getMode(), head->getMode() );
	}

	/**
	 * @test odemx::PortHeadT::cut()
	 *
	 * Expected function call effects:
	 * @li there are now two Ports, with the upstream one still holding the data
	 * @li a new head to the old buffer is returned
	 * @li the original tail now has a new internal buffer
	 * @li mode and capacity of the new Port are the same
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, Cut )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestCut", WAITING_MODE, 2, &observer );
		PortTestElement element1 = { 1 };
		PortTestElement element2 = { 2 };
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		tail->put( element1 );
		tail->put( element2 );
		CHECK_EQUAL( (std::size_t) 2, head->count() );

		// an intermediary Process could do something with the data
		// from old buffer and put it into the new buffer, to which
		// the original head is then attached
		PortHeadT< PortTestElement >::Ptr oldBufferHead( head->cut() );
		PortTailT< PortTestElement >::Ptr newBufferTail( head->getTail() );
		CHECK_EQUAL( head.get(), newBufferTail->getHead().get() );
		CHECK_EQUAL( tail.get(), oldBufferHead->getTail().get() );
		CHECK_EQUAL( (std::size_t) 0, newBufferTail->count() );
		CHECK_EQUAL( (std::size_t) 2, oldBufferHead->count() );
		CHECK_EQUAL( oldBufferHead->getMode(), newBufferTail->getMode() );
		CHECK_EQUAL( oldBufferHead->getMaxCapacity(), newBufferTail->getMaxCapacity() );
		CHECK( log->getTraceRecord( "cut", type ) );
		CHECK( observer.observed( "onCut", head->getLabel() ) );
	}

	/**
	 * @test odemx::PortHeadT::splice(PortTailT*)
	 *
	 * Expected function call effects:
	 * @li two ports with separate buffers are joined into one
	 * @li the upstream port's contents is appended to the downstream port's buffer
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, Splice )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestSplice", WAITING_MODE, 2, &observer );
		PortTestElement element1 = { 1 };
		PortTestElement element2 = { 2 };
		PortTestElement element3 = { 3 };
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		PortHeadT< PortTestElement >::Ptr oldBufferHead( head->cut() );
		PortTailT< PortTestElement >::Ptr newBufferTail( head->getTail() );
		tail->put( element1 );
		tail->put( element2 );
		CHECK_EQUAL( (std::size_t) 2, tail->count() );
		newBufferTail->put( element3 );
		CHECK_EQUAL( (std::size_t) 1, head->count() );

		// this will join the ports again and delete the two superflous objects
		data::Label label = oldBufferHead->getLabel();
		oldBufferHead->data::Observable< PortHeadTObserver< PortTestElement > >::addObserver( &observer );
		oldBufferHead->splice( newBufferTail );
		CHECK_EQUAL( (std::size_t) 3, head->count() );
		CHECK_EQUAL( element3, head->getBuffer().front() );
		CHECK_EQUAL( element2, head->getBuffer().back() );
		CHECK( log->getTraceRecord( "splice", type ) );
		CHECK( observer.observed( "onSplice", label, newBufferTail->getLabel() ) );
	}

	/**
	 * @test odemx::PortHeadT::splice(PortTailT*) with head alert
	 *
	 * Expected function call effects:
	 * @li when the upstream port gets the appended elements and if it has
	 * waiting processes, alert() is called
	 */
	TEST_FIXTURE( PortHeadTFixture, SpliceAlert )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestSplice", WAITING_MODE, 2, &observer );
		PortHeadTTestGetProcess getter( sim, "PortHeadTTestSpliceGetter", head );
		getter.activate();
		sim.step();
		CHECK_EQUAL( base::Process::IDLE, getter.getProcessState() );

		PortTestElement element1 = { 1 };
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		PortHeadT< PortTestElement >::Ptr oldBufferHead( head->cut() );
		PortTailT< PortTestElement >::Ptr newBufferTail( head->getTail() );
		tail->put( element1 );
		CHECK_EQUAL( (std::size_t) 1, tail->count() );
		CHECK_EQUAL( (std::size_t) 0, head->count() );

		// this will join the ports again and delete the two superflous objects
		oldBufferHead->splice( newBufferTail );
		CHECK_EQUAL( (std::size_t) 1, head->count() );
		CHECK( getter.isAlerted() );
	}

	/**
	 * @test odemx::PortHeadT::cppSplice(PortTailT*) append contents
	 *
	 * Expected function call effects:
	 * @li the contents of the tail's port is appended to head's port
	 * @li the tail's buffer is empty
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, CppSpliceAppend )
	{
		PortHeadT< PortTestElement >::Ptr port1Head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestCppSpliceAppend", WAITING_MODE, 2, &observer );
		PortTailT< PortTestElement >::Ptr port1Tail( port1Head->getTail() );
		PortTestElement element1 = { 1 };
		port1Tail->put( element1 );

		PortTailT< PortTestElement >::Ptr port2Tail = PortTailT< PortTestElement >::create( sim, "PortHeadTTestCppSpliceAppendTail", WAITING_MODE, 2 );
		PortTestElement element2 = { 2 };
		port2Tail->put( element2 );

		// default is appending of elements
		bool append = true;
		port1Head->cppSplice( port2Tail, append );
		CHECK_EQUAL( element1, port1Head->getBuffer().front() );
		CHECK_EQUAL( element2, port1Head->getBuffer().back() );
		CHECK( port2Tail->getBuffer().empty() );
		CHECK( log->getTraceRecord( "cpp splice", type ) );
		CHECK( observer.observed( "onCppSplice", port1Head->getLabel(), port2Tail->getLabel(), toString( append ) ) );
	}

	/**
	 * @test odemx::PortHeadT::cppSplice(PortTailT*) prepend contents
	 *
	 * Expected function call effects:
	 * @li the contents of the tail's port is prepended to head's port
	 * @li the tail's buffer is empty
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, CppSplicePrepend )
	{
		PortHeadT< PortTestElement >::Ptr port1Head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestCppSplicePrepend", WAITING_MODE, 2, &observer );
		PortTailT< PortTestElement >::Ptr port1Tail( port1Head->getTail() );
		PortTestElement element1 = { 1 };
		port1Tail->put( element1 );

		PortTailT< PortTestElement >::Ptr port2Tail = PortTailT< PortTestElement >::create(
				sim, "PortHeadTTestCppSplicePrependTail", WAITING_MODE, 2 );
		PortTestElement element2 = { 2 };
		port2Tail->put( element2 );

		// false says: prepend instead
		bool append = false;
		port1Head->cppSplice( port2Tail, append );
		CHECK_EQUAL( element2, port1Head->getBuffer().front() );
		CHECK_EQUAL( element1, port1Head->getBuffer().back() );
		CHECK( port2Tail->getBuffer().empty() );
		CHECK( log->getTraceRecord( "cpp splice", type ) );
		CHECK( observer.observed( "onCppSplice", port1Head->getLabel(), port2Tail->getLabel(), toString( append ) ) );
	}

	/**
	 * @test odemx::PortHeadT::setMode(PortMode)
	 *
	 * Expected function call effects:
	 * @li the mode is set correctly
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, SetMode )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestSetMode", WAITING_MODE, 2, &observer );
		head->setMode( ERROR_MODE );
		CHECK_EQUAL( ERROR_MODE, head->getMode() );
		CHECK( log->getTraceRecord( "change mode", type ) );
		CHECK( observer.observed( "onChangeMode", head->getLabel(), "WAITING_MODE", "ERROR_MODE" ) );
	}

	/**
	 * @test odemx::PortHeadT::setMaxCapacity(unsigned int)
	 *
	 * Expected function call effects:
	 * @li the new maximum capacity is set correctly
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, SetMaxCapacity )
	{
		unsigned int oldMax = 2;
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestSetMaxCapacity", WAITING_MODE, oldMax, &observer );
		unsigned int newMax = 125;
		head->setMaxCapacity( newMax );
		CHECK_EQUAL( newMax, head->getMaxCapacity() );
		CHECK( log->getTraceRecord( "change max capacity", type ) );
		CHECK( observer.observed( "onChangeMaxCapacity", head->getLabel(), toString( oldMax ), toString( newMax ) ) );
	}

	/**
	 * @test odemx::PortHeadT::alert()
	 *
	 * Expected function call effects:
	 * @li the waiting process is alerted
	 * @li the call can be observed
	 */
	TEST_FIXTURE( PortHeadTFixture, Alert )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestAlert", WAITING_MODE, 1, &observer );
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );

		// the Process that gets to wait
		PortHeadTTestGetProcess getterProcess( sim, "PortHeadTTestAlertProcess", head );
		getterProcess.activate();
		sim.step();

		// make an element available
		PortTestElement element1 = { 1 };
		tail->put( element1 );
		CHECK( getterProcess.isAlerted() );
		CHECK( log->getTraceRecord( "alert", type ) );
		CHECK( observer.observed( "onAlert", head->getLabel(), getterProcess.getLabel() ) );
	}

	/**
	 * @test odemx::PortHeadT::available()
	 *
	 * Expected function call effects:
	 * @li the function returns \c true when the buffer is not empty
	 */
	TEST_FIXTURE( PortHeadTFixture, Available )
	{
		PortHeadT< PortTestElement >::Ptr head = PortHeadT< PortTestElement >::create(
				sim, "PortHeadTTestAvailable", WAITING_MODE, 1, &observer );
		PortTailT< PortTestElement >::Ptr tail( head->getTail() );
		CHECK( ! head->isAvailable() );

		// make an element available
		PortTestElement element1 = { 1 };
		tail->put( element1 );
		CHECK( head->isAvailable() );
	}

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

