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));
|