#include <stdio.h>
#include <assert.h>
#include "ut_string_class.h"


#define DO_TEST(fn)		\
	fn();				\
	puts(#fn)

#define RT_TEST(e)       if (!(e)) test_report_error(__FILE__, __LINE__, #e)
#define RT_TEST_MSG(e,m) if (!(e)) test_report_error(__FILE__, __LINE__, #e, m)

class UT_String_tester
{
public:
	static void test()
	{
		DO_TEST(test_construction);
		DO_TEST(test_copy_construction);
		DO_TEST(test_addition);
		DO_TEST(test_append);
		DO_TEST(test_add_self);
		DO_TEST(test_grow);
		DO_TEST(test_shrink);
		DO_TEST(test_substr);
		DO_TEST(test_swap);
		DO_TEST(test_collating);

		if (!m_nErrCount) {
			puts("All tests passed\n");
		} else {
			printf("\n+++ Failed %d tests! +++\n\n", m_nErrCount);
		}
	}
#undef DO_TEST

private:
	static void test_report_error(	const char*	szFile,
									int			nLine,
									const char*	szExpression,
									const char*	szMsg = 0)
	{
		if (szMsg) {
			printf("%s(%d): Failed test %s (%s)\n", szFile, nLine, szExpression, szMsg);
		} else {
			printf("%s(%d): Failed test %s\n", szFile, nLine, szExpression);
		}
		++m_nErrCount;
	}

	static void assert_string_equality(const UT_String& s1, const UT_String& s2)
	{
		// Excercise comparison operators
		RT_TEST(s1 == s2);
		RT_TEST(s2 == s1);
		RT_TEST(s1.size() == s2.size());
		RT_TEST(!(s1 != s2));
		RT_TEST(((const char*)s1) == s2);
		RT_TEST(((const char*)s2) == s1);
		RT_TEST(s1 == ((const char*)s2));
		RT_TEST(s2 == ((const char*)s1));
		// If your compiler don't handle the following tests, just comment them
		// out. It's not like you'd encounter it in a real program.
		RT_TEST((s1.operator const char*()) == s2);
		RT_TEST((s2.operator const char*()) == s1);
		RT_TEST(s2 == (s1.operator const char*()));
		RT_TEST(s1 == (s2.operator const char*()));
	}

	static void test_construction()
	{
		const UT_String s1;			// empty
		const UT_String s2;			// empty
		const UT_String s3("foo");
		RT_TEST(s1.size() == 0);
		RT_TEST(s2.size() == 0);
		RT_TEST(s1.empty());
		RT_TEST(s2.empty());
		RT_TEST(s3.size() == 3);
		assert_string_equality(s1, s2);
		assert_string_equality(s2, s1);
		RT_TEST(s1 == "");
		RT_TEST(s2 == "");
		RT_TEST(s3 == "foo");
		RT_TEST(s3[0] == 'f');
		RT_TEST(s3[1] == 'o');
		RT_TEST(s3[2] == 'o');
		RT_TEST(s3[3] == '\0');
		const UT_String s4("foo", 2);
		RT_TEST(s4 == "fo");
		RT_TEST(s4.size() == 2);
		const UT_String s5("foo\0\0", 5);
		RT_TEST(s5 == "foo");
		RT_TEST(s5.size() != 3);
	}
	static void test_copy_construction()
	{
		const UT_String s2("foo");
		const UT_String s4(s2);
		RT_TEST(s2.size() == 3);
		RT_TEST(s4.size() == 3);
		assert_string_equality(s2, s4);
		assert_string_equality(s4, s2);
		RT_TEST(s2 == "foo");
		RT_TEST(s4 == "foo");
	}
	static void test_addition()
	{
		const UT_String s1("foo");
		const UT_String s2("bar1");
		UT_String s3 = s1 + s2;
		UT_String s4 = s1 + "bar2";
		RT_TEST(s3.size() == 7);
		RT_TEST(s4.size() == 7);
		RT_TEST(s3 == "foobar1");
		RT_TEST(s4 == "foobar2");
	}
	static void test_append()
	{
		UT_String s1("foo1");
		UT_String s2("foo2");
		const UT_String s3("bar2");
		s1 += "bar1";
		s2 += s3;
		const UT_String sRefStr1("foo1bar1");
		const UT_String sRefStr2("foo2bar2");
		RT_TEST(s1.size() == 8);
		RT_TEST(s2.size() == 8);
		RT_TEST(s1 == "foo1bar1");
		RT_TEST(s2 == "foo2bar2");
		assert_string_equality(s1, sRefStr1);
		assert_string_equality(s2, sRefStr2);
	}
	static void test_add_self()
	{
		UT_String s1("foo");
		s1 += s1;				// append with self
		RT_TEST(s1.size() == 6);
		RT_TEST(s1 == "foofoo");
		s1 = s1 + s1;
		RT_TEST(s1.size() == 12);
		RT_TEST(s1 == "foofoofoofoo");
	}
	static void test_grow()
	{
		UT_String s1("foo");
		s1 += "1";
		RT_TEST(s1.size() == 4);
		RT_TEST(s1 == "foo1");
		s1 += "23";
		RT_TEST(s1.size() == 6);
		RT_TEST(s1 == "foo123");
	}
	static void test_shrink()
	{
		UT_String s1("foo123");
		RT_TEST(s1.size() == 6);		// silly
		RT_TEST(s1 == "foo123");		// silly
		s1 = "foo12";
		RT_TEST(s1.size() == 5);
		RT_TEST(s1 == "foo12");
		s1 = "foo";
		RT_TEST(s1.size() == 3);
		RT_TEST(s1 == "foo");
		s1 = "";
		RT_TEST(s1.size() == 0);
		RT_TEST(s1 == "");
	}
	static void test_substr()
	{
		UT_String s1("abcdefgh");
		UT_String s2(s1.substr(0,100));
		RT_TEST(s2 == "abcdefgh");
		RT_TEST(s2.size() == 8);

		s2 = s1.substr(0,8);
		RT_TEST(s2 == "abcdefgh");
		RT_TEST(s2.size() == 8);
		
		s2 = s1.substr(0,7);
		RT_TEST(s2 == "abcdefg");
		RT_TEST(s2.size() == 7);

		s2 = s1.substr(0,1);
		RT_TEST(s2 == "a");
		RT_TEST(s2.size() == 1);

		s2 = s1.substr(1,7);
		RT_TEST(s2 == "bcdefgh");
		RT_TEST(s2.size() == 7);

		s2 = s1.substr(1,8);
		RT_TEST(s2 == "bcdefgh");
		RT_TEST(s2.size() == 7);
		
		s2 = s1.substr(1,6);
		RT_TEST(s2 == "bcdefg");
		RT_TEST(s2.size() == 6);

		s2 = s1.substr(1,1);
		RT_TEST(s2 == "b");
		RT_TEST(s2.size() == 1);
		
		s2 = s1.substr(1,0);
		RT_TEST(s2 == "");
		RT_TEST(s2.empty());
		RT_TEST(s2.size() == 0);

		s2 = s1.substr(3,2);
		RT_TEST(s2 == "de");
		RT_TEST(s2.size() == 2);
		
		s2 = s1.substr(3,5);
		RT_TEST(s2 == "defgh");
		RT_TEST(s2.size() == 5);

		s2 = s1.substr(3,6);
		RT_TEST(s2 == "defgh");
		RT_TEST(s2.size() == 5);

		s2 = s1.substr(3,7);
		RT_TEST(s2 == "defgh");
		RT_TEST(s2.size() == 5);

		s2 = s1.substr(7,1);
		RT_TEST(s2 == "h");
		RT_TEST(s2.size() == 1);

		s2 = s1.substr(7,2);
		RT_TEST(s2 == "h");
		RT_TEST(s2.size() == 1);
		
		s2 = s1.substr(8,1);
		RT_TEST(s2 == "");
		RT_TEST(s2.size() == 0);
		RT_TEST(s2.empty());
	}
	static void test_swap()
	{
		UT_String s1("foo");
		UT_String s2("bar");
		s1.swap(s2);
		RT_TEST(s1 == "bar");
		RT_TEST(s2 == "foo");
	}
	static void test_collating()
	{
		UT_String s1("foo");
		UT_String s2("bar");
		RT_TEST(s2 < s1);
		s1 = "a";
		s2 = "b";
		RT_TEST(s1 < s2);
		s1 = "ab";
		RT_TEST(s1 < s2);
		s1 = "a";
		s2 = "a";
		RT_TEST(!(s1 < s2));
		s2 = "aa";
		RT_TEST(s1 < s2);
	}

	static size_t m_nErrCount;
};

size_t UT_String_tester::m_nErrCount = 0;


int main()
{
	UT_String_tester().test();

	return 0;
}

