PageRenderTime 62ms CodeModel.GetById 12ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 1ms

/core/externals/update-engine/externals/google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.m

http://macfuse.googlecode.com/
Objective C | 257 lines | 180 code | 27 blank | 50 comment | 14 complexity | 775d2c36c21ed6abdbbd8c75436cab3c MD5 | raw file
  1//
  2//  GTMIPhoneUnitTestDelegate.m
  3//
  4//  Copyright 2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import "GTMIPhoneUnitTestDelegate.h"
 20
 21#import "GTMCodeCoverageApp.h"
 22#import "GTMDefines.h"
 23#if !GTM_IPHONE_SDK
 24#error GTMIPhoneUnitTestDelegate for iPhone only
 25#endif
 26#import <objc/runtime.h>
 27#import <stdio.h>
 28#import <UIKit/UIKit.h>
 29#import "GTMSenTestCase.h"
 30
 31@interface UIApplication (GTMIPhoneUnitTestDelegate)
 32
 33// SPI that we need to exit cleanly with a value.
 34- (void)_terminateWithStatus:(int)status;
 35
 36@end
 37
 38@interface GTMIPhoneUnitTestDelegate ()
 39// We have cases where we are created in UIApplicationMain, but then the
 40// user accidentally/intentionally replaces us as a delegate in their xib file
 41// which means that we never get the applicationDidFinishLaunching: message.
 42// We can register for the notification, but when the applications delegate
 43// is reset, it releases us, and we get dealloced. Therefore we have retainer
 44// which is responsible for retaining us until we get the notification.
 45// We do it through this slightly roundabout route (instead of just an extra
 46// retain in the init) so that clang doesn't complain about a leak.
 47// We also check to make sure we aren't called twice with the
 48// applicationDidFinishLaunchingCalled flag.
 49@property (readwrite, retain, nonatomic) GTMIPhoneUnitTestDelegate *retainer;
 50
 51- (void)runTestsAndExit:(UIApplication *)application;
 52
 53@end
 54
 55@implementation GTMIPhoneUnitTestDelegate
 56
 57@synthesize retainer = retainer_;
 58
 59- (id)init {
 60  if ((self = [super init])) {
 61    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
 62    [nc addObserver:self
 63           selector:@selector(handleApplicationDidFinishLaunchingNotification:)
 64               name:UIApplicationDidFinishLaunchingNotification
 65             object:[UIApplication sharedApplication]];
 66    [self setRetainer:self];
 67  }
 68  return self;
 69}
 70
 71// If running in the mode were we get the notification, bridge it over to
 72// the method we'd get if we are the app delegate.
 73- (void)handleApplicationDidFinishLaunchingNotification:(NSNotification *)note {
 74  [self applicationDidFinishLaunching:[note object]];
 75}
 76
 77// Run through all the registered classes and run test methods on any
 78// that are subclasses of SenTestCase. Terminate the application upon
 79// test completion.
 80- (void)applicationDidFinishLaunching:(UIApplication *)application {
 81
 82  // We could get called twice once from our notification registration, and
 83  // once if we actually still are the delegate of the application after
 84  // it has finished launching. So we'll just return if we've been called once.
 85  if (applicationDidFinishLaunchingCalled_) return;
 86  applicationDidFinishLaunchingCalled_ = YES;
 87
 88  NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
 89  [nc removeObserver:self
 90                name:UIApplicationDidFinishLaunchingNotification
 91              object:application];
 92
 93  // To permit UI-based tests, run the tests and exit app in a delayed selector
 94  // call. This ensures tests are run outside of applicationDidFinishLaunching:.
 95  [self performSelector:@selector(runTestsAndExit:)
 96             withObject:application
 97             afterDelay:0];
 98}
 99
