Last active
December 22, 2015 00:09
-
-
Save fjolnir/6387582 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <objc/runtime.h> | |
#import <stdlib.h> | |
#define ANSI_COLOR_RED "\x1b[31m" | |
#define ANSI_COLOR_GREEN "\x1b[32m" | |
#define ANSI_COLOR_YELLOW "\x1b[33m" | |
#define ANSI_COLOR_BLUE "\x1b[34m" | |
#define ANSI_COLOR_MAGENTA "\x1b[35m" | |
#define ANSI_COLOR_CYAN "\x1b[36m" | |
#define ANSI_COLOR_RESET "\x1b[0m" | |
#define SuppressPerformSelectorLeakWarning(code...) \ | |
_Pragma("clang diagnostic push") \ | |
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ | |
code \ | |
_Pragma("clang diagnostic pop") | |
NSString * const kTestFailedException = @"TestFailedException"; | |
#define TLog(format, args...) NSLog(@" * " format, ##args) | |
#define TAssert(cond, format, args...) do { \ | |
if(cond) \ | |
TLog(@"Assertion: " format ANSI_COLOR_GREEN "\tSuccessful"ANSI_COLOR_RESET, ##args); \ | |
else { \ | |
TLog(@"Assertion: " format ANSI_COLOR_RED "\tFAILED" ANSI_COLOR_RESET, ##args); \ | |
@throw [NSException exceptionWithName:kTestFailedException \ | |
reason:[NSString stringWithFormat:@"%s failed", #cond] \ | |
userInfo:nil]; \ | |
} \ | |
} while(0) | |
#define Describe(entity, contexts...) \ | |
@interface Test##entity : Test \ | |
@end \ | |
@implementation Test##entity \ | |
+ (NSString *)entityName { \ | |
return [@#entity stringByReplacingOccurrencesOfString:@"_" withString:@" "]; \ | |
} \ | |
contexts \ | |
@end | |
#define When(context, tests...) \ | |
- (TestResult *)When_##context { \ | |
TestResult *result = [TestResult new]; \ | |
tests \ | |
return result; \ | |
} | |
#define It(description, code...) ^{\ | |
@try { \ | |
result.totalTests += 1; \ | |
NSLog(@" * It %@", [@#description stringByReplacingOccurrencesOfString:@"_" withString:@" "]); \ | |
code \ | |
} \ | |
@catch(NSException *e) { \ | |
result.failedTests += 1; \ | |
} \ | |
}(); | |
@interface TestResult : NSObject | |
@property NSUInteger totalTests, failedTests; | |
@end | |
@implementation TestResult | |
@end | |
@interface Test : NSObject | |
+ (BOOL)performTests; | |
+ (NSString *)entityName; | |
- (void)setUp; | |
- (void)tearDown; | |
@end | |
@implementation Test | |
+ (BOOL)performTests | |
{ | |
if(self == [Test class]) { | |
int const classCount = objc_getClassList(NULL, 0); | |
Class * const classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * classCount); | |
objc_getClassList(classes, classCount); | |
NSUInteger totalTestClasses = 0; | |
NSUInteger failedTestClasses= 0; | |
for(int i = 0; i < classCount; ++i) { | |
Class klass = classes[i]; | |
while((klass = class_getSuperclass(klass))) { | |
if(klass == self) { | |
NSLog(@" * Checking '%@'", [classes[i] entityName]); | |
++totalTestClasses; | |
if(![classes[i] performTests]) | |
++failedTestClasses; | |
break; | |
} | |
} | |
} | |
free(classes); | |
NSLog(@"\n"); | |
if(failedTestClasses == 0) { | |
NSLog(@"" ANSI_COLOR_GREEN " * All tests succeeded" ANSI_COLOR_RESET); | |
} else { | |
NSLog(@"" ANSI_COLOR_RED " * Tests failed" ANSI_COLOR_RESET); | |
} | |
} else { | |
Method *methods = class_copyMethodList(self, NULL); | |
Method *method = methods; | |
id instance = [self new]; | |
NSUInteger totalTests = 0; | |
NSUInteger failedTests = 0; | |
while(*method) { | |
SEL const selector = method_getName(*method); | |
NSString * const selectorAsString = NSStringFromSelector(selector); | |
if([selectorAsString hasPrefix:@"When_"]) { | |
NSLog(@" * %@:", [selectorAsString stringByReplacingOccurrencesOfString:@"_" withString:@" "]); | |
[instance setUp]; | |
SuppressPerformSelectorLeakWarning( | |
TestResult * const result = [instance performSelector:selector]; | |
) | |
totalTests += result.totalTests; | |
failedTests += result.failedTests; | |
[instance tearDown]; | |
} | |
++method; | |
} | |
free(methods); | |
if(failedTests == 0) | |
NSLog(@"" ANSI_COLOR_GREEN " * %ld/%ld succeeded" ANSI_COLOR_RESET, totalTests - failedTests, totalTests); | |
else { | |
NSLog(@"" ANSI_COLOR_RED " * %ld/%ld succeeded" ANSI_COLOR_RESET, totalTests - failedTests, totalTests); | |
return NO; | |
} | |
} | |
return YES; | |
} | |
+ (NSString *)entityName | |
{ | |
return nil; | |
} | |
- (void)setUp { return; } | |
- (void)tearDown { return; } | |
@end | |
Describe(NSString, | |
When(newly_created, | |
It(should_be_zero_length, | |
TAssert(NO, @"defy reality"); | |
) | |
It(should_be_cool, | |
TAssert(YES, @"behave as expected"); | |
) | |
) | |
) | |
Describe(NSDictionary, | |
When(newly_created, | |
It(should_have_no_objects, | |
TAssert(YES, @"behave as expected"); | |
) | |
) | |
) | |
int main(int argc, char *argv[]) { | |
@autoreleasepool { | |
[Test performTests]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment