aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GTMDefines.h16
-rw-r--r--UnitTesting/GTMIPhoneUnitTestDelegate.h7
-rw-r--r--UnitTesting/GTMIPhoneUnitTestDelegate.m46
3 files changed, 66 insertions, 3 deletions
diff --git a/GTMDefines.h b/GTMDefines.h
index 687f2a7..76b7947 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -298,6 +298,14 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#endif
#endif
+#ifndef NS_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_ns_returns_not_retained)
+ #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
+ #else
+ #define NS_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
#ifndef CF_RETURNS_RETAINED
#if __has_feature(attribute_cf_returns_retained)
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
@@ -306,6 +314,14 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#endif
#endif
+#ifndef CF_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_cf_returns_not_retained)
+ #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+ #else
+ #define CF_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
// Defined on 10.6 and above.
#ifndef NS_FORMAT_ARGUMENT
#define NS_FORMAT_ARGUMENT(A)
diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.h b/UnitTesting/GTMIPhoneUnitTestDelegate.h
index 4583b5d..5a361b3 100644
--- a/UnitTesting/GTMIPhoneUnitTestDelegate.h
+++ b/UnitTesting/GTMIPhoneUnitTestDelegate.h
@@ -6,9 +6,9 @@
// 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
@@ -27,7 +27,10 @@
@private
NSUInteger totalFailures_;
NSUInteger totalSuccesses_;
+ BOOL applicationDidFinishLaunchingCalled_;
+ GTMIPhoneUnitTestDelegate *retainer_;
}
+
// Runs through all the registered classes and runs test methods on any
// that are subclasses of SenTestCase. Prints results and run time to
// the default output.
diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.m b/UnitTesting/GTMIPhoneUnitTestDelegate.m
index dc4b182..6d56602 100644
--- a/UnitTesting/GTMIPhoneUnitTestDelegate.m
+++ b/UnitTesting/GTMIPhoneUnitTestDelegate.m
@@ -29,17 +29,57 @@
@interface UIApplication (GTMIPhoneUnitTestDelegate)
-// SPI that we need to exti cleanly with a value.
+// SPI that we need to exit cleanly with a value.
- (void)_terminateWithStatus:(int)status;
@end
+@interface GTMIPhoneUnitTestDelegate ()
+// We have cases where we are created in UIApplicationMain, but then the
+// user accidentally/intentionally replaces us as a delegate in their xib file
+// which means that we never get the applicationDidFinishLaunching: message.
+// We can register for the notification, but when the applications delegate
+// is reset, it releases us, and we get dealloced. Therefore we have retainer
+// which is responsible for retaining us until we get the notification.
+// We do it through this slightly roundabout route (instead of just an extra
+// retain in the init) so that clang doesn't complain about a leak.
+// We also check to make sure we aren't called twice with the
+// applicationDidFinishLaunchingCalled flag.
+@property (readwrite, retain, nonatomic) GTMIPhoneUnitTestDelegate *retainer;
+@end
+
@implementation GTMIPhoneUnitTestDelegate
+@synthesize retainer = retainer_;
+
+- (id)init {
+ if ((self = [super init])) {
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self
+ selector:@selector(applicationDidFinishLaunching:)
+ name:UIApplicationDidFinishLaunchingNotification
+ object:[UIApplication sharedApplication]];
+ [self setRetainer:self];
+ }
+ return self;
+}
+
// 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 {
+
+ // We could get called twice once from our notification registration, and
+ // once if we actually still are the delegate of the application after
+ // it has finished launching. So we'll just return if we've been called once.
+ if (applicationDidFinishLaunchingCalled_) return;
+ applicationDidFinishLaunchingCalled_ = YES;
+
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc removeObserver:self
+ name:UIApplicationDidFinishLaunchingNotification
+ object:[UIApplication sharedApplication]];
+
[self runTests];
if (!getenv("GTM_DISABLE_TERMINATION")) {
@@ -55,6 +95,10 @@
exit(exitStatus);
}
}
+
+ // Release ourself now that we're done. If we really are the application
+ // delegate, it will have retained us, so we'll stick around if necessary.
+ [self setRetainer:nil];
}
// Run through all the registered classes and run test methods on any