jens@0: // jens@0: // Test.h jens@0: // MYUtilities jens@0: // jens@0: // Created by Jens Alfke on 1/5/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #import jens@0: #import "CollectionUtils.h" jens@9: #import "Logging.h" jens@0: jens@0: jens@0: /** Call this first thing in main() to run tests. jens@0: This function is a no-op if the DEBUG macro is not defined (i.e. in a release build). jens@0: At runtime, to cause a particular test "X" to run, add a command-line argument "Test_X". jens@0: To run all tests, add the argument "Test_All". jens@0: To run only tests without starting the main program, add the argument "Test_Only". */ jens@0: #if DEBUG jens@0: void RunTestCases( int argc, const char **argv ); jens@1: extern BOOL gRunningTestCase; jens@0: #else jens@0: #define RunTestCases(ARGC,ARGV) jens@1: #define gRunningTestCase NO jens@0: #endif jens@0: jens@0: /** The TestCase() macro declares a test case. jens@0: Its argument is a name for the test case (without quotes), and it's followed with a block jens@0: of code implementing the test. jens@0: The code should raise an exception if anything fails. jens@0: The CAssert, CAssertEqual and CAssertEq macros, below, are the most useful way to do this. jens@0: A test case can register a dependency on another test case by calling RequireTestCase(). jens@0: Example: jens@0: TestCase(MyLib) { jens@0: RequireTestCase("LibIDependOn"); jens@0: CAssertEq( myFunction(), 12345 ); jens@0: } jens@0: Test cases are disabled if the DEBUG macro is not defined (i.e. in a release build). */ jens@0: #if DEBUG jens@9: #define TestCase(NAME) __attribute__ ((section ("__TEXT, Tests"))) void Test_##NAME(void); \ jens@0: struct TestCaseLink linkToTest##NAME = {&Test_##NAME,#NAME}; \ jens@0: __attribute__((constructor)) static void registerTestCase##NAME() \ jens@0: {linkToTest##NAME.next = gAllTestCases; gAllTestCases=&linkToTest##NAME; } \ jens@0: void Test_##NAME(void) jens@0: #else jens@0: #define TestCase(NAME) __attribute__((unused)) static void Test_##NAME(void) jens@0: #endif jens@0: jens@0: /** Can call this in a test case to indicate a prerequisite. jens@0: The prerequisite test will be run first, and if it fails, the current test case will be skipped. */ jens@11: #if DEBUG jens@0: #define RequireTestCase(NAME) _RequireTestCase(#NAME) jens@0: void _RequireTestCase( const char *name ); jens@11: #else jens@11: #define RequireTestCase(NAME) jens@11: #endif jens@0: jens@0: jens@0: /** General-purpose assertions, replacing NSAssert etc.. You can use these outside test cases. */ jens@0: jens@9: #define Assert(COND,MSG...) do{ if( __builtin_expect(!(COND),NO) ) { \ jens@12: IN_SEGMENT_NORETURN(Logging) {_AssertFailed(self,_cmd, __FILE__, __LINE__,\ jens@9: #COND,##MSG,NULL);} } }while(0) jens@9: #define CAssert(COND,MSG...) do{ if( __builtin_expect(!(COND),NO) ) { \ jens@9: static const char *_name = __PRETTY_FUNCTION__;\ jens@9: IN_SEGMENT_NORETURN(Logging) {_AssertFailed(nil, _name, __FILE__, __LINE__,\ jens@9: #COND,##MSG,NULL);} } }while(0) jens@0: jens@13: // AssertEqual is for Obj-C objects jens@0: #define AssertEqual(VAL,EXPECTED) do{ id _val = VAL, _expected = EXPECTED;\ jens@0: Assert(_val==_expected || [_val isEqual: _expected], @"Unexpected value for %s: %@ (expected %@)", #VAL,_val,_expected); \ jens@0: }while(0) jens@0: #define CAssertEqual(VAL,EXPECTED) do{ id _val = (VAL), _expected = (EXPECTED);\ jens@0: CAssert(_val==_expected || [_val isEqual: _expected], @"Unexpected value for %s: %@ (expected %@)", #VAL,_val,_expected); \ jens@0: }while(0) jens@0: jens@0: // AssertEq is for scalars (int, float...) jens@13: #define AssertEq(VAL,EXPECTED) do{ __typeof(VAL) _val = VAL; __typeof(EXPECTED) _expected = EXPECTED;\ jens@0: Assert(_val==_expected, @"Unexpected value for %s: %@ (expected %@)", #VAL,$object(_val),$object(_expected)); \ jens@0: }while(0) jens@13: #define CAssertEq(VAL,EXPECTED) do{ __typeof(VAL) _val = VAL; __typeof(EXPECTED) _expected = EXPECTED;\ jens@0: CAssert(_val==_expected, @"Unexpected value for %s: %@ (expected %@)", #VAL,$object(_val),$object(_expected)); \ jens@0: }while(0) jens@0: jens@0: jens@0: // Nasty internals ... jens@0: #if DEBUG jens@0: void _RunTestCase( void (*testptr)(), const char *name ); jens@0: jens@0: struct TestCaseLink {void (*testptr)(); const char *name; BOOL passed; struct TestCaseLink *next;}; jens@0: extern struct TestCaseLink *gAllTestCases; jens@0: #endif DEBUG jens@12: void _AssertFailed( id rcvr, const void *selOrFn, const char *sourceFile, int sourceLine, jens@0: const char *condString, NSString *message, ... ) __attribute__((noreturn));