// UnitTestManager.cpp //------------------------------------------------------------------------------ // Includes //------------------------------------------------------------------------------ #include "UnitTestManager.h" #include "UnitTest.h" #include "Core/Env/Assert.h" #include "Core/Env/Types.h" #include "Core/Mem/MemTracker.h" #include "Core/Profile/Profile.h" #include "Core/Strings/AString.h" #include "Core/Tracing/Tracing.h" #include #if defined( __WINDOWS__ ) #include #endif // Static Data //------------------------------------------------------------------------------ /*static*/ UnitTestManager * UnitTestManager::s_Instance = nullptr; /*static*/ UnitTest * UnitTestManager::s_FirstTest = nullptr; // CONSTRUCTOR //------------------------------------------------------------------------------ UnitTestManager::UnitTestManager() : m_TestsRun( 0 ) , m_TestsPassed( 0 ) , m_CurrentTestName( nullptr ) { // manage singleton ASSERT( s_Instance == nullptr ); s_Instance = this; // if we're running outside the debugger, we don't want // failures to pop up a dialog. We want them to throw so // the test framework can catch the exception #ifdef ASSERTS_ENABLED if ( IsDebuggerAttached() == false ) { AssertHandler::SetThrowOnAssert( true ); } #endif } // DESTRUCTOR //------------------------------------------------------------------------------ UnitTestManager::~UnitTestManager() { #ifdef ASSERTS_ENABLED if ( IsDebuggerAttached() == false ) { AssertHandler::SetThrowOnAssert( false ); } #endif // free all registered tests UnitTest * testGroup = s_FirstTest; while ( testGroup ) { UnitTest * next = testGroup->m_NextTestGroup; FDELETE testGroup; testGroup = next; } // manage singleton ASSERT( s_Instance == this ); s_Instance = nullptr; } // Get //------------------------------------------------------------------------------ #ifdef DEBUG /*static*/ UnitTestManager & UnitTestManager::Get() { ASSERT( s_Instance ); return *s_Instance; } #endif // RegisterTest //------------------------------------------------------------------------------ /*static*/ void UnitTestManager::RegisterTestGroup( UnitTest * testGroup ) { // first ever test? place as head of list if ( s_FirstTest == nullptr ) { s_FirstTest = testGroup; return; } // link to end of list UnitTest * thisGroup = s_FirstTest; loop: if ( thisGroup->m_NextTestGroup == nullptr ) { thisGroup->m_NextTestGroup = testGroup; return; } thisGroup = thisGroup->m_NextTestGroup; goto loop; } // RunTests //------------------------------------------------------------------------------ bool UnitTestManager::RunTests( const char * testGroup ) { m_TestsRun = 0; m_TestsPassed = 0; // check for compile time filter UnitTest * test = s_FirstTest; while ( test ) { if ( testGroup != nullptr ) { // is this test the one we want? if ( AString::StrNCmp( test->GetName(), testGroup, strlen( testGroup ) ) != 0 ) { // no -skip it test = test->m_NextTestGroup; continue; } } OUTPUT( "------------------------------\n" ); OUTPUT( "Test Group: %s\n", test->GetName() ); try { test->RunTests(); } catch (...) { OUTPUT( " - Test '%s' *** FAILED ***\n", m_CurrentTestName ); } test = test->m_NextTestGroup; } OUTPUT( "------------------------------\n" ); OUTPUT( "Summary For All Tests\n" ); OUTPUT( " - Passed: %u\n", m_TestsPassed ); OUTPUT( " - Failed: %u\n", (m_TestsRun - m_TestsPassed ) ); OUTPUT( "------------------------------\n" ); return ( m_TestsRun == m_TestsPassed ); } // TestBegin //------------------------------------------------------------------------------ void UnitTestManager::TestBegin( const char * testName ) { m_TestsRun++; OUTPUT( " - Test '%s'\n", testName ); m_CurrentTestName = testName; #ifdef MEMTRACKER_ENABLED MemTracker::Reset(); #endif m_Timer.Start(); } // TestEnd //------------------------------------------------------------------------------ void UnitTestManager::TestEnd() { PROFILE_SYNCHRONIZE float timeTaken = m_Timer.GetElapsed(); #ifdef MEMTRACKER_ENABLED if ( MemTracker::GetCurrentAllocationCount() != 0 ) { OUTPUT( " - Test '%s' in %2.3fs : *** FAILED (Memory Leaks)***\n", m_CurrentTestName, timeTaken ); MemTracker::DumpAllocations(); return; } #endif OUTPUT( " - Test '%s' in %2.3fs : PASSED\n", m_CurrentTestName, timeTaken ); m_TestsPassed++; } // Assert //------------------------------------------------------------------------------ /*static*/ bool UnitTestManager::AssertFailure( const char * message, const char * file, uint32_t line ) { OUTPUT( "\n-------- TEST ASSERTION FAILED --------\n" ); OUTPUT( "%s(%i): Assert: %s", file, line, message ); OUTPUT( "\n-----^^^ TEST ASSERTION FAILED ^^^-----\n" ); if ( IsDebuggerAttached() ) { return true; // tell the calling code to break at the failure sight } // throw will be caught by the unit test framework and noted as a failure throw "Test Failed"; } //------------------------------------------------------------------------------