From bc6e0c3606a7392d611bffec8dd0de180bfc6f2e Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 11 Jan 2019 09:05:52 -0800 Subject: [PATCH 1/5] Prevent Messaging and IID singleton usage during tests. --- Example/Messaging/Tests/FIRMessagingTest.m | 36 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/Example/Messaging/Tests/FIRMessagingTest.m b/Example/Messaging/Tests/FIRMessagingTest.m index 66357e8b102..a4af7f06f2f 100644 --- a/Example/Messaging/Tests/FIRMessagingTest.m +++ b/Example/Messaging/Tests/FIRMessagingTest.m @@ -20,25 +20,37 @@ #import #import +#import #import "FIRMessaging.h" #import "FIRMessaging_Private.h" extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption; +/// The NSUserDefaults domain for testing. +NSString *const kFIRMessagingDefaultsTestDomain = @"com.messaging.tests"; + @interface FIRInstanceID (ExposedForTest) -+ (FIRInstanceID *)instanceIDForTests; +/// Private initializer to avoid singleton usage. +- (FIRInstanceID *)initPrivately; + +/// Starts fetching and configuration of InstanceID. This is necessary after the `initPrivately` +/// call. +- (void)start; @end @interface FIRMessaging () -+ (FIRMessaging *)messagingForTests; + +/// Surface internal initializer to avoid singleton usage during tests. +- (instancetype)initWithAnalytics:(nullable id)analytics + withInstanceID:(FIRInstanceID *)instanceID + withUserDefaults:(NSUserDefaults *)defaults; @property(nonatomic, readwrite, strong) NSString *defaultFcmToken; @property(nonatomic, readwrite, strong) NSData *apnsTokenData; @property(nonatomic, readwrite, strong) FIRInstanceID *instanceID; -@property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults; // Direct Channel Methods - (void)updateAutomaticClientConnection; @@ -49,6 +61,7 @@ - (BOOL)shouldBeConnectedAutomatically; @interface FIRMessagingTest : XCTestCase @property(nonatomic, readonly, strong) FIRMessaging *messaging; +@property(nonatomic, strong) NSUserDefaults *messagingDefaults; @property(nonatomic, readwrite, strong) id mockMessaging; @property(nonatomic, readwrite, strong) id mockInstanceID; @property(nonatomic, readwrite, strong) id mockFirebaseApp; @@ -59,8 +72,19 @@ @implementation FIRMessagingTest - (void)setUp { [super setUp]; - _messaging = [FIRMessaging messagingForTests]; - _messaging.instanceID = [FIRInstanceID instanceIDForTests]; + + // Create the messaging instance with all the necessary dependencies. + FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately]; + [instanceID start]; + + // Use custom defaults so we can empty it accordingly. + NSUserDefaults *defaults = + [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingDefaultsTestDomain]; + _messagingDefaults = defaults; + _messaging = [[FIRMessaging alloc] initWithAnalytics:nil + withInstanceID:instanceID + withUserDefaults:_messagingDefaults]; + _mockFirebaseApp = OCMClassMock([FIRApp class]); OCMStub([_mockFirebaseApp defaultApp]).andReturn(_mockFirebaseApp); _mockInstanceID = OCMPartialMock(self.messaging.instanceID); @@ -69,6 +93,8 @@ - (void)setUp { } - (void)tearDown { + [self.messagingDefaults removePersistentDomainForName:kFIRMessagingDefaultsTestDomain]; + self.messagingDefaults = nil; self.messaging.shouldEstablishDirectChannel = NO; self.messaging.defaultFcmToken = nil; self.messaging.apnsTokenData = nil; From bf6911d022fdd0fc3d46757f647964a17337c109 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Mon, 14 Jan 2019 10:50:50 -0500 Subject: [PATCH 2/5] Remove other uses of static Messaging instance. --- Example/Firebase.xcodeproj/project.pbxproj | 6 ++ .../Tests/FIRMessagingLinkHandlingTest.m | 10 ++- .../Tests/FIRMessagingReceiverTest.m | 19 ++++-- ...FIRMessagingRemoteNotificationsProxyTest.m | 22 ++----- .../Tests/FIRMessagingTestUtilities.h | 44 +++++++++++++ .../Tests/FIRMessagingTestUtilities.m | 64 +++++++++++++++++++ Firebase/Messaging/FIRMessaging.m | 2 +- Firebase/Messaging/FIRMessagingReceiver.h | 14 +++- Firebase/Messaging/FIRMessagingReceiver.m | 23 +++++-- 9 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 Example/Messaging/Tests/FIRMessagingTestUtilities.h create mode 100644 Example/Messaging/Tests/FIRMessagingTestUtilities.m diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 7debc404cc0..829c7cdfad8 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -617,6 +617,7 @@ EDD53E2A211B08A300376BFF /* FIRComponentTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD53E28211B08A300376BFF /* FIRComponentTestUtilities.m */; }; EDD53E2B211B08A300376BFF /* FIRComponentTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD53E28211B08A300376BFF /* FIRComponentTestUtilities.m */; }; EDD53E2C211B08A300376BFF /* FIRComponentTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD53E28211B08A300376BFF /* FIRComponentTestUtilities.m */; }; + EDF5242C21EA37AA00BB24C6 /* FIRMessagingTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1345,6 +1346,8 @@ EDD53E24211A442D00376BFF /* FIRAuthInteropFake.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIRAuthInteropFake.m; path = Shared/FIRAuthInteropFake.m; sourceTree = ""; }; EDD53E28211B08A300376BFF /* FIRComponentTestUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIRComponentTestUtilities.m; path = Shared/FIRComponentTestUtilities.m; sourceTree = ""; }; EDD53E29211B08A300376BFF /* FIRComponentTestUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FIRComponentTestUtilities.h; path = Shared/FIRComponentTestUtilities.h; sourceTree = ""; }; + EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIRMessagingTestUtilities.h; sourceTree = ""; }; + EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingTestUtilities.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2340,6 +2343,8 @@ DE9315D71E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.m */, DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */, DE9315D81E8738B70083EDBF /* Info.plist */, + EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */, + EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */, ); path = Tests; sourceTree = ""; @@ -4419,6 +4424,7 @@ DE9315F91E8738E60083EDBF /* FIRMessagingFakeConnection.m in Sources */, DE9316021E8738E60083EDBF /* FIRMessagingServiceTest.m in Sources */, DE9315FE1E8738E60083EDBF /* FIRMessagingRegistrarTest.m in Sources */, + EDF5242C21EA37AA00BB24C6 /* FIRMessagingTestUtilities.m in Sources */, DE9316031E8738E60083EDBF /* FIRMessagingSyncMessageManagerTest.m in Sources */, DE9315FF1E8738E60083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */, DEF61BFD216E8B1100A738D4 /* FIRMessagingReceiverTest.m in Sources */, diff --git a/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m b/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m index 33797a2f197..07fb0d635db 100644 --- a/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m +++ b/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m @@ -16,15 +16,18 @@ #import +#import #import #import "FIRMessaging.h" #import "FIRMessagingConstants.h" #import "FIRMessagingTestNotificationUtilities.h" +#import "FIRMessagingTestUtilities.h" + +NSString *const kFIRMessagingTestsLinkHandlingSuiteName = @"com.messaging.test_linkhandling"; @interface FIRMessaging () -+ (FIRMessaging *)messagingForTests; - (NSURL *)linkURLFromMessage:(NSDictionary *)message; @end @@ -39,10 +42,13 @@ @implementation FIRMessagingLinkHandlingTest - (void)setUp { [super setUp]; - _messaging = [FIRMessaging messagingForTests]; + + NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingTestsLinkHandlingSuiteName]; + _messaging = [FIRMessagingTestUtilities messagingForTestsWithUserDefaults:defaults]; } - (void)tearDown { + [self.messaging.messagingUserDefaults removePersistentDomainForName:kFIRMessagingTestsLinkHandlingSuiteName]; _messaging = nil; [super tearDown]; } diff --git a/Example/Messaging/Tests/FIRMessagingReceiverTest.m b/Example/Messaging/Tests/FIRMessagingReceiverTest.m index 02818b754b7..163af59f227 100644 --- a/Example/Messaging/Tests/FIRMessagingReceiverTest.m +++ b/Example/Messaging/Tests/FIRMessagingReceiverTest.m @@ -18,14 +18,15 @@ #import +#import +#import #import #import "FIRMessaging.h" #import "FIRMessaging_Private.h" +#import "FIRMessagingTestUtilities.h" -@interface FIRMessaging () -+ (FIRMessaging *)messagingForTests; -@end +NSString *const kFIRMessagingTestsReceiverSuiteName = @"com.messaging.test_receivierTest"; @interface FIRMessagingReceiverTest : XCTestCase @property(nonatomic, readonly, strong) FIRMessaging *messaging; @@ -36,9 +37,15 @@ @implementation FIRMessagingReceiverTest - (void)setUp { [super setUp]; - _messaging = [FIRMessaging messagingForTests]; - [[NSUserDefaults standardUserDefaults] - removePersistentDomainForName:[NSBundle mainBundle].bundleIdentifier]; + NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingTestsReceiverSuiteName]; + _messaging = [FIRMessagingTestUtilities messagingForTestsWithUserDefaults:defaults]; +} + +- (void)tearDown { + [self.messaging.messagingUserDefaults removePersistentDomainForName:kFIRMessagingTestsReceiverSuiteName]; + _messaging = nil; + + [super tearDown]; } - (void)testUseMessagingDelegate { diff --git a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m index 0e972798c12..70936ef0092 100644 --- a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m +++ b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m @@ -112,8 +112,6 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy; -@property(nonatomic, strong) id mockProxy; -@property(nonatomic, strong) id mockProxyClass; @property(nonatomic, strong) id mockMessagingClass; @end @@ -123,10 +121,6 @@ @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]); @@ -137,12 +131,6 @@ - (void)tearDown { [_mockMessagingClass stopMocking]; _mockMessagingClass = nil; - [_mockProxyClass stopMocking]; - _mockProxyClass = nil; - - [_mockProxy stopMocking]; - _mockProxy = nil; - _proxy = nil; [super tearDown]; } @@ -163,7 +151,7 @@ - (void)testSwizzlingAppDelegate { - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; + [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate]; [incompleteAppDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}]; @@ -173,7 +161,7 @@ - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; + [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate]; SEL remoteNotificationWithFetchHandler = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]); @@ -183,7 +171,7 @@ - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { - (void)testSwizzledAppDelegateRemoteNotificationMethods { FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:appDelegate]; + [self.proxy swizzleAppDelegateMethods:appDelegate]; [appDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}]; // Verify our swizzled method was called OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification); @@ -230,7 +218,7 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod { } IncompleteUserNotificationCenterDelegate *delegate = [[IncompleteUserNotificationCenterDelegate alloc] init]; - [self.mockProxy swizzleUserNotificationCenterDelegate:delegate]; + [self.proxy 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. @@ -248,7 +236,7 @@ - (void)testSwizzledUserNotificationsCenterDelegate { return; } FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init]; - [self.mockProxy swizzleUserNotificationCenterDelegate:delegate]; + [self.proxy swizzleUserNotificationCenterDelegate:delegate]; // Invoking delegate method should also invoke our swizzled method // The swizzled method uses the +sharedProxy, which should be // returning our mocked proxy. diff --git a/Example/Messaging/Tests/FIRMessagingTestUtilities.h b/Example/Messaging/Tests/FIRMessagingTestUtilities.h new file mode 100644 index 00000000000..55805567fff --- /dev/null +++ b/Example/Messaging/Tests/FIRMessagingTestUtilities.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRMessaging (TestUtilities) +// Surface the user defaults instance to clean up after tests. +@property(nonatomic, strong) NSUserDefaults *messagingUserDefaults; +@end + +@interface FIRMessagingTestUtilities : NSObject + +/** + Creates an instance of FIRMessaging to use with tests, and will instantiate a new instance of + InstanceID. + + Note: This does not create a FIRApp instance and call `configureWithApp:`. If required, it's up to + each test to do so. + + @param userDefaults The user defaults to be used for Messaging. + @return An instance of FIRMessaging with everything initialized. + */ ++ (FIRMessaging *)messagingForTestsWithUserDefaults:(NSUserDefaults *)userDefaults; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/Messaging/Tests/FIRMessagingTestUtilities.m b/Example/Messaging/Tests/FIRMessagingTestUtilities.m new file mode 100644 index 00000000000..729587587c5 --- /dev/null +++ b/Example/Messaging/Tests/FIRMessagingTestUtilities.m @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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 "FIRMessagingTestUtilities.h" + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRInstanceID (ExposedForTest) + +/// Private initializer to avoid singleton usage. +- (FIRInstanceID *)initPrivately; + +/// Starts fetching and configuration of InstanceID. This is necessary after the `initPrivately` +/// call. +- (void)start; + +@end + +@interface FIRMessaging (ExposedForTest) + +/// Surface internal initializer to avoid singleton usage during tests. +- (instancetype)initWithAnalytics:(nullable id)analytics + withInstanceID:(FIRInstanceID *)instanceID + withUserDefaults:(NSUserDefaults *)defaults; + +/// Kicks off required calls for some messaging tests. +- (void)start; + +@end + +@implementation FIRMessagingTestUtilities + ++ (FIRMessaging *)messagingForTestsWithUserDefaults:(NSUserDefaults *)userDefaults { + // Create the messaging instance with all the necessary dependencies. + FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately]; + [instanceID start]; + + // Create the messaging instance and call `start`. + FIRMessaging *messaging = [[FIRMessaging alloc] initWithAnalytics:nil + withInstanceID:instanceID + withUserDefaults:userDefaults]; + [messaging start]; + return messaging; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 7aa439191ed..9c5485efff4 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -322,7 +322,7 @@ - (void)setupNotificationListeners { } - (void)setupReceiver { - self.receiver = [[FIRMessagingReceiver alloc] init]; + self.receiver = [[FIRMessagingReceiver alloc] initWithUserDefaults:self.messagingUserDefaults]; self.receiver.delegate = self; } diff --git a/Firebase/Messaging/FIRMessagingReceiver.h b/Firebase/Messaging/FIRMessagingReceiver.h index e312420449f..8b5aa585bc2 100644 --- a/Firebase/Messaging/FIRMessagingReceiver.h +++ b/Firebase/Messaging/FIRMessagingReceiver.h @@ -17,18 +17,28 @@ #import "FIRMessagingDataMessageManager.h" #import "FIRMessaging.h" +NS_ASSUME_NONNULL_BEGIN + @class FIRMessagingReceiver; @protocol FIRMessagingReceiverDelegate -- (void)receiver:(nonnull FIRMessagingReceiver *)receiver - receivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage; +- (void)receiver:(FIRMessagingReceiver *)receiver + receivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage; @end @interface FIRMessagingReceiver : NSObject +/// Default initializer for creating the messaging receiver. +- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults NS_DESIGNATED_INITIALIZER; + +/// Use `initWithUserDefaults:` instead. +- (instancetype)init NS_UNAVAILABLE; + @property(nonatomic, weak, nullable) id delegate; /// Whether to use direct channel for direct channel message callback handler in all iOS versions. @property(nonatomic, assign) BOOL useDirectChannel; @end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/FIRMessagingReceiver.m b/Firebase/Messaging/FIRMessagingReceiver.m index ac8a6837561..05e6a75bc13 100644 --- a/Firebase/Messaging/FIRMessagingReceiver.m +++ b/Firebase/Messaging/FIRMessagingReceiver.m @@ -36,8 +36,22 @@ static int downstreamMessageID = 0; +@interface FIRMessagingReceiver () +@property(nonatomic, strong) NSUserDefaults *defaults; +@end + @implementation FIRMessagingReceiver +#pragma mark - Initializer + +- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults { + self = [super init]; + if (self != nil) { + _defaults = defaults; + } + return self; +} + #pragma mark - FIRMessagingDataMessageManager protocol - (void)didReceiveMessage:(NSDictionary *)message withIdentifier:(nullable NSString *)messageID { @@ -152,9 +166,8 @@ + (NSString *)nextMessageID { - (BOOL)useDirectChannel { // Check storage - NSUserDefaults *messagingDefaults = [NSUserDefaults standardUserDefaults]; id shouldUseMessagingDelegate = - [messagingDefaults objectForKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; + [_defaults objectForKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; if (shouldUseMessagingDelegate) { return [shouldUseMessagingDelegate boolValue]; } @@ -170,12 +183,10 @@ - (BOOL)useDirectChannel { } - (void)setUseDirectChannel:(BOOL)useDirectChannel { - NSUserDefaults *messagingDefaults = [NSUserDefaults standardUserDefaults]; BOOL shouldUseMessagingDelegate = [self useDirectChannel]; if (useDirectChannel != shouldUseMessagingDelegate) { - [messagingDefaults setBool:useDirectChannel - forKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; - [messagingDefaults synchronize]; + [_defaults setBool:useDirectChannel forKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; + [_defaults synchronize]; } } From aa1eca17d26907e02ea0f0b185b8b09501869b92 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Tue, 22 Jan 2019 23:02:17 -0500 Subject: [PATCH 3/5] Removed usages of messagingForTests. --- .../Tests/FIRMessagingReceiverTest.m | 2 +- ...FIRMessagingRemoteNotificationsProxyTest.m | 22 ++++++++++++---- .../Messaging/Tests/FIRMessagingServiceTest.m | 9 +++++-- Firebase/Messaging/FIRMessaging.m | 25 +++++-------------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Example/Messaging/Tests/FIRMessagingReceiverTest.m b/Example/Messaging/Tests/FIRMessagingReceiverTest.m index 163af59f227..257b232a470 100644 --- a/Example/Messaging/Tests/FIRMessagingReceiverTest.m +++ b/Example/Messaging/Tests/FIRMessagingReceiverTest.m @@ -26,7 +26,7 @@ #import "FIRMessaging_Private.h" #import "FIRMessagingTestUtilities.h" -NSString *const kFIRMessagingTestsReceiverSuiteName = @"com.messaging.test_receivierTest"; +NSString *const kFIRMessagingTestsReceiverSuiteName = @"com.messaging.test_receiverTest"; @interface FIRMessagingReceiverTest : XCTestCase @property(nonatomic, readonly, strong) FIRMessaging *messaging; diff --git a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m index 70936ef0092..0e972798c12 100644 --- a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m +++ b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m @@ -112,6 +112,8 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy; +@property(nonatomic, strong) id mockProxy; +@property(nonatomic, strong) id mockProxyClass; @property(nonatomic, strong) id mockMessagingClass; @end @@ -121,6 +123,10 @@ @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]); @@ -131,6 +137,12 @@ - (void)tearDown { [_mockMessagingClass stopMocking]; _mockMessagingClass = nil; + [_mockProxyClass stopMocking]; + _mockProxyClass = nil; + + [_mockProxy stopMocking]; + _mockProxy = nil; + _proxy = nil; [super tearDown]; } @@ -151,7 +163,7 @@ - (void)testSwizzlingAppDelegate { - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate]; + [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; [incompleteAppDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}]; @@ -161,7 +173,7 @@ - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate]; + [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; SEL remoteNotificationWithFetchHandler = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]); @@ -171,7 +183,7 @@ - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { - (void)testSwizzledAppDelegateRemoteNotificationMethods { FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init]; - [self.proxy swizzleAppDelegateMethods:appDelegate]; + [self.mockProxy swizzleAppDelegateMethods:appDelegate]; [appDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}]; // Verify our swizzled method was called OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification); @@ -218,7 +230,7 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod { } IncompleteUserNotificationCenterDelegate *delegate = [[IncompleteUserNotificationCenterDelegate alloc] init]; - [self.proxy swizzleUserNotificationCenterDelegate:delegate]; + [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. @@ -236,7 +248,7 @@ - (void)testSwizzledUserNotificationsCenterDelegate { return; } FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init]; - [self.proxy swizzleUserNotificationCenterDelegate:delegate]; + [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. diff --git a/Example/Messaging/Tests/FIRMessagingServiceTest.m b/Example/Messaging/Tests/FIRMessagingServiceTest.m index bb447472951..65019806c66 100644 --- a/Example/Messaging/Tests/FIRMessagingServiceTest.m +++ b/Example/Messaging/Tests/FIRMessagingServiceTest.m @@ -22,6 +22,7 @@ #import "FIRMessaging.h" #import "FIRMessagingClient.h" #import "FIRMessagingPubSub.h" +#import "FIRMessagingTestUtilities.h" #import "FIRMessagingTopicsCommon.h" #import "InternalHeaders/FIRMessagingInternalUtilities.h" #import "NSError+FIRMessaging.h" @@ -31,8 +32,9 @@ @"yUTTzK6dhIvLqzqqCSabaa4TQVM0pGTmF6r7tmMHPe6VYiGMHuCwJFgj5v97xl78sUNMLwuPPhoci8z_" @"QGlCrTbxCFGzEUfvA3fGpGgIVQU2W6"; +NSString *const kFIRMessagingTestsServiceSuiteName = @"com.messaging.test_serviceTest"; + @interface FIRMessaging () -+ (FIRMessaging *)messagingForTests; @property(nonatomic, readwrite, strong) FIRMessagingClient *client; @property(nonatomic, readwrite, strong) FIRMessagingPubSub *pubsub; @property(nonatomic, readwrite, strong) NSString *defaultFcmToken; @@ -55,7 +57,8 @@ @interface FIRMessagingServiceTest : XCTestCase { @implementation FIRMessagingServiceTest - (void)setUp { - _messaging = [FIRMessaging messagingForTests]; + NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingTestsServiceSuiteName]; + _messaging = [FIRMessagingTestUtilities messagingForTestsWithUserDefaults:defaults]; _messaging.defaultFcmToken = kFakeToken; _mockPubSub = OCMPartialMock(_messaging.pubsub); [_mockPubSub setClient:nil]; @@ -63,6 +66,8 @@ - (void)setUp { } - (void)tearDown { + [_messaging.messagingUserDefaults removePersistentDomainForName:kFIRMessagingTestsServiceSuiteName]; + _messaging = nil; [_mockPubSub stopMocking]; [super tearDown]; } diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 9c5485efff4..57e1df55829 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -162,32 +162,19 @@ @interface FIRMessaging () @implementation FIRMessaging -// File static to support InstanceID tests that call [FIRMessaging messaging] after -// [FIRMessaging messagingForTests]. -static FIRMessaging *sMessaging; - + (FIRMessaging *)messaging { - if (sMessaging != nil) { - return sMessaging; - } FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here. - id messaging = + id instance = FIR_COMPONENT(FIRMessagingInstanceProvider, defaultApp.container); + // We know the instance coming from the container is a FIRMessaging instance, cast it and move on. + FIRMessaging *messaging = (FIRMessaging *)instance; + static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [(FIRMessaging *)messaging start]; + [messaging start]; }); - sMessaging = (FIRMessaging *)messaging; - return sMessaging; -} - -+ (FIRMessaging *)messagingForTests { - sMessaging = [[FIRMessaging alloc] initWithAnalytics:nil - withInstanceID:[FIRInstanceID instanceID] - withUserDefaults:[NSUserDefaults standardUserDefaults]]; - [sMessaging start]; - return sMessaging; + return messaging; } - (instancetype)initWithAnalytics:(nullable id)analytics From 2e54c48b0a8280762d2e40c4d23c1c40a09397e4 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Tue, 22 Jan 2019 23:18:41 -0500 Subject: [PATCH 4/5] Remove unused imports, unify testing code. --- .../Tests/FIRMessagingLinkHandlingTest.m | 1 - .../Tests/FIRMessagingReceiverTest.m | 2 -- Example/Messaging/Tests/FIRMessagingTest.m | 31 +++---------------- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m b/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m index 07fb0d635db..b8a2937e606 100644 --- a/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m +++ b/Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m @@ -16,7 +16,6 @@ #import -#import #import #import "FIRMessaging.h" diff --git a/Example/Messaging/Tests/FIRMessagingReceiverTest.m b/Example/Messaging/Tests/FIRMessagingReceiverTest.m index 257b232a470..95e6dd9c497 100644 --- a/Example/Messaging/Tests/FIRMessagingReceiverTest.m +++ b/Example/Messaging/Tests/FIRMessagingReceiverTest.m @@ -18,8 +18,6 @@ #import -#import -#import #import #import "FIRMessaging.h" diff --git a/Example/Messaging/Tests/FIRMessagingTest.m b/Example/Messaging/Tests/FIRMessagingTest.m index a4af7f06f2f..3fb0477ff45 100644 --- a/Example/Messaging/Tests/FIRMessagingTest.m +++ b/Example/Messaging/Tests/FIRMessagingTest.m @@ -24,30 +24,15 @@ #import "FIRMessaging.h" #import "FIRMessaging_Private.h" +#import "FIRMessagingTestUtilities.h" extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption; /// The NSUserDefaults domain for testing. NSString *const kFIRMessagingDefaultsTestDomain = @"com.messaging.tests"; -@interface FIRInstanceID (ExposedForTest) - -/// Private initializer to avoid singleton usage. -- (FIRInstanceID *)initPrivately; - -/// Starts fetching and configuration of InstanceID. This is necessary after the `initPrivately` -/// call. -- (void)start; - -@end - @interface FIRMessaging () -/// Surface internal initializer to avoid singleton usage during tests. -- (instancetype)initWithAnalytics:(nullable id)analytics - withInstanceID:(FIRInstanceID *)instanceID - withUserDefaults:(NSUserDefaults *)defaults; - @property(nonatomic, readwrite, strong) NSString *defaultFcmToken; @property(nonatomic, readwrite, strong) NSData *apnsTokenData; @property(nonatomic, readwrite, strong) FIRInstanceID *instanceID; @@ -61,7 +46,6 @@ - (BOOL)shouldBeConnectedAutomatically; @interface FIRMessagingTest : XCTestCase @property(nonatomic, readonly, strong) FIRMessaging *messaging; -@property(nonatomic, strong) NSUserDefaults *messagingDefaults; @property(nonatomic, readwrite, strong) id mockMessaging; @property(nonatomic, readwrite, strong) id mockInstanceID; @property(nonatomic, readwrite, strong) id mockFirebaseApp; @@ -74,16 +58,9 @@ - (void)setUp { [super setUp]; // Create the messaging instance with all the necessary dependencies. - FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately]; - [instanceID start]; - - // Use custom defaults so we can empty it accordingly. NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingDefaultsTestDomain]; - _messagingDefaults = defaults; - _messaging = [[FIRMessaging alloc] initWithAnalytics:nil - withInstanceID:instanceID - withUserDefaults:_messagingDefaults]; + _messaging = [FIRMessagingTestUtilities messagingForTestsWithUserDefaults:defaults]; _mockFirebaseApp = OCMClassMock([FIRApp class]); OCMStub([_mockFirebaseApp defaultApp]).andReturn(_mockFirebaseApp); @@ -93,14 +70,14 @@ - (void)setUp { } - (void)tearDown { - [self.messagingDefaults removePersistentDomainForName:kFIRMessagingDefaultsTestDomain]; - self.messagingDefaults = nil; + [self.messaging.messagingUserDefaults removePersistentDomainForName:kFIRMessagingDefaultsTestDomain]; self.messaging.shouldEstablishDirectChannel = NO; self.messaging.defaultFcmToken = nil; self.messaging.apnsTokenData = nil; [_mockMessaging stopMocking]; [_mockInstanceID stopMocking]; [_mockFirebaseApp stopMocking]; + _messaging = nil; [super tearDown]; } From 2d239bcf524a533d7f2b0e42513286d74af43ccf Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Wed, 23 Jan 2019 07:31:33 -0500 Subject: [PATCH 5/5] Use local import path instead of framework import. --- Example/Messaging/Tests/FIRMessagingTestUtilities.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/Messaging/Tests/FIRMessagingTestUtilities.h b/Example/Messaging/Tests/FIRMessagingTestUtilities.h index 55805567fff..1226cd17c02 100644 --- a/Example/Messaging/Tests/FIRMessagingTestUtilities.h +++ b/Example/Messaging/Tests/FIRMessagingTestUtilities.h @@ -16,7 +16,7 @@ #import -#import +#import "FIRMessaging.h" NS_ASSUME_NONNULL_BEGIN