100// Run through all tests and then exit application if required.
101- (void)runTestsAndExit:(UIApplication *)application {
102  [self runTests];
103
104  if ([application respondsToSelector:@selector(gtm_gcov_flush)]) {
105    [application performSelector:@selector(gtm_gcov_flush)];
106  }
107
108  if (!getenv("GTM_DISABLE_TERMINATION")) {
109    // To help using xcodebuild, make the exit status 0/1 to signal the tests
110    // success/failure.
111    int exitStatus = (([self totalFailures] == 0U) ? 0 : 1);
112    // Alternative to exit(status); so it cleanly terminates the UIApplication
113    // and classes that depend on this signal to exit cleanly.
114    NSMethodSignature * terminateSignature
115      = [UIApplication instanceMethodSignatureForSelector:@selector(_terminateWithStatus:)];
116    if (terminateSignature != nil) {
117      NSInvocation * terminateInvocation
118        = [NSInvocation invocationWithMethodSignature:terminateSignature];
119      [terminateInvocation setTarget:application];
120      [terminateInvocation setSelector:@selector(_terminateWithStatus:)];
121      [terminateInvocation setArgument:&exitStatus atIndex:2];
122      [terminateInvocation invoke];
123    } else {
124      exit(exitStatus);
125    }
126  }
127
128  // Release ourself now that we're done. If we really are the application
129  // delegate, it will have retained us, so we'll stick around if necessary.
130  [self setRetainer:nil];
131}
132
133static int ClassSort(const void *a, const void *b) {
134  Class *classA = (Class *)a;
135  Class *classB = (Class *)b;
136  const char *nameA = class_getName(*classA);
137  const char *nameB = class_getName(*classB);
138  return strcmp(nameA, nameB);
139}
140
141// Run through all the registered classes and run test methods on any
142// that are subclasses of SenTestCase. Print results and run time to
143// the default output.
144- (void)runTests {
145  int count = objc_getClassList(NULL, 0);
146  NSMutableData *classData
147    = [NSMutableData dataWithLength:sizeof(Class) * count];
148  Class *classes = (Class*)[classData mutableBytes];
149  _GTMDevAssert(classes, @"Couldn't allocate class list");
150  objc_getClassList(classes, count);
151
152  // Follow SenTest's lead and sort the classes.  (This may only be a change
153  // in the iOS 5 runtime, but make sure it is always sorted just incase)
154  mergesort(classes, count, sizeof(Class), ClassSort);
155
156  totalFailures_ = 0;
157  totalSuccesses_ = 0;
158  NSString *suiteName = [[NSBundle mainBundle] bundlePath];
159  NSDate *suiteStartDate = [NSDate date];
160  NSString *suiteStartString
161    = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
162                                 suiteName, suiteStartDate];
163  fputs([suiteStartString UTF8String], stderr);
164  fflush(stderr);
165  for (int i = 0; i < count; ++i) {
166    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
167    Class currClass = classes[i];
168    if (class_respondsToSelector(currClass, @selector(conformsToProtocol:)) &&
169        [currClass conformsToProtocol:@protocol(SenTestCase)]) {
170      NSArray *invocations = [currClass testInvocations];
171      if ([invocations count]) {
172        NSDate *fixtureStartDate = [NSDate date];
173        NSString *fixtureName = NSStringFromClass(currClass);
174        NSString *fixtureStartString
175          = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
176                                       fixtureName, fixtureStartDate];
177        int fixtureSuccesses = 0;
178        int fixtureFailures = 0;
179        fputs([fixtureStartString UTF8String], stderr);
180        fflush(stderr);
181        NSInvocation *invocation;
182        GTM_FOREACH_OBJECT(invocation, invocations) {
183          GTMTestCase *testCase
184            = [[currClass alloc] initWithInvocation:invocation];
185          BOOL failed = NO;
186          NSDate *caseStartDate = [NSDate date];
187          NSString *selectorName = NSStringFromSelector([invocation selector]);
188          NSString *caseStartString
189            = [NSString stringWithFormat:@"Test Case '-[%@ %@]' started.\n",
190               fixtureName, selectorName];
191          fputs([caseStartString UTF8String], stderr);
192          fflush(stderr);
193          @try {
194            [testCase performTest];
195          } @catch (NSException *exception) {
196            failed = YES;
197          }
198          if (failed) {
199            fixtureFailures += 1;
200          } else {
201            fixtureSuccesses += 1;
202          }
203          NSTimeInterval caseEndTime
204            = [[NSDate date] timeIntervalSinceDate:caseStartDate];
205          NSString *caseEndString
206            = [NSString stringWithFormat:@"Test Case '-[%@ %@]' %@ (%0.3f "
207               "seconds).\n",
208               fixtureName, selectorName,
209               failed ? @"failed" : @"passed",
210               caseEndTime];
211          fputs([caseEndString UTF8String], stderr);
212          fflush(stderr);
213          [testCase release];
214        }
215        NSDate *fixtureEndDate = [NSDate date];
216        NSTimeInterval fixtureEndTime
217          = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate];
218        NSString *fixtureEndString
219          = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
220             "Executed %d tests, with %d failures (%d "
221             "unexpected) in %0.3f (%0.3f) seconds\n\n",
222             fixtureName, fixtureEndDate,
223             fixtureSuccesses + fixtureFailures,
224             fixtureFailures, fixtureFailures,
225             fixtureEndTime, fixtureEndTime];
226        fputs([fixtureEndString UTF8String], stderr);
227        fflush(stderr);
228        totalSuccesses_ += fixtureSuccesses;
229        totalFailures_ += fixtureFailures;
230      }
231    }
232    [pool release];
233  }
234  NSDate *suiteEndDate = [NSDate date];
235  NSTimeInterval suiteEndTime
236    = [suiteEndDate timeIntervalSinceDate:suiteStartDate];
237  NSString *suiteEndString
238    = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
239                                 "Executed %tu tests, with %tu failures (%tu "
240                                 "unexpected) in %0.3f (%0.3f) seconds\n\n",
241                                 suiteName, suiteEndDate,
242                                 totalSuccesses_ + totalFailures_,
243                                 totalFailures_, totalFailures_,
244                                 suiteEndTime, suiteEndTime];
245  fputs([suiteEndString UTF8String], stderr);
246  fflush(stderr);
247}
248
249- (NSUInteger)totalSuccesses {
250  return totalSuccesses_;
251}
252
253- (NSUInteger)totalFailures {
254  return totalFailures_;
255}
256
257@end