//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * テストケースヘッダ
 * @author Junpee
 */

#ifndef TEST_CASE_H_
#define TEST_CASE_H_

#include <LampUnit/Test.h>

/**
 * テスト用失敗マクロ
 * @param message メッセージ
 */
#define TestFail(message) \
	{ addFailure((message), __FILE__, __LINE__); }

/**
 * テスト用アサートマクロ
 * @param condition falseならアサート発生
 */
#define TestAssert(condition) \
	if(!(condition)){ TestFail((#condition)); }

/**
 * テスト用アサートマクロ
 * @param condition falseならアサート発生
 * @param message メッセージ
 */
#define TestAssertMessage(condition, message) \
	if(!(condition)){ TestFail((message)); }

/**
 * テスト用イコールマクロ
 * @param expected 期待される値
 * @param actual 実際の値
 */
#define TestEquals(expected, actual) \
	{ assertEquals((expected), (actual), __FILE__, __LINE__); }

/**
 * テスト用ノットイコールマクロ
 * @param notExpected 期待されない値
 * @param actual 実際の値
 */
#define TestNotEquals(notExpected, actual) \
	{ assertNotEquals((notExpected), (actual), __FILE__, __LINE__); }

/**
 * テスト用イコールマクロ
 * @param expected 期待される値
 * @param actual 実際の値
 * @param epsilon 許容誤差
 */
#define TestEpsilonEquals(expected, actual, epsilon) \
	{ assertEquals((expected), (actual), __FILE__, __LINE__, (epsilon)); }

/**
 * テスト用ノットイコールマクロ
 * @param notExpected 期待されない値
 * @param actual 実際の値
 * @param epsilon 許容誤差
 */
#define TestNotEpsilonEquals(notExpected, actual, epsilon) \
	{ assertNotEquals((notExpected), (actual), __FILE__, __LINE__, (epsilon)); }

namespace LampUnit{

//------------------------------------------------------------------------------
/**
 * テストケースヘッダ
 */
class TestCase : public Test{
public:
	/**
	 * コンストラクタ
	 * @param name テストの名前
	 */
	explicit TestCase(Lamp::String name);

	/**
	 * デストラクタ
	 */
	virtual ~TestCase(){}

	/**
	 * テストを実行する
	 */
	virtual void run(TestResult* result);

	/**
	 * テストケースの個数を取得
	 * @return テストケースの個数
	 */
	virtual int getCountTestCases() const{ return 1; }

	/**
	 * テストの名前を取得
	 * @return テストの名前
	 */
	virtual const Lamp::String& getName() const{ return name_; }

	/**
	 * テストを文字列に変換
	 * @return テストの文字列表記
	 */
	virtual const Lamp::String& toString() const{ return name_; }

	/**
	 * テストの初期化
	 */
	virtual void setUp(){}

	/**
	 * テストの後始末
	 */
	virtual void tearDown(){}

	/**
	 * テストスィートの取得
	 *
	 * 各テストクラスのスィートを作成する場合は、同じ定義を持つメソッドを
	 * 各テストクラスで新たに作成してください。
	 * @return テストスィート
	 */
	static Test* suite(){ return NULL; }

//------------------------------------------------------------------------------
protected:
	/**
	 * コンストラクタ
	 *
	 * TestCallerから呼ばれるときに使用する
	 */
	TestCase();

	/**
	 * テストを実行する
	 */
	virtual void runTest() = 0;

	/**
	 * 失敗の追加
	 * @param message メッセージ
	 * @param file ファイル名
	 * @param line 行数
	 */
	virtual void addFailure(
		const Lamp::String& message, const char* file, int line);

	//--------------------------------------------------------------------------
	// 値が同じかどうか
	//--------------------------------------------------------------------------
	/**
	 * 値が同じかどうか
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @return 値が同じならtrue
	 */
	bool assertEquals(int expected, int actual, const char* file, int line){
		if(expected == actual){ return true; }
		addFailure(notEqualMessage(expected, actual), file, line);
		return false;
	}

	/**
	 * 値が同じかどうか
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @return 値が同じならtrue
	 */
	bool assertEquals(u_int expected, u_int actual,
		const char* file, int line){
		if(expected == actual){ return true; }
		addFailure(notEqualMessage(expected, actual), file, line);
		return false;
	}

	/**
	 * 値が同じかどうか
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	bool assertEquals(float expected, float actual,
		const char* file, int line, float epsilon = 0.f){
		Assert(epsilon >= 0.f);
		if(equals(expected, actual, epsilon)){ return true; }
		addFailure(notEqualMessage(expected, actual, epsilon), file, line);
		return false;
	}

	/**
	 * 値が同じかどうか
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	bool assertEquals(double expected, double actual,
		const char* file, int line, double epsilon = 0.f){
		Assert(epsilon >= 0.f);
		if(equals(expected, actual, epsilon)){ return true; }
		addFailure(notEqualMessage(expected, actual, epsilon), file, line);
		return false;
	}

	/**
	 * 文字列が同じかどうか
	 * @param expected 期待される文字列
	 * @param actual 実際の文字列
	 * @param file ファイル名
	 * @param line 行数
	 * @return 文字列が同じならtrue
	 */
	bool assertEquals(
		const char* expected, const char* actual, const char* file, int line){
		if(equals(expected, actual)){ return true; }
		addFailure(notEqualMessage(expected, actual), file, line);
		return false;
	}

	/**
	 * 文字列が同じかどうか
	 * @param expected 期待される文字列
	 * @param actual 実際の文字列
	 * @param file ファイル名
	 * @param line 行数
	 * @return 文字列が同じならtrue
	 */
	bool assertEquals(const Lamp::String& expected, const Lamp::String& actual,
		const char* file, int line){
		return assertEquals(
			expected.getBytes(), actual.getBytes(), file, line);
	}

	//--------------------------------------------------------------------------
	// 値が同じでないかどうか
	//--------------------------------------------------------------------------
	/**
	 * 値が同じでないかどうか
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @return 値が同じならtrue
	 */
	bool assertNotEquals(
		int notExpected, int actual, const char* file, int line){
		if(notExpected != actual){ return true; }
		addFailure(equalMessage(notExpected, actual), file, line);
		return false;
	}

	/**
	 * 値が同じでないかどうか
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @return 値が同じならtrue
	 */
	bool assertNotEquals(u_int notExpected, u_int actual,
		const char* file, int line){
		if(notExpected != actual){ return true; }
		addFailure(equalMessage(notExpected, actual), file, line);
		return false;
	}

	/**
	 * 値が同じでないかどうか
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	bool assertNotEquals(float notExpected, float actual,
		const char* file, int line, float epsilon = 0.f){
		Assert(epsilon >= 0.f);
		if(!equals(notExpected, actual, epsilon)){ return true; }
		addFailure(equalMessage(notExpected, actual, epsilon), file, line);
		return false;
	}

	/**
	 * 値が同じでないかどうか
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param file ファイル名
	 * @param line 行数
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	bool assertNotEquals(double notExpected, double actual,
		const char* file, int line, double epsilon = 0.f){
		Assert(epsilon >= 0.f);
		if(!equals(notExpected, actual, epsilon)){ return true; }
		addFailure(equalMessage(notExpected, actual, epsilon), file, line);
		return false;
	}

	/**
	 * 文字列が同じでないかどうか
	 * @param notExpected 期待されない文字列
	 * @param actual 実際の文字列
	 * @param file ファイル名
	 * @param line 行数
	 * @return 文字列が同じならtrue
	 */
	bool assertNotEquals(const char* notExpected, const char* actual,
		const char* file, int line){
		if(!equals(notExpected, actual)){ return true; }
		addFailure(equalMessage(notExpected, actual), file, line);
		return false;
	}

	/**
	 * 文字列が同じでないかどうか
	 * @param notExpected 期待されない文字列
	 * @param actual 実際の文字列
	 * @param file ファイル名
	 * @param line 行数
	 * @return 文字列が同じならtrue
	 */
	bool assertNotEquals(
		const Lamp::String& notExpected, const Lamp::String& actual,
		const char* file, int line){
		return assertNotEquals(
			notExpected.getBytes(), actual.getBytes(), file, line);
	}

	//--------------------------------------------------------------------------
	// 値の比較
	//--------------------------------------------------------------------------
	/**
	 * floatの比較
	 * @param leftValue 左の値
	 * @param rightValue 右の値
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	static inline bool equals(
		float leftValue, float rightValue, float epsilon = 0.f){
		if(leftValue == rightValue){ return true; }
		float difference = leftValue - rightValue;
		if(difference < 0.f){ difference = -difference; }
		return (difference <= epsilon);
	}

	/**
	 * doubleの比較
	 * @param leftValue 左の値
	 * @param rightValue 右の値
	 * @param epsilon 許容誤差
	 * @return 値が同じならtrue
	 */
	static inline bool equals(
		double leftValue, double rightValue, double epsilon = 0.f){
		if(leftValue == rightValue){ return true; }
		double difference = leftValue - rightValue;
		if(difference < 0.f){ difference = -difference; }
		return (difference <= epsilon);
	}

	/**
	 * 文字列の比較
	 * @param leftValue 左の値
	 * @param rightValue 右の値
	 * @return 値が同じならtrue
	 */
	static bool equals(const char* leftValue, const char* rightValue);

	//--------------------------------------------------------------------------
	// 値が同じでないメッセージ
	//--------------------------------------------------------------------------
	/**
	 * 値が同じでないメッセージ
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @return 作成されたメッセージ
	 */
	static Lamp::String notEqualMessage(int expected, int actual);

	/**
	 * 値が同じでないメッセージ
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @return 作成されたメッセージ
	 */
	static Lamp::String notEqualMessage(
		u_int expected, u_int actual);

	/**
	 * 値が同じでないメッセージ
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param epsilon 許容誤差
	 * @return 作成されたメッセージ
	 */
	static Lamp::String notEqualMessage(
		float expected, float actual, float epsilon);

	/**
	 * 値が同じでないメッセージ
	 * @param expected 期待される値
	 * @param actual 実際の値
	 * @param epsilon 許容誤差
	 * @return 作成されたメッセージ
	 */
	static Lamp::String notEqualMessage(
		double expected, double actual, double epsilon);

	/**
	 * 文字列が同じでないメッセージ
	 * @param expected 期待される文字列
	 * @param actual 実際の文字列
	 * @return 作成されたメッセージ
	 */
	static Lamp::String notEqualMessage(
		const char* expected, const char* actual);

	//--------------------------------------------------------------------------
	// 値が同じメッセージ
	//--------------------------------------------------------------------------
	/**
	 * 値が同じメッセージ
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @return 作成されたメッセージ
	 */
	static Lamp::String equalMessage(int notExpected, int actual);

	/**
	 * 値が同じメッセージ
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @return 作成されたメッセージ
	 */
	static Lamp::String equalMessage(
		u_int notExpected, u_int actual);

	/**
	 * 値が同じメッセージ
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param epsilon 許容誤差
	 * @return 作成されたメッセージ
	 */
	static Lamp::String equalMessage(
		float notExpected, float actual, float epsilon);

	/**
	 * 値が同じメッセージ
	 * @param notExpected 期待されない値
	 * @param actual 実際の値
	 * @param epsilon 許容誤差
	 * @return 作成されたメッセージ
	 */
	static Lamp::String equalMessage(
		double notExpected, double actual, double epsilon);

	/**
	 * 文字列が同じメッセージ
	 * @param notExpected 期待されない文字列
	 * @param actual 実際の文字列
	 * @return 作成されたメッセージ
	 */
	static Lamp::String equalMessage(
		const char* notExpected, const char* actual);

//------------------------------------------------------------------------------
private:
	// コピーコンストラクタの隠蔽
	TestCase(const TestCase& copy);

	// 代入コピーの隠蔽
	void operator =(const TestCase& copy);

	// 名前
    const Lamp::String name_;
	// テスト結果
	TestResult* result_;
};

//------------------------------------------------------------------------------
} // End of namespace LampUnit
#endif // End of TEST_CASE_H_
//------------------------------------------------------------------------------
