aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m
diff options
context:
space:
mode:
Diffstat (limited to 'Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m')
-rw-r--r--Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m450
1 files changed, 450 insertions, 0 deletions
diff --git a/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m b/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m
new file mode 100644
index 0000000..9ff7473
--- /dev/null
+++ b/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 <XCTest/XCTest.h>
+
+#import <objc/runtime.h>
+
+#import "FIRAuthAppDelegateProxy.h"
+#import <OCMock/OCMock.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIRAuthEmptyAppDelegate
+ @brief A @c UIApplicationDelegate implementation that does nothing.
+ */
+@interface FIRAuthEmptyAppDelegate : NSObject <UIApplicationDelegate>
+@end
+@implementation FIRAuthEmptyAppDelegate
+@end
+
+/** @class FIRAuthLegacyAppDelegate
+ @brief A @c UIApplicationDelegate implementation that implements
+ `application:didReceiveRemoteNotification:`.
+ */
+@interface FIRAuthLegacyAppDelegate : NSObject <UIApplicationDelegate>
+
+/** @var notificationReceived
+ @brief The last notification received, if any.
+ */
+@property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
+
+@end
+
+@implementation FIRAuthLegacyAppDelegate
+
+- (void)application:(UIApplication *)application
+ didReceiveRemoteNotification:(NSDictionary *)userInfo {
+ self.notificationReceived = userInfo;
+}
+
+@end
+
+/** @class FIRAuthModernAppDelegate
+ @brief A @c UIApplicationDelegate implementation that implements both
+ `application:didRegisterForRemoteNotificationsWithDeviceToken:` and
+ `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
+ */
+@interface FIRAuthModernAppDelegate : NSObject <UIApplicationDelegate>
+
+/** @var deviceTokenReceived
+ @brief The last device token received, if any.
+ */
+@property(nonatomic, copy, nullable) NSData *deviceTokenReceived;
+
+/** @var notificationReceived
+ @brief The last notification received, if any.
+ */
+@property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
+
+@end
+
+@implementation FIRAuthModernAppDelegate
+
+- (void)application:(UIApplication *)application
+ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+ self.deviceTokenReceived = deviceToken;
+}
+
+- (void)application:(UIApplication *)application
+ didReceiveRemoteNotification:(NSDictionary *)userInfo
+ fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+ self.notificationReceived = userInfo;
+ completionHandler(UIBackgroundFetchResultNewData);
+}
+
+@end
+
+/** @class FIRAuthAppDelegateProxyTests
+ @brief Unit tests for @c FIRAuthAppDelegateProxy .
+ */
+@interface FIRAuthAppDelegateProxyTests : XCTestCase
+@end
+@implementation FIRAuthAppDelegateProxyTests {
+ /** @var _mockApplication
+ @brief The mock UIApplication used for testing.
+ */
+ id _mockApplication;
+
+ /** @var _deviceToken
+ @brief The fake APNs device token for testing.
+ */
+ NSData *_deviceToken;
+
+ /** @var _notification
+ @brief The fake notification for testing.
+ */
+ NSDictionary* _notification;
+}
+
+- (void)setUp {
+ [super setUp];
+ _mockApplication = OCMClassMock([UIApplication class]);
+ _deviceToken = [@"asdf" dataUsingEncoding:NSUTF8StringEncoding];
+ _notification = @{ @"zxcv" : @1234 };
+}
+
+- (void)tearDown {
+ OCMVerifyAll(_mockApplication);
+ [super tearDown];
+}
+
+/** @fn testSharedInstance
+ @brief Tests that the shared instance is the same one.
+ */
+- (void)testSharedInstance {
+ FIRAuthAppDelegateProxy *proxy1 = [FIRAuthAppDelegateProxy sharedInstance];
+ FIRAuthAppDelegateProxy *proxy2 = [FIRAuthAppDelegateProxy sharedInstance];
+ XCTAssertEqual(proxy1, proxy2);
+}
+
+/** @fn testNilApplication
+ @brief Tests that initialization fails if the application is nil.
+ */
+- (void)testNilApplication {
+ XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:nil]);
+}
+
+/** @fn testNilDelegate
+ @brief Tests that initialization fails if the application's delegate is nil.
+ */
+- (void)testNilDelegate {
+ OCMExpect([_mockApplication delegate]).andReturn(nil);
+ XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
+}
+
+/** @fn testNonconformingDelegate
+ @brief Tests that initialization fails if the application's delegate does not conform to
+ @c UIApplicationDelegate protocol.
+ */
+- (void)testNonconformingDelegate {
+ OCMExpect([_mockApplication delegate]).andReturn(@"abc");
+ XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
+}
+
+/** @fn testDisabledByBundleEntry
+ @brief Tests that initialization fails if the proxy is disabled by a bundle entry.
+ */
+- (void)testDisabledByBundleEntry {
+ // Swizzle NSBundle's objectForInfoDictionaryKey to return @NO for the specific key.
+ Method method = class_getInstanceMethod([NSBundle class], @selector(objectForInfoDictionaryKey:));
+ __block IMP originalImplementation;
+ IMP newImplmentation = imp_implementationWithBlock(^id(id object, NSString *key) {
+ if ([key isEqualToString:@"FirebaseAppDelegateProxyEnabled"]) {
+ return @NO;
+ }
+ typedef id (*Implementation)(id object, SEL cmd, NSString *key);
+ return ((Implementation)originalImplementation)(object, @selector(objectForInfoDictionaryKey:),
+ key);
+ });
+ originalImplementation = method_setImplementation(method, newImplmentation);
+
+ // Verify that initialization fails.
+ FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
+ OCMStub([_mockApplication delegate]).andReturn(delegate);
+ XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
+
+ // Unswizzle.
+ imp_removeBlock(method_setImplementation(method, originalImplementation));
+}
+
+/** @fn testEmptyDelegateOneHandler
+ @brief Tests that the proxy works against an empty @c UIApplicationDelegate for one handler.
+ */
+- (void)testEmptyDelegateOneHandler {
+ FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
+ OCMExpect([_mockApplication delegate]).andReturn(delegate);
+ __weak id weakProxy;
+ @autoreleasepool {
+ FIRAuthAppDelegateProxy *proxy =
+ [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
+ XCTAssertNotNil(proxy);
+
+ // Verify `application:didReceiveRemoteNotification:` is not swizzled.
+ XCTAssertFalse([delegate respondsToSelector:
+ @selector(application:didReceiveRemoteNotification:)]);
+
+ // Verify the handler is called after being added.
+ __weak id weakHandler;
+ @autoreleasepool {
+ id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
+ [proxy addHandler:mockHandler];
+
+ // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
+ OCMExpect([mockHandler setAPNSToken:_deviceToken]);
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ OCMVerifyAll(mockHandler);
+
+ // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
+ OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
+ __block BOOL fetchCompletionHandlerCalled = NO;
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTAssertEqual(result, UIBackgroundFetchResultNoData);
+ fetchCompletionHandlerCalled = YES;
+ }];
+ OCMVerifyAll(mockHandler);
+ XCTAssertTrue(fetchCompletionHandlerCalled);
+
+ weakHandler = mockHandler;
+ XCTAssertNotNil(weakHandler);
+ }
+ // Verify the handler is not retained by the proxy.
+ XCTAssertNil(weakHandler);
+
+ // Verify nothing bad happens after the handler is released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTFail(@"Should not call completion handler.");
+ }];
+
+ weakProxy = proxy;
+ XCTAssertNotNil(weakProxy);
+ }
+ // Verify the proxy does not retain itself.
+ XCTAssertNil(weakProxy);
+ // Verify nothing bad happens after the proxy is released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTFail(@"Should not call completion handler.");
+ }];
+}
+
+/** @fn testLegacyDelegateTwoHandlers
+ @brief Tests that the proxy works against a legacy @c UIApplicationDelegate for two handlers.
+ */
+- (void)testLegacyDelegateTwoHandlers {
+ FIRAuthLegacyAppDelegate *delegate = [[FIRAuthLegacyAppDelegate alloc] init];
+ OCMExpect([_mockApplication delegate]).andReturn(delegate);
+ __weak id weakProxy;
+ @autoreleasepool {
+ FIRAuthAppDelegateProxy *proxy =
+ [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
+ XCTAssertNotNil(proxy);
+
+ // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler` is not swizzled.
+ XCTAssertFalse([delegate respondsToSelector:
+ @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]);
+
+ // Verify the handler is called after being added.
+ __weak id weakHandler1;
+ @autoreleasepool {
+ id mockHandler1 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
+ [proxy addHandler:mockHandler1];
+ __weak id weakHandler2;
+ @autoreleasepool {
+ id mockHandler2 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
+ [proxy addHandler:mockHandler2];
+
+ // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
+ OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
+ OCMExpect([mockHandler2 setAPNSToken:_deviceToken]);
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ OCMVerifyAll(mockHandler1);
+ OCMVerifyAll(mockHandler2);
+
+ // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
+ OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(YES);
+ // handler2 shouldn't been invoked because it is already handled by handler1.
+ [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
+ OCMVerifyAll(mockHandler1);
+ OCMVerifyAll(mockHandler2);
+ XCTAssertNil(delegate.notificationReceived);
+
+ weakHandler2 = mockHandler2;
+ XCTAssertNotNil(weakHandler2);
+ }
+ // Verify the handler2 is not retained by the proxy.
+ XCTAssertNil(weakHandler2);
+
+ // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
+ OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ OCMVerifyAll(mockHandler1);
+
+ // Verify NOT handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
+ OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(NO);
+ [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
+ OCMVerifyAll(mockHandler1);
+ XCTAssertEqualObjects(delegate.notificationReceived, _notification);
+ delegate.notificationReceived = nil;
+
+ weakHandler1 = mockHandler1;
+ XCTAssertNotNil(weakHandler1);
+ }
+ // Verify the handler1 is not retained by the proxy.
+ XCTAssertNil(weakHandler1);
+
+ // Verify the delegate still works after all handlers are released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
+ XCTAssertEqualObjects(delegate.notificationReceived, _notification);
+ delegate.notificationReceived = nil;
+
+ weakProxy = proxy;
+ XCTAssertNotNil(weakProxy);
+ }
+ // Verify the proxy does not retain itself.
+ XCTAssertNil(weakProxy);
+
+ // Verify the delegate still works after the proxy is released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
+ XCTAssertEqualObjects(delegate.notificationReceived, _notification);
+ delegate.notificationReceived = nil;
+}
+
+/** @fn testModernDelegateWithOtherInstance
+ @brief Tests that the proxy works against a modern @c UIApplicationDelegate along with
+ another unaffected instance.
+ */
+- (void)testModernDelegateWithUnaffectedInstance {
+ FIRAuthModernAppDelegate *delegate = [[FIRAuthModernAppDelegate alloc] init];
+ OCMExpect([_mockApplication delegate]).andReturn(delegate);
+ FIRAuthModernAppDelegate *unaffectedDelegate = [[FIRAuthModernAppDelegate alloc] init];
+ __weak id weakProxy;
+ @autoreleasepool {
+ FIRAuthAppDelegateProxy *proxy =
+ [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
+ XCTAssertNotNil(proxy);
+
+ // Verify `application:didReceiveRemoteNotification:` is not swizzled.
+ XCTAssertFalse([delegate respondsToSelector:
+ @selector(application:didReceiveRemoteNotification:)]);
+
+ // Verify the handler is called after being added.
+ __weak id weakHandler;
+ @autoreleasepool {
+ id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
+ [proxy addHandler:mockHandler];
+
+ // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
+ OCMExpect([mockHandler setAPNSToken:_deviceToken]);
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ OCMVerifyAll(mockHandler);
+ XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
+ delegate.deviceTokenReceived = nil;
+
+ // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
+ OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
+ __block BOOL fetchCompletionHandlerCalled = NO;
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTAssertEqual(result, UIBackgroundFetchResultNoData);
+ fetchCompletionHandlerCalled = YES;
+ }];
+ OCMVerifyAll(mockHandler);
+ XCTAssertTrue(fetchCompletionHandlerCalled);
+ XCTAssertNil(delegate.notificationReceived);
+
+ // Verify unaffected delegate instance.
+ [unaffectedDelegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ XCTAssertEqualObjects(unaffectedDelegate.deviceTokenReceived, _deviceToken);
+ unaffectedDelegate.deviceTokenReceived = nil;
+ fetchCompletionHandlerCalled = NO;
+ [unaffectedDelegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTAssertEqual(result, UIBackgroundFetchResultNewData);
+ fetchCompletionHandlerCalled = YES;
+ }];
+ XCTAssertTrue(fetchCompletionHandlerCalled);
+ XCTAssertEqualObjects(unaffectedDelegate.notificationReceived, _notification);
+ unaffectedDelegate.notificationReceived = nil;
+
+ weakHandler = mockHandler;
+ XCTAssertNotNil(weakHandler);
+ }
+ // Verify the handler is not retained by the proxy.
+ XCTAssertNil(weakHandler);
+
+ // Verify the delegate still works after the handler is released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
+ delegate.deviceTokenReceived = nil;
+ __block BOOL fetchCompletionHandlerCalled = NO;
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTAssertEqual(result, UIBackgroundFetchResultNewData);
+ fetchCompletionHandlerCalled = YES;
+ }];
+ XCTAssertEqualObjects(delegate.notificationReceived, _notification);
+ delegate.notificationReceived = nil;
+ XCTAssertTrue(fetchCompletionHandlerCalled);
+
+ weakProxy = proxy;
+ XCTAssertNotNil(weakProxy);
+ }
+ // Verify the proxy does not retain itself.
+ XCTAssertNil(weakProxy);
+
+ // Verify the delegate still works after the proxy is released.
+ [delegate application:_mockApplication
+ didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
+ XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
+ delegate.deviceTokenReceived = nil;
+ __block BOOL fetchCompletionHandlerCalled = NO;
+ [delegate application:_mockApplication
+ didReceiveRemoteNotification:_notification
+ fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+ XCTAssertEqual(result, UIBackgroundFetchResultNewData);
+ fetchCompletionHandlerCalled = YES;
+ }];
+ XCTAssertEqualObjects(delegate.notificationReceived, _notification);
+ delegate.notificationReceived = nil;
+ XCTAssertTrue(fetchCompletionHandlerCalled);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END