// // 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 #import #import #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