5 // Created by Wincent Colaiuta on 12 October 2004.
7 // Copyright 2004-2007 Wincent Colaiuta.
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #import "WOTestClass.h"
26 #import <objc/objc-runtime.h>
27 #import <sys/types.h> /* write() */
28 #import <sys/uio.h> /* write() */
29 #import <unistd.h> /* write(), _exit() */
35 #import "exc.h" /* generated by MiG */
37 // make what(1) produce meaningful output
38 #import "WOTest_Version.h"
40 #define WO_UNICODE_PLUS_MINUS_SIGN 0x00b1
41 #define WO_UNICODE_ELLIPSIS 0x2026
43 // convenience macros, call method to truncate description to 64 characters
44 #define WO_TRUNCATE_INDEX 64
46 // return truncated description of \p object, expects a BOOL variable of the format objectTruncated to be defined within the same scope
47 #define WO_DESC(object) [self description:object truncatedAt:WO_TRUNCATE_INDEX didTruncate:&object ## Truncated]
49 // return untruncated description of object
50 #define WO_LONG_DESC(object) [self description:object truncatedAt:0 didTruncate:NULL]
52 #define WO_UNCAUGHT_EXCEPTION_ERROR @"uncaught exception"
54 // convenience macro to throw exception when NSString type checking fails
55 #define WO_EXPECTED_STRING_EXCEPTION_REASON(object) \
56 [NSString stringWithFormat:@"Expected NSString object but got object of class \"%@\"", NSStringFromClass([object class])]
58 // convenience macro to throw exception when NSArray type checking fails
59 #define WO_EXPECTED_ARRAY_EXCEPTION_REASON(object) \
60 [NSString stringWithFormat:@"Expected NSArray object but got object of class \"%@\"", NSStringFromClass([object class])]
62 // convenience macro to throw exception when NSDictionary type check fails
63 #define WO_EXPECTED_DICTIONARY_EXCEPTION_REASON(object) \
64 [NSString stringWithFormat:@"Expected NSDictionary object but got object of class \"%@\"", NSStringFromClass([object class])]
66 #define WO_NIL_PARAMETER_EXCEPTION_REASON @"A test which does not accept nil parameters was passed a nil parameter"
68 // Return a random offset between 0 and WO_RANDOMIZATION_RANGE inclusive.
69 #define WO_RANDOM_OFFSET (random() % (WO_RANDOMIZATION_RANGE - 1))
71 // Return +1 or -1 randomly.
72 #define WO_RANDOM_SIGN ((BOOL)(random() % 2) ? 1 : -1)
75 #pragma mark Class variables
77 static WOTest *WOTestSharedInstance = nil;
78 static volatile BOOL WOTestCanJump = NO;
79 static volatile ExceptionKind WOLastLowLevelException;
80 static volatile sig_atomic_t WOTestExceptionTriggered = 0;
81 static ExceptionHandlerUPP WOOldLowLevelExceptionHandler;
83 // for the timebeing, only store/restore a limited number of registers; may remove the unused ones from the struct at a later time
84 typedef struct WOJumpBuffer {
85 unsigned long eax; // store/restore
86 unsigned long ebx; // store/restore
89 unsigned long edi; // store/restore
90 unsigned long esi; // store/restore
91 unsigned long ebp; // store/restore
92 unsigned long esp; // store/restore
101 static volatile WOJumpBuffer WOLowLevelExceptionJumpBuffer;
104 static unsigned long WOProgramCounter;
105 #elif defined (__ppc__)
106 static UnsignedWide WOProgramCounter;
108 #error Unsupported architecture
112 #pragma mark Functions
114 OSStatus WOLowLevelExceptionHandler(ExceptionInformation *theException)
116 if (!WOTestCanJump) // unexpected exception
118 fprintf(stderr, "error: WOTest internal error (unexpected exception in WOLowLevelExceptionHandler)\n");
119 fprintf(stderr, "Exception type: %lu\n", (unsigned long)(theException->theKind));
122 // forwarding to old exception handler doesn't seem to work (get into infinite loop)
123 //return InvokeExceptionHandlerUPP(theException, WOOldLowLevelExceptionHandler);
127 WOLastLowLevelException = theException->theKind;
130 // set flag to indicate that an exception was triggerd
131 WOTestExceptionTriggered = 1;
133 // will resume execution at previously marked "safe place": longjmp would be fine here
135 // set only the registers that setjmp() saves and longjmp() restores
136 theException->machineState->EIP = WOProgramCounter;
137 theException->registerImage->EBP = WOLowLevelExceptionJumpBuffer.ebp;
138 theException->registerImage->EAX = WOLowLevelExceptionJumpBuffer.eax;
139 theException->registerImage->EBX = WOLowLevelExceptionJumpBuffer.ebx;
140 theException->registerImage->EDI = WOLowLevelExceptionJumpBuffer.edi;
141 theException->registerImage->ESI = WOLowLevelExceptionJumpBuffer.esi;
142 theException->registerImage->ESP = WOLowLevelExceptionJumpBuffer.esp;
144 // clear out exception state (probably not necessary)
145 theException->info.memoryInfo = NULL;
147 #elif defined (__ppc__)
148 theException->machineState->PC = WOProgramCounter;
149 // TODO: must restore more state here
151 #error Unsupported architecture
158 - (void)installLowLevelExceptionHandler;
159 - (void)removeLowLevelExceptionHandler;
161 /*! Check to see that the start date has been recorded. If it has not, record it. */
162 - (void)checkStartDate;
164 /*! Helper method for optionally trimming path names before printing them to the console. */
165 - (NSString *)trimmedPath:(char *)path;
168 #pragma mark Properties
171 //! Public properties previously declared readonly have a private readwrite implementation internally to the class.
174 // BUG: Objective-C 2.0 bug; the documentation would suggest that redeclaring all the attributes is not necessary
175 // "You can re-declare properties in a subclass, and you can repeat properties' attributes in whole or in part"
176 // "The same holds true for properties declared in a category"
177 // "the property's attributes must only be repeated in whole or part"
179 // no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed
180 // furthermore, it appears that the readwrite override does not actually cause a setter to be synthesized further down
181 // despite what the docs say:
182 // "In the case of a category redeclaration, that the property was redeclared prior any @synthesize statement will cause the setter to be synthesized"
183 // we get unrecognized selector exceptions:
184 // *** -[WOTest setTestsFailedExpected:]: unrecognized selector sent to instance 0x102eee0)
185 // so for now must abandon the pattern
186 //@property(readwrite, assign) NSDate *startDate;
187 //@property(readwrite) unsigned testsRun;
188 //@property(readwrite) unsigned testsPassed;
189 //@property(readwrite) unsigned testsFailed;
190 //@property(readwrite) unsigned uncaughtExceptions;
191 //@property(readwrite) unsigned testsFailedExpected;
192 //@property(readwrite) unsigned testsPassedUnexpected;
193 //@property(readwrite) unsigned lowLevelExceptionsExpected;
194 //@property(readwrite) unsigned lowLevelExceptionsUnexpected;
196 // BUG: as above (shouldn't have to explicitly re-declare "assign" here)
197 //@property(readwrite, assign) NSString *lastReportedFile;
198 //@property(readwrite) int lastReportedLine;
204 @implementation WOTest
207 #pragma mark Singleton pattern enforcement methods
209 + (WOTest *)sharedInstance;
211 // speed less of a concern here than robustness so always lock (instead of using double-checked locking plus memory barriers)
212 volatile id instance = nil;
213 @synchronized (WOTestSharedInstance)
215 if (WOTestSharedInstance)
216 instance = WOTestSharedInstance;
218 instance = [[self alloc] init];
225 @synchronized (WOTestSharedInstance)
227 if (!WOTestSharedInstance) // first time here
229 if ((self = [super init]))
230 // once-off initialization and setting of defaults:
231 self->warnsAboutSignComparisons = YES;
232 WOTestSharedInstance = self;
235 NSDeallocateObject(self); // were racing, but lost the race
237 return WOTestSharedInstance;
240 // overriding allocWithZone also effectively overrides alloc
241 + (id)allocWithZone:(NSZone *)aZone
243 volatile id instance = nil;
244 @synchronized (WOTestSharedInstance)
246 if (WOTestSharedInstance)
247 instance = WOTestSharedInstance;
249 instance = NSAllocateObject([self class], 0, aZone);
254 - (oneway void)release
261 [self doesNotRecognizeSelector:_cmd]; // being a singleton, officially does not support dealloc
262 [super dealloc]; // this line necessary to suppress compiler warning
265 - (unsigned)retainCount
280 // overriding this also overrides copy
281 - (id)copyWithZone:(NSZone *)zone
286 // overriding this also overrides mutableCopy
287 - (id)mutableCopyWithZone:(NSZone *)zone
293 #pragma mark Utility methods
295 - (NSString *)description:(id)anObject truncatedAt:(unsigned)index didTruncate:(BOOL *)didTruncate
297 if (didTruncate) *didTruncate = NO;
298 NSString *description = nil;
300 description = @"(nil)";
305 description = [NSObject WOTest_descriptionForObject:anObject];
306 unsigned int originalLength = [description length];
307 if (index > 0) // a value of 0 would indicate that no truncation is to be performed
309 description = [description WOTest_stringByCollapsingWhitespace];
310 if ([description length] > index)
311 description = [[description substringToIndex:index] WOTest_stringByAppendingCharacter:WO_UNICODE_ELLIPSIS];
312 if (([description length] != originalLength) && (didTruncate))
318 description = @"(exception caught trying to get object description)";
324 - (void)seedRandomNumberGenerator
329 - (void)seedRandomNumberGenerator:(unsigned long)seed
334 - (BOOL)isClassMethod:(NSString *)method
336 return (method && [method hasPrefix:@"+"]);
339 - (BOOL)isInstanceMethod:(NSString *)method
341 return (method && [method hasPrefix:@"-"]);
344 - (SEL)selectorFromMethod:(NSString *)method
346 NSParameterAssert(method != nil);
347 NSParameterAssert([method length] > 1);
348 NSString *selectorName = [method substringFromIndex:1];
349 return NSSelectorFromString(selectorName);
353 #pragma mark Test-running methods
355 - (void)checkStartDate
359 if (self.startDate == nil)
360 self.startDate = [NSDate date];
367 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
368 for (NSString *class in [self testableClasses])
369 [self runTestsForClassName:class] ? : failures++;
370 [self printTestResultsSummary];
372 return (failures > 0) ? NO : YES;
375 - (BOOL)runTestsForClassName:(NSString *)className
377 NSParameterAssert(className != nil);
378 return [self runTestsForClass:NSClassFromString(className)];
381 // all other test-running methods ultimately get funnelled through this method
382 - (BOOL)runTestsForClass:(Class)aClass
384 NSParameterAssert(aClass != nil);
385 [self checkStartDate];
386 BOOL noTestFailed = YES;
387 NSDate *startClass = [NSDate date];
390 _WOLog(@"Running tests for class %@", NSStringFromClass(aClass));
391 if ([NSObject WOTest_instancesOfClass:aClass conformToProtocol:@protocol(WOTest)])
393 for (NSString *method in [self testableMethodsFrom:aClass])
395 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
396 NSDate *startMethod = [NSDate date];
397 SEL preflight = @selector(preflight);
398 SEL postflight = @selector(postflight);
400 _WOLog(@"Running test method %@", method);
403 // minimize time spent with exception handlers in place
404 [self installLowLevelExceptionHandler];
406 // record program counter and some other registers right now
408 // LowLevelABI.pdf says "EDI, ESI, EBX, EBP" are the preserved registers (across function calls)
409 // ebp is the "saved frame pointer": "the base address of the caller's stack frame"
410 // eax is used to return pointer and integral results to callers: "The called function places integral or pointer results in EAX"
412 // info on inline assembly: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
413 __asm__ volatile("movl %%eax, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.eax));
414 __asm__ volatile("movl %%ebx, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.ebx));
415 __asm__ volatile("movl %%edi, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.edi));
416 __asm__ volatile("movl %%esi, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.esi));
417 __asm__ volatile("movl %%esp, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.esp));
418 __asm__ volatile("movl %%ebp, %0\n" : "=m" (WOLowLevelExceptionJumpBuffer.ebp));
420 // done this way in Linux (see acpi_save_register_state function)
421 WOProgramCounter = (unsigned long)&&jump_point;
422 #elif defined (__ppc__)
423 // equivalent to (psuedo code) "WOProgramCounter = current contents of PC register"
424 unsigned long counter;
425 __asm__ volatile("mflr %0" : "=r" (counter));
426 WOProgramCounter.lo = counter & 0xffffffff;
427 WOProgramCounter.hi = (counter & 0xffffffff00000000) >> 32;
429 #error Unsupported architecture
433 goto jump_point; // necessary to silence compiler warning about unused label
435 // if flag set, that means we crashed: throw an exception
436 if (WOTestExceptionTriggered)
438 WOTestExceptionTriggered = 0;
439 @throw [WOTestLowLevelException exceptionWithType:WOLastLowLevelException];
442 if ([self isClassMethod:method])
444 if ([NSObject WOTest_class:aClass respondsToSelector:preflight])
445 objc_msgSend(aClass, preflight);
446 objc_msgSend(aClass, [self selectorFromMethod:method]);
447 if ([NSObject WOTest_class:aClass respondsToSelector:postflight])
448 objc_msgSend(aClass, postflight);
450 else if ([self isInstanceMethod:method])
452 // class must implement alloc, init and release
453 if ([NSObject WOTest_object:aClass respondsToSelector:@selector(alloc)] &&
454 [NSObject WOTest_instancesOfClass:aClass respondToSelector:@selector(init)] &&
455 [NSObject WOTest_instancesOfClass:aClass respondToSelector:@selector(release)])
457 id instance = [[aClass alloc] init];
458 if ([NSObject WOTest_instancesOfClass:aClass respondToSelector:preflight])
459 objc_msgSend(instance, preflight);
460 objc_msgSend(instance, [self selectorFromMethod:method]);
461 if ([NSObject WOTest_instancesOfClass:aClass respondToSelector:postflight])
462 objc_msgSend(instance, postflight);
467 [self writeError:@"Class %@ must respond to the alloc, init and release selectors",
468 NSStringFromClass(aClass)];
469 [self writeLastKnownLocation];
472 else // should never get here
473 [self writeError:@"WOTest internal error"];
475 @catch (WOTestLowLevelException *lowLevelException)
477 if (self.expectLowLevelExceptions)
479 [self writeStatus:[lowLevelException reason]]; // expected low-level exceptions are not an error
480 self.lowLevelExceptionsExpected++;
484 [self writeError:[lowLevelException reason]]; // unexpected low-level exceptions are an error
485 [self writeLastKnownLocation];
487 self.lowLevelExceptionsUnexpected++;
492 [self writeError:@"uncaught exception (%@) in test method %@", [NSException WOTest_descriptionForException:e],
494 [self writeLastKnownLocation];
496 self.uncaughtExceptions++;
500 if (lowLevelExceptionHandlerInstalled)
501 [self removeLowLevelExceptionHandler];
502 _WOLog(@"Finished test method %@ (%.4f seconds)", method, -[startMethod timeIntervalSinceNow]);
510 [self writeError:@"uncaught exception (%@) testing class %@", [NSException WOTest_descriptionForException:e],
511 NSStringFromClass(aClass)];
512 [self writeLastKnownLocation];
514 self.uncaughtExceptions++;
518 _WOLog(@"Finished tests for class %@ (%.4f seconds)", NSStringFromClass(aClass), -[startClass timeIntervalSinceNow]);
523 - (NSArray *)testableClasses
525 // return an array of class names
526 NSMutableArray *testableClasses = [NSMutableArray array];
528 unsigned classCount = 0; // the total number of classes
529 unsigned conformingClassCount = 0; // classes conforming to WOTest
530 unsigned nonconformingClassCount = 0; // unconforming classes
531 unsigned excludedClassCount = 0; // excluded classes
532 unsigned exceptionCount = 0; // classes provoking exceptions
535 int newNumClasses = objc_getClassList(NULL, 0);
536 Class *classes = NULL;
538 // get a list of all classes on the system
539 while (numClasses < newNumClasses)
541 numClasses = newNumClasses;
542 size_t bufferSize = sizeof(Class) * numClasses;
543 classes = realloc(classes, bufferSize);
544 NSAssert1((classes != NULL), @"realloc() failed (size %d)", bufferSize);
545 newNumClasses = objc_getClassList(classes, numClasses);
552 // skip over some classes because they not only cause exceptions but also spew out ugly console messages
553 SInt32 systemVersion;
554 Gestalt(gestaltSystemVersion, &systemVersion);
555 systemVersion = systemVersion & 0x0000ffff; // Apple instructs to ignore the high-order word
557 NSArray *excludedClasses = (systemVersion < 0x00001040) ?
558 [NSArray arrayWithObjects: @"Protocol", @"List", @"Object", @"_NSZombie", nil] : // 10.3
559 [NSArray arrayWithObjects: @"Protocol", @"List", @"Object", @"_NSZombie", @"NSATSGlyphGenerator", nil]; // 10.4
561 if (self.verbosity > 1)
562 _WOLog(@"Examining classes for WOTest protocol compliance");
564 for (int i = 0; i < newNumClasses; i++)
568 Class aClass = classes[i];
569 NSString *className = NSStringFromClass(aClass);
573 if ([excludedClasses containsObject:className])
575 excludedClassCount++;
576 if (self.verbosity > 1)
577 _WOLog(@"Skipping class %@ (appears in exclusion list)", className);
579 else if ([NSObject WOTest_instancesOfClass:aClass conformToProtocol:@protocol(WOTest)])
581 conformingClassCount++;
582 [testableClasses addObject:className];
583 if (self.verbosity > 0)
584 _WOLog(@"Class %@ complies with the WOTest protocol", className);
588 nonconformingClassCount++;
589 if (self.verbosity > 1)
590 _WOLog(@"Class %@ does not comply with the WOTest protocol", className);
593 @catch (id exception)
596 // a number of classes are known to provoke exceptions:
597 if (self.verbosity > 1)
598 _WOLog(@"Cannot test protocol compliance for class %@ (caught exception)", className);
608 _WOLog(@"Uncaught exception...");
611 _WOLog(@"Runtime Summary:\n"
612 @"Total classes: %d\n"
613 @"Classes which conform to the WOTest protocol: %d\n"
614 @"Classes which do not conform to the protocol: %d\n"
615 @"Classes excluded from scanning: %d\n"
616 @"Classes that could not be scanned due to exceptions: %d",
618 conformingClassCount,
619 nonconformingClassCount,
623 return [testableClasses sortedArrayUsingSelector:@selector(compare:)];
626 - (NSArray *)testableClassesFrom:(NSBundle *)aBundle
628 NSMutableArray *classNames = [NSMutableArray array];
630 if (aBundle) // only search if actually passed a non-nil bundle
632 // add only classes that match the passed bundle and conform to WOTest
633 for (NSString *className in [self testableClasses])
635 Class aClass = NSClassFromString(className);
636 NSBundle *classBundle = [NSBundle bundleForClass:aClass];
637 if ([classBundle isEqualTo:aBundle])
638 [classNames addObject:className];
642 // return autoreleased, immutable NSArray
643 return [NSArray arrayWithArray:classNames];
646 - (NSArray *)testableMethodsFrom:(Class)aClass
648 // catch crashes caused by passing an "id" instead of a "Class"
649 NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass] || [NSObject WOTest_isMetaClass:aClass]);
651 NSMutableArray *methodNames = [NSMutableArray array];
654 NSString *prefix = @"-"; // default prefix (instance methods)
655 if (class_isMetaClass(aClass))
656 prefix = @"+"; // special prefix (class methods)
657 else // this is not a metaclass
659 // get the metaclass; could also use object_getClass
660 Class metaClass = object_getClass(aClass);
661 NSArray *classMethods = [self testableMethodsFrom:metaClass];
662 [methodNames addObjectsFromArray:classMethods];
666 Method *methods = class_copyMethodList(aClass, &count);
669 for (unsigned int i = 0, max = count; i < max; i++)
671 SEL aSelector = method_getName(methods[i]);
672 NSString *name = NSStringFromSelector(aSelector);
673 if (name && [name hasPrefix:@"test"])
674 [methodNames addObject:[NSString stringWithFormat:@"%@%@", prefix, name]];
681 NSString *error = [NSString stringWithFormat:@"exception caught trying to identify testable methods in class %@",
682 NSStringFromClass(aClass)];
683 [self writeError:error];
686 return [methodNames sortedArrayUsingSelector:@selector(compare:)];
689 - (void)printTestResultsSummary;
691 [self checkStartDate]; // just in case no tests were run, make sure that startDate is non-nil
692 double successRate = 0.0;
693 double failureRate = 0.0;
694 if (self.testsRun > 0) // watch out for divide-by-zero if no tests run
696 successRate = ((double)(self.testsPassed + self.testsFailedExpected) / (double)self.testsRun) * 100.0;
697 failureRate = ((double)(self.testsFailed + self.testsPassedUnexpected) / (double)self.testsRun) * 100.0;
699 _WOLog(@"Run summary:\n"
701 @"Tests passed: %d + %d expected failures (%.2f%% success rate)\n"
702 @"Tests failed: %d + %d unexpected passes (%.2f%% failure rate)\n"
703 @"Uncaught exceptions: %d\n"
704 @"Low-level exceptions (crashers): %d + %d expected\n"
705 @"Total run time: %.2f seconds\n",
707 self.testsPassed, self.testsFailedExpected, successRate,
708 self.testsFailed, self.testsPassedUnexpected, failureRate,
709 self.uncaughtExceptions,
710 self.lowLevelExceptionsUnexpected, self.lowLevelExceptionsExpected,
711 -[self.startDate timeIntervalSinceNow]);
713 if (self.testsRun == 0)
714 _WOLog(@"warning: no tests were run\n");
716 // TODO: make Growl notifications optional
717 // TODO: include information about project being tested in Growl notification title
718 // TODO: add options for showing coalesced growl notifications showing individual test failures (with path and line info)
719 // TODO: make clicking on notification bring Xcode to the front, or open the file with the last failure in it etc
720 NSString *status = [NSString stringWithFormat:@"%d tests passed, %d tests failed",
721 self.testsPassed + self.testsFailedExpected, self.testsFailed + self.testsPassedUnexpected];
723 if ([self testsWereSuccessful])
724 [self growlNotifyTitle:@"WOTest run successful" message:status isWarning:NO sticky:NO];
727 _WOLog(@"error: testing did not complete without errors\n");
728 [self growlNotifyTitle:@"WOTest run failed" message:status isWarning:YES sticky:YES];
733 self.startDate = nil;
736 - (BOOL)testsWereSuccessful
738 return ((self.testsFailed + self.testsPassedUnexpected + self.uncaughtExceptions + self.lowLevelExceptionsUnexpected) == 0);
742 #pragma mark Low-level exception handling
744 - (void)installLowLevelExceptionHandler
746 if (!lowLevelExceptionHandlerInstalled)
748 WOOldLowLevelExceptionHandler = InstallExceptionHandler(NewExceptionHandlerUPP(WOLowLevelExceptionHandler));
749 lowLevelExceptionHandlerInstalled = YES;
753 - (void)removeLowLevelExceptionHandler
755 if (lowLevelExceptionHandlerInstalled)
757 DisposeExceptionHandlerUPP(InstallExceptionHandler(WOOldLowLevelExceptionHandler));
758 lowLevelExceptionHandlerInstalled = NO;
763 #pragma mark Growl support
765 - (void)growlNotifyTitle:(NSString *)title message:(NSString *)message isWarning:(BOOL)isWarning sticky:(BOOL)sticky
767 NSParameterAssert(title != nil);
768 NSParameterAssert(message != nil);
770 // clean up enviroment a bit (hides possible warnings caused if these set for WOTestRunner)
771 NSMutableDictionary *environment = [NSMutableDictionary dictionaryWithDictionary:[[NSProcessInfo processInfo] environment]];
772 [environment removeObjectForKey:@"DYLD_INSERT_LIBRARIES"];
773 [environment removeObjectForKey:@"WOTestBundleInjector"];
775 NSTask *task = [[[NSTask alloc] init] autorelease];
776 [task setLaunchPath:@"/usr/bin/env"]; // use env so as to pick up PATH, if set
777 [task setEnvironment:environment];
778 NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"growlnotify",
779 @"--name", @"com.wincent.WOTest",
780 @"--appIcon", @"Xcode",
781 @"--priority", (isWarning ? @"2" : @"0"),
782 @"--message", message,
784 if (sticky) [arguments insertObject:@"--sticky" atIndex:0];
785 [task setArguments:arguments];
787 // if env can't find growl it will write a message like "env: growlnotify: No such file or directory" to the standard error
788 // suppress it by redirecting the standard error to /dev/null
789 [task setStandardError:[NSFileHandle fileHandleForWritingAtPath:@"/dev/null"]];
794 [task waitUntilExit];
796 // handle error conditions
797 if (![task isRunning])
799 int status = [task terminationStatus];
800 if (status == 127) // env returns this when "[t]he utility specified by utility could not be found"
801 _WOLog(@"note: growlnotify not launched (not found in the current PATH)");
802 else if (status != EXIT_SUCCESS)
803 // a failure to run growlnotify is relatively harmless, so use warning rather than error
804 _WOLog(@"warning: env terminated with exit status %d while trying to run growlnotify", status);
807 @catch (NSException *e)
809 // highly unlikely that we'd ever get here, but report it anyway
810 _WOLog(@"warning: exception caught while trying to execute growlnotify using env (%@: %@)", [e name], [e reason]);
815 #pragma mark Logging methods
817 - (NSString *)trimmedPath:(char *)path
819 NSParameterAssert(path != NULL);
820 NSString *pathString = [NSString stringWithUTF8String:path];
822 unsigned trim = self.trimInitialPathComponents;
823 if (trim == 0) return pathString;
824 if (![pathString isAbsolutePath]) return pathString; // only trim absolute paths
825 NSArray *components = [pathString pathComponents]; // note: Cocoa returns "/" here as an additional first component
826 NSAssert(components != nil, @"components != nil");
827 unsigned count = [components count];
828 if (count < trim + 2) return pathString; // only trim if there will be at least one component left over
829 return [NSString pathWithComponents:[components subarrayWithRange:NSMakeRange(trim + 1, count - trim - 1)]];
832 - (void)writePassed:(BOOL)passed inFile:(char *)path atLine:(int)line message:(NSString *)message, ...
836 va_start(args, message);
837 NSString *string = [NSString WOTest_stringWithFormat:message arguments:args];
839 if (self.expectFailures) // invert sense of tests (ie. failure is good)
841 if (passed) // passed: bad
843 [self writeErrorInFile:path atLine:line message:[NSString stringWithFormat:@"Passed (unexpected pass): %@", string]];
844 self.testsPassedUnexpected++;
848 [self writeStatusInFile:path atLine:line message:[NSString stringWithFormat:@"Failed (expected failure): %@", string]];
849 self.testsFailedExpected++;
852 else // normal handling (ie. passing is good, failing is bad)
854 if (passed) // passed: good
856 [self writeStatusInFile:path atLine:line message:[NSString stringWithFormat:@"Passed: %@", string]];
861 [self writeErrorInFile:path atLine:line message:[NSString stringWithFormat:@"Failed: %@", string]];
867 - (void)cacheFile:(char *)path line:(int)line
869 self.lastReportedFile = [self trimmedPath:path];
870 self.lastReportedLine = line;
873 - (void)writeLastKnownLocation
875 NSString *path = self.lastReportedFile;
877 _WOLog(@"%@:%d: last known location was %@:%d", path, self.lastReportedLine, path, self.lastReportedLine);
880 - (void)writeErrorInFile:(char *)path atLine:(int)line message:(NSString *)message, ...
883 va_start(args, message);
884 NSString *error = [NSString WOTest_stringWithFormat:message arguments:args];
885 _WOLog(@"%@:%d: error: %@", [self trimmedPath:path], line, error);
886 [self cacheFile:path line:line];
890 - (void)writeWarningInFile:(char *)path atLine:(int)line message:(NSString *)message, ...
893 va_start(args, message);
894 NSString *warning = [NSString WOTest_stringWithFormat:message arguments:args];
895 _WOLog(@"%@:%d: warning: %@", [self trimmedPath:path], line, warning);
896 [self cacheFile:path line:line];
900 - (void)writeUncaughtException:(NSString *)info inFile:(char *)path atLine:(int)line
902 _WOLog(@"%@:%d: error: uncaught exception during test execution: %@", [self trimmedPath:path], line, info);
903 self.uncaughtExceptions++;
906 - (void)writeStatusInFile:(char *)path atLine:(int)line message:(NSString *)message, ...
909 va_start(args, message);
910 NSString *status = [NSString WOTest_stringWithFormat:message arguments:args];
911 _WOLog(@"%@:%d %@", [self trimmedPath:path], line, status); // omit colin after line number or Xcode will show this as an error
912 [self cacheFile:path line:line];
916 - (void)writeStatus:(NSString *)message, ...
919 va_start(args, message);
920 NSString *status = [NSString WOTest_stringWithFormat:message arguments:args];
921 _WOLog(@"%@", status);
925 - (void)writeWarning:(NSString *)message, ...
928 va_start(args, message);
929 NSString *warning = [NSString WOTest_stringWithFormat:message arguments:args];
930 _WOLog(@"warning: %@", warning); // older versions of Xcode required initial colons "::" to show this as a warning
934 - (void)writeError:(NSString *)message, ...
937 va_start(args, message);
938 NSString *error = [NSString WOTest_stringWithFormat:message arguments:args];
939 _WOLog(@"error: %@", error); // older versions of Xcode required initial colons "::" to show this as an error
944 #pragma mark Empty (do-nothing) test methods
946 - (void)passTestInFile:(char *)path atLine:(int)line
948 [self writePassed:YES inFile:path atLine:line message:@"(always passes)"];
951 - (void)failTestInFile:(char *)path atLine:(int)line
953 [self writePassed:NO inFile:path atLine:line message:@"(always fails)"];
957 #pragma mark Boolean test methods
959 - (void)testTrue:(BOOL)expr inFile:(char *)path atLine:(int)line
961 [self writePassed:expr inFile:path atLine:line message:@"expected YES, got %@", (expr ? @"YES" : @"NO")];
964 - (void)testFalse:(BOOL)expr inFile:(char *)path atLine:(int)line
966 [self writePassed:!expr inFile:path atLine:line message:@"expected NO, got %@", (expr ? @"YES" : @"NO")];
970 #pragma mark NSValue-based tests
972 - (void)testValue:(NSValue *)actual isEqualTo:(NSValue *)expected inFile:(char *)path atLine:(int)line
974 NSParameterAssert(actual);
975 NSParameterAssert(expected);
978 // NSValue category will throw an exception for invalid input(s)
980 equal = [actual WOTest_testIsEqualToValue:expected];
983 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
984 self.uncaughtExceptions++;
986 BOOL expectedTruncated, actualTruncated;
987 [self writePassed:equal inFile:path atLine:line message:@"expected %@, got %@", WO_DESC(expected), WO_DESC(actual)];
988 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
989 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
992 - (void)testValue:(NSValue *)actual isNotEqualTo:(NSValue *)expected inFile:(char *)path atLine:(int)line
994 NSParameterAssert(actual);
995 NSParameterAssert(expected);
998 // NSValue category will throw an exception for invalid input(s)
1000 equal = [actual WOTest_testIsEqualToValue:expected];
1003 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
1004 self.uncaughtExceptions++;
1006 BOOL expectedTruncated, actualTruncated;
1007 [self writePassed:(!equal) inFile:path atLine:line message:@"expected (not) %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1008 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1009 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1012 - (void)testValue:(NSValue *)actual greaterThan:(NSValue *)expected inFile:(char *)path atLine:(int)line
1014 NSParameterAssert(actual);
1015 NSParameterAssert(expected);
1016 BOOL greaterThan = NO;
1018 // NSValue category will throw an exception for invalid input(s)
1020 greaterThan = [actual WOTest_testIsGreaterThanValue:expected];
1023 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
1024 self.uncaughtExceptions++;
1026 BOOL expectedTruncated, actualTruncated;
1027 [self writePassed:greaterThan inFile:path atLine:line message:@"expected > %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1028 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1029 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1032 - (void)testValue:(NSValue *)actual notGreaterThan:(NSValue *)expected inFile:(char *)path atLine:(int)line
1034 NSParameterAssert(actual);
1035 NSParameterAssert(expected);
1036 BOOL notGreaterThan = NO;
1038 // NSValue category will throw an exception for invalid input(s)
1040 notGreaterThan = [actual WOTest_testIsNotGreaterThanValue:expected];
1043 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
1044 self.uncaughtExceptions++;
1046 BOOL expectedTruncated, actualTruncated;
1047 [self writePassed:notGreaterThan inFile:path atLine:line message:@"expected <= %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1048 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1049 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1052 - (void)testValue:(NSValue *)actual lessThan:(NSValue *)expected inFile:(char *)path atLine:(int)line
1054 NSParameterAssert(actual);
1055 NSParameterAssert(expected);
1058 // NSValue category will throw an exception for invalid input(s)
1060 lessThan = [actual WOTest_testIsLessThanValue:expected];
1063 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
1064 self.uncaughtExceptions++;
1066 BOOL expectedTruncated, actualTruncated;
1067 [self writePassed:lessThan inFile:path atLine:line message:@"expected < %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1068 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1069 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1072 - (void)testValue:(NSValue *)actual notLessThan:(NSValue *)expected inFile:(char *)path atLine:(int)line
1074 NSParameterAssert(actual);
1075 NSParameterAssert(expected);
1076 BOOL notLessThan = NO;
1078 // NSValue category will throw an exception for invalid input(s)
1080 notLessThan = [actual WOTest_testIsNotLessThanValue:expected];
1083 [self writeErrorInFile:path atLine:line message:@"uncaught exception (%@)", [NSException WOTest_descriptionForException:e]];
1084 self.uncaughtExceptions++;
1086 BOOL expectedTruncated, actualTruncated;
1087 [self writePassed:notLessThan inFile:path atLine:line message:@"expected >= %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1088 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1089 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1093 #pragma mark Pointer to void test methods
1095 - (void)testNil:(void *)pointer inFile:(char *)path atLine:(int)line
1097 BOOL result = (pointer ? NO : YES);
1098 [self writePassed:result inFile:path atLine:line message:@"expected nil, got %@",
1099 (result ? @"nil" : [NSString stringWithFormat:@"%x", pointer])];
1102 - (void)testNotNil:(void *)pointer inFile:(char *)path atLine:(int)line
1104 BOOL result = (pointer ? YES : NO);
1105 [self writePassed:result inFile:path atLine:line message:@"expected (not) nil, got %@",
1106 (result ? [NSString stringWithFormat:@"%x", pointer] : @"nil")];
1109 - (void)testPointer:(void *)actual isEqualTo:(void *)expected inFile:(char *)path atLine:(int)line
1111 BOOL result = (actual == expected);
1112 [self writePassed:result inFile:path atLine:line message:@"expected %x, got %x", expected, actual];
1115 - (void)testPointer:(void *)actual isNotEqualTo:(void *)expected inFile:(char *)path atLine:(int)line
1117 BOOL result = (actual != expected);
1118 [self writePassed:result inFile:path atLine:line message:@"expected (not) %x, got %x", expected, actual];
1122 #pragma mark int test methods
1124 - (void)testIsInt:(char *)type inFile:(char *)path atLine:(int)line
1126 BOOL result = (strcmp(type, "i") == 0);
1127 [self writePassed:result inFile:path atLine:line message:[NSString stringWithFormat: @"expected type \"i\", got \"%s\"", type]];
1130 - (void)testIsNotInt:(char *)type inFile:(char *)path atLine:(int)line
1132 BOOL result = (strcmp(type, "i") != 0);
1133 [self writePassed:result
1136 message:[NSString stringWithFormat:@"expected type (not) \"i\", got \"%s\"", type]];
1139 - (void)testIntPositive:(int)aInt inFile:(char *)path atLine:(int)line
1141 [self testInt:aInt greaterThan:(int)0 inFile:path atLine:line];
1144 - (void)testIntNegative:(int)aInt inFile:(char *)path atLine:(int)line
1146 [self testInt:aInt lessThan:(int)0 inFile:path atLine:line];
1149 - (void)testIntZero:(int)aInt inFile:(char *)path atLine:(int)line
1151 [self testInt:aInt isEqualTo:(int)0 inFile:path atLine:line];
1154 - (void)testIntNotZero:(int)aInt inFile:(char *)path atLine:(int)line
1156 [self testInt:aInt isNotEqualTo:(int)0 inFile:path atLine:line];
1159 - (void)testInt:(int)actual isEqualTo:(int)expected inFile:(char *)path atLine:(int)line
1161 BOOL result = (actual == expected);
1162 [self writePassed:result inFile:path atLine:line message:[NSString stringWithFormat:@"expected %d, got %d", expected, actual]];
1165 - (void)testInt:(int)actual isNotEqualTo:(int)expected inFile:(char *)path atLine:(int)line
1167 BOOL result = (actual != expected);
1168 [self writePassed:result
1171 message:[NSString stringWithFormat:@"expected (not) %d, got %d", expected, actual]];
1174 - (void)testInt:(int)actual greaterThan:(int)expected inFile:(char *)path atLine:(int)line
1176 BOOL result = (actual > expected);
1177 [self writePassed:result
1180 message:[NSString stringWithFormat:@"expected > %d, got %d", expected, actual]];
1183 - (void)testInt:(int)actual notGreaterThan:(int)expected inFile:(char *)path atLine:(int)line
1185 BOOL result = (actual <= expected);
1186 [self writePassed:result
1189 message:[NSString stringWithFormat:@"expected <= %d, got %d", expected, actual]];
1192 - (void)testInt:(int)actual lessThan:(int)expected inFile:(char *)path atLine:(int)line
1194 BOOL result = (actual < expected);
1195 [self writePassed:result
1198 message:[NSString stringWithFormat:@"expected < %d, got %d", expected, actual]];
1201 - (void)testInt:(int)actual notLessThan:(int)expected inFile:(char *)path atLine:(int)line
1203 BOOL result = (actual >= expected);
1204 [self writePassed:result
1207 message:[NSString stringWithFormat:@"expected >= %d, got %d", expected, actual]];
1211 #pragma mark unsigned test methods
1213 - (void)testIsUnsigned:(char *)type inFile:(char *)path atLine:(int)line
1215 BOOL result = (strcmp(type, "I") == 0);
1216 [self writePassed:result inFile:path atLine:line message:[NSString stringWithFormat: @"expected type \"I\", got \"%s\"", type]];
1219 - (void)testIsNotUnsigned:(char *)type inFile:(char *)path atLine:(int)line
1221 BOOL result = (strcmp(type, "I") != 0);
1222 [self writePassed:result
1225 message:[NSString stringWithFormat:@"expected type (not) \"I\", got \"%s\"", type]];
1228 - (void)testUnsignedZero:(unsigned)aUnsigned inFile:(char *)path atLine:(int)line
1230 return [self testUnsigned:aUnsigned isEqualTo:(unsigned)0 inFile:path atLine:line];
1233 - (void)testUnsignedNotZero:(unsigned)aUnsigned inFile:(char *)path atLine:(int)line
1235 return [self testUnsigned:aUnsigned isNotEqualTo:(unsigned)0 inFile:path atLine:line];
1238 - (void)testUnsigned:(unsigned)actual isEqualTo:(unsigned)expected inFile:(char *)path atLine:(int)line
1240 BOOL result = (actual == expected);
1241 [self writePassed:result
1244 message:[NSString stringWithFormat:@"expected %u, got %u", expected, actual]];
1247 - (void)testUnsigned:(unsigned)actual isNotEqualTo:(unsigned)expected inFile:(char *)path atLine:(int)line
1249 BOOL result = (actual != expected);
1250 [self writePassed:result
1253 message:[NSString stringWithFormat:@"expected (not) %u, got %u", expected, actual]];
1256 - (void)testUnsigned:(unsigned)actual greaterThan:(unsigned)expected inFile:(char *)path atLine:(int)line
1258 BOOL result = (actual > expected);
1259 [self writePassed:result
1262 message:[NSString stringWithFormat:@"expected > %u, got %u", expected, actual]];
1265 - (void)testUnsigned:(unsigned)actual notGreaterThan:(unsigned)expected inFile:(char *)path atLine:(int)line
1267 BOOL result = (actual <= expected);
1268 [self writePassed:result
1271 message:[NSString stringWithFormat:@"expected <= %u, got %u", expected, actual]];
1274 - (void)testUnsigned:(unsigned)actual lessThan:(unsigned)expected inFile:(char *)path atLine:(int)line
1276 BOOL result = (actual < expected);
1277 [self writePassed:result
1280 message:[NSString stringWithFormat:@"expected < %u, got %u", expected, actual]];
1283 - (void)testUnsigned:(unsigned)actual notLessThan:(unsigned)expected inFile:(char *)path atLine:(int)line
1285 BOOL result = (actual >= expected);
1286 [self writePassed:result
1289 message:[NSString stringWithFormat:@"expected >= %u, got %u", expected, actual]];
1293 #pragma mark float test methods without error margins
1295 - (void)testIsFloat:(char *)type inFile:(char *)path atLine:(int)line
1297 BOOL result = (strcmp(type, "f") == 0);
1298 [self writePassed:result inFile:path atLine:line message:[NSString stringWithFormat: @"expected type \"f\", got \"%s\"", type]];
1301 - (void)testIsNotFloat:(char *)type inFile:(char *)path atLine:(int)line
1303 BOOL result = (strcmp(type, "f") != 0);
1304 [self writePassed:result
1307 message:[NSString stringWithFormat:@"expected type (not) \"f\", got \"%s\"", type]];
1310 - (void)testFloatPositive:(float)aFloat inFile:(char *)path atLine:(int)line
1312 return [self testFloat:aFloat greaterThan:(float)0.0 withinError:(float)0.0 inFile:path atLine:line];
1315 - (void)testFloatNegative:(float)aFloat inFile:(char *)path atLine:(int)line
1317 return [self testFloat:aFloat lessThan:(float)0.0 withinError:(float)0.0 inFile:path atLine:line];
1320 - (void)testFloatZero:(float)aFloat inFile:(char *)path atLine:(int)line
1322 return [self testFloat:aFloat isEqualTo:(float)0.0 withinError:(float)0.0 inFile:path atLine:line];
1325 - (void)testFloatNotZero:(float)aFloat inFile:(char *)path atLine:(int)line
1327 return [self testFloat:aFloat isNotEqualTo:(float)0.0 withinError:(float)0.0 inFile:path atLine:line];
1330 - (void)testFloat:(float)actual isEqualTo:(float)expected inFile:(char *)path atLine:(int)line
1332 return [self testFloat:actual isEqualTo:expected withinError:(float)0.0 inFile:path atLine:line];
1335 - (void)testFloat:(float)actual isNotEqualTo:(float)expected inFile:(char *)path atLine:(int)line
1337 return [self testFloat:actual isNotEqualTo:expected withinError:(float)0.0 inFile:path atLine:line];
1340 - (void)testFloat:(float)actual greaterThan:(float)expected inFile:(char *)path atLine:(int)line
1342 return [self testFloat:actual greaterThan:expected withinError:(float)0.0 inFile:path atLine:line];
1345 - (void)testFloat:(float)actual notGreaterThan:(float)expected inFile:(char *)path atLine:(int)line
1347 return [self testFloat:actual notGreaterThan:expected withinError:(float)0.0 inFile:path atLine:line];
1350 - (void)testFloat:(float)actual lessThan:(float)expected inFile:(char *)path atLine:(int)line
1352 return [self testFloat:actual lessThan:expected withinError:(float)0.0 inFile:path atLine:line];
1355 - (void)testFloat:(float)actual notLessThan:(float)expected inFile:(char *)path atLine:(int)line
1357 return [self testFloat:actual notLessThan:expected withinError:(float)0.0 inFile:path atLine:line];
1361 #pragma mark float test methods with error margins
1363 - (void)testFloatPositive:(float)aFloat withinError:(float)error inFile:(char *)path atLine:(int)line
1365 return [self testFloat:aFloat greaterThan:(float)0.0 withinError:error inFile:path atLine:line];
1368 - (void)testFloatNegative:(float)aFloat withinError:(float)error inFile:(char *)path atLine:(int)line
1370 return [self testFloat:aFloat lessThan:(float)0.0 withinError:error inFile:path atLine:line];
1373 - (void)testFloatZero:(float)aFloat withinError:(float)error inFile:(char *)path atLine:(int)line
1375 return [self testFloat:aFloat isEqualTo:(float)0.0 withinError:error inFile:path atLine:line];
1378 - (void)testFloatNotZero:(float)aFloat withinError:(float)error inFile:(char *)path atLine:(int)line
1380 return [self testFloat:aFloat isNotEqualTo:(float)0.0 withinError:error inFile:path atLine:line];
1383 - (void)testFloat:(float)actual isEqualTo:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1385 BOOL result = (fabsf(actual - expected) <= error);
1386 [self writePassed:result
1390 [NSString stringWithFormat:@"expected %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1393 - (void)testFloat:(float)actual isNotEqualTo:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1395 BOOL result = (fabsf(actual - expected) > -error);
1396 [self writePassed:result
1400 [NSString stringWithFormat:@"expected (not) %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1403 - (void)testFloat:(float)actual greaterThan:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1405 BOOL result = ((actual - expected) > -error);
1406 [self writePassed:result
1410 [NSString stringWithFormat:@"expected > %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1413 - (void)testFloat:(float)actual notGreaterThan:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1415 BOOL result = ((actual - expected) <= error);
1416 [self writePassed:result
1420 [NSString stringWithFormat:@"expected <= %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1423 - (void)testFloat:(float)actual lessThan:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1425 BOOL result = ((actual - expected) < -error);
1426 [self writePassed:result
1430 [NSString stringWithFormat:@"expected < %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1433 - (void)testFloat:(float)actual notLessThan:(float)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1435 BOOL result = ((actual - expected) >= error);
1436 [self writePassed:result
1440 [NSString stringWithFormat:@"expected >= %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1444 #pragma mark double test methods without error margins
1446 - (void)testIsDouble:(char *)type inFile:(char *)path atLine:(int)line
1448 BOOL result = (strcmp(type, "d") == 0);
1449 [self writePassed:result inFile:path atLine:line message:[NSString stringWithFormat: @"expected type \"d\", got \"%s\"", type]];
1452 - (void)testIsNotDouble:(char *)type inFile:(char *)path atLine:(int)line
1454 BOOL result = (strcmp(type, "d") != 0);
1455 [self writePassed:result
1458 message:[NSString stringWithFormat:@"expected type (not) \"d\", got \"%s\"", type]];
1461 - (void)testDoublePositive:(double)aDouble inFile:(char *)path atLine:(int)line
1463 return [self testDouble:aDouble greaterThan:(double)0.0 withinError:(double)0.0 inFile:path atLine:line];
1466 - (void)testDoubleNegative:(double)aDouble inFile:(char *)path atLine:(int)line
1468 return [self testDouble:aDouble lessThan:(double)0.0 withinError:(double)0.0 inFile:path atLine:line];
1471 - (void)testDoubleZero:(double)aDouble inFile:(char *)path atLine:(int)line
1473 return [self testDouble:aDouble isEqualTo:(double)0.0 withinError:(double)0.0 inFile:path atLine:line];
1476 - (void)testDoubleNotZero:(double)aDouble inFile:(char *)path atLine:(int)line
1478 return [self testDouble:aDouble isNotEqualTo:(double)0.0 withinError:(double)0.0 inFile:path atLine:line];
1481 - (void)testDouble:(double)actual isEqualTo:(double)expected inFile:(char *)path atLine:(int)line
1483 return [self testDouble:actual isEqualTo:expected withinError:(double)0.0 inFile:path atLine:line];
1486 - (void)testDouble:(double)actual isNotEqualTo:(double)expected inFile:(char *)path atLine:(int)line
1488 return [self testDouble:actual isNotEqualTo:expected withinError:(double)0.0 inFile:path atLine:line];
1491 - (void)testDouble:(double)actual greaterThan:(double)expected inFile:(char *)path atLine:(int)line
1493 return [self testDouble:actual greaterThan:expected withinError:(double)0.0 inFile:path atLine:line];
1496 - (void)testDouble:(double)actual notGreaterThan:(double)expected inFile:(char *)path atLine:(int)line
1498 return [self testDouble:actual notGreaterThan:expected withinError:(double)0.0 inFile:path atLine:line];
1501 - (void)testDouble:(double)actual lessThan:(double)expected inFile:(char *)path atLine:(int)line
1503 return [self testDouble:actual lessThan:expected withinError:(double)0.0 inFile:path atLine:line];
1506 - (void)testDouble:(double)actual notLessThan:(double)expected inFile:(char *)path atLine:(int)line
1508 return [self testDouble:actual notLessThan:expected withinError:(double)0.0 inFile:path atLine:line];
1512 #pragma mark double test methods with error margins
1514 - (void)testDoublePositive:(double)aDouble withinError:(double)error inFile:(char *)path atLine:(int)line
1516 return [self testDouble:aDouble greaterThan:(double)0.0 withinError:error inFile:path atLine:line];
1519 - (void)testDoubleNegative:(double)aDouble withinError:(double)error inFile:(char *)path atLine:(int)line
1521 return [self testDouble:aDouble lessThan:(double)0.0 withinError:error inFile:path atLine:line];
1524 - (void)testDoubleZero:(double)aDouble withinError:(double)error inFile:(char *)path atLine:(int)line
1526 return [self testDouble:aDouble isEqualTo:(double)0.0 withinError:error inFile:path atLine:line];
1529 - (void)testDoubleNotZero:(double)aDouble withinError:(double)error inFile:(char *)path atLine:(int)line
1531 return [self testDouble:aDouble isNotEqualTo:(double)0.0 withinError:error inFile:path atLine:line];
1534 - (void)testDouble:(double)actual isEqualTo:(double)expected withinError:(double)error inFile:(char *)path atLine:(int)line
1536 BOOL result = (fabs(actual - expected) <= error);
1537 [self writePassed:result
1541 [NSString stringWithFormat:@"expected %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1544 - (void)testDouble:(double)actual isNotEqualTo:(double)expected withinError:(double)error inFile:(char *)path atLine:(int)line
1546 BOOL result = (fabs(actual - expected) > -error);
1547 [self writePassed:result
1551 [NSString stringWithFormat:@"expected (not) %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1554 - (void)testDouble:(double)actual greaterThan:(double)expected withinError:(double)error inFile:(char *)path atLine:(int)line
1556 BOOL result = ((actual - expected) > -error);
1557 [self writePassed:result
1561 [NSString stringWithFormat:@"expected > %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1564 - (void)testDouble:(double)actual notGreaterThan:(double)expected withinError:(double)error inFile:(char *)path atLine:(int)line
1566 BOOL result = ((actual - expected) <= error);
1567 [self writePassed:result
1571 [NSString stringWithFormat:@"expected <= %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1574 - (void)testDouble:(double)actual lessThan:(double)expected withinError:(double)error inFile:(char *)path atLine:(int)line
1576 BOOL result = ((actual - expected) < -error);
1577 [self writePassed:result
1581 [NSString stringWithFormat:@"expected < %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1584 - (void)testDouble:(double)actual notLessThan:(double)expected withinError:(float)error inFile:(char *)path atLine:(int)line
1586 BOOL result = ((actual - expected) >= error);
1587 [self writePassed:result
1591 [NSString stringWithFormat:@"expected >= %f (%C%f), got %f", expected, WO_UNICODE_PLUS_MINUS_SIGN, error, actual]];
1595 #pragma mark object test methods
1597 - (void)testObject:(id)actual isEqualTo:(id)expected inFile:(char *)path atLine:(int)line
1600 if (!actual && !expected) equal = YES; // equal (both nil)
1601 else if (actual) equal = [actual isEqual:expected];
1602 BOOL expectedTruncated, actualTruncated;
1603 [self writePassed:equal inFile:path atLine:line message:@"expected \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1604 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1605 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1608 - (void)testObject:(id)actual isNotEqualTo:(id)expected inFile:(char *)path atLine:(int)line
1611 if (!actual && !expected) equal = YES; // equal (both nil)
1612 else if (actual) equal = [actual isEqual:expected];
1613 BOOL expectedTruncated, actualTruncated;
1614 [self writePassed:(!equal)
1617 message:@"expected (not) \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1618 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1619 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1623 #pragma mark NSString test methods
1625 - (void)testString:(NSString *)actual isEqualTo:(NSString *)expected inFile:(char *)path atLine:(int)line
1627 if (actual && ![actual isKindOfClass:[NSString class]])
1628 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1629 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1632 if (expected && ![expected isKindOfClass:[NSString class]])
1633 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1634 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1638 if (!actual && !expected) equal = YES; // equal (both nil)
1639 else if (actual) equal = [actual isEqualToString:expected];
1640 BOOL expectedTruncated, actualTruncated;
1641 [self writePassed:equal inFile:path atLine:line message:@"expected \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1642 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1643 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1646 - (void)testString:(NSString *)actual isNotEqualTo:(NSString *)expected inFile:(char *)path atLine:(int)line
1648 if (actual && ![actual isKindOfClass:[NSString class]])
1649 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1650 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1653 if (expected && ![expected isKindOfClass:[NSString class]])
1654 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1655 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1659 if (!actual && !expected) equal = YES; // equal (both nil)
1660 else if (actual) equal = [actual isEqualToString:expected];
1661 BOOL expectedTruncated, actualTruncated;
1662 [self writePassed:equal
1665 message:@"expected (not) \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1666 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1667 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1670 - (void)testString:(NSString *)actual hasPrefix:(NSString *)expected inFile:(char *)path atLine:(int)line
1673 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1674 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1677 if (actual && ![actual isKindOfClass:[NSString class]])
1678 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1679 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1682 if (![expected isKindOfClass:[NSString class]])
1683 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1684 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1687 BOOL result = actual ? NO : [actual hasPrefix:expected];
1688 BOOL expectedTruncated, actualTruncated;
1689 [self writePassed:result
1692 message:@"expected prefix \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1693 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1694 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1697 - (void)testString:(NSString *)actual doesNotHavePrefix:(NSString *)expected inFile:(char *)path atLine:(int)line
1700 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1701 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1704 if (actual && ![actual isKindOfClass:[NSString class]])
1705 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1706 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1709 if (![expected isKindOfClass:[NSString class]])
1710 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1711 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1714 BOOL result = actual ? (![actual hasPrefix:expected]) : NO;
1715 BOOL expectedTruncated, actualTruncated;
1716 [self writePassed:result
1719 message:@"expected prefix (not) \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1720 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1721 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1724 - (void)testString:(NSString *)actual hasSuffix:(NSString *)expected inFile:(char *)path atLine:(int)line
1727 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1728 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1731 if (actual && ![actual isKindOfClass:[NSString class]])
1732 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1733 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1736 if (![expected isKindOfClass:[NSString class]])
1737 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1738 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1741 BOOL result = actual ? NO : [actual hasSuffix:expected];
1742 BOOL expectedTruncated, actualTruncated;
1743 [self writePassed:result
1746 message:@"expected suffix \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1747 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1748 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1751 - (void)testString:(NSString *)actual doesNotHaveSuffix:(NSString *)expected inFile:(char *)path atLine:(int)line
1754 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1755 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1758 if (actual && ![actual isKindOfClass:[NSString class]])
1759 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1760 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1763 if (![expected isKindOfClass:[NSString class]])
1764 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1765 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1768 BOOL result = actual ? (![actual hasSuffix:expected]) : NO;
1769 BOOL expectedTruncated, actualTruncated;
1770 [self writePassed:result
1773 message:@"expected suffix (not) \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1774 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1775 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1778 - (void)testString:(NSString *)actual contains:(NSString *)expected inFile:(char *)path atLine:(int)line
1781 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1782 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1785 if (actual && ![actual isKindOfClass:[NSString class]])
1786 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1787 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1790 if (![expected isKindOfClass:[NSString class]])
1791 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1792 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1795 BOOL result = actual ? NO : (!NSEqualRanges([actual rangeOfString:expected], NSMakeRange(NSNotFound, 0)));
1796 BOOL expectedTruncated, actualTruncated;
1797 [self writePassed:result
1800 message:@"expected contains \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1801 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1802 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1805 - (void)testString:(NSString *)actual doesNotContain:(NSString *)expected inFile:(char *)path atLine:(int)line
1808 [NSException WOTest_raise:WO_TEST_NIL_PARAMETER_EXCEPTION
1809 reason:WO_NIL_PARAMETER_EXCEPTION_REASON
1812 if (actual && ![actual isKindOfClass:[NSString class]])
1813 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1814 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(actual)
1817 if (![expected isKindOfClass:[NSString class]])
1818 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1819 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(expected)
1822 BOOL result = actual ? YES : (NSEqualRanges([actual rangeOfString:expected], NSMakeRange(NSNotFound, 0)));
1823 BOOL expectedTruncated, actualTruncated;
1824 [self writePassed:result
1827 message:@"expected contains (not) \"%@\", got \"%@\"", WO_DESC(expected), WO_DESC(actual)];
1828 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1829 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1833 #pragma mark NSArray test methods
1835 - (void)testArray:(NSArray *)actual isEqualTo:(NSArray *)expected inFile:(char *)path atLine:(int)line
1837 if (actual && ![actual isKindOfClass:[NSArray class]])
1838 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1839 reason:WO_EXPECTED_ARRAY_EXCEPTION_REASON(actual)
1842 if (expected && ![expected isKindOfClass:[NSArray class]])
1843 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1844 reason:WO_EXPECTED_ARRAY_EXCEPTION_REASON(expected)
1848 if (!actual && !expected) equal = YES; // equal (both nil)
1849 else if (actual) equal = [actual isEqualToArray:expected];
1850 BOOL expectedTruncated, actualTruncated;
1851 [self writePassed:equal inFile:path atLine:line message:@"expected %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1852 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1853 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1856 - (void)testArray:(NSArray *)actual isNotEqualTo:(NSArray *)expected inFile:(char *)path atLine:(int)line
1858 if (actual && ![actual isKindOfClass:[NSArray class]])
1859 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1860 reason:WO_EXPECTED_ARRAY_EXCEPTION_REASON(actual)
1863 if (expected && ![expected isKindOfClass:[NSArray class]])
1864 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1865 reason:WO_EXPECTED_ARRAY_EXCEPTION_REASON(expected)
1869 if (!actual && !expected) equal = YES; // equal (both nil)
1870 else if (actual) equal = [actual isEqualToArray:expected];
1871 BOOL expectedTruncated, actualTruncated;
1872 [self writePassed:(!equal) inFile:path atLine:line message:@"expected (not) %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1873 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1874 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1878 #pragma mark NSDictionary test methods
1880 - (void)testDictionary:(NSDictionary *)actual isEqualTo:(NSDictionary *)expected inFile:(char *)path atLine:(int)line
1882 if (actual && ![actual isKindOfClass:[NSArray class]])
1883 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1884 reason:WO_EXPECTED_DICTIONARY_EXCEPTION_REASON(actual)
1887 if (expected && ![expected isKindOfClass:[NSArray class]])
1888 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1889 reason:WO_EXPECTED_DICTIONARY_EXCEPTION_REASON(expected)
1893 if (!actual && !expected) equal = YES; // equal (both nil)
1894 else if (actual) equal = [actual isEqualToDictionary:expected];
1895 BOOL expectedTruncated, actualTruncated;
1896 [self writePassed:equal inFile:path atLine:line message:@"expected %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1897 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1898 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1901 - (void)testDictionary:(NSDictionary *)actual isNotEqualTo:(NSDictionary *)expected inFile:(char *)path atLine:(int)line
1903 if (actual && ![actual isKindOfClass:[NSDictionary class]])
1904 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1905 reason:WO_EXPECTED_DICTIONARY_EXCEPTION_REASON(actual)
1908 if (expected && ![expected isKindOfClass:[NSDictionary class]])
1909 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1910 reason:WO_EXPECTED_DICTIONARY_EXCEPTION_REASON(expected)
1914 if (!actual && !expected) equal = YES; // equal (both nil)
1915 else if (actual) equal = [actual isEqualToDictionary:expected];
1916 BOOL expectedTruncated, actualTruncated;
1917 [self writePassed:(!equal) inFile:path atLine:line message:@"expected (not) %@, got %@", WO_DESC(expected), WO_DESC(actual)];
1918 if (expectedTruncated) _WOLog(@"expected result (not truncated): %@", WO_LONG_DESC(expected));
1919 if (actualTruncated) _WOLog(@"actual result (not truncated): %@", WO_LONG_DESC(actual));
1923 #pragma mark Exception test methods
1925 - (void)testThrowsException:(id)exception inFile:(char *)path atLine:(int)line
1927 BOOL result = (exception ? YES : NO);
1928 [self writePassed:result
1931 message:[NSString stringWithFormat:@"expected exception, got %@", [NSException WOTest_nameForException:exception]]];
1934 - (void)testDoesNotThrowException:(id)exception inFile:(char *)path atLine:(int)line
1936 BOOL result = (exception ? NO : YES);
1937 [self writePassed:result
1941 [NSString stringWithFormat:@"expected no exception, got %@", [NSException WOTest_nameForException:exception]]];
1944 - (void)testThrowsException:(id)exception named:(NSString *)name inFile:(char *)path atLine:(int)line
1946 if (name && ![name isKindOfClass:[NSString class]])
1948 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1949 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(name)
1955 NSString *actualName = [NSException WOTest_nameForException:exception];
1957 if (exception && [actualName isEqualToString:name]) result = YES;
1959 [self writePassed:result
1962 message:[NSString stringWithFormat:@"expected %@, got %@", name, actualName]];
1965 - (void)testDoesNotThrowException:(id)exception named:(NSString *)name inFile:(char *)path atLine:(int)line
1967 if (name && ![name isKindOfClass:[NSString class]])
1969 [NSException WOTest_raise:WO_TEST_CLASS_MISMATCH_EXCEPTION
1970 reason:WO_EXPECTED_STRING_EXCEPTION_REASON(name)
1976 NSString *actualName = [NSException WOTest_nameForException:exception];
1978 if (exception && [actualName isEqualToString:name]) result = NO;
1980 [self writePassed:result inFile:path atLine:line message:@"expected (not) %@, got %@", name, actualName];
1984 #pragma mark Random value generator methods
1986 // TODO: move these into a category, these are more "test helpers" rather than actual "testing methods"
1990 return (int)(WO_RANDOM_SIGN * (WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
1995 return (int)(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2000 return (int)-(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2010 return (int)(WO_RANDOM_SIGN * (WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2013 - (int)aBigPositiveInt
2015 return (int)(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2018 - (int)aBigNegativeInt
2020 return (int)-(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2025 return (int)(WO_RANDOM_SIGN * (WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2028 - (int)aSmallPositiveInt
2030 return (int)(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2033 - (int)aSmallNegativeInt
2035 return (int)-(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2038 - (unsigned)anUnsigned
2040 return (unsigned)(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2043 - (unsigned)aZeroUnsigned
2048 - (unsigned)aBigUnsigned
2050 return (unsigned)(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2053 - (unsigned)aSmallUnsigned
2055 return (unsigned)(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2060 return (float)(WO_RANDOM_SIGN * (WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2063 - (float)aPositiveFloat
2065 return (float)(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2068 - (float)aNegativeFloat
2070 return (float)-(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2080 return (float)(WO_RANDOM_SIGN * (WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2083 - (float)aBigPositiveFloat
2085 return (float)(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2088 - (float)aBigNegativeFloat
2090 return (float)-(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2093 - (float)aSmallFloat
2095 return (float)(WO_RANDOM_SIGN * (WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2098 - (float)aSmallPositiveFloat
2100 return (float)(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2103 - (float)aSmallNegativeFloat
2105 return (float)-(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2110 return (double)(WO_RANDOM_SIGN * (WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2113 - (double)aPositiveDouble
2115 return (double)(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2118 - (double)aNegativeDouble
2120 return (double)-(WO_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2123 - (double)aZeroDouble
2128 - (double)aBigDouble
2130 return (double)(WO_RANDOM_SIGN * (WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2133 - (double)aBigPositiveDouble
2135 return (double)(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2138 - (double)aBigNegativeDouble
2140 return (double)-(WO_BIG_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2143 - (double)aSmallDouble
2145 return (double)(WO_RANDOM_SIGN * (WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN)));
2148 - (double)aSmallPositiveDouble
2150 return (double)(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2153 - (double)aSmallNegativeDouble
2155 return (double)-(WO_SMALL_TEST_VALUE + (WO_RANDOM_OFFSET * WO_RANDOM_SIGN));
2159 #pragma mark Properties
2161 @synthesize startDate;
2162 @synthesize testsRun;
2163 @synthesize testsPassed;
2164 @synthesize testsFailed;
2165 @synthesize uncaughtExceptions;
2166 @synthesize testsFailedExpected;
2167 @synthesize testsPassedUnexpected;
2168 @synthesize expectFailures;
2169 @synthesize lowLevelExceptionsExpected;
2170 @synthesize lowLevelExceptionsUnexpected;
2171 @synthesize expectLowLevelExceptions;
2172 @synthesize verbosity;
2173 @synthesize trimInitialPathComponents;
2174 @synthesize lastReportedFile;
2175 @synthesize lastReportedLine;
2176 @synthesize warnsAboutSignComparisons;