/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */


#include <boost/test/unit_test.hpp>
#include "Event.h"
#include "PortableThread.h"
#include "Timer.h"


using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Common;


BOOST_AUTO_TEST_SUITE(SuiteCommonLib)


class Sleeper : public Thread
{
public:
	int seconds;
	Sleeper(int sec) : seconds(sec) {
		start();
		wait();
	}
	virtual ~Sleeper() {};
	
protected:
	virtual void run() {
		sleep(1000*seconds);
	}
};


class MockCallback : public Callback
{
private:
	int  expectedTicks;
	int  ticks;
	Event event;

public:
	MockCallback(int expTicks = 1) : expectedTicks(expTicks), ticks(0) {};
	virtual ~MockCallback ()  {};
	
	void Call() {
		if (expectedTicks == ++ticks)
			event.signal();
	}
	
	int ticksReceived() {
		return ticks;
	}
	
	void wait() {
		if (expectedTicks > ticks)
			event.wait();
	}
};


BOOST_AUTO_TEST_CASE(TestCase_TimerCreate)
{
	// test timer's initial state
	Timer timer;
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_CASE(TestCase_TimerPlainStart)
{
	// test timer's state after start
	Timer timer;
	
	timer.Start();
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_CASE(TestCase_TimerTick1)
{
	// test timer is able to tick once
	const int ticksToTest = 1;
	MockCallback callback(ticksToTest);
	Timer timer;
	
	timer.SetCallback(&callback);
	timer.SetInterval(1);
	timer.SetTicksCount(ticksToTest);
	timer.Start();
	
	callback.wait();
	Sleeper sleeper(3);
	
	BOOST_CHECK( ticksToTest == callback.ticksReceived() );
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_CASE(TestCase_TimerTick5)
{
	// test timer is able to tick 5 times
	const int ticksToTest = 5;
	MockCallback callback(ticksToTest);
	Timer timer;
	
	timer.SetCallback(&callback);
	timer.SetInterval(1);
	timer.SetTicksCount(ticksToTest);
	timer.Start();
	
	callback.wait();
	// wait for 3 seconnds for the timer's close 
	Sleeper sleeper(3);

	BOOST_CHECK( ticksToTest == callback.ticksReceived() );
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_CASE(TestCase_TimerRepeatedStart)
{
	// test that timer is able to be restarted after it completes all tick from the previous start
	const int ticksToTest = 3;
	MockCallback callback(ticksToTest);
	Timer timer;
	
	timer.SetCallback(&callback);
	timer.SetInterval(1);
	timer.SetTicksCount(ticksToTest);
	timer.Start();
	
	callback.wait();
	Sleeper sleeper(3);
	
	BOOST_CHECK( ticksToTest == callback.ticksReceived() );
	BOOST_CHECK( !timer.IsRunning() );



	MockCallback callback2(ticksToTest);
	timer.SetCallback(&callback2);
	timer.Start();
	
	callback2.wait();
	// wait for 3 seconnds for the timer's close 
	Sleeper sleeper2(3);
	
	BOOST_CHECK( ticksToTest == callback.ticksReceived() );
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_CASE(TestCase_TimerStop)
{
	// test that timer is able to stop ticking
	const int ticksToTest = 10;
	MockCallback callback(ticksToTest);
	Timer timer;
	
	timer.SetCallback(&callback);
	timer.SetInterval(1);
	timer.SetTicksCount(ticksToTest);

	timer.Start();
	Sleeper sleeper(3);

	timer.Stop();
	Sleeper sleeper2(3);
	
	BOOST_CHECK( !timer.IsRunning() );
}


BOOST_AUTO_TEST_SUITE_END()
