#include "stdafx.h"
#include "CppUnitTest.h"

#include "../../include/iutest.hpp"
#include "../../include/gtest/iutest_switch.hpp"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#ifdef USE_IUTEST_TR1
#include "../../include/tr1/iutest_vc_unittest.hpp"

namespace iutest_unittest
{
	TEST_MODULE_INITIALIZE(iutest)
	{
		::iuutil::VisualStudio::SetUpCppUnitTest();
	}

	TEST_CLASS(UnitTest1)
	{
	public:
		TEST_CLASS_INITIALIZE(a) { Logger::WriteMessage(__FUNCTION__); }
		TEST_METHOD_INITIALIZE(b) { Logger::WriteMessage(__FUNCTION__); }

		TEST_METHOD(TestMethod1)
		{
			Assert::AreEqual(0, 1);
		}
		TEST_METHOD(TestMethod2)
		{
			Assert::AreEqual(0, 1);
		}

	};
}
#endif

/** --------------------------------------------------
 * 簡単なテスト
*//*--------------------------------------------------*/
IUTEST(Test, Version)
{
	unsigned long v = (IUTEST_MAJORVER << 24) | (IUTEST_MINORVER << 16) | (IUTEST_BUILD << 8) | IUTEST_REVISION;
	IUTEST_ASSERT_EQ( IUTEST_VER, v );
}

IUTEST(Test, GetName)
{
	IUTEST_ASSERT_STREQ("GetName", iutest::UnitTest::GetInstance()->current_test_info()->name());
	RecordProperty("test_name", iutest::UnitTest::GetInstance()->current_test_info()->name());
	RecordProperty("number", 1);
}

IUTEST(Test, Stream)
{
	IUTEST_SUCCEED() << "OK!!";
	IUTEST_SUCCEED() << L"OK!!";
}

/** --------------------------------------------------
 * テストフィクスチャの利用
*//*--------------------------------------------------*/
class TestFixed : public iutest::Test
{
protected:
	static int x;
public:
	virtual void SetUp(void)
	{
		++x;
	}
	static void SetUpTestCase(void)
	{
		x = 0;
	}
};
int TestFixed::x = -1;

IUTEST_F(TestFixed, Test1)
{
	IUTEST_ASSERT_EQ(1, x);
}

IUTEST_F(TestFixed, Test2)
{
	IUTEST_ASSERT_EQ(1, x);
}

typedef TestFixed TestFixed2;

IUTEST_F(TestFixed2, Test1)
{
	IUTEST_ASSERT_EQ(1, x);
}

IUTEST_F(TestFixed2, Test2)
{
	IUTEST_ASSERT_EQ(1, x);
}

/** --------------------------------------------------
 * 各種アサーション
*//*--------------------------------------------------*/
IUTEST(AssertionTest, NoFailure)
{
	IUTEST_ASSERT_NO_FATAL_FAILURE( IUTEST_EXPECT_TRUE(true) );
	IUTEST_EXPECT_NO_FATAL_FAILURE( IUTEST_EXPECT_TRUE(true) );
	IUTEST_INFORM_NO_FATAL_FAILURE( IUTEST_EXPECT_TRUE(true) );
}
IUTEST(AssertionTest, Base)
{
	int x0=0, y0=0, x1=1;
	float f0=0.0f, f1=1.0f;
	double d0=0.0, d1=1.0;
	// EQ
	{
		IUTEST_ASSERT_EQ(x0, y0);
		IUTEST_EXPECT_EQ(x0, y0);
		IUTEST_INFORM_EQ(x0, y0);
		int* zero=NULL;
		IUTEST_ASSERT_EQ(NULL, zero);
	}

	// NE
	{
		IUTEST_ASSERT_NE(x0, x1);
		IUTEST_EXPECT_NE(x0, x1);
		IUTEST_INFORM_NE(x0, x1);
		int* one=(int*)1;
		IUTEST_ASSERT_NE(NULL, one);
	}

	// LE, LT
	{
		IUTEST_ASSERT_LE(x0, y0);
		IUTEST_EXPECT_LE(f0, f1);
		IUTEST_INFORM_LE(0.0, 0x1);
		IUTEST_ASSERT_LT(x0, x1);
		IUTEST_EXPECT_LT(d0, d1);
		IUTEST_INFORM_LT(0.0, 0x1);
	}

	// GE, GT
	{
		IUTEST_ASSERT_GE(x0, y0);
		IUTEST_EXPECT_GE(f1, f0);
		IUTEST_INFORM_GE(0x1, 0.0f);
		IUTEST_ASSERT_GT(x1, x0);
		IUTEST_EXPECT_GT(d1, d0);
		IUTEST_INFORM_GT(0x1, 0.0f);
	}

	// float. double
	{
		IUTEST_ASSERT_FLOAT_EQ(1.0f, f1);
		IUTEST_EXPECT_FLOAT_EQ(0.0f, f0);
		IUTEST_INFORM_FLOAT_EQ(-1.0f, -2.0f/2.0f);

		IUTEST_ASSERT_DOUBLE_EQ(1.0, d1);
		IUTEST_EXPECT_DOUBLE_EQ(0.0, d0);
		IUTEST_INFORM_DOUBLE_EQ(-1.0, -2.0/2.0);
	}

	// Near
	{
		IUTEST_ASSERT_NEAR(0, 1, 2);
		IUTEST_EXPECT_NEAR(1.0f, 4.0f, 4);
		IUTEST_INFORM_NEAR(2.0, 1.0, 2);
	}
}

