Test.h
author Jens Alfke <jens@mooseyard.com>
Fri Apr 18 09:25:10 2008 -0700 (2008-04-18)
changeset 6 2d492d8c2053
parent 0 d84d25d6cdbb
child 9 823e7e74088e
permissions -rw-r--r--
Added -reopenWith:.
jens@0
     1
//
jens@0
     2
//  Test.h
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 <Foundation/Foundation.h>
jens@0
    10
#import "CollectionUtils.h"
jens@0
    11
jens@0
    12
jens@0
    13
/** Call this first thing in main() to run tests.
jens@0
    14
    This function is a no-op if the DEBUG macro is not defined (i.e. in a release build).
jens@0
    15
    At runtime, to cause a particular test "X" to run, add a command-line argument "Test_X".
jens@0
    16
    To run all tests, add the argument "Test_All".
jens@0
    17
    To run only tests without starting the main program, add the argument "Test_Only". */
jens@0
    18
#if DEBUG
jens@0
    19
void RunTestCases( int argc, const char **argv );
jens@1
    20
extern BOOL gRunningTestCase;
jens@0
    21
#else
jens@0
    22
#define RunTestCases(ARGC,ARGV)
jens@1
    23
#define gRunningTestCase NO
jens@0
    24
#endif
jens@0
    25
jens@0
    26
/** The TestCase() macro declares a test case.
jens@0
    27
    Its argument is a name for the test case (without quotes), and it's followed with a block
jens@0
    28
    of code implementing the test.
jens@0
    29
    The code should raise an exception if anything fails.
jens@0
    30
    The CAssert, CAssertEqual and CAssertEq macros, below, are the most useful way to do this.
jens@0
    31
    A test case can register a dependency on another test case by calling RequireTestCase().
jens@0
    32
    Example:
jens@0
    33
        TestCase(MyLib) {
jens@0
    34
            RequireTestCase("LibIDependOn");
jens@0
    35
            CAssertEq( myFunction(), 12345 );
jens@0
    36
        }
jens@0
    37
    Test cases are disabled if the DEBUG macro is not defined (i.e. in a release build). */
jens@0
    38
#if DEBUG
jens@0
    39
#define TestCase(NAME)      void Test_##NAME(void); \
jens@0
    40
                            struct TestCaseLink linkToTest##NAME = {&Test_##NAME,#NAME}; \
jens@0
    41
                            __attribute__((constructor)) static void registerTestCase##NAME() \
jens@0
    42
                                {linkToTest##NAME.next = gAllTestCases; gAllTestCases=&linkToTest##NAME; } \
jens@0
    43
                            void Test_##NAME(void)
jens@0
    44
#else
jens@0
    45
#define TestCase(NAME)      __attribute__((unused)) static void Test_##NAME(void)
jens@0
    46
#endif
jens@0
    47
jens@0
    48
/** Can call this in a test case to indicate a prerequisite.
jens@0
    49
    The prerequisite test will be run first, and if it fails, the current test case will be skipped. */
jens@0
    50
#define RequireTestCase(NAME)   _RequireTestCase(#NAME)
jens@0
    51
void _RequireTestCase( const char *name );
jens@0
    52
jens@0
    53
jens@0
    54
jens@0
    55
/** General-purpose assertions, replacing NSAssert etc.. You can use these outside test cases. */
jens@0
    56
jens@0
    57
#define Assert(COND,MSG...)    do{ if( __builtin_expect(!(COND),NO) ) \
jens@0
    58
                                    _AssertFailed(self,(const char*)_cmd, __FILE__, __LINE__,\
jens@0
    59
                                                  #COND,##MSG,NULL); }while(0)
jens@0
    60
#define CAssert(COND,MSG...)    do{ if( __builtin_expect(!(COND),NO) ) \
jens@0
    61
                                    _AssertFailed(nil, __PRETTY_FUNCTION__, __FILE__, __LINE__,\
jens@0
    62
                                                  #COND,##MSG,NULL); }while(0)
jens@0
    63
jens@0
    64
#define AssertEqual(VAL,EXPECTED)   do{ id _val = VAL, _expected = EXPECTED;\
jens@0
    65
                                        Assert(_val==_expected || [_val isEqual: _expected], @"Unexpected value for %s: %@ (expected %@)", #VAL,_val,_expected); \
jens@0
    66
                                    }while(0)
jens@0
    67
#define CAssertEqual(VAL,EXPECTED)  do{ id _val = (VAL), _expected = (EXPECTED);\
jens@0
    68
                                        CAssert(_val==_expected || [_val isEqual: _expected], @"Unexpected value for %s: %@ (expected %@)", #VAL,_val,_expected); \
jens@0
    69
                                    }while(0)
jens@0
    70
jens@0
    71
// AssertEq is for scalars (int, float...)
jens@0
    72
#define AssertEq(VAL,EXPECTED)  do{ typeof(VAL) _val = VAL; typeof(EXPECTED) _expected = EXPECTED;\
jens@0
    73
                                    Assert(_val==_expected, @"Unexpected value for %s: %@ (expected %@)", #VAL,$object(_val),$object(_expected)); \
jens@0
    74
                                }while(0)
jens@0
    75
#define CAssertEq(VAL,EXPECTED) do{ typeof(VAL) _val = VAL; typeof(EXPECTED) _expected = EXPECTED;\
jens@0
    76
                                    CAssert(_val==_expected, @"Unexpected value for %s: %@ (expected %@)", #VAL,$object(_val),$object(_expected)); \
jens@0
    77
                                }while(0)
jens@0
    78
jens@0
    79
jens@0
    80
#pragma mark -
jens@0
    81
#pragma mark EXCEPTION UTILITIES:
jens@0
    82
jens@0
    83
@interface NSException (MooseyardUtil)
jens@0
    84
/** Returns a textual, human-readable backtrace of the point where the exception was thrown. */
jens@0
    85
- (NSString*) my_callStack;
jens@0
    86
@end
jens@0
    87
jens@0
    88
jens@0
    89
jens@0
    90
// Nasty internals ...
jens@0
    91
#if DEBUG
jens@0
    92
void _RunTestCase( void (*testptr)(), const char *name );
jens@0
    93
jens@0
    94
struct TestCaseLink {void (*testptr)(); const char *name; BOOL passed; struct TestCaseLink *next;};
jens@0
    95
extern struct TestCaseLink *gAllTestCases;
jens@0
    96
#endif DEBUG
jens@0
    97
void _AssertFailed( id rcvr, const char *selOrFn, const char *sourceFile, int sourceLine,
jens@0
    98
                   const char *condString, NSString *message, ... ) __attribute__((noreturn));