/**
 * @file TestObservable.cpp
 * @date Jul 6, 2008
 * @author Ronald Kluth
 *
 * @brief Tests for ODEMx class template Observable
 */

#include "TestData.h"
#include "../TestBase/TestBase.h"

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

	using namespace SuiteBase;

	/**
	 * @struct ObservableFixture
	 * @brief Helper struct providing set-up/tear-down of Observable tests
	 *
	 * @copydetails EventFixture
	 */
	struct ObservableFixture
	{
		SimulationTest sim;
		ObservableTestObserver<> observer1;
		ObservableTestObserver<> observer2;
		ObservableTestObserver< double > observerT;
		ObservableTest observable;
		ObservableTestT< double > observableTemplate;

		ObservableFixture()
		:	sim( "ObservableTestSim" ),
		 	observer1( false ),
			observer2( false ),
			observerT( false ),
			observable( sim, "ObservableTest", 999, &observer1 ),
			observableTemplate( sim, "TObservableTest", 12.34, &observerT )
			{}
	};

	/**
	 * @test odemx::Observable construction
	 *
	 * Tests member initialization:
	 * @li default construction
	 * @li registering an Observer from the constructor
	 */
	TEST_FIXTURE( ObservableFixture, ConstructionDestruction )
	{
		// default construction
		Observable< ObservableTestObserver<> > defaultObservable;
		CHECK_EQUAL( static_cast< std::size_t >( 0 ), defaultObservable.getObservers().size() );

		// check constructor observer registration
		CHECK_EQUAL( static_cast< std::size_t >( 1 ), observable.getObservers().size() );
		CHECK_EQUAL( &observer1, observable.getObservers().front() );
	}

	/**
	 * @test odemx::Observable::addObserver(Observer*)
	 *
	 * Expected function call effects:
	 * @li observer list size increased by one
	 * @li the added observer is found in the list
	 */
	TEST_FIXTURE( ObservableFixture, AddObserver )
	{
		observable.addObserver( &observer2 );
		CHECK_EQUAL( static_cast< std::size_t >( 2 ), observable.getObservers().size() );
		CHECK( std::find( observable.getObservers().begin(),
				observable.getObservers().end(), &observer2 )
				!= observable.getObservers().end() );
	}

	/**
	 * @test odemx::Observable::removeObserver(Observer*)
	 *
	 * Expected function call effects:
	 * @li observer list size decreased by one
	 * @li the removed observer is not found in the list
	 */
	TEST_FIXTURE( ObservableFixture, RemoveObserver )
	{
		observable.removeObserver( &observer1 );
		CHECK_EQUAL( static_cast< std::size_t >( 0 ), observable.getObservers().size() );
		CHECK( std::find( observable.getObservers().begin(),
				observable.getObservers().end(), &observer1 )
				== observable.getObservers().end() );
	}

	/**
	 * @test odemx::Observable event macro ODEMX_OBS
	 *
	 * Precondition: \c observable has two registered observers.
	 *
	 * Expected function call effects after \c execute():
	 * @li both observers observed an "onExecute" event from \c observable
	 */
	TEST_FIXTURE( ObservableFixture, EventMacro )
	{
		observable.addObserver( &observer2 );
		observable.execute();

		CHECK( observer1.observed( "onExecute", observable.getName() ) );
		CHECK( observer2.observed( "onExecute", observable.getName() ) );
	}

	/**
	 * @test odemx::Observable attribute change macro ODEMX_OBS_ATTR
	 *
	 * Precondition: \c observable has two registered observers.
	 *
	 * Expected function call effects after \c execute():
	 * @li both observers observed an "onChangeMember" event from \c observable
	 * with old value and new value parameters
	 */
	TEST_FIXTURE( ObservableFixture, AttributeChangeMacro )
	{
		int oldValue = observable.member;
		observable.addObserver( &observer2 );
		observable.setMember( observable.member+1 );

		CHECK( observer1.observed( "onChangeMember", observable.getName(),
				toString( oldValue ), toString( observable.member ) ) );
		CHECK( observer2.observed( "onChangeMember", observable.getName(),
				toString( oldValue ), toString( observable.member ) ) );
	}

	/**
	 * @test odemx::Observable template event macro ODEMX_OBS_T
	 *
	 * Precondition: \c observable has two registered observers.
	 *
	 * Expected function call effects after \c execute():
	 * @li both observers observed an "onExecute" event from \c observable
	 */
	TEST_FIXTURE( ObservableFixture, TemplateEventMacro )
	{
		observableTemplate.execute();
		CHECK( observerT.observed( "onExecute", observableTemplate.getName() ) );
	}

	/**
	 * @test odemx::Observable template attribute change macro ODEMX_OBS_ATTR

	 * Precondition: \c observable has two registered observers.
	 *
	 * Expected function call effects after \c execute():
	 * @li both observers observed an "onChangeMember" event from \c observable
	 * with old value and new value parameters
	 */
	TEST_FIXTURE( ObservableFixture, TemplateAttributeChangeMacro )
	{
		double oldValue = observableTemplate.t_member;
		observableTemplate.setMember( observableTemplate.t_member + 1.22 );

		CHECK( observerT.observed( "onChangeMember", observableTemplate.getName(),
				toString( oldValue ), toString( observableTemplate.t_member ) ) );
	}

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