jens@0: // jens@0: // Test.m 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 "Test.h" jens@10: jens@0: jens@0: #if DEBUG jens@0: jens@1: BOOL gRunningTestCase; jens@1: jens@0: struct TestCaseLink *gAllTestCases; jens@0: static int sPassed, sFailed; jens@10: static int sCurTestCaseExceptions; jens@10: jens@10: jens@10: static void TestCaseExceptionReporter( NSException *x ) { jens@10: sCurTestCaseExceptions++; jens@10: Log(@"XXX FAILED test case -- backtrace:\n%@\n\n", x.my_callStack); jens@10: } jens@0: jens@0: static BOOL RunTestCase( struct TestCaseLink *test ) jens@0: { jens@1: BOOL oldLogging = EnableLog(YES); jens@1: gRunningTestCase = YES; jens@0: if( test->testptr ) { jens@0: NSAutoreleasePool *pool = [NSAutoreleasePool new]; jens@0: Log(@"=== Testing %s ...",test->name); jens@0: @try{ jens@10: sCurTestCaseExceptions = 0; jens@10: MYSetExceptionReporter(&TestCaseExceptionReporter); jens@10: jens@10: test->testptr(); //SHAZAM! jens@10: jens@10: if( sCurTestCaseExceptions == 0 ) { jens@10: Log(@"√√√ %s passed\n\n",test->name); jens@10: test->passed = YES; jens@10: sPassed++; jens@10: } else { jens@10: Log(@"XXX FAILED test case '%s' due to %i exception(s) already reported above", jens@10: test->name,sCurTestCaseExceptions); jens@10: sFailed++; jens@10: } jens@0: }@catch( NSException *x ) { jens@0: if( [x.name isEqualToString: @"TestCaseSkipped"] ) jens@0: Log(@"... skipping test %s since %@\n\n", test->name, x.reason); jens@0: else { jens@0: Log(@"XXX FAILED test case '%s' due to:\nException: %@\n%@\n\n", jens@0: test->name,x,x.my_callStack); jens@0: sFailed++; jens@0: } jens@0: }@finally{ jens@0: [pool drain]; jens@0: test->testptr = NULL; // prevents test from being run again jens@0: } jens@0: } jens@1: gRunningTestCase = NO; jens@1: EnableLog(oldLogging); jens@0: return test->passed; jens@0: } jens@0: jens@0: jens@0: static BOOL RunTestCaseNamed( const char *name ) jens@0: { jens@0: for( struct TestCaseLink *test = gAllTestCases; test; test=test->next ) jens@0: if( strcmp(name,test->name)==0 ) { jens@0: return RunTestCase(test); jens@0: } jens@0: Log(@"... WARNING: Could not find test case named '%s'\n\n",name); jens@0: return NO; jens@0: } jens@0: jens@0: jens@0: void _RequireTestCase( const char *name ) jens@0: { jens@0: if( ! RunTestCaseNamed(name) ) { jens@0: [NSException raise: @"TestCaseSkipped" jens@0: format: @"prerequisite %s failed", name]; jens@0: } jens@0: } jens@0: jens@0: jens@0: void RunTestCases( int argc, const char **argv ) jens@0: { jens@0: sPassed = sFailed = 0; jens@0: BOOL stopAfterTests = NO; jens@0: for( int i=1; inext ) jens@0: RunTestCase(link); jens@0: } else { jens@0: RunTestCaseNamed(arg); jens@0: } jens@0: } jens@0: } jens@0: if( sPassed>0 || sFailed>0 || stopAfterTests ) { jens@0: if( sFailed==0 ) jens@0: Log(@"√√√√√√ ALL %i TESTS PASSED √√√√√√", sPassed); jens@0: else { jens@0: Log(@"****** %i TESTS FAILED, %i PASSED ******", sFailed,sPassed); jens@0: exit(1); jens@0: } jens@0: if( stopAfterTests ) { jens@0: Log(@"Stopping after tests ('Test_Only' arg detected)"); jens@0: exit(0); jens@0: } jens@0: } jens@0: } jens@0: jens@0: jens@0: #endif DEBUG jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark ASSERTION FAILURE HANDLER: jens@0: jens@0: jens@0: void _AssertFailed( id rcvr, const char *selOrFn, const char *sourceFile, int sourceLine, jens@0: const char *condString, NSString *message, ... ) jens@0: { jens@0: if( message ) { jens@0: va_list args; jens@0: va_start(args,message); jens@0: message = [[[NSString alloc] initWithFormat: message arguments: args] autorelease]; jens@0: va_end(args); jens@0: } else jens@0: message = [NSString stringWithUTF8String: condString]; jens@0: jens@0: if( rcvr ) jens@0: [[NSAssertionHandler currentHandler] handleFailureInMethod: (SEL)selOrFn jens@0: object: rcvr jens@0: file: [NSString stringWithUTF8String: sourceFile] jens@0: lineNumber: sourceLine jens@0: description: @"%@", message]; jens@0: else jens@0: [[NSAssertionHandler currentHandler] handleFailureInFunction: [NSString stringWithUTF8String:selOrFn] jens@0: file: [NSString stringWithUTF8String: sourceFile] jens@0: lineNumber: sourceLine jens@0: description: @"%@", message]; jens@0: abort(); // unreachable, but appeases compiler jens@0: }