/** @file string_unittest.cpp
 *  Functionality for testing EABaseType Strings
 *	@author Chris Burns (cburns@ea.com)
 *
 *	EABaseTypes
 *	(c) 2011 Electronic Arts Inc. 
 */

#if EA_RUNTIME_TESTING && !EABASETYPES_NO_STRING_INTERN

#include "EABaseTypes/factory.h"
#include "EABaseTypes/stringtable.h"
#include "EABaseTypes/datatypes.h"

#include "EAStdC/EAString.h"

#include "unit/unit.h"
#include "coreunit/interface.h"
#include "coreallocator/icoreallocatormacros.h"

#include "ppmalloc/eageneralallocator.h"
#include "ppmalloc/eageneralallocatordebug.h"
#include "coreunit/interface.h"

class StringUnitTests : public EATest::TestSuite
{
	public:

		StringUnitTests()
		{
		}

		virtual void Initialize()
		{
			SuiteName("StringUnitTests");
			EATEST_REGISTER("TestEmptyString", "Testing Empty String", StringUnitTests, TestEmptyString);
			EATEST_REGISTER("TestRefCount", "Testing RefCount", StringUnitTests, TestRefCount);
			EATEST_REGISTER("TestLongString", "Testing Long String", StringUnitTests, TestLongString);
			EATEST_REGISTER("TestManyStrings", "Testing Many Strings", StringUnitTests, TestManyStrings);
		}

		void Setup(void* data)
		{
			CoreUnit::UnitTestInfo* info = static_cast<CoreUnit::UnitTestInfo*>(data);
			EA::Allocator::ICoreAllocator* allocator = info->mAllocator;

			mAllocator = allocator;

			mFactory = EA::Types::CreateDefaultFactory(allocator);
		}

		void Teardown(void* data)
		{
			CORE_DELETE(mAllocator, mFactory);
		}

		void TestEmptyString(void* data);
		void TestRefCount(void* data);
		void TestLongString(void* data);
		void TestManyStrings(void* data);

private:
		
		EA::Allocator::ICoreAllocator*	mAllocator;
		EA::Types::Factory*				mFactory;
};  

// make the unit test suite instance
StringUnitTests StringUnitTestsInstance;

// this declaration is so that main.cpp in the game can pretend to use it hence keeping
// this entire translation unit from being excluded from the build
EATest::TestSuite* gStringUnitTests = &StringUnitTestsInstance;

// *********************************************************************************************
// The test functions
// *********************************************************************************************

void StringUnitTests::TestEmptyString( void* data )
{
    EA::StringTable* stringTable = mFactory->GetStringTable();
    
	EA::String empty(mFactory);
	EATESTAssert(empty.GetRefCount() == -1, "Empty string should not have a ref count");
	EATESTAssert(stringTable->GetSize() == 0, "String table should be empty");
	empty = "Test String";
	EATESTAssert(empty.GetRefCount() == 1, "expected ref count of 1");
	EATESTAssert(stringTable->GetSize() == 1, "String table should have 1 entry");
	empty = "";
	EATESTAssert(empty.GetRefCount() == -1, "Empty string should not have a ref count");
	EATESTAssert(stringTable->GetSize() == 0, "String table should be empty");
}

void StringUnitTests::TestRefCount( void* data )
{
    EA::StringTable* stringTable = mFactory->GetStringTable();

	EA::String str1(mFactory, "Foo");
	EATESTAssert(str1.GetRefCount() == 1, "expected ref count of 1");
	EA::String str2(mFactory, "Foo");
	EATESTAssert(stringTable->GetSize() == 1, "String table should have 1 entry");
	EATESTAssert(str1.GetRefCount() == 2, "expected ref count of 2");
	EATESTAssert(str2.GetRefCount() == 2, "expected ref count of 2");

	str2 = "Bar";
	EATESTAssert(stringTable->GetSize() == 2, "String table should have 2 entries");
	EATESTAssert(str1.GetRefCount() == 1, "expected ref count of 1");
	EATESTAssert(str2.GetRefCount() == 1, "expected ref count of 1");

	str2 = "Foo";
	EATESTAssert(stringTable->GetSize() == 1, "String table should have 1 entry");
	EATESTAssert(str1.GetRefCount() == 2, "expected ref count of 2");
	EATESTAssert(str2.GetRefCount() == 2, "expected ref count of 2");

}