IUTEST(AssertionTest, String)
{
	const char mbs[] = "test";
	const wchar_t wcs[] = L"test";
	IUTEST_ASSERT_STREQ( "test", mbs);
	IUTEST_ASSERT_STREQ(L"test", wcs);
	IUTEST_EXPECT_STREQ( "test", mbs);
	IUTEST_INFORM_STREQ(L"test", wcs);
	IUTEST_ASSERT_STRNE( "host", mbs);
	IUTEST_ASSERT_STRNE(L"host", wcs);
	IUTEST_EXPECT_STRNE( "host", mbs);
	IUTEST_INFORM_STRNE(L"host", wcs);

	IUTEST_ASSERT_STRLNEQ(4, mbs);
	IUTEST_ASSERT_STRLNEQ(4, wcs);
	IUTEST_EXPECT_STRLNEQ(4, wcs);
	IUTEST_INFORM_STRLNEQ(4, wcs);

	{
		std::string str1 = "tEst";
		std::string str2 = "teSt";
		std::string str3 = "hoSt";

		IUTEST_ASSERT_STRCASEEQ("TeSt", mbs);
		IUTEST_ASSERT_STRCASEEQ("TeSt", str1);
		IUTEST_EXPECT_STRCASEEQ(str1, "TeSt");
		IUTEST_INFORM_STRCASEEQ(str1, str2);

		IUTEST_ASSERT_STRCASENE("HoSt", mbs);
		IUTEST_ASSERT_STRCASENE("HoSt", str1);
		IUTEST_EXPECT_STRCASENE(str1, "HoSt");
		IUTEST_INFORM_STRCASENE(str1, str3);
	}
}

#if defined(IUTEST_OS_WINDOWS)

IUTEST(AssertionTest, HResult)
{
	IUTEST_ASSERT_HRESULT_SUCCEEDED(0);
	IUTEST_EXPECT_HRESULT_SUCCEEDED(0);
	IUTEST_INFORM_HRESULT_SUCCEEDED(0);
	IUTEST_ASSERT_HRESULT_FAILED(-1);
	IUTEST_EXPECT_HRESULT_FAILED(-1);
	IUTEST_INFORM_HRESULT_FAILED(-1);
}

#endif

/** --------------------------------------------------
 * 述語アサーション
*//*--------------------------------------------------*/
static bool IsOdd(int val)
{
	return (val & 1) != 0;
}
static bool	IsGreater(int a, int b)
{
	return a > b;
}
static bool	PredTest3(int , int , int )
{
	return true;
}
static bool	PredTest4(int , int , int , int )
{
	return true;
}
static bool	PredTest5(int , int , int , int , int )
{
	return true;
}
IUTEST(AssertionTest, Pred)
{
	IUTEST_ASSERT_PRED1(IsOdd, 3);
	IUTEST_EXPECT_PRED1(IsOdd, 3);
	IUTEST_INFORM_PRED1(IsOdd, 3);
	IUTEST_ASSERT_PRED2(IsGreater, 3, 1);
	IUTEST_EXPECT_PRED2(IsGreater, 3, 1);
	IUTEST_INFORM_PRED2(IsGreater, 3, 1);
	IUTEST_ASSERT_PRED3(PredTest3, 0, 1, 2);
	IUTEST_EXPECT_PRED3(PredTest3, 0, 1, 2);
	IUTEST_INFORM_PRED3(PredTest3, 0, 1, 2);
	IUTEST_ASSERT_PRED4(PredTest4, 0, 1, 2, 3);
	IUTEST_EXPECT_PRED4(PredTest4, 0, 1, 2, 3);
	IUTEST_INFORM_PRED4(PredTest4, 0, 1, 2, 3);
	IUTEST_ASSERT_PRED5(PredTest5, 0, 1, 2, 3, 4);
	IUTEST_EXPECT_PRED5(PredTest5, 0, 1, 2, 3, 4);
	IUTEST_INFORM_PRED5(PredTest5, 0, 1, 2, 3, 4);
}

