| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /*
- * 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.
- */
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- #import <UserNotifications/UserNotifications.h>
- #endif
- #import <XCTest/XCTest.h>
- #import <OCMock/OCMock.h>
- #import "FIRMessaging.h"
- #import "FIRMessagingRemoteNotificationsProxy.h"
- #pragma mark - Expose Internal Methods for Testing
- // Expose some internal properties and methods here, in order to test
- @interface FIRMessagingRemoteNotificationsProxy ()
- @property(readonly, nonatomic) BOOL didSwizzleMethods;
- @property(readonly, nonatomic) BOOL didSwizzleAppDelegateMethods;
- @property(readonly, nonatomic) BOOL hasSwizzledUserNotificationDelegate;
- @property(readonly, nonatomic) BOOL isObservingUserNotificationDelegateChanges;
- @property(strong, readonly, nonatomic) id userNotificationCenter;
- @property(strong, readonly, nonatomic) id currentUserNotificationCenterDelegate;
- + (instancetype)sharedProxy;
- - (BOOL)swizzleAppDelegateMethods:(id<UIApplicationDelegate>)appDelegate;
- - (void)listenForDelegateChangesInUserNotificationCenter:(id)notificationCenter;
- - (void)swizzleUserNotificationCenterDelegate:(id)delegate;
- - (void)unswizzleUserNotificationCenterDelegate:(id)delegate;
- void FCM_swizzle_appDidReceiveRemoteNotification(id self,
- SEL _cmd,
- UIApplication *app,
- NSDictionary *userInfo);
- void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler(
- id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo,
- void (^handler)(UIBackgroundFetchResult));
- void FCM_swizzle_willPresentNotificationWithHandler(
- id self, SEL _cmd, id center, id notification, void (^handler)(NSUInteger));
- void FCM_swizzle_didReceiveNotificationResponseWithHandler(
- id self, SEL _cmd, id center, id response, void (^handler)());
- @end
- #pragma mark - Incomplete App Delegate
- @interface IncompleteAppDelegate : NSObject <UIApplicationDelegate>
- @end
- @implementation IncompleteAppDelegate
- @end
- #pragma mark - Fake AppDelegate
- @interface FakeAppDelegate : NSObject <UIApplicationDelegate>
- @property(nonatomic) BOOL remoteNotificationMethodWasCalled;
- @property(nonatomic) BOOL remoteNotificationWithFetchHandlerWasCalled;
- @end
- @implementation FakeAppDelegate
- - (void)application:(UIApplication *)application
- didReceiveRemoteNotification:(NSDictionary *)userInfo {
- self.remoteNotificationMethodWasCalled = YES;
- }
- - (void)application:(UIApplication *)application
- didReceiveRemoteNotification:(NSDictionary *)userInfo
- fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
- self.remoteNotificationWithFetchHandlerWasCalled = YES;
- }
- @end
- #pragma mark - Incompete UNUserNotificationCenterDelegate
- @interface IncompleteUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
- @end
- @implementation IncompleteUserNotificationCenterDelegate
- @end
- #pragma mark - Fake UNUserNotificationCenterDelegate
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- @interface FakeUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
- @property(nonatomic) BOOL willPresentWasCalled;
- @property(nonatomic) BOOL didReceiveResponseWasCalled;
- @end
- @implementation FakeUserNotificationCenterDelegate
- - (void)userNotificationCenter:(UNUserNotificationCenter *)center
- willPresentNotification:(UNNotification *)notification
- withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))
- completionHandler {
- self.willPresentWasCalled = YES;
- }
- - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
- self.didReceiveResponseWasCalled = YES;
- }
- @end
- #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- #pragma mark - Local, Per-Test Properties
- @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase
- @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy;
- @property(nonatomic, strong) id mockProxy;
- @property(nonatomic, strong) id mockProxyClass;
- @property(nonatomic, strong) id mockMessagingClass;
- @end
- @implementation FIRMessagingRemoteNotificationsProxyTest
- - (void)setUp {
- [super setUp];
- _proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init];
- _mockProxy = OCMPartialMock(_proxy);
- _mockProxyClass = OCMClassMock([FIRMessagingRemoteNotificationsProxy class]);
- // Update +sharedProxy to always return our partial mock of FIRMessagingRemoteNotificationsProxy
- OCMStub([_mockProxyClass sharedProxy]).andReturn(_mockProxy);
- // Many of our swizzled methods call [FIRMessaging messaging], but we don't need it,
- // so just stub it to return nil
- _mockMessagingClass = OCMClassMock([FIRMessaging class]);
- OCMStub([_mockMessagingClass messaging]).andReturn(nil);
- }
- - (void)tearDown {
- [_mockMessagingClass stopMocking];
- _mockMessagingClass = nil;
- [_mockProxyClass stopMocking];
- _mockProxyClass = nil;
- [_mockProxy stopMocking];
- _mockProxy = nil;
- _proxy = nil;
- [super tearDown];
- }
- #pragma mark - Method Swizzling Tests
- - (void)testSwizzlingNonAppDelegate {
- id randomObject = @"Random Object that is not an App Delegate";
- [self.proxy swizzleAppDelegateMethods:randomObject];
- XCTAssertFalse(self.proxy.didSwizzleAppDelegateMethods);
- }
- - (void)testSwizzlingAppDelegate {
- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
- [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate];
- XCTAssertTrue(self.proxy.didSwizzleAppDelegateMethods);
- }
- - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod {
- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
- [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate];
- SEL selector = @selector(application:didReceiveRemoteNotification:);
- XCTAssertTrue([incompleteAppDelegate respondsToSelector:selector]);
- [incompleteAppDelegate application:OCMClassMock([UIApplication class])
- didReceiveRemoteNotification:@{}];
- // Verify our swizzled method was called
- OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification);
- }
- // If the remote notification with fetch handler is NOT implemented, we will force-implement
- // the backup -application:didReceiveRemoteNotification: method
- - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod {
- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
- [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate];
- SEL remoteNotificationWithFetchHandler =
- @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
- XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]);
- SEL remoteNotification = @selector(application:didReceiveRemoteNotification:);
- XCTAssertTrue([incompleteAppDelegate respondsToSelector:remoteNotification]);
- }
- - (void)testSwizzledAppDelegateRemoteNotificationMethods {
- FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init];
- [self.mockProxy swizzleAppDelegateMethods:appDelegate];
- [appDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}];
- // Verify our swizzled method was called
- OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification);
- // Verify our original method was called
- XCTAssertTrue(appDelegate.remoteNotificationMethodWasCalled);
- // Now call the remote notification with handler method
- [appDelegate application:OCMClassMock([UIApplication class])
- didReceiveRemoteNotification:@{}
- fetchCompletionHandler:^(UIBackgroundFetchResult result) {}];
- // Verify our swizzled method was called
- OCMVerify(FCM_swizzle_appDidReceiveRemoteNotificationWithHandler);
- // Verify our original method was called
- XCTAssertTrue(appDelegate.remoteNotificationWithFetchHandlerWasCalled);
- }
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- - (void)testListeningForDelegateChangesOnInvalidUserNotificationCenter {
- id randomObject = @"Random Object that is not a User Notification Center";
- [self.proxy listenForDelegateChangesInUserNotificationCenter:randomObject];
- XCTAssertFalse(self.proxy.isObservingUserNotificationDelegateChanges);
- }
- - (void)testSwizzlingInvalidUserNotificationCenterDelegate {
- id randomObject = @"Random Object that is not a User Notification Center Delegate";
- [self.proxy swizzleUserNotificationCenterDelegate:randomObject];
- XCTAssertFalse(self.proxy.hasSwizzledUserNotificationDelegate);
- }
- - (void)testSwizzlingUserNotificationsCenterDelegate {
- FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init];
- [self.proxy swizzleUserNotificationCenterDelegate:delegate];
- XCTAssertTrue(self.proxy.hasSwizzledUserNotificationDelegate);
- }
- // Use a fake delegate that doesn't actually implement the needed delegate method.
- // Our swizzled method should not be called.
- - (void)testIncompleteUserNotificationCenterDelegateMethod {
- // Early exit if running on pre iOS 10
- if (![UNNotification class]) {
- return;
- }
- IncompleteUserNotificationCenterDelegate *delegate =
- [[IncompleteUserNotificationCenterDelegate alloc] init];
- [self.mockProxy swizzleUserNotificationCenterDelegate:delegate];
- // Because the incomplete delete does not implement either of the optional delegate methods, we
- // should swizzle nothing. If we had swizzled them, then respondsToSelector: would return YES
- // even though the delegate does not implement the methods.
- SEL willPresentSelector = @selector(userNotificationCenter:willPresentNotification:withCompletionHandler:);
- XCTAssertFalse([delegate respondsToSelector:willPresentSelector]);
- SEL didReceiveResponseSelector =
- @selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:);
- XCTAssertFalse([delegate respondsToSelector:didReceiveResponseSelector]);
- }
- // Use an object that does actually implement the optional methods. Both should be called.
- - (void)testSwizzledUserNotificationsCenterDelegate {
- // Early exit if running on pre iOS 10
- if (![UNNotification class]) {
- return;
- }
- FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init];
- [self.mockProxy swizzleUserNotificationCenterDelegate:delegate];
- // Invoking delegate method should also invoke our swizzled method
- // The swizzled method uses the +sharedProxy, which should be
- // returning our mocked proxy.
- // Use non-nil, proper classes, otherwise our SDK bails out.
- [delegate userNotificationCenter:OCMClassMock([UNUserNotificationCenter class])
- willPresentNotification:[self generateMockNotification]
- withCompletionHandler:^(NSUInteger options) {}];
- // Verify our swizzled method was called
- OCMVerify(FCM_swizzle_willPresentNotificationWithHandler);
- // Verify our original method was called
- XCTAssertTrue(delegate.willPresentWasCalled);
- [delegate userNotificationCenter:OCMClassMock([UNUserNotificationCenter class])
- didReceiveNotificationResponse:[self generateMockNotificationResponse]
- withCompletionHandler:^{}];
- // Verify our swizzled method was called
- OCMVerify(FCM_swizzle_appDidReceiveRemoteNotificationWithHandler);
- // Verify our original method was called
- XCTAssertTrue(delegate.didReceiveResponseWasCalled);
- }
- - (id)generateMockNotification {
- // Stub out: notification.request.content.userInfo
- id mockNotification = OCMClassMock([UNNotification class]);
- id mockRequest = OCMClassMock([UNNotificationRequest class]);
- id mockContent = OCMClassMock([UNNotificationContent class]);
- OCMStub([mockContent userInfo]).andReturn(@{});
- OCMStub([mockRequest content]).andReturn(mockContent);
- OCMStub([mockNotification request]).andReturn(mockRequest);
- return mockNotification;
- }
- - (id)generateMockNotificationResponse {
- // Stub out: response.[mock notification above]
- id mockNotificationResponse = OCMClassMock([UNNotificationResponse class]);
- id mockNotification = [self generateMockNotification];
- OCMStub([mockNotificationResponse notification]).andReturn(mockNotification);
- return mockNotificationResponse;
- }
- #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- @end
|