1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Test.m Sat Mar 08 21:04:41 2008 -0800
1.3 @@ -0,0 +1,203 @@
1.4 +//
1.5 +// Test.m
1.6 +// MYUtilities
1.7 +//
1.8 +// Created by Jens Alfke on 1/5/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "Test.h"
1.13 +#import <unistd.h>
1.14 +
1.15 +#if DEBUG
1.16 +
1.17 +struct TestCaseLink *gAllTestCases;
1.18 +static int sPassed, sFailed;
1.19 +
1.20 +static BOOL RunTestCase( struct TestCaseLink *test )
1.21 +{
1.22 + if( test->testptr ) {
1.23 + NSAutoreleasePool *pool = [NSAutoreleasePool new];
1.24 + Log(@"=== Testing %s ...",test->name);
1.25 + @try{
1.26 + test->testptr();
1.27 + Log(@"√√√ %s passed\n\n",test->name);
1.28 + test->passed = YES;
1.29 + sPassed++;
1.30 + }@catch( NSException *x ) {
1.31 + if( [x.name isEqualToString: @"TestCaseSkipped"] )
1.32 + Log(@"... skipping test %s since %@\n\n", test->name, x.reason);
1.33 + else {
1.34 + Log(@"XXX FAILED test case '%s' due to:\nException: %@\n%@\n\n",
1.35 + test->name,x,x.my_callStack);
1.36 + sFailed++;
1.37 + }
1.38 + }@finally{
1.39 + [pool drain];
1.40 + test->testptr = NULL; // prevents test from being run again
1.41 + }
1.42 + }
1.43 + return test->passed;
1.44 +}
1.45 +
1.46 +
1.47 +static BOOL RunTestCaseNamed( const char *name )
1.48 +{
1.49 + for( struct TestCaseLink *test = gAllTestCases; test; test=test->next )
1.50 + if( strcmp(name,test->name)==0 ) {
1.51 + return RunTestCase(test);
1.52 + }
1.53 + Log(@"... WARNING: Could not find test case named '%s'\n\n",name);
1.54 + return NO;
1.55 +}
1.56 +
1.57 +
1.58 +void _RequireTestCase( const char *name )
1.59 +{
1.60 + if( ! RunTestCaseNamed(name) ) {
1.61 + [NSException raise: @"TestCaseSkipped"
1.62 + format: @"prerequisite %s failed", name];
1.63 + }
1.64 +}
1.65 +
1.66 +
1.67 +void RunTestCases( int argc, const char **argv )
1.68 +{
1.69 + gShouldLog = YES;
1.70 + sPassed = sFailed = 0;
1.71 + BOOL stopAfterTests = NO;
1.72 + for( int i=1; i<argc; i++ ) {
1.73 + const char *arg = argv[i];
1.74 + if( strncmp(arg,"Test_",5)==0 ) {
1.75 + arg += 5;
1.76 + if( strcmp(arg,"Only")==0 )
1.77 + stopAfterTests = YES;
1.78 + else if( strcmp(arg,"All") == 0 ) {
1.79 + for( struct TestCaseLink *link = gAllTestCases; link; link=link->next )
1.80 + RunTestCase(link);
1.81 + } else {
1.82 + RunTestCaseNamed(arg);
1.83 + }
1.84 + }
1.85 + }
1.86 + if( sPassed>0 || sFailed>0 || stopAfterTests ) {
1.87 + if( sFailed==0 )
1.88 + Log(@"√√√√√√ ALL %i TESTS PASSED √√√√√√", sPassed);
1.89 + else {
1.90 + Log(@"****** %i TESTS FAILED, %i PASSED ******", sFailed,sPassed);
1.91 + exit(1);
1.92 + }
1.93 + if( stopAfterTests ) {
1.94 + Log(@"Stopping after tests ('Test_Only' arg detected)");
1.95 + exit(0);
1.96 + }
1.97 + }
1.98 +}
1.99 +
1.100 +
1.101 +#endif DEBUG
1.102 +
1.103 +
1.104 +#pragma mark -
1.105 +#pragma mark ASSERTION FAILURE HANDLER:
1.106 +
1.107 +
1.108 +void _AssertFailed( id rcvr, const char *selOrFn, const char *sourceFile, int sourceLine,
1.109 + const char *condString, NSString *message, ... )
1.110 +{
1.111 + if( message ) {
1.112 + va_list args;
1.113 + va_start(args,message);
1.114 + message = [[[NSString alloc] initWithFormat: message arguments: args] autorelease];
1.115 + va_end(args);
1.116 + } else
1.117 + message = [NSString stringWithUTF8String: condString];
1.118 +
1.119 + if( rcvr )
1.120 + [[NSAssertionHandler currentHandler] handleFailureInMethod: (SEL)selOrFn
1.121 + object: rcvr
1.122 + file: [NSString stringWithUTF8String: sourceFile]
1.123 + lineNumber: sourceLine
1.124 + description: @"%@", message];
1.125 + else
1.126 + [[NSAssertionHandler currentHandler] handleFailureInFunction: [NSString stringWithUTF8String:selOrFn]
1.127 + file: [NSString stringWithUTF8String: sourceFile]
1.128 + lineNumber: sourceLine
1.129 + description: @"%@", message];
1.130 + abort(); // unreachable, but appeases compiler
1.131 +}
1.132 +
1.133 +
1.134 +#pragma mark -
1.135 +#pragma mark EXCEPTION BACKTRACES:
1.136 +
1.137 +
1.138 +@implementation NSException (MooseyardUtil)
1.139 +
1.140 +
1.141 +- (NSArray*) my_callStackReturnAddresses
1.142 +{
1.143 + // On 10.5 or later, can get the backtrace:
1.144 + if( [self respondsToSelector: @selector(callStackReturnAddresses)] )
1.145 + return [self valueForKey: @"callStackReturnAddresses"];
1.146 + else
1.147 + return nil;
1.148 +}
1.149 +
1.150 +- (NSArray*) my_callStackReturnAddressesSkipping: (unsigned)skip limit: (unsigned)limit
1.151 +{
1.152 + NSArray *addresses = [self my_callStackReturnAddresses];
1.153 + if( addresses ) {
1.154 + unsigned n = [addresses count];
1.155 + skip = MIN(skip,n);
1.156 + limit = MIN(limit,n-skip);
1.157 + addresses = [addresses subarrayWithRange: NSMakeRange(skip,limit)];
1.158 + }
1.159 + return addresses;
1.160 +}
1.161 +
1.162 +
1.163 +- (NSString*) my_callStack
1.164 +{
1.165 + NSArray *addresses = [self my_callStackReturnAddressesSkipping: 2 limit: 15];
1.166 + if (!addresses)
1.167 + return nil;
1.168 +
1.169 + // We pipe the hex return addresses through the 'atos' tool to get symbolic names:
1.170 + // Adapted from <http://paste.lisp.org/display/47196>:
1.171 + NSMutableString *cmd = [NSMutableString stringWithFormat: @"/usr/bin/atos -p %d", getpid()];
1.172 + NSValue *addr;
1.173 + foreach(addr,addresses) {
1.174 + [cmd appendFormat: @" %p", [addr pointerValue]];
1.175 + }
1.176 + FILE *file = popen( [cmd UTF8String], "r" );
1.177 + if( ! file )
1.178 + return nil;
1.179 +
1.180 + NSMutableData *output = [NSMutableData data];
1.181 + char buffer[512];
1.182 + size_t length;
1.183 + while ((length = fread( buffer, 1, sizeof( buffer ), file ) ))
1.184 + [output appendBytes: buffer length: length];
1.185 + pclose( file );
1.186 + NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease];
1.187 +
1.188 + NSMutableString *result = [NSMutableString string];
1.189 + NSString *line;
1.190 + foreach( line, [outStr componentsSeparatedByString: @"\n"] ) {
1.191 + // Skip frames that are part of the exception/assertion handling itself:
1.192 + if( [line hasPrefix: @"-[NSAssertionHandler"] || [line hasPrefix: @"+[NSException"]
1.193 + || [line hasPrefix: @"-[NSException"] || [line hasPrefix: @"_AssertFailed"] )
1.194 + continue;
1.195 + if( result.length )
1.196 + [result appendString: @"\n"];
1.197 + [result appendString: @"\t"];
1.198 + [result appendString: line];
1.199 + // Don't show the "__start" frame below "main":
1.200 + if( [line hasPrefix: @"main "] )
1.201 + break;
1.202 + }
1.203 + return result;
1.204 +}
1.205 +
1.206 +@end