/** --------------------------------------------------
 * 値をパラメータ化したテスト
*//*--------------------------------------------------*/
#if IUTEST_HAS_PARAM_TEST
// Param Test Range
class TestP : public iutest::TestWithParam<int>
{
protected:
	static int a;
	static int b;
public:
	static void SetUpTestCase(void)
	{
		a = 0;
		b = 0;
		IUTEST_SUCCEED() << "SetUpTestCase";
	}
	static void TearDownTestCase(void)
	{
		IUTEST_SUCCEED() << "TearDownTestCase";
	}
};
int TestP::a = 0;
int TestP::b = 0;

IUTEST_INSTANTIATE_TEST_CASE_P(TestPInstance, TestP, iutest::Range<int>(0, 10));

IUTEST_P(TestP, TestA)
{
	IUTEST_ASSERT_EQ(a, GetParam());
	++a;
}

IUTEST_P(TestP, TestB)
{
	IUTEST_ASSERT_EQ(b, GetParam());
	++b;
}

// Param Test Bool
class TestBool : public iutest::TestWithParam<bool> {};
IUTEST_INSTANTIATE_TEST_CASE_P(TestBoolInstance, TestBool, iutest::Bool());

IUTEST_P(TestBool, TestA)
{
	static bool x=false;
	IUTEST_ASSERT_EQ(x, GetParam());
	x = !x;
}

// Param Test ValueIn
const char ValueInTestText[] = "ValueInTestText";
class TestPValueIn : public iutest::TestWithParam<char>
{
protected:
	static int a;
public:
	static void SetUpTestCase(void)
	{
		a = 0;
	}
};
int TestPValueIn::a = 0;
IUTEST_INSTANTIATE_TEST_CASE_P(TestPValueInInstance, TestPValueIn, iutest::ValuesIn(ValueInTestText));

IUTEST_P(TestPValueIn, TestA)
{
	IUTEST_ASSERT_EQ(ValueInTestText[a++], GetParam());
}

// Param Test Values
class TestPValues1 : public iutest::TestWithParam<float> {};
IUTEST_INSTANTIATE_TEST_CASE_P(TestPValues1Instance, TestPValues1, iutest::Values(1.0f));

IUTEST_P(TestPValues1, TestA)
{
	IUTEST_ASSERT_FLOAT_EQ(1.0f, GetParam());
}

class TestPValuesN : public iutest::TestWithParam<int>
{
protected:
	static int a;
public:
	static void SetUpTestCase(void)
	{
		a = 1;
	}
};
int TestPValuesN::a = 1;
IUTEST_INSTANTIATE_TEST_CASE_P(TestPValuesNInstance, TestPValuesN, iutest::Values(1, 2, 3, 4, 5, 6, 7, 8, 9
																	//, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
																	//, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
																	//, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
																	//, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
																	//, 50
																	));

IUTEST_P(TestPValuesN, TestA)
{
	IUTEST_ASSERT_EQ(a++, GetParam());
}

#if IUTEST_HAS_COMBINE

class TestPCombine : public iutest::TestWithParam< iutest::tuples::tuple<bool, int, int> >
{
};

IUTEST_P(TestPCombine, TestA)
{
	bool b = iutest::tuples::get<0>(GetParam());
	int i1 = iutest::tuples::get<1>(GetParam());
	int i2 = iutest::tuples::get<2>(GetParam());
	IUTEST_SUCCEED() << b << ", " << i1 << ", " << i2;
}

IUTEST_INSTANTIATE_TEST_CASE_P(TestPCombineInstance, TestPCombine
							   , iutest::Combine( iutest::Bool(), iutest::Values(1, 2), iutest::Values(10, 11) ) );
#endif

#endif

/** --------------------------------------------------
 * 型付けテスト
*//*--------------------------------------------------*/
#if IUTEST_HAS_TYPED_TEST

