
//////////////////////////////////////////////////////////////////////////////
//
// Asura_SpeedTest.cpp
//
// Copyright (c) 2005 Rebellion. All rights reserved.
//
// Description:
//
//     Speed testing code, based on Asura_UnitTest.cpp
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_SUPPORT_UNITTEST

#include "UnitTest/Asura_SpeedTest.h"

#include "Core/Asura_Maths.h"
#include "Core/Asura_String.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"

#include <stdio.h>
#include <string.h> 

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////
class Asura_SpeedTest_Test;

// C L A S S E S /////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Asura_SpeedTest_TestRegistry 
// 
// a singleton collection of all the tests to run in a system.  
// 
///////////////////////////////////////////////////////////////////////////////

class Asura_SpeedTest_TestRegistry
{
public:
	static void AddTest(Asura_SpeedTest_Test* pxTest);
	static void RunAllTests();

private:

	static Asura_SpeedTest_TestRegistry&	GetInstance();
	void					Add(Asura_SpeedTest_Test* pxTest);
	void					Run();

	const Asura_SpeedTest_Test* GetNextByAddress(u_int uCurAddress);
	
	Asura_SpeedTest_Test* m_pxTests;

};

void Asura_SpeedTest_TestRegistry::AddTest(Asura_SpeedTest_Test* pxTest) 
{
	GetInstance().Add(pxTest);
}


void Asura_SpeedTest_TestRegistry::RunAllTests () 
{
	GetInstance().Run();
}


Asura_SpeedTest_TestRegistry& Asura_SpeedTest_TestRegistry::GetInstance() 
{
	static Asura_SpeedTest_TestRegistry ls_Registry;
	return ls_Registry;
}


void Asura_SpeedTest_TestRegistry::Add (Asura_SpeedTest_Test* pxTest) 
{
	if (m_pxTests == 0) 
	{
		m_pxTests = pxTest;
		return;
	}
	
	pxTest->SetNext(m_pxTests);
	m_pxTests = pxTest;
}


void Asura_SpeedTest_TestRegistry::Run () 
{
	u_int uNumTests = 0;

	for (Asura_SpeedTest_Test *pxTest = m_pxTests; pxTest != 0; pxTest = pxTest->GetNext())
	{
		if (!pxTest->IsDummy())
		{
			float fStart = Asura_Timers::GetTimeInFrame();
			pxTest->Run();
			Asura_OutputToDebugger_Retail(( "test %s took %.03f milliseconds\n", 
				pxTest->GetName(), (Asura_Timers::GetTimeInFrame() - fStart) * 1000.0f ));
			++uNumTests;
		}
	}

	// output the code sizes.
	u_int uCurAddress = 0;
	const Asura_SpeedTest_Test *pxTest = GetNextByAddress(uCurAddress);
	while (pxTest)
	{
		uCurAddress = pxTest->GetMarker();
		const Asura_SpeedTest_Test *pxNextTest = GetNextByAddress(uCurAddress);
		if (pxNextTest)
		{
			u_int uNextAddress = pxNextTest->GetMarker();
			if (!pxTest->IsDummy())
			{
				Asura_OutputToDebugger_Retail(("test %s code size = %u bytes\n", 
					pxTest->GetName(), uNextAddress - uCurAddress));
			}
		}
		pxTest = pxNextTest;
	}
}

const Asura_SpeedTest_Test* Asura_SpeedTest_TestRegistry::GetNextByAddress(u_int uCurAddress)
{
	Asura_SpeedTest_Test *pxRet = NULL;
	u_int uLeastAddress = 0xffffffff;
	for (Asura_SpeedTest_Test *pxTest = m_pxTests; pxTest != 0; pxTest = pxTest->GetNext())
	{
		u_int uAddress = pxTest->GetMarker();
		if ( (uAddress > uCurAddress) && (uAddress < uLeastAddress))
		{
			uLeastAddress = uAddress;
			pxRet = pxTest;
		}
	}
	return pxRet;
}


//////////////////////////////////////////////////////////////////////////////
//
// Asura_SpeedTest_Test
// 
///////////////////////////////////////////////////////////////////////////////

Asura_SpeedTest_Test::Asura_SpeedTest_Test (const Asura_String& xTestName) 
	: m_xName (xTestName) 
{
	Asura_SpeedTest_TestRegistry::AddTest(this);
}


Asura_SpeedTest_Test *Asura_SpeedTest_Test::GetNext() const
{
	return m_pxNext;
}


void Asura_SpeedTest_Test::SetNext(Asura_SpeedTest_Test* pxTest)
{	
	m_pxNext = pxTest;
}


// global function to run all tests
void Asura_SpeedTest_RunAllTests()
{
    Asura_SpeedTest_TestRegistry::RunAllTests();
}

#endif // #ifdef ASURA_SUPPORT_UNITTEST
