/**
 * @file TestServiceProvider.cpp
 * @date May 5, 2010
 * @author ron
 *
 * @brief
 */

#include "TestProtocol.h"
#include "../TestBase/TestBase.h"

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

	/**
	 * @struct ServiceProviderFixture
	 * @brief Helper struct providing set-up/tear-down of ServiceProvider tests
	 *
	 * @copydetails EventFixture
	 */
	struct ServiceProviderFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		ServiceProviderTest sp;
		data::TypeInfo type;

		ServiceProviderFixture()
		:	sim( "ServiceProviderTestSim" )
		,	log( TestLogConsumer::create() )
		,	sp( sim, "ServiceProviderTest" )
		,	type( typeid(ServiceProvider) )
		{
			sim.addConsumer( log );
		}
	};

	TEST_FIXTURE( ServiceProviderFixture, ConstructionDestruction )
	{
		data::Label label = "ServiceProviderTestConstructionDestruction";
		{
			ServiceProviderTest sp( sim, label );
			// service providers are automatically activated
			CHECK( sp.isScheduled() );
			CHECK( log->getTraceRecord( "create", type ) );

			sp.addSap( "SAP" );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );
		CHECK( log->getTraceRecord( "destroy", typeid( odemx::synchronization::PortHeadT< PduPtr > ) ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, MainSapError )
	{
		// start the sp without SAPs
		sim.run();
		CHECK( log->getErrorRecord( "ServiceProvider::main(): "
					"no SAPs registered, cannot enter wait state", type ) );
		CHECK_EQUAL( base::Process::TERMINATED, sp.getProcessState() );
	}

	TEST_FIXTURE( ServiceProviderFixture, MainAlertError )
	{
		// need to add a SAP for waiting
		sp.addSap( "SAP" );

		// start the sp, which will go into wait state
		sim.step();
		CHECK( ! sp.isScheduled() );

		sp.activate();
		sim.step();
		CHECK( log->getErrorRecord( "ServiceProvider::main(): alerter is 0", type ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, Main )
	{
		// need to add a SAP for waiting
		Sap* sap = sp.addSap( "SAP" );

		// start the sp, which will go into wait state
		sim.step();
		CHECK( ! sp.isScheduled() );

		sap->getBuffer()->getTail()->setMode( odemx::synchronization::ERROR_MODE );
		sap->write( PduPtr( new PduTest() ) );
		sim.step();
		CHECK_EQUAL( "SAP", sp.inputSap );
		CHECK( sp.handledInput );
		CHECK( log->getTraceRecord( "handle input", type ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, SetLayer )
	{
		Layer layer( sim, "ServiceProviderTestSetLayer" );
		sp.setLayer( &layer );

		CHECK_EQUAL( &layer, sp.getLayer() );
		CHECK( log->getTraceRecord( "set layer", type ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, AddSap )
	{
		Layer layer( sim, "ServiceProviderTestSetLayer" );
		sp.setLayer( &layer );

		Sap* sap = sp.addSap( "SAP" );
		CHECK_EQUAL( (std::size_t) 1, sp.getSaps().size() );
		CHECK_EQUAL( sap, sp.getSaps().front() );
		// the SAP also gets added to the layer if it is set
		CHECK_EQUAL( (std::size_t) 1, layer.getSaps().size() );
		CHECK_EQUAL( sap, layer.getSaps().begin()->second );

		Sap* found = sp.addSap( "SAP" );
		CHECK_EQUAL( sap, found );
		CHECK( log->getWarningRecord( "ServiceProvider::addSap(): SAP already exists", type ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, GetSap )
	{
		CHECK_EQUAL( (Sap*) 0, sp.getSap( "SAP" ) );
		Sap* sap = sp.addSap( "SAP" );
		CHECK_EQUAL( sap, sp.getSap( "SAP" ) );
	}

	TEST_FIXTURE( ServiceProviderFixture, RemoveSap )
	{
		bool success = sp.removeSap( "SAP" );
		CHECK( ! success );

		Layer layer( sim, "ServiceProviderTestSetLayer" );
		sp.setLayer( &layer );
		sp.addSap( "SAP" );
		CHECK( ! layer.getSaps().empty() );

		success = sp.removeSap( "SAP" );
		CHECK( log->getTraceRecord( "remove SAP", type ) );
		CHECK( layer.getSaps().empty() );
		// check that the sap is deleted by checking its buffer
		CHECK( log->getTraceRecord( "destroy", typeid( odemx::synchronization::PortHeadT< PduPtr > ) ) );
	}
/// @cond DOXYGEN_SKIP
}
/// @endcond
