2014-04-22 23:54:04 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2014 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
2023-07-24 11:38:36 +00:00
# include "unitTesting.h"
2014-04-22 23:54:04 +00:00
2023-07-24 11:38:36 +00:00
# include "app/mainLoop.h"
2023-04-06 13:28:09 +00:00
# include "console/console.h"
# include "console/codeBlock.h"
2014-04-22 23:54:04 +00:00
# include "console/engineAPI.h"
# include "console/consoleInternal.h"
2023-07-25 12:29:22 +00:00
# if defined(TORQUE_OS_WIN)
# define _CRTDBG_MAP_ALLOC
# include <crtdbg.h>
# endif
2014-04-22 23:54:04 +00:00
//-----------------------------------------------------------------------------
class TorqueUnitTestListener : public : : testing : : EmptyTestEventListener
{
// Called before a test starts.
2023-07-24 11:38:36 +00:00
virtual void OnTestStart ( const : : testing : : TestInfo & testInfo )
2014-04-22 23:54:04 +00:00
{
2023-07-24 11:38:36 +00:00
if ( mVerbose )
2014-09-28 06:16:23 +00:00
Con : : printf ( " > Starting Test '%s.%s' " ,
testInfo . test_case_name ( ) , testInfo . name ( ) ) ;
2014-04-22 23:54:04 +00:00
}
// Called after a failed assertion or a SUCCEED() invocation.
2023-07-24 11:38:36 +00:00
virtual void OnTestPartResult ( const : : testing : : TestPartResult & testPartResult )
2014-04-22 23:54:04 +00:00
{
2023-07-24 11:38:36 +00:00
if ( testPartResult . failed ( ) )
2014-04-22 23:54:04 +00:00
{
2014-09-28 06:16:23 +00:00
Con : : warnf ( " >> Failed with '%s' in '%s' at (line:%d) \n " ,
2014-04-22 23:54:04 +00:00
testPartResult . summary ( ) ,
testPartResult . file_name ( ) ,
testPartResult . line_number ( )
2023-07-24 11:38:36 +00:00
) ;
2014-04-22 23:54:04 +00:00
}
2023-07-24 11:38:36 +00:00
else if ( mVerbose )
2014-04-22 23:54:04 +00:00
{
Con : : printf ( " >> Passed with '%s' in '%s' at (line:%d) " ,
testPartResult . summary ( ) ,
testPartResult . file_name ( ) ,
testPartResult . line_number ( )
2023-07-24 11:38:36 +00:00
) ;
2014-04-22 23:54:04 +00:00
}
}
// Called after a test ends.
2023-07-24 11:38:36 +00:00
virtual void OnTestEnd ( const : : testing : : TestInfo & testInfo )
2014-04-22 23:54:04 +00:00
{
2023-07-24 11:38:36 +00:00
if ( testInfo . result ( ) - > Failed ( ) )
{
Con : : printf ( " TestClass:%s Test:%s Failed! " ,
testInfo . test_case_name ( ) , testInfo . name ( ) ) ;
}
2023-07-26 14:49:58 +00:00
if ( ! mVerbose )
return ;
2023-07-24 11:38:36 +00:00
else if ( testInfo . result ( ) - > Passed ( ) )
{
Con : : printf ( " TestClass:%s Test:%s Succeeded! " ,
testInfo . test_case_name ( ) , testInfo . name ( ) ) ;
}
else
{
Con : : printf ( " TestClass:%s Test:%s Skipped! " ,
2014-09-28 06:16:23 +00:00
testInfo . test_case_name ( ) , testInfo . name ( ) ) ;
2023-07-24 11:38:36 +00:00
}
Con : : printf ( " > Ending Test \n " ) ;
2014-04-22 23:54:04 +00:00
}
2014-09-28 06:16:23 +00:00
bool mVerbose ;
public :
2023-07-24 11:38:36 +00:00
TorqueUnitTestListener ( bool verbose ) : mVerbose ( verbose ) { }
2014-04-22 23:54:04 +00:00
} ;
2023-07-25 12:29:22 +00:00
class MemoryLeakDetector : public : : testing : : EmptyTestEventListener
{
public :
virtual void OnTestStart ( const : : testing : : TestInfo & testInfo )
{
# if defined(TORQUE_OS_WIN)
_CrtMemCheckpoint ( & memState_ ) ;
# endif
}
virtual void OnTestEnd ( const : : testing : : TestInfo & testInfo )
{
if ( testInfo . result ( ) - > Passed ( ) )
{
# if defined(TORQUE_OS_WIN)
_CrtMemState stateNow , stateDiff ;
_CrtMemCheckpoint ( & stateNow ) ;
int diffResult = _CrtMemDifference ( & stateDiff , & memState_ , & stateNow ) ;
if ( diffResult )
{
FAIL ( ) < < " Memory leak of " < < stateDiff . lSizes [ 1 ] < < " byte(s) detected. " ;
}
# endif
}
}
private :
# if defined(TORQUE_OS_WIN)
_CrtMemState memState_ ;
# endif
public :
MemoryLeakDetector ( ) { }
} ;
2023-04-06 13:28:09 +00:00
class TorqueScriptFixture : public testing : : Test { } ;
class TorqueScriptTest : public TorqueScriptFixture {
public :
explicit TorqueScriptTest ( const char * pFunctionName ) : mFunctionName ( pFunctionName ) { }
void TestBody ( ) override
{
Con : : executef ( mFunctionName ) ;
}
private :
const char * mFunctionName ;
} ;
2023-07-24 11:38:36 +00:00
// uncomment to debug tests and use the test explorer.
2023-07-25 12:30:53 +00:00
//#define TEST_EXPLORER
2023-07-24 11:38:36 +00:00
# if !defined(TEST_EXPLORER)
int main ( int argc , char * * argv )
{
StandardMainLoop : : init ( ) ;
StandardMainLoop : : handleCommandLine ( argc , ( const char * * ) argv ) ;
StandardMainLoop : : shutdown ( ) ;
return StandardMainLoop : : getReturnStatus ( ) ;
}
# else
int main ( int argc , char * * argv )
{
StandardMainLoop : : init ( ) ;
printf ( " Running main() from %s \n " , __FILE__ ) ;
// setup simular to runTests
Con : : evaluate ( " GFXInit::createNullDevice(); " ) ;
Con : : evaluate ( " if (!isObject(GuiDefaultProfile)) new GuiControlProfile(GuiDefaultProfile){}; if (!isObject(GuiTooltipProfile)) new GuiControlProfile(GuiTooltipProfile){}; " ) ;
testing : : InitGoogleTest ( & argc , argv ) ;
2023-07-25 12:29:22 +00:00
// Fetch the unit test instance.
testing : : UnitTest & unitTest = * testing : : UnitTest : : GetInstance ( ) ;
// Fetch the unit test event listeners.
testing : : TestEventListeners & listeners = unitTest . listeners ( ) ;
listeners . Append ( new MemoryLeakDetector ( ) ) ;
// Add the Torque unit test listener.
listeners . Append ( new TorqueUnitTestListener ( true ) ) ;
2023-07-24 11:38:36 +00:00
int res = RUN_ALL_TESTS ( ) ;
StandardMainLoop : : shutdown ( ) ;
return res ;
}
# endif
DefineEngineFunction ( addUnitTest , void , ( const char * function ) , ,
2023-04-06 13:28:09 +00:00
" Add a TorqueScript function as a GTest unit test. \n "
" @note This is only implemented rudimentarily to open the door for future development in unit-testing the engine. \n "
" @tsexample \n "
" function MyTest() { \n "
" expectTrue(2+2 == 4, \" basic math should work \" ); \n "
" } \n "
" addUnitTest(MyTest); \n "
" @endtsexample \n "
" @see expectTrue " )
{
Namespace : : Entry * entry = Namespace : : global ( ) - > lookup ( StringTable - > insert ( function ) ) ;
const char * file = __FILE__ ;
U32 ln = __LINE__ ;
if ( entry ! = NULL )
{
file = entry - > mCode - > name ;
U32 inst ;
entry - > mCode - > findBreakLine ( entry - > mFunctionOffset , ln , inst ) ;
}
else
{
Con : : warnf ( " failed to register unit test %s, could not find the function " , function ) ;
}
testing : : RegisterTest ( " TorqueScriptFixture " , function , NULL , NULL , file , ln ,
[ = ] ( ) - > TorqueScriptFixture * { return new TorqueScriptTest ( function ) ; } ) ;
}
String scriptFileMessage ( const char * message )
{
Dictionary * frame = & gEvalState . getCurrentFrame ( ) ;
CodeBlock * code = frame - > code ;
const char * scriptLine = code - > getFileLine ( frame - > ip ) ;
return String : : ToString ( " at %s: %s " , scriptLine , message ) ;
}
2023-07-24 11:38:36 +00:00
DefineEngineFunction ( expectTrue , void , ( bool test , const char * message ) , ( " " ) ,
2023-04-06 13:28:09 +00:00
" TorqueScript wrapper around the EXPECT_TRUE assertion in GTest. \n "
" @tsexample \n "
" expectTrue(2+2 == 4, \" basic math should work \" ); \n "
" @endtsexample " )
{
EXPECT_TRUE ( test ) < < scriptFileMessage ( message ) . c_str ( ) ;
}
2023-07-24 11:38:36 +00:00
DefineEngineFunction ( runAllUnitTests , int , ( const char * testSpecs , const char * reportFormat ) , ( " " ) ,
2014-08-02 00:14:08 +00:00
" Runs engine unit tests. Some tests are marked as 'stress' tests which do not "
" necessarily check correctness, just performance or possible nondeterministic "
" glitches. There may also be interactive or networking tests which may be "
" excluded by using the testSpecs argument. \n "
" This function should only be called once per executable run, because of "
" googletest's design. \n \n "
" @param testSpecs A space-sepatated list of filters for test cases. "
" See https://code.google.com/p/googletest/wiki/AdvancedGuide#Running_a_Subset_of_the_Tests "
2014-09-29 01:00:43 +00:00
" and http://stackoverflow.com/a/14021997/945863 "
2014-08-02 00:14:08 +00:00
" for a description of the flag format. " )
2014-04-22 23:54:04 +00:00
{
2023-04-06 13:28:09 +00:00
Vector < char * > args ;
args . push_back ( NULL ) ; // Program name is unused by googletest.
String specsArg ;
2023-07-24 11:38:36 +00:00
if ( dStrlen ( testSpecs ) > 0 )
2014-08-01 05:00:57 +00:00
{
2023-04-06 13:28:09 +00:00
specsArg = testSpecs ;
specsArg . replace ( ' ' , ' : ' ) ;
specsArg . insert ( 0 , " --gtest_filter= " ) ;
args . push_back ( const_cast < char * > ( specsArg . c_str ( ) ) ) ;
}
String reportFormatArg ;
2023-07-24 11:38:36 +00:00
if ( dStrlen ( reportFormat ) > 0 )
2023-04-06 13:28:09 +00:00
{
reportFormatArg = String : : ToString ( " --gtest_output=%s " , reportFormat ) ;
args . push_back ( const_cast < char * > ( reportFormatArg . c_str ( ) ) ) ;
2014-08-01 05:00:57 +00:00
}
2023-04-06 13:28:09 +00:00
S32 argc = args . size ( ) ;
2023-07-24 11:38:36 +00:00
2014-04-22 23:54:04 +00:00
// Initialize Google Test.
2023-07-24 11:38:36 +00:00
testing : : InitGoogleTest ( & argc , args . address ( ) ) ;
2014-04-22 23:54:04 +00:00
// Fetch the unit test instance.
testing : : UnitTest & unitTest = * testing : : UnitTest : : GetInstance ( ) ;
// Fetch the unit test event listeners.
testing : : TestEventListeners & listeners = unitTest . listeners ( ) ;
// Release the default listener.
2023-07-24 11:38:36 +00:00
delete listeners . Release ( listeners . default_result_printer ( ) ) ;
2014-04-22 23:54:04 +00:00
// Add the Torque unit test listener.
2023-07-26 14:49:58 +00:00
listeners . Append ( new TorqueUnitTestListener ( false ) ) ;
2014-04-22 23:54:04 +00:00
2014-08-02 00:14:08 +00:00
// Perform googletest run.
2023-07-24 11:38:36 +00:00
Con : : printf ( " \n Unit Tests Starting... \n " ) ;
2014-04-22 23:54:04 +00:00
const S32 result = RUN_ALL_TESTS ( ) ;
2023-07-24 11:38:36 +00:00
Con : : printf ( " ... Unit Tests Ended. \n " ) ;
2014-04-22 23:54:04 +00:00
return result ;
}