// // 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" @interface UIApplication (GTMIPhoneUnitTestDelegate) // SPI that we need to exti cleanly with a value. - (void)_terminateWithStatus:(int)status; @end @implementation GTMIPhoneUnitTestDelegate // 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]; if (!getenv("GTM_DISABLE_TERMINATION")) { // To help using xcodebuild, make the exit status 0/1 to signal the tests // success/failure. int exitStatus = (([self totalFailures] == 0U) ? 0 : 1); // Alternative to exit(status); so it cleanly terminates the UIApplication // and classes that depend on this signal to exit cleanly. if ([application respondsToSelector:@selector(_terminateWithStatus:)]) { [application performSelector:@selector(_terminateWithStatus:) withObject:(id)exitStatus]; } else { exit(exitStatus); } } } // 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); NSMutableData *classData = [NSMutableData dataWithLength:sizeof(Class) * count]; Class *classes = (Class*)[classData mutableBytes]; _GTMDevAssert(classes, @"Couldn't allocate class list"); objc_getClassList(classes, count); totalFailures_ = 0; totalSuccesses_ = 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) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; Class currClass = classes[i]; if (class_respondsToSelector(currClass, @selector(conformsToProtocol:)) && [currClass conformsToProtocol:@protocol(SenTestCase)]) { 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; fputs([fixtureStartString UTF8String], stderr); fflush(stderr); NSArray *invocations = [currClass testInvocations]; if ([invocations count]) { NSInvocation *invocation; GTM_FOREACH_OBJECT(invocation, invocations) { GTMTestCase *testCase = [[currClass alloc] initWithInvocation:invocation]; BOOL failed = NO; NSDate *caseStartDate = [NSDate date]; NSString *selectorName = NSStringFromSelector([invocation selector]); NSString *caseStartString = [NSString stringWithFormat:@"Test Case '-[%@ %@]' started.\n", fixtureName, selectorName]; fputs([caseStartString UTF8String], stderr); fflush(stderr); @try { [testCase performTest]; } @catch (NSException *exception) { failed = YES; } if (failed) { fixtureFailures += 1; } else { fixtureSuccesses += 1; } NSTimeInterval caseEndTime = [[NSDate date] timeIntervalSinceDate:caseStartDate]; NSString *caseEndString = [NSString stringWithFormat:@"Test Case '-[%@ %@]' %@ (%0.3f " @"seconds).\n", fixtureName, selectorName, failed ? @"failed" : @"passed", caseEndTime]; fputs([caseEndString UTF8String], stderr); fflush(stderr); [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\n", fixtureName, fixtureEndDate, fixtureSuccesses + fixtureFailures, fixtureFailures, fixtureFailures, fixtureEndTime, fixtureEndTime]; fputs([fixtureEndString UTF8String], stderr); fflush(stderr); totalSuccesses_ += fixtureSuccesses; totalFailures_ += fixtureFailures; } [pool release]; } 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\n", suiteName, suiteEndDate, totalSuccesses_ + totalFailures_, totalFailures_, totalFailures_, suiteEndTime, suiteEndTime]; fputs([suiteEndString UTF8String], stderr); fflush(stderr); } - (NSUInteger)totalSuccesses { return totalSuccesses_; } - (NSUInteger)totalFailures { return totalFailures_; } @end