Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -15,11 +15,13 @@ #include "config.h" #import "OTAppDelegate.h" +#import "OFColor.h" #import "OFSet.h" +#import "OFStdIOStream.h" #import "OFValue.h" #import "OTTestCase.h" #import "OTAssertionFailedException.h" @@ -81,16 +83,29 @@ } - (void)applicationDidFinishLaunching: (OFNotification *)notification { OFSet OF_GENERIC(Class) *testClasses = [self testClasses]; + size_t numSucceeded = 0, numFailed = 0; + + [OFStdOut writeFormat: @"Running %zu test case(s)\n", + testClasses.count]; for (Class class in testClasses) { + [OFStdOut writeFormat: @"Running tests in %@\n", class]; + for (OFValue *test in [self testsInClass: class]) { void *pool = objc_autoreleasePoolPush(); - OTTestCase *instance = - [[[class alloc] init] autorelease]; + bool failed = false; + OTTestCase *instance; + + [OFStdOut setForegroundColor: [OFColor yellow]]; + [OFStdOut writeFormat: + @"-[%@ %s]: ", + class, sel_getName(test.pointerValue)]; + + instance = [[[class alloc] init] autorelease]; @try { [instance setUp]; [instance performSelector: test.pointerValue]; } @catch (OTAssertionFailedException *e) { @@ -98,22 +113,55 @@ * If an assertion during -[setUp], don't run * the test. * If an assertion fails during a test, abort * the test. */ + [OFStdOut setForegroundColor: [OFColor red]]; + [OFStdOut writeFormat: + @"\r-[%@ %s]: failed\n", + class, sel_getName(test.pointerValue)]; + [OFStdOut writeLine: e.description]; + + failed = true; } @try { [instance tearDown]; } @catch (OTAssertionFailedException *e) { /* * If an assertion fails during -[tearDown], * abort the tear down. */ + if (!failed) { + [OFStdOut setForegroundColor: + [OFColor red]]; + [OFStdOut writeFormat: + @"\r-[%@ %s]: failed\n", + class, + sel_getName(test.pointerValue)]; + [OFStdOut writeLine: e.description]; + + failed = true; + } } + if (!failed) { + [OFStdOut setForegroundColor: [OFColor green]]; + [OFStdOut writeFormat: + @"\r-[%@ %s]: ok\n", + class, sel_getName(test.pointerValue)]; + + numSucceeded++; + } else + numFailed++; + + [OFStdOut reset]; + objc_autoreleasePoolPop(pool); } } + + [OFStdOut writeFormat: @"%zu test(s) succeeded, %zu test(s) failed.\n", + numSucceeded, numFailed]; [OFApplication terminate]; } @end Index: src/test/OTAssert.m ================================================================== --- src/test/OTAssert.m +++ src/test/OTAssert.m @@ -13,30 +13,25 @@ * file. */ #include "config.h" -#import "OFColor.h" -#import "OFStdIOStream.h" #import "OFString.h" #import "OTAssertionFailedException.h" void OTAssertImpl(id testCase, SEL test, bool condition, OFString *check, OFString *file, size_t line, ...) { - void *pool; va_list arguments; OFConstantString *format; OFString *message = nil; if (condition) return; - pool = objc_autoreleasePoolPush(); - va_start(arguments, line); format = va_arg(arguments, OFConstantString *); if (format != nil) message = [[[OFString alloc] @@ -43,16 +38,8 @@ initWithFormat: format arguments: arguments] autorelease]; va_end(arguments); - [OFStdErr setForegroundColor: [OFColor red]]; - [OFStdErr writeFormat: @"-[%@ %s]: Condition failed: %@%@%@\n", - [testCase className], sel_getName(test), - check, (message != nil ? @": " : @""), - (message != nil ? message : @"")]; - [OFStdErr reset]; - - objc_autoreleasePoolPop(pool); - - @throw [OTAssertionFailedException exception]; + @throw [OTAssertionFailedException exceptionWithCondition: check + message: message]; } Index: src/test/OTAssertionFailedException.h ================================================================== --- src/test/OTAssertionFailedException.h +++ src/test/OTAssertionFailedException.h @@ -12,8 +12,27 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFException.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN @interface OTAssertionFailedException: OFException +{ + OFString *_condition; + OFString *_Nullable _message; +} + +@property (readonly, nonatomic) OFString *condition; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *message; + ++ (instancetype)exceptionWithCondition: (OFString *)condition + message: (nullable OFString *)message; ++ (instancetype)exception OF_UNAVAILABLE; +- (instancetype)initWithCondition: (OFString *)condition + message: (nullable OFString *)message; +- (instancetype)init OF_UNAVAILABLE; @end + +OF_ASSUME_NONNULL_END Index: src/test/OTAssertionFailedException.m ================================================================== --- src/test/OTAssertionFailedException.m +++ src/test/OTAssertionFailedException.m @@ -16,6 +16,58 @@ #include "config.h" #import "OTAssertionFailedException.h" @implementation OTAssertionFailedException +@synthesize condition = _condition, message = _message; + ++ (instancetype)exceptionWithCondition: (OFString *)condition + message: (OFString *)message +{ + return [[[self alloc] initWithCondition: condition + message: message] autorelease]; +} + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (instancetype)initWithCondition: (OFString *)condition + message: (OFString *)message +{ + self = [super init]; + + @try { + _condition = [condition copy]; + _message = [message copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_condition release]; + [_message release]; + + [super dealloc]; +} + +- (OFString *)description +{ + if (_message != nil) + return [OFString stringWithFormat: @"Assertion failed: %@: %@", + _condition, _message]; + else + return [OFString stringWithFormat: @"Assertion failed: %@", + _condition]; +} @end