* Added $apply and some other collection utils.
* Moved logging code to a separate code segment.
* Fixed uninitialized variable in Target.
5 // Created by Jens Alfke on 1/5/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
14 BOOL gRunningTestCase;
16 struct TestCaseLink *gAllTestCases;
17 static int sPassed, sFailed;
19 static BOOL RunTestCase( struct TestCaseLink *test )
21 BOOL oldLogging = EnableLog(YES);
22 gRunningTestCase = YES;
24 NSAutoreleasePool *pool = [NSAutoreleasePool new];
25 Log(@"=== Testing %s ...",test->name);
28 Log(@"√√√ %s passed\n\n",test->name);
31 }@catch( NSException *x ) {
32 if( [x.name isEqualToString: @"TestCaseSkipped"] )
33 Log(@"... skipping test %s since %@\n\n", test->name, x.reason);
35 Log(@"XXX FAILED test case '%s' due to:\nException: %@\n%@\n\n",
36 test->name,x,x.my_callStack);
41 test->testptr = NULL; // prevents test from being run again
44 gRunningTestCase = NO;
45 EnableLog(oldLogging);
50 static BOOL RunTestCaseNamed( const char *name )
52 for( struct TestCaseLink *test = gAllTestCases; test; test=test->next )
53 if( strcmp(name,test->name)==0 ) {
54 return RunTestCase(test);
56 Log(@"... WARNING: Could not find test case named '%s'\n\n",name);
61 void _RequireTestCase( const char *name )
63 if( ! RunTestCaseNamed(name) ) {
64 [NSException raise: @"TestCaseSkipped"
65 format: @"prerequisite %s failed", name];
70 void RunTestCases( int argc, const char **argv )
72 sPassed = sFailed = 0;
73 BOOL stopAfterTests = NO;
74 for( int i=1; i<argc; i++ ) {
75 const char *arg = argv[i];
76 if( strncmp(arg,"Test_",5)==0 ) {
78 if( strcmp(arg,"Only")==0 )
80 else if( strcmp(arg,"All") == 0 ) {
81 for( struct TestCaseLink *link = gAllTestCases; link; link=link->next )
84 RunTestCaseNamed(arg);
88 if( sPassed>0 || sFailed>0 || stopAfterTests ) {
90 Log(@"√√√√√√ ALL %i TESTS PASSED √√√√√√", sPassed);
92 Log(@"****** %i TESTS FAILED, %i PASSED ******", sFailed,sPassed);
95 if( stopAfterTests ) {
96 Log(@"Stopping after tests ('Test_Only' arg detected)");
107 #pragma mark ASSERTION FAILURE HANDLER:
110 void _AssertFailed( id rcvr, const char *selOrFn, const char *sourceFile, int sourceLine,
111 const char *condString, NSString *message, ... )
115 va_start(args,message);
116 message = [[[NSString alloc] initWithFormat: message arguments: args] autorelease];
119 message = [NSString stringWithUTF8String: condString];
122 [[NSAssertionHandler currentHandler] handleFailureInMethod: (SEL)selOrFn
124 file: [NSString stringWithUTF8String: sourceFile]
125 lineNumber: sourceLine
126 description: @"%@", message];
128 [[NSAssertionHandler currentHandler] handleFailureInFunction: [NSString stringWithUTF8String:selOrFn]
129 file: [NSString stringWithUTF8String: sourceFile]
130 lineNumber: sourceLine
131 description: @"%@", message];
132 abort(); // unreachable, but appeases compiler
137 #pragma mark EXCEPTION BACKTRACES:
140 @implementation NSException (MooseyardUtil)
143 - (NSArray*) my_callStackReturnAddresses
145 // On 10.5 or later, can get the backtrace:
146 if( [self respondsToSelector: @selector(callStackReturnAddresses)] )
147 return [self valueForKey: @"callStackReturnAddresses"];
152 - (NSArray*) my_callStackReturnAddressesSkipping: (unsigned)skip limit: (unsigned)limit
154 NSArray *addresses = [self my_callStackReturnAddresses];
156 unsigned n = [addresses count];
158 limit = MIN(limit,n-skip);
159 addresses = [addresses subarrayWithRange: NSMakeRange(skip,limit)];
165 - (NSString*) my_callStack
167 NSArray *addresses = [self my_callStackReturnAddressesSkipping: 2 limit: 15];
171 // We pipe the hex return addresses through the 'atos' tool to get symbolic names:
172 // Adapted from <http://paste.lisp.org/display/47196>:
173 NSMutableString *cmd = [NSMutableString stringWithFormat: @"/usr/bin/atos -p %d", getpid()];
175 foreach(addr,addresses) {
176 [cmd appendFormat: @" %p", [addr pointerValue]];
178 FILE *file = popen( [cmd UTF8String], "r" );
182 NSMutableData *output = [NSMutableData data];
185 while ((length = fread( buffer, 1, sizeof( buffer ), file ) ))
186 [output appendBytes: buffer length: length];
188 NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease];
190 NSMutableString *result = [NSMutableString string];
192 foreach( line, [outStr componentsSeparatedByString: @"\n"] ) {
193 // Skip frames that are part of the exception/assertion handling itself:
194 if( [line hasPrefix: @"-[NSAssertionHandler"] || [line hasPrefix: @"+[NSException"]
195 || [line hasPrefix: @"-[NSException"] || [line hasPrefix: @"_AssertFailed"] )
198 [result appendString: @"\n"];
199 [result appendString: @"\t"];
200 [result appendString: line];
201 // Don't show the "__start" frame below "main":
202 if( [line hasPrefix: @"main "] )