diff options
Diffstat (limited to 'UnitTesting/GTMIPhoneUnitTestDelegate.m')
-rw-r--r-- | UnitTesting/GTMIPhoneUnitTestDelegate.m | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.m b/UnitTesting/GTMIPhoneUnitTestDelegate.m new file mode 100644 index 0000000..28d2fe0 --- /dev/null +++ b/UnitTesting/GTMIPhoneUnitTestDelegate.m @@ -0,0 +1,160 @@ +// +// GTMIPhoneUnitTestDelegate.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMIPhoneUnitTestDelegate.h" + +#import "GTMDefines.h" +#if !GTM_IPHONE_SDK +#error GTMIPhoneUnitTestDelegate for iPhone only +#endif +#import <objc/runtime.h> +#import <stdio.h> +#import <UIKit/UIKit.h> +#import "GTMSenTestCase.h" + +// Used for sorting methods below +static int MethodSort(const void *a, const void *b) { + const char *nameA = sel_getName(method_getName(*(Method*)a)); + const char *nameB = sel_getName(method_getName(*(Method*)b)); + return strcmp(nameA, nameB); +} + +@interface UIApplication (iPhoneUnitTestAdditions) +// "Private" method that we need +- (void)terminate; +@end + +@implementation GTMIPhoneUnitTestDelegate + +// Return YES if class is subclass (1 or more generations) of SenTestCase +- (BOOL)isTestFixture:(Class)aClass { + BOOL iscase = NO; + Class testCaseClass = [SenTestCase class]; + Class superclass; + for (superclass = aClass; + !iscase && superclass; + superclass = class_getSuperclass(superclass)) { + iscase = superclass == testCaseClass ? YES : NO; + } + return iscase; +} + +// Run through all the registered classes and run test methods on any +// that are subclasses of SenTestCase. Terminate the application upon +// test completion. +- (void)applicationDidFinishLaunching:(UIApplication *)application { + [self runTests]; + // Using private call to end our tests + [[UIApplication sharedApplication] terminate]; +} + +// Run through all the registered classes and run test methods on any +// that are subclasses of SenTestCase. Print results and run time to +// the default output. +- (void)runTests { + int count = objc_getClassList(NULL, 0); + Class *classes = (Class*)malloc(sizeof(Class) * count); + _GTMDevAssert(classes, @"Couldn't allocate class list"); + objc_getClassList(classes, count); + int suiteSuccesses = 0; + int suiteFailures = 0; + int suiteTotal = 0; + NSString *suiteName = [[NSBundle mainBundle] bundlePath]; + NSDate *suiteStartDate = [NSDate date]; + NSString *suiteStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n", + suiteName, suiteStartDate]; + fputs([suiteStartString UTF8String], stderr); + fflush(stderr); + for (int i = 0; i < count; ++i) { + Class currClass = classes[i]; + if ([self isTestFixture:currClass]) { + NSDate *fixtureStartDate = [NSDate date]; + NSString *fixtureName = NSStringFromClass(currClass); + NSString *fixtureStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n", + fixtureName, fixtureStartDate]; + int fixtureSuccesses = 0; + int fixtureFailures = 0; + int fixtureTotal = 0; + fputs([fixtureStartString UTF8String], stderr); + fflush(stderr); + id testcase = [[currClass alloc] init]; + _GTMDevAssert(testcase, @"Unable to instantiate Test Suite: '%@'\n", + fixtureName); + unsigned int methodCount; + Method *methods = class_copyMethodList(currClass, &methodCount); + // Sort our methods so they are called in Alphabetical order just + // because we can. + qsort(methods, methodCount, sizeof(Method), MethodSort); + for (size_t j = 0; j < methodCount; ++j) { + Method currMethod = methods[j]; + SEL sel = method_getName(currMethod); + const char *name = sel_getName(sel); + // If it starts with test, run it. + if (strstr(name, "test") == name) { + fixtureTotal += 1; + BOOL failed = NO; + NSDate *caseStartDate = [NSDate date]; + @try { + [testcase performTest:sel]; + } @catch (NSException *exception) { + failed = YES; + } + if (failed) { + fixtureFailures += 1; + } else { + fixtureSuccesses += 1; + } + NSTimeInterval caseEndTime = [[NSDate date] timeIntervalSinceDate:caseStartDate]; + NSString *caseEndString = [NSString stringWithFormat:@"Test Case '-[%@ %s]' %s (%0.3f seconds).\n", + fixtureName, name, + failed ? "failed" : "passed", caseEndTime]; + fputs([caseEndString UTF8String], stderr); + fflush(stderr); + } + } + if (methods) { + free(methods); + } + [testcase release]; + NSDate *fixtureEndDate = [NSDate date]; + NSTimeInterval fixtureEndTime = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate]; + NSString *fixtureEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" + "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n", + fixtureName, fixtureEndDate, fixtureTotal, + fixtureFailures, fixtureFailures, + fixtureEndTime, fixtureEndTime]; + + fputs([fixtureEndString UTF8String], stderr); + fflush(stderr); + suiteTotal += fixtureTotal; + suiteSuccesses += fixtureSuccesses; + suiteFailures += fixtureFailures; + } + } + NSDate *suiteEndDate = [NSDate date]; + NSTimeInterval suiteEndTime = [suiteEndDate timeIntervalSinceDate:suiteStartDate]; + NSString *suiteEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" + "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n", + suiteName, suiteEndDate, suiteTotal, + suiteFailures, suiteFailures, + suiteEndTime, suiteEndTime]; + fputs([suiteEndString UTF8String], stderr); + fflush(stderr); +} + +@end |