| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * 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 || \
- __TV_OS_VERSION_MAX_ALLOWED >= __TV_10_0 || \
- __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_14
- #import <UserNotifications/UserNotifications.h>
- #endif
- #import <XCTest/XCTest.h>
- #import <OCMock/OCMock.h>
- #import "FIRMessaging.h"
- #import "FIRMessagingRemoteNotificationsProxy.h"
- #import <GoogleUtilities/GULAppDelegateSwizzler.h>
- #pragma mark - Invalid App Delegate or UNNotificationCenter
- @interface RandomObject : NSObject
- @property(nonatomic, weak) id delegate;
- @end
- @implementation RandomObject
- - (void)application:(GULApplication *)application
- didReceiveRemoteNotification:(NSDictionary *)userInfo {
- }
- - (void)userNotificationCenter:(UNUserNotificationCenter *)center
- willPresentNotification:(UNNotification *)notification
- withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(ios(10.0), macos(10.14), tvos(10.0)){
- }
- #if TARGET_OS_IOS || TARGET_OS_OSX
- - (void)userNotificationCenter:(UNUserNotificationCenter *)center
- didReceiveNotificationResponse:(UNNotificationResponse *)response
- withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(macos(10.14), ios(10.0)){
- }
- #endif
- @end
- #pragma mark - Incomplete App Delegate
- @interface IncompleteAppDelegate : NSObject <GULApplicationDelegate>
- @end
- @implementation IncompleteAppDelegate
- @end
- #pragma mark - Fake AppDelegate
- @interface FakeAppDelegate : NSObject <GULApplicationDelegate>
- @property(nonatomic) BOOL remoteNotificationMethodWasCalled;
- @property(nonatomic) BOOL remoteNotificationWithFetchHandlerWasCalled;
- @property(nonatomic, strong) NSData *deviceToken;
- @property(nonatomic, strong) NSError *registerForRemoteNotificationsError;
- @end
- @implementation FakeAppDelegate
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-implementations"
- - (void)application:(GULApplication *)application
- didReceiveRemoteNotification:(NSDictionary *)userInfo {
- self.remoteNotificationMethodWasCalled = YES;
- }
- #pragma clang diagnostic pop
- #if TARGET_OS_IOS || TARGET_OS_TV
- - (void)application:(UIApplication *)application
- didReceiveRemoteNotification:(NSDictionary *)userInfo
- fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
- self.remoteNotificationWithFetchHandlerWasCalled = YES;
- }
- #endif
- - (void)application:(GULApplication *)application
- didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
- self.deviceToken = deviceToken;
- }
- - (void)application:(GULApplication *)application
- didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
- self.registerForRemoteNotificationsError = error;
- }
- @end
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 || \
- __TV_OS_VERSION_MAX_ALLOWED >= __TV_10_0 || \
- __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_14
- #pragma mark - Incompete UNUserNotificationCenterDelegate
- @interface IncompleteUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
- @end
- @implementation IncompleteUserNotificationCenterDelegate
- @end
- #pragma mark - Fake UNUserNotificationCenterDelegate
- @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 API_AVAILABLE(ios(10.0), macos(10.14), tvos(10.0)){
- self.willPresentWasCalled = YES;
- }
- #if TARGET_OS_IOS || TARGET_OS_OSX
- - (void)userNotificationCenter:(UNUserNotificationCenter *)center
- didReceiveNotificationResponse:(UNNotificationResponse *)response
- withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0), macos(10.14)){
- self.didReceiveResponseWasCalled = YES;
- }
- #endif
- @end
- #endif
- @interface GULAppDelegateSwizzler (FIRMessagingRemoteNotificationsProxyTest)
- + (void)resetProxyOriginalDelegateOnceToken;
- @end
- #pragma mark - Local, Per-Test Properties
- @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase
- @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy;
- @property(nonatomic, strong) id mockProxyClass;
- @property(nonatomic, strong) id mockMessaging;
- @property(nonatomic, strong) id mockUserNotificationCenter;
- @end
- @implementation FIRMessagingRemoteNotificationsProxyTest
- - (void)setUp {
- [super setUp];
- [GULAppDelegateSwizzler resetProxyOriginalDelegateOnceToken];
- _mockMessaging = OCMClassMock([FIRMessaging class]);
- OCMStub([_mockMessaging messaging]).andReturn(_mockMessaging);
- _proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init];
- _mockProxyClass = OCMClassMock([FIRMessagingRemoteNotificationsProxy class]);
- // Update +sharedProxy to always return our test instance
- OCMStub([_mockProxyClass sharedProxy]).andReturn(self.proxy);
- if (@available(macOS 10.14, iOS 10.0, *)) {
- _mockUserNotificationCenter = OCMPartialMock([UNUserNotificationCenter currentNotificationCenter]);
- }
- }
- - (void)tearDown {
- [_mockProxyClass stopMocking];
- _mockProxyClass = nil;
- [_mockMessaging stopMocking];
- _mockMessaging = nil;
- if (@available(macOS 10.14, iOS 10.0, *)) {
- [_mockUserNotificationCenter stopMocking];
- _mockUserNotificationCenter = nil;
- }
- _proxy = nil;
- [super tearDown];
- }
- #pragma mark - Method Swizzling Tests
- - (void)testSwizzlingNonAppDelegate {
- RandomObject *invalidAppDelegate = [[RandomObject alloc] init];
- [[GULAppDelegateSwizzler sharedApplication] setDelegate:(id<GULApplicationDelegate>)invalidAppDelegate];
- [self.proxy swizzleMethodsIfPossible];
- OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
- [invalidAppDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didReceiveRemoteNotification:@{}];
- }
- - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod {
- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
- [[GULAppDelegateSwizzler sharedApplication] setDelegate:incompleteAppDelegate];
- [self.proxy swizzleMethodsIfPossible];
- NSDictionary *notification = @{@"test" : @""};
- OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [incompleteAppDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didReceiveRemoteNotification:notification];
- #pragma clang diagnostic pop
- [self.mockMessaging verify];
- }
- - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod {
- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
- [[GULAppDelegateSwizzler sharedApplication] setDelegate:incompleteAppDelegate];
- [self.proxy swizzleMethodsIfPossible];
- #if TARGET_OS_IOS || TARGET_OS_TV
- SEL remoteNotificationWithFetchHandler =
- @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
- XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]);
- #endif
- SEL remoteNotification = @selector(application:didReceiveRemoteNotification:);
- XCTAssertTrue([incompleteAppDelegate respondsToSelector:remoteNotification]);
- }
- - (void)testSwizzledAppDelegateRemoteNotificationMethods {
- FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init];
- [[GULAppDelegateSwizzler sharedApplication] setDelegate:appDelegate];
- [self.proxy swizzleMethodsIfPossible];
- NSDictionary *notification = @{@"test" : @""};
- //Test application:didReceiveRemoteNotification:
- // Verify our swizzled method was called
- OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didReceiveRemoteNotification:notification];
- #pragma clang diagnostic pop
- // Verify our original method was called
- XCTAssertTrue(appDelegate.remoteNotificationMethodWasCalled);
- [self.mockMessaging verify];
- //Test application:didReceiveRemoteNotification:fetchCompletionHandler:
- #if TARGET_OS_IOS || TARGET_OS_TV
- // Verify our swizzled method was called
- OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
- [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didReceiveRemoteNotification:notification
- fetchCompletionHandler:^(UIBackgroundFetchResult result) {}];
- // Verify our original method was called
- XCTAssertTrue(appDelegate.remoteNotificationWithFetchHandlerWasCalled);
- [self.mockMessaging verify];
- #endif
- // Verify application:didRegisterForRemoteNotificationsWithDeviceToken:
- NSData *deviceToken = [NSData data];
- OCMExpect([self.mockMessaging setAPNSToken:deviceToken]);
- [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
- XCTAssertEqual(appDelegate.deviceToken, deviceToken);
- [self.mockMessaging verify];
- // Verify application:didFailToRegisterForRemoteNotificationsWithError:
- NSError *error = [NSError errorWithDomain:@"tests" code:-1 userInfo:nil];
- [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
- didFailToRegisterForRemoteNotificationsWithError:error];
- XCTAssertEqual(appDelegate.registerForRemoteNotificationsError, error);
- }
- - (void)testListeningForDelegateChangesOnInvalidUserNotificationCenter {
- if (@available(macOS 10.14, iOS 10.0, *)) {
- RandomObject *invalidNotificationCenter = [[RandomObject alloc] init];
- OCMStub([self.mockUserNotificationCenter currentNotificationCenter]).andReturn(invalidNotificationCenter);
- [self.proxy swizzleMethodsIfPossible];
- OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
- [(id<UNUserNotificationCenterDelegate>)invalidNotificationCenter.delegate
- userNotificationCenter:self.mockUserNotificationCenter
- willPresentNotification:OCMClassMock([UNNotification class])
- withCompletionHandler:^(UNNotificationPresentationOptions options) {
- }];
- }
- }
- - (void)testSwizzlingInvalidUserNotificationCenterDelegate {
- if (@available(macOS 10.14, iOS 10.0, *)) {
- RandomObject *invalidDelegate = [[RandomObject alloc] init];
- OCMStub([self.mockUserNotificationCenter delegate]).andReturn(invalidDelegate);
- [self.proxy swizzleMethodsIfPossible];
- OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
- [invalidDelegate
- userNotificationCenter:self.mockUserNotificationCenter
- willPresentNotification:OCMClassMock([UNNotification class])
- withCompletionHandler:^(UNNotificationPresentationOptions options) {
- }];
- }
- }
- - (void)testSwizzlingUserNotificationsCenterDelegate {
- if (@available(macOS 10.14, iOS 10.0, *)) {
- FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init];
- OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
- [self.proxy swizzleMethodsIfPossible];
- NSDictionary *message = @{@"message": @""};
- id notification = [self userNotificationWithMessage:message];
- OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
- [delegate
- userNotificationCenter:self.mockUserNotificationCenter
- willPresentNotification:notification
- withCompletionHandler:^(UNNotificationPresentationOptions options) {
- }];
- [self.mockMessaging verify];
- }
- }
- // Use a fake delegate that doesn't actually implement the needed delegate method.
- // Our swizzled method should not be called.
- - (void)testIncompleteUserNotificationCenterDelegateMethod {
- if (@available(macOS 10.14, iOS 10.0, *)) {
- IncompleteUserNotificationCenterDelegate *delegate =
- [[IncompleteUserNotificationCenterDelegate alloc] init];
- OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
- [self.proxy swizzleMethodsIfPossible];
- // 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 {
- FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init];
- OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
- [self.proxy swizzleMethodsIfPossible];
- NSDictionary *message = @{@"message": @""};
- // Verify userNotificationCenter:willPresentNotification:withCompletionHandler:
- OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
- if (@available(macOS 10.14, iOS 10.0, tvOS 10.0, *)) {
- // Invoking delegate method should also invoke our swizzled method
- // The swizzled method uses the +sharedProxy, which should be
- // returning our proxy.
- // Use non-nil, proper classes, otherwise our SDK bails out.
- [delegate userNotificationCenter:self.mockUserNotificationCenter
- willPresentNotification:[self userNotificationWithMessage:message]
- withCompletionHandler:^(NSUInteger options) {}];
- // Verify our original method was called
- XCTAssertTrue(delegate.willPresentWasCalled);
- // Verify our swizzled method was called
- [self.mockMessaging verify];
- }
- if (@available(macOS 10.14, iOS 10.0, *)) {
- // Verify userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
- OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
- #if TARGET_OS_IOS || TARGET_OS_OSX
- [delegate userNotificationCenter:self.mockUserNotificationCenter
- didReceiveNotificationResponse:[self userNotificationResponseWithMessage:message]
- withCompletionHandler:^{}];
- // Verify our original method was called
- XCTAssertTrue(delegate.didReceiveResponseWasCalled);
- // Verify our swizzled method was called
- [self.mockMessaging verify];
- #endif
- }
- }
- - (id)userNotificationResponseWithMessage:(NSDictionary *)message {
- #if TARGET_OS_IOS || TARGET_OS_OSX
- if (@available(macOS 10.14, iOS 10.0, *)) {
- // Stub out: response.[mock notification above]
- id mockNotificationResponse = OCMClassMock([UNNotificationResponse class]);
- id mockNotification = [self userNotificationWithMessage:message];
- OCMStub([mockNotificationResponse notification]).andReturn(mockNotification);
- return mockNotificationResponse;
- }
- #endif
- return nil;
- }
- - (UNNotification *)userNotificationWithMessage:(NSDictionary *)message API_AVAILABLE(macos(10.14), ios(10.0)){
- UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
- content.userInfo = message;
- id notificationRequest = OCMClassMock([UNNotificationRequest class]);
- OCMStub([notificationRequest content]).andReturn(content);
- id notification = OCMClassMock([UNNotification class]);
- OCMStub([notification request]).andReturn(notificationRequest);
- return notification;
- }
- @end
|