Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Last active December 22, 2015 00:09
Show Gist options
  • Save fjolnir/6387582 to your computer and use it in GitHub Desktop.
Save fjolnir/6387582 to your computer and use it in GitHub Desktop.
#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