2019-01-23 14:54:29 -05:00
// Helper macros for writing exception-based tests
2019-08-26 15:38:34 -04:00
/*
The tests are supposed to be contained in small static functions that get called from a main function provided by this framework :
static void test1 ( )
{
// Perform the test
}
. . .
IMPLEMENT_TEST_MAIN ( " TestName " ,
test1 ( ) ;
. . .
)
*/
2019-01-23 14:54:29 -05:00
/** The exception that is thrown if a test fails.
It doesn ' t inherit from any type so that it is not possible to catch it by a mistake ,
it needs to be caught explicitly ( used in the TEST_THROWS ) .
It bears a single message that is to be displayed to stderr . */
class TestException
{
public :
2019-08-26 15:38:34 -04:00
TestException ( const AString & aFileName , int aLineNumber , const AString & aFunctionName , const AString & aMessage ) :
mFileName ( aFileName ) ,
mLineNumber ( aLineNumber ) ,
mFunctionName ( aFunctionName ) ,
2019-01-23 14:54:29 -05:00
mMessage ( aMessage )
{
}
2019-08-26 15:38:34 -04:00
AString mFileName ;
int mLineNumber ;
AString mFunctionName ;
2019-01-23 14:54:29 -05:00
AString mMessage ;
} ;
/** Checks that the two values are equal; if not, throws a TestException. */
# define TEST_EQUAL(VAL1, VAL2) \
if ( VAL1 ! = VAL2 ) \
{ \
2019-08-26 15:38:34 -04:00
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Equality test failed: %s != %s " , \
2019-01-23 14:54:29 -05:00
# VAL1, #VAL2 \
) ) ; \
}
2019-08-26 15:38:34 -04:00
/** Checks that the two values are equal; if not, throws a TestException, includes the specified message. */
# define TEST_EQUAL_MSG(VAL1, VAL2, MSG) \
if ( VAL1 ! = VAL2 ) \
{ \
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Equality test failed: %s != %s (%s) " , \
# VAL1, #VAL2, MSG \
) ) ; \
}
2019-01-23 14:54:29 -05:00
/** Checks that the two values are not equal; if they are, throws a TestException. */
# define TEST_NOTEQUAL(VAL1, VAL2) \
if ( VAL1 = = VAL2 ) \
{ \
2019-08-26 15:38:34 -04:00
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Inequality test failed: %s == %s " , \
2019-01-23 14:54:29 -05:00
# VAL1, #VAL2 \
) ) ; \
}
2019-08-26 15:38:34 -04:00
/** Checks that the statement evaluates to true. */
# define TEST_TRUE(X) TEST_EQUAL(X, true)
/** Checks that the statement evaluates to false. */
# define TEST_FALSE(X) TEST_EQUAL(X, false)
/** Checks that the statement returns a value greater than or equal to the specified value. */
# define TEST_GREATER_THAN_OR_EQUAL(Stmt, Val) \
do { \
if ( Stmt < Val ) \
{ \
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Comparison failed: %s < %s " , # Stmt , # Val ) ) ; \
} \
} while ( false )
/** Checks that the statement returns a value less than or equal to the specified value. */
# define TEST_LESS_THAN_OR_EQUAL(Stmt, Val) \
do { \
if ( Stmt > Val ) \
{ \
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Comparison failed: %s > %s " , # Stmt , # Val ) ) ; \
} \
} while ( false )
2019-01-23 14:54:29 -05:00
/** Checks that the statement throws an exception of the specified class. */
# define TEST_THROWS(Stmt, ExcClass) \
try \
{ \
Stmt ; \
2019-08-26 15:38:34 -04:00
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " Failed to throw an exception of type %s " , \
2019-01-23 14:54:29 -05:00
# ExcClass \
) ) ; \
} \
catch ( const ExcClass & ) \
{ \
/* This is the expected case. */ \
} \
catch ( const std : : exception & exc ) \
{ \
2019-08-26 15:38:34 -04:00
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " An unexpected std::exception descendant was thrown, was expecting type %s. Message is: %s " , \
2019-01-23 14:54:29 -05:00
# ExcClass, exc.what() \
) ) ; \
} \
catch ( . . . ) \
{ \
2019-08-26 15:38:34 -04:00
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , Printf ( " An unexpected unknown exception object was thrown, was expecting type %s " , \
2019-01-23 14:54:29 -05:00
# ExcClass \
) ) ; \
}
2019-08-26 15:38:34 -04:00
/** Checks that the statement throws an exception of any type. */
# define TEST_THROWS_ANY(Stmt) \
try \
{ \
Stmt ; \
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , " Failed to throw an exception of any type " ) ; \
} \
catch ( const TestException & exc ) \
{ \
throw exc ; \
} \
catch ( . . . ) \
{ \
/* This is the expected case */ \
}
2019-09-02 00:46:40 -04:00
/** Fails the test unconditionally, with the specified message. */
# define TEST_FAIL(MSG) \
throw TestException ( __FILE__ , __LINE__ , __FUNCTION__ , MSG ) ;
2019-08-26 15:38:34 -04:00
/** Checks that the statement causes an ASSERT trigger. */
# ifdef _DEBUG
# define TEST_ASSERTS(Stmt) TEST_THROWS(Stmt, cAssertFailure)
# else
# define TEST_ASSERTS(Stmt) LOG("Skipped, cannot test in Release mode: TEST_ASSERT(%s); (%s:%d)", #Stmt, __FILE__, __LINE__);
# endif // else _DEBUG
/** Used to implement the main() function for tests. */
# define IMPLEMENT_TEST_MAIN(TestName, TestCode) \
int main ( ) \
{ \
LOG ( " Test started: %s " , TestName ) ; \
\
try \
{ \
TestCode ; \
} \
catch ( const TestException & exc ) \
{ \
LOGERROR ( " Test has failed at file %s, line %d, function %s: %s " , \
exc . mFileName . c_str ( ) , \
exc . mLineNumber , \
exc . mFunctionName . c_str ( ) , \
exc . mMessage . c_str ( ) \
) ; \
return 1 ; \
} \
catch ( const std : : exception & exc ) \
{ \
LOGERROR ( " Test has failed, an exception was thrown: %s " , exc . what ( ) ) ; \
return 1 ; \
} \
catch ( const cAssertFailure & exc ) \
{ \
2019-09-02 00:46:40 -04:00
LOGERROR ( " Test has failed, an unexpected ASSERT was triggered at %s:%d: %s " , \
exc . fileName ( ) . c_str ( ) , exc . lineNumber ( ) , exc . expression ( ) . c_str ( ) \
) ; \
2019-08-26 15:38:34 -04:00
return 1 ; \
} \
catch ( . . . ) \
{ \
LOGERROR ( " Test has failed, an unhandled exception was thrown. " ) ; \
return 1 ; \
} \
\
LOG ( " Test finished " ) ; \
return 0 ; \
}