//
// FakeXCTest
//
// Copyright (c) 2014 Apple. All rights reserved.
//
#import "FakeXCTest.h"
#import <objc/runtime.h>
#import <stdarg.h>
#import <fnmatch.h>
static int testCaseResult = 0;
static char *testLimitedGlobingRule = NULL;
int (*XFakeXCTestCallback)(const char *fmt, va_list) = vprintf;
static void
FXCTPrintf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
XFakeXCTestCallback(fmt, ap);
va_end(ap);
}
void
FakeXCFailureHandler(XCTestCase * __unused test, BOOL __unused expected, const char * filePath,
NSUInteger lineNumber, NSString * __unused condition, NSString * format, ...)
{
va_list ap;
va_start(ap, format);
NSString *errorString = [[NSString alloc] initWithFormat:format arguments:ap];
va_end(ap);
FXCTPrintf("FAILED assertion at:
#if !__has_feature(objc_arc)
[errorString release];
#endif
testCaseResult = 1;
}
static bool
checkLimited(const char *testname)
{
return testLimitedGlobingRule == NULL ||
fnmatch(testLimitedGlobingRule, testname, 0) == 0;
}
static bool
returnNull(Method method)
{
bool res = false;
char *ret = method_copyReturnType(method);
if (ret && strcmp(ret, "v") == 0)
res = true;
free(ret);
return res;
}
@implementation XCTest
+ (void)setLimit:(const char *)testLimit
{
if (testLimitedGlobingRule)
free(testLimitedGlobingRule);
testLimitedGlobingRule = strdup(testLimit);
}
+ (int) runTests
{
NSMutableArray *testsClasses = [NSMutableArray array];
unsigned long ranTests = 0, failedTests = 0;
Class xctestClass = [XCTestCase class];
int numClasses;
int superResult = 0;
FXCTPrintf("[TEST]
numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0 ) {
Class *classes = NULL;
classes = (Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class s = classes[i];
while ((s = class_getSuperclass(s)) != NULL) {
if (s == xctestClass) {
[testsClasses addObject:classes[i]];
break;
}
}
}
free(classes);
}
for (Class testClass in testsClasses) {
unsigned int numMethods, n;
if (!class_getClassMethod(testClass, @selector(respondsToSelector:)))
continue;
/* get all methods conforming to void test...*/
XCTestCase *tc = NULL;;
@autoreleasepool {
Method *methods = NULL;
tc = [[testClass alloc] init];
if (tc == NULL)
continue;
methods = class_copyMethodList(testClass, &numMethods);
if (methods == NULL) {
#if !__has_feature(objc_arc)
[tc release];
#endif
continue;
}
for (n = 0; n < numMethods; n++) {
SEL ms = method_getName(methods[n]);
const char *mname = sel_getName(ms);
if (strncmp("test", mname, 4) == 0
&& method_getNumberOfArguments(methods[n]) == 2
&& returnNull(methods[n])
&& checkLimited(mname))
{
testCaseResult = 0;
if ([tc respondsToSelector:@selector(setUp)])
[tc setUp];
ranTests++;
FXCTPrintf("[BEGIN] @try {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[tc performSelector:ms];
#pragma clang diagnostic pop
}
@catch (NSException *exception) {
FXCTPrintf("exception: [[exception name] UTF8String],
[[exception name] UTF8String]);
testCaseResult = 1;
}
@finally {
FXCTPrintf("[ testCaseResult == 0 ? "PASS" : "FAIL",
class_getName(testClass), mname);
if (testCaseResult) {
superResult++;
failedTests++;
}
if ([tc respondsToSelector:@selector(tearDown)])
[tc tearDown];
}
}
}
free(methods);
#if !__has_feature(objc_arc)
[tc release];
#endif
}
}
FXCTPrintf("[SUMMARY]\n"
"ran ranTests, failedTests);
return superResult;
}
@end