2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 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.
//-----------------------------------------------------------------------------
# include <stdio.h>
# include <string.h>
# include "core/strings/stringFunctions.h"
# include "console/console.h"
# include "unit/test.h"
2014-05-25 14:50:19 +00:00
# include "core/util/journal/process.h"
2012-09-19 15:15:01 +00:00
namespace UnitTesting
{
//-----------------------------------------------------------------------------
TestRegistry * TestRegistry : : _list = 0 ;
//-----------------------------------------------------------------------------
2013-08-04 21:26:01 +00:00
static const S32 MaxMarginCount = 32 ;
static const S32 MaxMarginValue = 128 ;
static S32 _Margin [ MaxMarginCount ] = { 3 } ;
static S32 * _MarginPtr = _Margin ;
2012-09-19 15:15:01 +00:00
static char _MarginString [ MaxMarginValue ] ;
static void _printMargin ( )
{
if ( * _MarginPtr )
: : fwrite ( _MarginString , 1 , * _MarginPtr , stdout ) ;
}
2013-08-04 21:26:01 +00:00
void UnitMargin : : Push ( S32 margin )
2012-09-19 15:15:01 +00:00
{
if ( _MarginPtr < _Margin + MaxMarginCount ) {
* + + _MarginPtr = ( margin < MaxMarginValue ) ? margin : MaxMarginValue ;
memset ( _MarginString , ' ' , * _MarginPtr ) ;
}
}
void UnitMargin : : Pop ( )
{
if ( _MarginPtr > _Margin ) {
_MarginPtr - - ;
memset ( _MarginString , ' ' , * _MarginPtr ) ;
}
}
2013-08-04 21:26:01 +00:00
S32 UnitMargin : : Current ( )
2012-09-19 15:15:01 +00:00
{
return * _MarginPtr ;
}
void UnitPrint ( const char * str )
{
static bool lineStart = true ;
Platform : : outputDebugString ( str ) ;
// Need to scan for '\n' in order to support margins
const char * ptr = str , * itr = ptr ;
for ( ; * itr ! = 0 ; itr + + )
if ( * itr = = ' \n ' )
{
if ( lineStart )
_printMargin ( ) ;
: : fwrite ( ptr , 1 , itr - ptr + 1 , stdout ) ;
ptr = itr + 1 ;
lineStart = true ;
}
// End the line with a carriage return unless the
// line ends with a line continuation char.
if ( ptr ! = itr ) {
if ( lineStart )
_printMargin ( ) ;
if ( itr [ - 1 ] = = ' \\ ' ) {
: : fwrite ( ptr , 1 , itr - ptr - 1 , stdout ) ;
lineStart = false ;
}
else {
: : fwrite ( ptr , 1 , itr - ptr , stdout ) ;
: : fwrite ( " \n " , 1 , 1 , stdout ) ;
lineStart = true ;
}
}
else {
: : fwrite ( " \n " , 1 , 1 , stdout ) ;
lineStart = true ;
}
: : fflush ( stdout ) ;
}
//-----------------------------------------------------------------------------
UnitTest : : UnitTest ( ) {
_testCount = 0 ;
_failureCount = 0 ;
_warningCount = 0 ;
_lastTestResult = true ;
}
void UnitTest : : fail ( const char * msg )
{
Con : : warnf ( " ** Failed: %s " , msg ) ;
dFetchAndAdd ( _failureCount , 1 ) ;
}
void UnitTest : : warn ( const char * msg )
{
Con : : warnf ( " ** Warning: %s " , msg ) ;
dFetchAndAdd ( _warningCount , 1 ) ;
}
//-----------------------------------------------------------------------------
TestRegistry : : TestRegistry ( const char * name , bool interactive , const char * className )
{
// Check that no existing test uses the same class-name; this is guaranteed
// to lead to funkiness.
TestRegistry * walk = _list ;
while ( walk )
{
if ( walk - > _className )
{
AssertFatal ( dStricmp ( className , walk - > _className ) , " TestRegistry::TestRegistry - got two unit tests with identical class names; they must have unique class names! " ) ;
}
walk = walk - > _next ;
}
// Add us to the list.
_next = _list ;
_list = this ;
// And fill in our fields.
_name = name ;
_className = className ;
_isInteractive = interactive ;
}
DynamicTestRegistration : : DynamicTestRegistration ( const char * name , UnitTest * test ) : TestRegistry ( name , false , NULL ) , mUnitTest ( test )
{
}
DynamicTestRegistration : : ~ DynamicTestRegistration ( )
{
// Un-link ourselves from the test registry
TestRegistry * walk = _list ;
// Easy case!
if ( walk = = this )
_list = _next ;
else
{
// Search for us and remove
while ( ( walk ! = 0 ) & & ( walk - > _next ! = 0 ) & & ( walk - > _next ! = this ) )
walk = walk - > _next ;
// When this loop is broken, walk will be the unit test in the list previous to this one
if ( walk ! = 0 & & walk - > _next ! = 0 )
walk - > _next = walk - > _next - > _next ;
}
}
//-----------------------------------------------------------------------------
TestRun : : TestRun ( )
{
_subCount = 0 ;
_testCount = 0 ;
_failureCount = 0 ;
_warningCount = 0 ;
}
void TestRun : : printStats ( )
{
Con : : printf ( " -- %d test%s run (with %d sub-test%s) " ,
_testCount , ( _testCount ! = 1 ) ? " s " : " " ,
_subCount , ( _subCount ! = 1 ) ? " s " : " " ) ;
if ( _testCount )
{
if ( _failureCount )
Con : : printf ( " ** %d reported failure%s " ,
_failureCount , ( _failureCount ! = 1 ) ? " s " : " " ) ;
else if ( _warningCount )
Con : : printf ( " ** %d reported warning%s " ,
_warningCount , ( _warningCount ! = 1 ) ? " s " : " " ) ;
else
Con : : printf ( " -- No reported failures " ) ;
}
}
void TestRun : : test ( TestRegistry * reg )
{
Con : : printf ( " -- Testing: %s %s " , reg - > getName ( ) , reg - > isInteractive ( ) ? " (interactive) " : " " ) ;
UnitMargin : : Push ( _Margin [ 0 ] ) ;
// Run the test.
UnitTest * test = reg - > newTest ( ) ;
test - > run ( ) ;
UnitMargin : : Pop ( ) ;
// Update stats.
_failureCount + = test - > getFailureCount ( ) ;
_subCount + = test - > getTestCount ( ) ;
_warningCount + = test - > getWarningCount ( ) ;
_testCount + + ;
// Don't forget to delete the test!
delete test ;
}
// [tom, 2/5/2007] To provide a predictable environment for the tests, this
// now changes the current directory to the executable's directory before
// running the tests. The previous current directory is restored on exit.
bool TestRun : : test ( const char * module , bool skipInteractive )
{
StringTableEntry cwdSave = Platform : : getCurrentDirectory ( ) ;
2013-08-04 21:26:01 +00:00
S32 len = strlen ( module ) ;
2012-09-19 15:15:01 +00:00
const char * skipMsg = skipInteractive ? " (skipping interactive tests) " : " " ;
// Indicate to the user what we're up to.
if ( ! len )
Con : : printf ( " -- Running all unit tests %s " , skipMsg ) ;
else
Con : : printf ( " -- Running %s tests %s " , module , skipMsg ) ;
for ( TestRegistry * itr = TestRegistry : : getFirst ( ) ; itr ; itr = itr - > getNext ( ) )
{
if ( ! len | | ! dStrnicmp ( module , itr - > getName ( ) , len ) )
{
// Skip the test if it's interactive and we're in skipinteractive mode.
if ( skipInteractive & & itr - > isInteractive ( ) )
continue ;
// Otherwise, run the test!
Platform : : setCurrentDirectory ( Platform : : getMainDotCsDir ( ) ) ;
test ( itr ) ;
}
}
// Print out a nice report on how we did.
printStats ( ) ;
Platform : : setCurrentDirectory ( cwdSave ) ;
2014-05-25 14:50:19 +00:00
// sanity check for avoid Process::requestShutdown() called on some tests
Process : : processEvents ( ) ;
2012-09-19 15:15:01 +00:00
// And indicate our failure situation in the return value.
return ! _failureCount ;
}
} // Namespace