template<typename T>
class TypedTest : public iutest::Test 
{
public:
	static void SetUpTestCase(void)
	{
		IUTEST_SUCCEED() << "SetUpTestCase";
	}
	static void TearDownTestCase(void)
	{
		IUTEST_SUCCEED() << "TearDownTestCase";
	}
};
typedef iutest::Types<int, long, short> TypedTestTypes;
IUTEST_TYPED_TEST_CASE(TypedTest, TypedTestTypes);

IUTEST_TYPED_TEST(TypedTest, Equal)
{
	TypeParam a = 0;
	IUTEST_ASSERT_EQ(0, a);
}

IUTEST_TYPED_TEST(TypedTest, Litle)
{
	TypeParam a = 1;
	IUTEST_ASSERT_LT(0, a);
}

#endif

/** --------------------------------------------------
 * 型をパラメータ化したテスト
*//*--------------------------------------------------*/
#if IUTEST_HAS_TYPED_TEST_P

template<typename T>
class TypedTestP : public iutest::Test {};
typedef iutest::Types<int, long, short> TypedTestTypes;
IUTEST_TYPED_TEST_CASE_P(TypedTestP);

IUTEST_TYPED_TEST_P(TypedTestP, Equal)
{
	TypeParam a = 0;
	IUTEST_ASSERT_EQ(0, a);
}

IUTEST_TYPED_TEST_P(TypedTestP, Litle)
{
	TypeParam a = 1;
	IUTEST_ASSERT_LT(0, a);
}

IUTEST_REGISTER_TYPED_TEST_CASE_P(TypedTestP, Equal, Litle);
IUTEST_INSTANTIATE_TYPED_TEST_CASE_P(TypedTestPInstance, TypedTestP, TypedTestTypes);

#endif

/** --------------------------------------------------
 * Diabled テスト
*//*--------------------------------------------------*/
// このテストは無効テスト
IUTEST(TestDisabled, DISABLED_Test1)
{
	IUTEST_ASSERT_TRUE(false);
}

IUTEST(TestDisabled, Test2)
{
	IUTEST_ASSERT_TRUE(true);
}

// テストケースすべてを無効にする
IUTEST(DISABLED_TestCaseDisabled, Test1)
{
	IUTEST_ASSERT_TRUE(false);
}
IUTEST(DISABLED_TestCaseDisabled, Test2)
{
	IUTEST_ASSERT_TRUE(false);
}

/** --------------------------------------------------
 * 例外アサーション
*//*--------------------------------------------------*/
#if IUTEST_HAS_EXCEPTIONS

static void	ExceptionFunction(int i)
{
	switch( i )
	{
	case 0:
		return;
	case 1:
		throw 2;
		break;
	case 2:
		throw std::bad_exception();
		break;
	default:
		break;
	}
}

IUTEST(AssertionTest, Exception)
{
	//IUTEST_ASSERT_THROW(throw std::bad_exception(), std::bad_exception);
	IUTEST_ASSERT_THROW(ExceptionFunction(2), std::bad_exception);
	IUTEST_EXPECT_THROW(ExceptionFunction(2), std::bad_exception);
	IUTEST_INFORM_THROW(ExceptionFunction(2), std::bad_exception);
	IUTEST_ASSERT_ANY_THROW(ExceptionFunction(1));
	IUTEST_EXPECT_ANY_THROW(ExceptionFunction(1));
	IUTEST_INFORM_ANY_THROW(ExceptionFunction(1));
	IUTEST_ASSERT_NO_THROW(ExceptionFunction(0));
	IUTEST_EXPECT_NO_THROW(ExceptionFunction(0));
	IUTEST_INFORM_NO_THROW(ExceptionFunction(0));
}

class exception_test
{
public:
	exception_test(const std::vector<int>&)
	{
		IUTEST_SUPPRESS_UNREACHABLE_CODE_WARNING(throw std::exception());
	}
};

IUTEST(AssertionTest, Exception2)
{
	std::vector<int> a;
	IUTEST_ASSERT_THROW(exception_test(a), std::exception);
}

#endif

/** --------------------------------------------------
 * 型の一致テスト
*//*--------------------------------------------------*/
IUTEST(StaticTest, Eq)
{
	IUTEST_ASSERT_TRUE( (iutest::StaticAssertTypeEq<int, int>()) );
	//iutest::StaticAssertTypeEq<bool, int>();
}

