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