| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /*
- * 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
|