void StringUnitTests::TestLongString( void* data )
{
    EA::StringTable* stringTable = mFactory->GetStringTable();

	const char* act1Scene2 = "SCENE II. A camp near Forres. \
		Alarum within. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, with Attendants, meeting a bleeding Sergeant \
		DUNCAN \
		What bloody man is that? He can report, \
		As seemeth by his plight, of the revolt \
		The newest state. \
		MALCOLM \
		This is the sergeant \
		Who like a good and hardy soldier fought \
		'Gainst my captivity. Hail, brave friend! \
		Say to the king the knowledge of the broil \
		As thou didst leave it. \
		Sergeant \
		Doubtful it stood;\
		As two spent swimmers, that do cling together \
		And choke their art. The merciless Macdonwald-- \
		Worthy to be a rebel, for to that \
		The multiplying villanies of nature \
		Do swarm upon him--from the western isles \
		Of kerns and gallowglasses is supplied; \
		And fortune, on his damned quarrel smiling, \
		Show'd like a rebel's whore: but all's too weak: \
		For brave Macbeth--well he deserves that name-- \
		Disdaining fortune, with his brandish'd steel, \
		Which smoked with bloody execution, \
		Like valour's minion carved out his passage \
		Till he faced the slave; \
		Which ne'er shook hands, nor bade farewell to him, \
		Till he unseam'd him from the nave to the chaps, \
		And fix'd his head upon our battlements. \
		DUNCAN \
		O valiant cousin! worthy gentleman! \
		Sergeant \
		As whence the sun 'gins his reflection \
		Shipwrecking storms and direful thunders break, \
		So from that spring whence comfort seem'd to come \
		Discomfort swells. Mark, king of Scotland, mark: \
		No sooner justice had with valour arm'd \
		Compell'd these skipping kerns to trust their heels, \
		But the Norweyan lord surveying vantage, \
		With furbish'd arms and new supplies of men \
		Began a fresh assault. \
		DUNCAN \
		Dismay'd not this \
		Our captains, Macbeth and Banquo? \
		Sergeant \
		Yes; \
		As sparrows eagles, or the hare the lion. \
		If I say sooth, I must report they were \
		As cannons overcharged with double cracks, so they \
		Doubly redoubled strokes upon the foe: \
		Except they meant to bathe in reeking wounds, \
		Or memorise another Golgotha, \
		I cannot tell. \
		But I am faint, my gashes cry for help. \
		DUNCAN \
		So well thy words become thee as thy wounds; \
		They smack of honour both. Go get him surgeons. \
		Exit Sergeant, attended \
		Who comes here? \
		Enter ROSS \
		MALCOLM \
		The worthy thane of Ross. \
		LENNOX \
		What a haste looks through his eyes! So should he look \
		That seems to speak things strange. \
		ROSS \
		God save the king! \
		DUNCAN \
		Whence camest thou, worthy thane? \
		ROSS \
		From Fife, great king; \
		Where the Norweyan banners flout the sky \
		And fan our people cold. Norway himself, \
		With terrible numbers, \
		Assisted by that most disloyal traitor \
		The thane of Cawdor, began a dismal conflict; \
		Till that Bellona's bridegroom, lapp'd in proof, \
		Confronted him with self-comparisons, \
		Point against point rebellious, arm 'gainst arm. \
		Curbing his lavish spirit: and, to conclude, \
		The victory fell on us. \
		DUNCAN \
		Great happiness! \
		ROSS \
		That now \
		Sweno, the Norways' king, craves composition: \
		Nor would we deign him burial of his men \
		Till he disbursed at Saint Colme's inch \
		Ten thousand dollars to our general use. \
		DUNCAN \
		No more that thane of Cawdor shall deceive \
		Our bosom interest: go pronounce his present death, \
		And with his former title greet Macbeth. \
		ROSS \
		I'll see it done. \
		DUNCAN \
		What he hath lost noble Macbeth hath won. \
		Exeunt";

	EA::String longString(mFactory, act1Scene2);
	EATESTAssert(longString.compare(act1Scene2) == 0, "Strings should match");
	EATESTAssert(stringTable->GetSize() == 1, "String table should have 1 entry");
	EA::String anotherLongString = longString;
	EATESTAssert(stringTable->GetSize() == 1, "String table should have 1 entry");
	EATESTAssert(longString.compare(anotherLongString.c_str()) == 0, "Strings should match");
}

void StringUnitTests::TestManyStrings( void* data )
{
	const unsigned int numStrings = 1024;
	EA::Types::ArrayRef strArray = mFactory->Array();
	for(unsigned int i = 0; i < numStrings; ++i)
	{
		char num[EA::StdC::kInt32MinCapacity];
		strArray->push_back(EA::StdC::I32toa(i, num, 10));
	}

	for(unsigned int i = 0; i < numStrings; ++i)
	{
		EATESTAssert((*strArray)[i]->AsEAString().GetRefCount() == 1, "Unexpected ref count");
	}
	EATESTAssert(mFactory->GetStringTable()->GetSize() == numStrings, "String table has unexpected size");
}

#endif
