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