Test.m
author Jens Alfke <jens@mooseyard.com>
Wed Jul 01 14:04:56 2009 -0700 (2009-07-01)
changeset 33 d52f6b0d94be
parent 22 a9da6c5d3f7c
child 35 5cab3034d3a1
permissions -rw-r--r--
Added NSData categories for GZip and Mnemonicode.
Minor tweaks elsewhere.
     1 //
     2 //  Test.m
     3 //  MYUtilities
     4 //
     5 //  Created by Jens Alfke on 1/5/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "Test.h"
    10 #import "ExceptionUtils.h"
    11 
    12 
    13 #if DEBUG
    14 
    15 BOOL gRunningTestCase;
    16 
    17 struct TestCaseLink *gAllTestCases;
    18 static int sPassed, sFailed;
    19 static int sCurTestCaseExceptions;
    20 
    21 
    22 static void TestCaseExceptionReporter( NSException *x ) {
    23     sCurTestCaseExceptions++;
    24     fflush(stderr);
    25     Log(@"XXX FAILED test case -- backtrace:\n%@\n\n", x.my_callStack);
    26 }
    27 
    28 static BOOL RunTestCase( struct TestCaseLink *test )
    29 {
    30     BOOL oldLogging = EnableLog(YES);
    31     gRunningTestCase = YES;
    32     if( test->testptr ) {
    33         NSAutoreleasePool *pool = [NSAutoreleasePool new];
    34         Log(@"=== Testing %s ...",test->name);
    35         @try{
    36             sCurTestCaseExceptions = 0;
    37             MYSetExceptionReporter(&TestCaseExceptionReporter);
    38 
    39             test->testptr();    //SHAZAM!
    40             
    41             if( sCurTestCaseExceptions == 0 ) {
    42                 Log(@"√√√ %s passed\n\n",test->name);
    43                 test->passed = YES;
    44                 sPassed++;
    45             } else {
    46                 Log(@"XXX FAILED test case '%s' due to %i exception(s) already reported above",
    47                     test->name,sCurTestCaseExceptions);
    48                 sFailed++;
    49             }
    50         }@catch( NSException *x ) {
    51             if( [x.name isEqualToString: @"TestCaseSkipped"] )
    52                 Log(@"... skipping test %s since %@\n\n", test->name, x.reason);
    53             else {
    54                 fflush(stderr);
    55                 Log(@"XXX FAILED test case '%s' due to:\nException: %@\n%@\n\n", 
    56                       test->name,x,x.my_callStack);
    57                 sFailed++;
    58             }
    59         }@finally{
    60             [pool drain];
    61             test->testptr = NULL;       // prevents test from being run again
    62         }
    63     }
    64     gRunningTestCase = NO;
    65     EnableLog(oldLogging);
    66     return test->passed;
    67 }
    68 
    69 
    70 static BOOL RunTestCaseNamed( const char *name )
    71 {
    72     for( struct TestCaseLink *test = gAllTestCases; test; test=test->next )
    73         if( strcmp(name,test->name)==0 ) {
    74             return RunTestCase(test);
    75         }
    76     Log(@"... WARNING: Could not find test case named '%s'\n\n",name);
    77     return NO;
    78 }
    79 
    80 
    81 void _RequireTestCase( const char *name )
    82 {
    83     if( ! RunTestCaseNamed(name) ) {
    84         [NSException raise: @"TestCaseSkipped" 
    85                     format: @"prerequisite %s failed", name];
    86     }
    87 }
    88 
    89 
    90 void RunTestCases( int argc, const char **argv )
    91 {
    92     sPassed = sFailed = 0;
    93     BOOL stopAfterTests = NO;
    94     for( int i=1; i<argc; i++ ) {
    95         const char *arg = argv[i];
    96         if( strncmp(arg,"Test_",5)==0 ) {
    97             arg += 5;
    98             if( strcmp(arg,"Only")==0 )
    99                 stopAfterTests = YES;
   100             else if( strcmp(arg,"All") == 0 ) {
   101                 for( struct TestCaseLink *link = gAllTestCases; link; link=link->next )
   102                     RunTestCase(link);
   103             } else {
   104                 RunTestCaseNamed(arg);
   105             }
   106         }
   107     }
   108     if( sPassed>0 || sFailed>0 || stopAfterTests ) {
   109         if( sFailed==0 )
   110             AlwaysLog(@"√√√√√√ ALL %i TESTS PASSED √√√√√√", sPassed);
   111         else {
   112             Warn(@"****** %i TESTS FAILED, %i PASSED ******", sFailed,sPassed);
   113             exit(1);
   114         }
   115         if( stopAfterTests ) {
   116             Log(@"Stopping after tests ('Test_Only' arg detected)");
   117             exit(0);
   118         }
   119     }
   120 }
   121 
   122 
   123 #endif DEBUG
   124 
   125 
   126 #pragma mark -
   127 #pragma mark ASSERTION FAILURE HANDLER:
   128 
   129 
   130 void _AssertFailed( id rcvr, const void *selOrFn, const char *sourceFile, int sourceLine,
   131                     const char *condString, NSString *message, ... )
   132 {
   133     if( message ) {
   134         va_list args;
   135         va_start(args,message);
   136         message = [[[NSString alloc] initWithFormat: message arguments: args] autorelease];
   137         message = [@"Assertion failed: " stringByAppendingString: message];
   138         va_end(args);
   139     } else
   140         message = [NSString stringWithUTF8String: condString];
   141     
   142     if( rcvr )
   143         [[NSAssertionHandler currentHandler] handleFailureInMethod: (SEL)selOrFn
   144                                                             object: rcvr 
   145                                                               file: [NSString stringWithUTF8String: sourceFile]
   146                                                         lineNumber: sourceLine 
   147                                                        description: @"%@", message];
   148     else
   149         [[NSAssertionHandler currentHandler] handleFailureInFunction: [NSString stringWithUTF8String:selOrFn]
   150                                                                 file: [NSString stringWithUTF8String: sourceFile]
   151                                                           lineNumber: sourceLine 
   152                                                          description: @"%@", message];
   153     abort(); // unreachable, but appeases compiler
   154 }
   155 
   156 
   157 void _AssertAbstractMethodFailed( id rcvr, SEL cmd)
   158 {
   159     [NSException raise: NSInternalInconsistencyException 
   160                 format: @"Class %@ forgot to implement abstract method %@",
   161                          [rcvr class], NSStringFromSelector(cmd)];
   162     abort(); // unreachable, but appeases compiler
   163 }
   164 
   165 
   166 /*
   167  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   168  
   169  Redistribution and use in source and binary forms, with or without modification, are permitted
   170  provided that the following conditions are met:
   171  
   172  * Redistributions of source code must retain the above copyright notice, this list of conditions
   173  and the following disclaimer.
   174  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   175  and the following disclaimer in the documentation and/or other materials provided with the
   176  distribution.
   177  
   178  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   179  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   180  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   181  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   182  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   183   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   184  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   185  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   186  */