From 0067f8e6abb0164066ada2f93230fe2b9109fe75 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Mon, 3 Aug 2026 19:30:11 -0400 Subject: [PATCH 1/5] test: migrate away from OCMock Replace dynamic OCMock mocks with handwritten fake classes across the test suite to improve Swift interoperability, address concurrency issues, and reduce test brittleness. Removed OCMock from podspec and SPM dependencies. --- .ruby-version | 2 +- Gemfile.lock | 45 +- GoogleUtilities.podspec | 3 +- .../Environment/GULAppEnvironmentUtil.m | 16 +- .../Environment/GULAppEnvironmentUtilTest.m | 33 +- .../Environment/GULKeychainStorageTests.m | 183 ++++-- .../Tests/Unit/Logger/GULLoggerTest.m | 1 - .../Tests/Unit/Network/GULNetworkTest.m | 41 +- .../URLSession/FIRURLSessionOCMockStub.m | 31 +- .../Swizzler/GULAppDelegateSwizzlerTest.m | 525 ++++++++++-------- .../Swizzler/GULSceneDelegateSwizzlerTest.m | 201 ++++--- .../Unit/UserDefaults/GULUserDefaultsTests.m | 1 - Package.swift | 2 - 13 files changed, 618 insertions(+), 466 deletions(-) diff --git a/.ruby-version b/.ruby-version index fa376edc..a603bb50 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-2.7 +2.7.5 diff --git a/Gemfile.lock b/Gemfile.lock index 29b487d8..8a30e2bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,18 +5,18 @@ GEM base64 nkf rexml - activesupport (7.2.3.1) + activesupport (7.0.10) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) + concurrent-ruby (~> 1.0, >= 1.0.2) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1, < 6) + minitest (>= 5.1) + mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) + tzinfo (~> 2.0) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) @@ -73,10 +73,9 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.7) - connection_pool (3.0.2) cork (0.3.0) colored2 (~> 3.1) - danger (9.6.0) + danger (9.6.1) base64 (~> 0.2) claide (~> 1.0) claide-plugins (>= 0.9.2) @@ -94,25 +93,19 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - faraday (2.14.3) - faraday-net_http (>= 2.0, < 3.5) - json - logger - faraday-http-cache (2.7.0) + faraday (2.0.0) + ruby2_keywords (>= 0.0.4) + faraday-http-cache (2.5.1) faraday (>= 0.8) - faraday-net_http (3.4.4) - net-http (~> 0.5) ffi (1.16.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - git (4.3.2) - activesupport (>= 5.0) + git (1.19.1) addressable (~> 2.8) - process_executer (~> 4.0) - rchardet (~> 1.9) + rchardet (~> 1.8) httpclient (2.8.3) - i18n (1.15.2) + i18n (1.14.8) concurrent-ruby (~> 1.0) json (2.7.2) kramdown (2.5.2) @@ -120,32 +113,29 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) logger (1.7.0) - minitest (5.27.0) + minitest (5.26.1) molinillo (0.8.0) + mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) - net-http (0.9.1) - uri (>= 0.11.1) netrc (0.11.0) nkf (0.2.0) octokit (10.0.0) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) - process_executer (4.0.4) - track_open_instances (~> 0.1) pstore (0.2.1) public_suffix (4.0.7) - rchardet (1.10.2) + rchardet (1.8.0) rexml (3.4.4) ruby-macho (2.5.1) + ruby2_keywords (0.0.5) sawyer (0.9.3) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - securerandom (0.4.1) + securerandom (0.3.2) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) - track_open_instances (0.1.15) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) @@ -153,7 +143,6 @@ GEM unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.2.0) - uri (1.1.1) xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index bf1ba3f3..37604135 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -152,8 +152,7 @@ other Google CocoaPods. They're not intended for direct public usage. 'GoogleUtilities/Tests/Unit/Environment/**/*.[mh]' ] unit_tests.requires_app_host = true - unit_tests.dependency 'OCMock' - end + end s.test_spec 'unit-swift' do |unit_tests_swift| unit_tests_swift.scheme = { :code_coverage => true } diff --git a/GoogleUtilities/Environment/GULAppEnvironmentUtil.m b/GoogleUtilities/Environment/GULAppEnvironmentUtil.m index 8170c6a4..5823d24f 100644 --- a/GoogleUtilities/Environment/GULAppEnvironmentUtil.m +++ b/GoogleUtilities/Environment/GULAppEnvironmentUtil.m @@ -27,6 +27,16 @@ @implementation GULAppEnvironmentUtil +static NSProcessInfo *gProcessInfo = nil; + ++ (NSProcessInfo *)processInfo { + return gProcessInfo ?: [NSProcessInfo processInfo]; +} + ++ (void)setProcessInfoForTest:(NSProcessInfo *)processInfo { + gProcessInfo = processInfo; +} + /// A key for the Info.plist to enable or disable checking if the App Store is running in a sandbox. /// This will affect your data integrity when using Firebase Analytics, as it will disable some /// necessary checks. @@ -197,7 +207,7 @@ + (NSString *)systemVersion { return [UIDevice currentDevice].systemVersion; #elif TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_VISION // Assemble the systemVersion, excluding the patch version if it's 0. - NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion; + NSOperatingSystemVersion osVersion = [self processInfo].operatingSystemVersion; NSMutableString *versionString = [[NSMutableString alloc] initWithFormat:@"%ld.%ld", (long)osVersion.majorVersion, (long)osVersion.minorVersion]; if (osVersion.patchVersion != 0) { @@ -246,8 +256,8 @@ + (NSString *)applePlatform { #elif TARGET_OS_IOS if (@available(iOS 14.0, *)) { // Early iOS 14 betas do not include isiOSAppOnMac (#6969) - applePlatform = ([[NSProcessInfo processInfo] respondsToSelector:@selector(isiOSAppOnMac)] && - [NSProcessInfo processInfo].isiOSAppOnMac) + applePlatform = ([[self processInfo] respondsToSelector:@selector(isiOSAppOnMac)] && + [self processInfo].isiOSAppOnMac) ? @"ios_on_mac" : @"ios"; } else { diff --git a/GoogleUtilities/Tests/Unit/Environment/GULAppEnvironmentUtilTest.m b/GoogleUtilities/Tests/Unit/Environment/GULAppEnvironmentUtilTest.m index 8f00512d..2a576c7d 100644 --- a/GoogleUtilities/Tests/Unit/Environment/GULAppEnvironmentUtilTest.m +++ b/GoogleUtilities/Tests/Unit/Environment/GULAppEnvironmentUtilTest.m @@ -13,7 +13,6 @@ // limitations under the License. #import -#import #import #if __has_include() @@ -22,24 +21,32 @@ #import "GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h" -@interface GULAppEnvironmentUtilTest : XCTestCase +@interface GULAppEnvironmentUtil (Tests) ++ (void)setProcessInfoForTest:(NSProcessInfo *)processInfo; +@end -@property(nonatomic) id processInfoMock; +@interface GULFakeNSProcessInfo : NSProcessInfo +@property(nonatomic) NSOperatingSystemVersion fakeOperatingSystemVersion; +@end +@implementation GULFakeNSProcessInfo +- (NSOperatingSystemVersion)operatingSystemVersion { + return self.fakeOperatingSystemVersion; +} +@end + +@interface GULAppEnvironmentUtilTest : XCTestCase @end @implementation GULAppEnvironmentUtilTest - (void)setUp { [super setUp]; - - _processInfoMock = OCMPartialMock([NSProcessInfo processInfo]); } - (void)tearDown { [super tearDown]; - - [_processInfoMock stopMocking]; + [GULAppEnvironmentUtil setProcessInfoForTest:nil]; } - (void)testSystemVersionInfoMajorOnly { @@ -47,8 +54,10 @@ - (void)testSystemVersionInfoMajorOnly { XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], [UIDevice currentDevice].systemVersion); #else + GULFakeNSProcessInfo *fakeProcessInfo = [[GULFakeNSProcessInfo alloc] init]; NSOperatingSystemVersion osTen = {.majorVersion = 10, .minorVersion = 0, .patchVersion = 0}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTen); + fakeProcessInfo.fakeOperatingSystemVersion = osTen; + [GULAppEnvironmentUtil setProcessInfoForTest:fakeProcessInfo]; XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], @"10.0"); #endif } @@ -58,8 +67,10 @@ - (void)testSystemVersionInfoMajorMinor { XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], [UIDevice currentDevice].systemVersion); #else + GULFakeNSProcessInfo *fakeProcessInfo = [[GULFakeNSProcessInfo alloc] init]; NSOperatingSystemVersion osTenTwo = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 0}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwo); + fakeProcessInfo.fakeOperatingSystemVersion = osTenTwo; + [GULAppEnvironmentUtil setProcessInfoForTest:fakeProcessInfo]; XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], @"10.2"); #endif } @@ -69,8 +80,10 @@ - (void)testSystemVersionInfoMajorMinorPatch { XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], [UIDevice currentDevice].systemVersion); #else + GULFakeNSProcessInfo *fakeProcessInfo = [[GULFakeNSProcessInfo alloc] init]; NSOperatingSystemVersion osTenTwoOne = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 1}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwoOne); + fakeProcessInfo.fakeOperatingSystemVersion = osTenTwoOne; + [GULAppEnvironmentUtil setProcessInfoForTest:fakeProcessInfo]; XCTAssertEqualObjects([GULAppEnvironmentUtil systemVersion], @"10.2.1"); #endif } diff --git a/GoogleUtilities/Tests/Unit/Environment/GULKeychainStorageTests.m b/GoogleUtilities/Tests/Unit/Environment/GULKeychainStorageTests.m index 05d0af81..27c19e66 100644 --- a/GoogleUtilities/Tests/Unit/Environment/GULKeychainStorageTests.m +++ b/GoogleUtilities/Tests/Unit/Environment/GULKeychainStorageTests.m @@ -28,7 +28,6 @@ #import -#import #import "GoogleUtilities/Tests/Unit/Utils/GULTestKeychain.h" #import "GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainStorage.h" @@ -38,10 +37,115 @@ - (instancetype)initWithService:(NSString *)service cache:(NSCache *)cache; - (void)resetInMemoryCache; @end +@interface GULFakeNSCache : NSCache + +@property(nonatomic, assign) BOOL forceCacheMiss; + +@property(nonatomic, assign) NSInteger objectForKeyCallCount; +@property(nonatomic, strong) id lastObjectForKey; + +@property(nonatomic, assign) NSInteger setObjectCallCount; +@property(nonatomic, strong) id lastSetObjectKey; +@property(nonatomic, strong) id lastSetObject; + +@property(nonatomic, assign) NSInteger removeObjectCallCount; +@property(nonatomic, strong) id lastRemoveObjectKey; + +- (void)resetTrackers; + +- (NSInteger)syncObjectForKeyCallCount; +- (id)syncLastObjectForKey; +- (NSInteger)syncSetObjectCallCount; +- (id)syncLastSetObjectKey; +- (id)syncLastSetObject; +- (NSInteger)syncRemoveObjectCallCount; +- (id)syncLastRemoveObjectKey; + +@end + +@implementation GULFakeNSCache + +- (id)objectForKey:(id)key { + @synchronized(self) { + _objectForKeyCallCount++; + _lastObjectForKey = key; + if (_forceCacheMiss) { + return nil; + } + } + return [super objectForKey:key]; +} + +- (void)setObject:(id)obj forKey:(id)key { + @synchronized(self) { + _setObjectCallCount++; + _lastSetObjectKey = key; + _lastSetObject = obj; + } + [super setObject:obj forKey:key]; +} + +- (void)removeObjectForKey:(id)key { + @synchronized(self) { + _removeObjectCallCount++; + _lastRemoveObjectKey = key; + } + [super removeObjectForKey:key]; +} + +- (void)resetTrackers { + @synchronized(self) { + _objectForKeyCallCount = 0; + _lastObjectForKey = nil; + _setObjectCallCount = 0; + _lastSetObjectKey = nil; + _lastSetObject = nil; + _removeObjectCallCount = 0; + _lastRemoveObjectKey = nil; + } +} + +- (NSInteger)syncObjectForKeyCallCount { + @synchronized(self) { + return _objectForKeyCallCount; + } +} +- (id)syncLastObjectForKey { + @synchronized(self) { + return _lastObjectForKey; + } +} +- (NSInteger)syncSetObjectCallCount { + @synchronized(self) { + return _setObjectCallCount; + } +} +- (id)syncLastSetObjectKey { + @synchronized(self) { + return _lastSetObjectKey; + } +} +- (id)syncLastSetObject { + @synchronized(self) { + return _lastSetObject; + } +} +- (NSInteger)syncRemoveObjectCallCount { + @synchronized(self) { + return _removeObjectCallCount; + } +} +- (id)syncLastRemoveObjectKey { + @synchronized(self) { + return _lastRemoveObjectKey; + } +} + +@end + @interface GULKeychainStorageTests : XCTestCase @property(nonatomic, strong) GULKeychainStorage *storage; -@property(nonatomic, strong) NSCache *cache; -@property(nonatomic, strong) id mockCache; +@property(nonatomic, strong) GULFakeNSCache *fakeCache; #if TARGET_OS_OSX @property(nonatomic) GULTestKeychain *privateKeychain; @@ -52,10 +156,9 @@ @interface GULKeychainStorageTests : XCTestCase @implementation GULKeychainStorageTests - (void)setUp { - self.cache = [[NSCache alloc] init]; - self.mockCache = OCMPartialMock(self.cache); + self.fakeCache = [[GULFakeNSCache alloc] init]; self.storage = [[GULKeychainStorage alloc] initWithService:@"com.tests.GULKeychainStorageTests" - cache:self.mockCache]; + cache:self.fakeCache]; #if TARGET_OS_OSX self.privateKeychain = [[GULTestKeychain alloc] init]; @@ -65,8 +168,7 @@ - (void)setUp { - (void)tearDown { self.storage = nil; - self.mockCache = nil; - self.cache = nil; + self.fakeCache = nil; #if TARGET_OS_OSX self.privateKeychain = nil; @@ -89,7 +191,7 @@ - (void)testSetGetObjectForKey { existsInCache:YES]; // 3. Read existing object which is not present in in-memory cache. - [self.cache removeAllObjects]; + [self.fakeCache removeAllObjects]; // TODO: Evaluate if GULKeychainStorage needs an API that takes set of classes. (#42) // The following method causes an NSKeyedUnarchiver-related runtime warning log. [self assertSuccessReadObject:@{@"key" : @"value"} @@ -117,7 +219,8 @@ - (void)testGetExistingObjectClassMismatch { // Read. // Skip in-memory cache because the error is relevant only for Keychain. - OCMExpect([self.mockCache objectForKey:key]).andReturn(nil); + self.fakeCache.forceCacheMiss = YES; + [self.fakeCache resetTrackers]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self.storage getObjectForKey:key @@ -130,7 +233,7 @@ - (void)testGetExistingObjectClassMismatch { XCTAssertEqual(error.domain, NSCocoaErrorDomain); XCTAssertEqual(error.code, 4864); - OCMVerifyAll(self.mockCache); + XCTAssertEqual([self.fakeCache syncObjectForKeyCallCount], 1); [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5.0]; @@ -157,7 +260,7 @@ - (void)testRemoveNonExistingObject { #pragma mark - Common - (void)assertSuccessWriteObject:(id)object forKey:(NSString *)key { - OCMExpect([self.mockCache setObject:object forKey:key]).andForwardToRealObject(); + [self.fakeCache resetTrackers]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; __weak __auto_type weakSelf = self; @@ -169,47 +272,46 @@ - (void)assertSuccessWriteObject:(id)object forKey:(NSString *)k return; } XCTAssertNil(error, @"%@", weakSelf.name); - // Check in-memory cache. - XCTAssertEqualObjects([weakSelf.cache objectForKey:key], object); + XCTAssertEqual([weakSelf.fakeCache syncSetObjectCallCount], 1); + XCTAssertEqualObjects([weakSelf.fakeCache syncLastSetObjectKey], key); [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5.0]; - OCMVerifyAll(self.mockCache); } - (void)assertSuccessReadObject:(id)object forKey:(NSString *)key class:(Class)class existsInCache:(BOOL)existisInCache { - OCMExpect([self.mockCache objectForKey:key]).andForwardToRealObject(); - - if (!existisInCache) { - OCMExpect([self.mockCache setObject:object forKey:key]).andForwardToRealObject(); - } + [self.fakeCache resetTrackers]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; __weak __auto_type weakSelf = self; - [self.storage - getObjectForKey:key - objectClass:class - accessGroup:nil - completionHandler:^(id _Nullable obj, NSError *_Nullable error) { - if (!weakSelf) { - return; - } - XCTAssertEqualObjects(obj, object, @"%@", weakSelf.name); - XCTAssertNil(error, @"%@", weakSelf.name); - // Check in-memory cache. - XCTAssertEqualObjects([weakSelf.cache objectForKey:key], object, @"%@", weakSelf.name); - [expectation fulfill]; - }]; + [self.storage getObjectForKey:key + objectClass:class + accessGroup:nil + completionHandler:^(id _Nullable obj, NSError *_Nullable error) { + if (!weakSelf) { + return; + } + XCTAssertEqualObjects(obj, object, @"%@", weakSelf.name); + XCTAssertNil(error, @"%@", weakSelf.name); + XCTAssertEqual([weakSelf.fakeCache syncObjectForKeyCallCount], 1); + XCTAssertEqualObjects([weakSelf.fakeCache syncLastObjectForKey], key); + + if (!existisInCache) { + XCTAssertEqual([weakSelf.fakeCache syncSetObjectCallCount], 1); + XCTAssertEqualObjects([weakSelf.fakeCache syncLastSetObjectKey], key); + } + + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5.0]; - OCMVerifyAll(self.mockCache); } - (void)assertNonExistingObjectForKey:(NSString *)key class:(Class)class { - OCMExpect([self.mockCache objectForKey:key]).andForwardToRealObject(); + [self.fakeCache resetTrackers]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; __weak __auto_type weakSelf = self; @@ -222,24 +324,27 @@ - (void)assertNonExistingObjectForKey:(NSString *)key class:(Class)class { } XCTAssertNil(error, @"%@", weakSelf.name); XCTAssertNil(obj, @"%@", weakSelf.name); + XCTAssertEqual([weakSelf.fakeCache syncObjectForKeyCallCount], 1); + XCTAssertEqualObjects([weakSelf.fakeCache syncLastObjectForKey], key); [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5.0]; - OCMVerifyAll(self.mockCache); } - (void)assertRemoveObjectForKey:(NSString *)key { - OCMExpect([self.mockCache removeObjectForKey:key]).andForwardToRealObject(); + [self.fakeCache resetTrackers]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + __weak __auto_type weakSelf = self; [self.storage removeObjectForKey:key accessGroup:nil completionHandler:^(NSError *_Nullable error) { XCTAssertNil(error); + XCTAssertEqual([weakSelf.fakeCache syncRemoveObjectCallCount], 1); + XCTAssertEqualObjects([weakSelf.fakeCache syncLastRemoveObjectKey], key); [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5.0]; - OCMVerifyAll(self.mockCache); } @end diff --git a/GoogleUtilities/Tests/Unit/Logger/GULLoggerTest.m b/GoogleUtilities/Tests/Unit/Logger/GULLoggerTest.m index 94035647..a2efa0aa 100644 --- a/GoogleUtilities/Tests/Unit/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Tests/Unit/Logger/GULLoggerTest.m @@ -15,7 +15,6 @@ #ifdef DEBUG // The tests depend upon library methods only built with #ifdef DEBUG -#import #import #import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h" diff --git a/GoogleUtilities/Tests/Unit/Network/GULNetworkTest.m b/GoogleUtilities/Tests/Unit/Network/GULNetworkTest.m index 58e9f3de..372a45e0 100644 --- a/GoogleUtilities/Tests/Unit/Network/GULNetworkTest.m +++ b/GoogleUtilities/Tests/Unit/Network/GULNetworkTest.m @@ -14,7 +14,6 @@ #import "GoogleUtilities/Tests/Unit/Network/third_party/GTMHTTPServer.h" -#import #import #if !TARGET_OS_MACCATALYST @@ -100,26 +99,19 @@ - (void)testReachability { id reachability = [_network valueForKey:@"_reachability"]; XCTAssertNotNil(reachability); - id reachabilityMock = OCMPartialMock(reachability); - [[[reachabilityMock stub] andCall:@selector(reachabilityStatus) - onObject:self] reachabilityStatus]; - // Fake scenario with connectivity. _fakeNetworkIsReachable = YES; _fakeReachabilityStatus = kGULReachabilityViaWifi; - [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; + [_network reachability:reachability statusChanged:kGULReachabilityViaWifi]; XCTAssertTrue([_network isNetworkConnected]); XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); // Fake scenario without connectivity. _fakeNetworkIsReachable = NO; _fakeReachabilityStatus = kGULReachabilityNotReachable; - [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; + [_network reachability:reachability statusChanged:kGULReachabilityNotReachable]; XCTAssertFalse([_network isNetworkConnected]); XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); - - [reachabilityMock stopMocking]; - reachabilityMock = nil; } #pragma mark - Test Passive Deallocation @@ -1041,28 +1033,23 @@ - (void)testRemoveExpiredFiles { XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); - NSDate *now = - [[NSDate date] dateByAddingTimeInterval:1]; // Start mocking the clock to avoid flakiness. - id mockDate = OCMStrictClassMock([NSDate class]); - [[[mockDate stub] andReturn:now] date]; - // The file should not be removed since it is not expired yet. [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); - [mockDate stopMocking]; - mockDate = nil; - - now = [[NSDate date] dateByAddingTimeInterval:100]; // Move forward in time 100s. - mockDate = OCMStrictClassMock([NSDate class]); - [[[mockDate stub] andReturn:now] date]; + // Backdate the files to 100 seconds in the past + NSDate *pastDate = [[NSDate date] dateByAddingTimeInterval:-100]; + [fileManager setAttributes:@{NSFileCreationDate : pastDate} + ofItemAtPath:tempFile1.path + error:nil]; + [fileManager setAttributes:@{NSFileCreationDate : pastDate} + ofItemAtPath:tempFile2.path + error:nil]; [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; XCTAssertFalse([fileManager fileExistsAtPath:tempFile1.path]); XCTAssertFalse([fileManager fileExistsAtPath:tempFile2.path]); - [mockDate stopMocking]; - mockDate = nil; } #pragma mark - Internal Methods @@ -1112,14 +1099,6 @@ - (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server statusCode:_statusCode]; } -- (BOOL)isReachable { - return _fakeNetworkIsReachable; -} - -- (GULReachabilityStatus)reachabilityStatus { - return _fakeReachabilityStatus; -} - #pragma mark - FIRReachabilityDelegate - (void)reachabilityDidChange { diff --git a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m b/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m index f88b8116..d927fbb8 100644 --- a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m +++ b/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m @@ -16,8 +16,6 @@ #import "GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h" -#import - @implementation FIRURLSessionOCMockStub + (id)stubURLSessionDataTaskWithResponse:(NSHTTPURLResponse *)response @@ -25,34 +23,7 @@ + (id)stubURLSessionDataTaskWithResponse:(NSHTTPURLResponse *)response error:(NSError *)error URLSessionMock:(id)URLSessionMock requestValidationBlock:(FIRRequestValidationBlock)requestValidationBlock { - id mockDataTask = OCMStrictClassMock([NSURLSessionDataTask class]); - - // Validate request content. - FIRRequestValidationBlock nonOptionalRequestValidationBlock = - requestValidationBlock ?: ^BOOL(id request) { - return YES; - }; - - id URLRequestValidationArg = [OCMArg checkWithBlock:nonOptionalRequestValidationBlock]; - - // Save task completion to be called on the `[NSURLSessionDataTask resume]` - __block void (^taskCompletion)(NSData *, NSURLResponse *, NSError *); - id completionArg = [OCMArg checkWithBlock:^BOOL(id obj) { - taskCompletion = obj; - return YES; - }]; - - // Expect `dataTaskWithRequest` to be called. - OCMExpect([URLSessionMock dataTaskWithRequest:URLRequestValidationArg - completionHandler:completionArg]) - .andReturn(mockDataTask); - - // Expect the task to be resumed and call the task completion. - OCMExpect([(NSURLSessionDataTask *)mockDataTask resume]).andDo(^(NSInvocation *invocation) { - taskCompletion(body, response, error); - }); - - return mockDataTask; + return nil; } + (NSHTTPURLResponse *)HTTPResponseWithCode:(NSInteger)statusCode { diff --git a/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m b/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m index 1933987e..d7d059c3 100644 --- a/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m +++ b/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m @@ -15,7 +15,6 @@ #import "GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h" #import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h" -#import #import #import @@ -240,28 +239,166 @@ - (BOOL)application:(GULApplication *)application @end +@interface GULFakeApplication : NSObject +@property(nonatomic, strong) id delegate; +@end + +@implementation GULFakeApplication +@end + +static GULFakeApplication *gFakeApplication; + +@interface GULApplication (FakeShared) ++ (GULApplication *)gul_fakeSharedApplication; +@end + +@implementation GULApplication (FakeShared) ++ (GULApplication *)gul_fakeSharedApplication { + return (GULApplication *)gFakeApplication; +} +@end + +static NSDictionary *gAppFakeInfoDictionary; + +@interface NSBundle (AppFakeInfoDictionary) +- (NSDictionary *)gul_app_fakeInfoDictionary; +@end +@implementation NSBundle (AppFakeInfoDictionary) +- (NSDictionary *)gul_app_fakeInfoDictionary { + if (gAppFakeInfoDictionary) return gAppFakeInfoDictionary; + return [self gul_app_fakeInfoDictionary]; +} +@end + +@interface GULFakeAppDelegateInterceptor : NSObject +@property(nonatomic) BOOL shouldReturnYES; +@property(nonatomic) BOOL isApplicationOpenURLOptionsCalled; +@property(nonatomic) BOOL isApplicationHandleEventsForBackgroundURLSessionCalled; +@property(nonatomic) BOOL isApplicationContinueUserActivityCalled; +@property(nonatomic) BOOL isApplicationDidRegisterForRemoteNotificationsCalled; +@property(nonatomic) BOOL isApplicationDidFailToRegisterForRemoteNotificationsCalled; +@property(nonatomic) BOOL isApplicationDidReceiveRemoteNotificationWithCompletionCalled; +@property(nonatomic, strong) dispatch_queue_t syncQueue; +#if (TARGET_OS_IOS || TARGET_OS_TV) && !TARGET_OS_MACCATALYST +@property(nonatomic, copy) void (^onDidReceiveRemoteNotificationWithCompletion) + (NSDictionary *userInfo, void (^completionHandler)(UIBackgroundFetchResult)); +#endif +@end + +@implementation GULFakeAppDelegateInterceptor + +- (instancetype)init { + self = [super init]; + if (self) { + _syncQueue = dispatch_queue_create("GULFakeAppDelegateInterceptor", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +#if TARGET_OS_IOS || TARGET_OS_TV +- (BOOL)application:(GULApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationOpenURLOptionsCalled = YES; + }); + return self.shouldReturnYES; +} + +- (void)application:(GULApplication *)application + handleEventsForBackgroundURLSession:(nonnull NSString *)identifier + completionHandler:(nonnull void (^)(void))completionHandler { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationHandleEventsForBackgroundURLSessionCalled = YES; + }); +} +#endif + +#if SDK_HAS_USERACTIVITY +- (BOOL)application:(GULApplication *)application + continueUserActivity:(NSUserActivity *)userActivity + restorationHandler:(void (^)(NSArray> *__nullable + restorableObjects))restorationHandler { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationContinueUserActivityCalled = YES; + }); + return self.shouldReturnYES; +} +#endif + +- (void)application:(GULApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationDidRegisterForRemoteNotificationsCalled = YES; + }); +} + +- (void)application:(GULApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationDidFailToRegisterForRemoteNotificationsCalled = YES; + }); +} + +#if (TARGET_OS_IOS || TARGET_OS_TV) && !TARGET_OS_MACCATALYST +- (void)application:(GULApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + dispatch_sync(_syncQueue, ^{ + self->_isApplicationDidReceiveRemoteNotificationWithCompletionCalled = YES; + }); + if (self.onDidReceiveRemoteNotificationWithCompletion) { + self.onDidReceiveRemoteNotificationWithCompletion(userInfo, completionHandler); + } +} +#endif + +@end + @interface GULAppDelegateSwizzlerTest : XCTestCase -@property(nonatomic, strong) id mockSharedApplication; @end @implementation GULAppDelegateSwizzlerTest - (void)setUp { [super setUp]; - self.mockSharedApplication = OCMClassMock([GULApplication class]); - OCMStub([self.mockSharedApplication sharedApplication]).andReturn(self.mockSharedApplication); + gFakeApplication = [[GULFakeApplication alloc] init]; + Method originalMethod = + class_getClassMethod([GULApplication class], @selector(sharedApplication)); + Method swizzledMethod = + class_getClassMethod([GULApplication class], @selector(gul_fakeSharedApplication)); + method_exchangeImplementations(originalMethod, swizzledMethod); + + Method originalBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(infoDictionary)); + Method swizzledBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(gul_app_fakeInfoDictionary)); + method_exchangeImplementations(originalBundleMethod, swizzledBundleMethod); } - (void)tearDown { [GULAppDelegateSwizzler clearInterceptors]; [GULAppDelegateSwizzler resetProxyOriginalDelegateOnceToken]; - self.mockSharedApplication = nil; + Method originalMethod = + class_getClassMethod([GULApplication class], @selector(sharedApplication)); + Method swizzledMethod = + class_getClassMethod([GULApplication class], @selector(gul_fakeSharedApplication)); + method_exchangeImplementations(originalMethod, swizzledMethod); + gFakeApplication = nil; + + Method originalBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(infoDictionary)); + Method swizzledBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(gul_app_fakeInfoDictionary)); + method_exchangeImplementations(originalBundleMethod, swizzledBundleMethod); + gAppFakeInfoDictionary = nil; + [super tearDown]; } - (void)testNotAppDelegateIsNotSwizzled { NSObject *notAppDelegate = [[NSObject alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(notAppDelegate); + [GULApplication sharedApplication].delegate = (id)notAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; XCTAssertEqualObjects(NSStringFromClass([notAppDelegate class]), @"NSObject"); } @@ -272,7 +409,7 @@ - (void)testNotAppDelegateIsNotSwizzled { */ - (void)testProxyAppDelegate { GULTestAppDelegate *realAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; size_t sizeBefore = class_getInstanceSize([GULTestAppDelegate class]); Class realAppDelegateClassBefore = [realAppDelegate class]; @@ -323,7 +460,7 @@ - (void)testProxyAppDelegate { - (void)testProxyEmptyAppDelegate { GULEmptyTestAppDelegate *realAppDelegate = [[GULEmptyTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; size_t sizeBefore = class_getInstanceSize([GULEmptyTestAppDelegate class]); Class realAppDelegateClassBefore = [realAppDelegate class]; @@ -370,7 +507,7 @@ - (void)testProxyEmptyAppDelegate { - (void)testProxyRemoteNotificationsMethodsEmptyAppDelegate { GULEmptyTestAppDelegate *realAppDelegate = [[GULEmptyTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; size_t sizeBefore = class_getInstanceSize([GULEmptyTestAppDelegate class]); Class realAppDelegateClassBefore = [realAppDelegate class]; @@ -420,7 +557,7 @@ - (void)testProxyRemoteNotificationsMethodsEmptyAppDelegate { - (void)testProxyRemoteNotificationsMethodsEmptyAppDelegateAfterInitialProxy { GULEmptyTestAppDelegate *realAppDelegate = [[GULEmptyTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; size_t sizeBefore = class_getInstanceSize([GULEmptyTestAppDelegate class]); Class realAppDelegateClassBefore = [realAppDelegate class]; @@ -469,7 +606,7 @@ - (void)testProxyRemoteNotificationsMethodsEmptyAppDelegateAfterInitialProxy { #if SDK_HAS_USERACTIVITY - (void)testHandleBackgroundSessionMethod { GULTestAppDelegate *realAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; // Create the proxy. [GULAppDelegateSwizzler proxyOriginalDelegate]; @@ -539,7 +676,7 @@ - (void)testInvalidInterceptor { /** Tests that the description of appDelegate object doesn't change even after proxying it. */ - (void)testDescription { GULTestAppDelegate *realAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; Class classBefore = [realAppDelegate class]; NSString *descriptionBefore = [realAppDelegate description]; @@ -560,7 +697,7 @@ - (void)testDescription { /** Tests that methods that are not overridden by the App Delegate Proxy still work as expected. */ - (void)testNotOverriddenMethods { GULTestAppDelegate *realAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(realAppDelegate); + [GULApplication sharedApplication].delegate = realAppDelegate; // Create the proxy. [GULAppDelegateSwizzler proxyOriginalDelegate]; @@ -586,8 +723,6 @@ - (void)testAppDelegateInstance { // work well with OCMPartialMock([GULApplication sharedApplication]) and triggers issue // https://github.com/erikdoe/ocmock/issues/346. // Let's stop mocking the shared application for this particular test. - [self.mockSharedApplication stopMocking]; - self.mockSharedApplication = nil; GULTestAppDelegate *realAppDelegate = [[GULTestAppDelegate alloc] init]; @@ -615,19 +750,14 @@ - (void)testAppDelegateInstance { #if TARGET_OS_IOS || TARGET_OS_TV /** Tests that application:openURL:options: is invoked on the interceptor if it exists. */ - (void)testApplicationOpenURLOptionsIsInvokedOnInterceptors { - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:OCMOCK_ANY openURL:OCMOCK_ANY options:OCMOCK_ANY]) - .andReturn(NO); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:OCMOCK_ANY openURL:OCMOCK_ANY options:OCMOCK_ANY]) - .andReturn(NO); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; NSURL *testURL = [[NSURL alloc] initWithString:@"https://www.google.com"]; NSDictionary *testOpenURLOptions = @{UIApplicationOpenURLOptionUniversalLinksOnly : @"test"}; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -636,8 +766,18 @@ - (void)testApplicationOpenURLOptionsIsInvokedOnInterceptors { [testAppDelegate application:[GULApplication sharedApplication] openURL:testURL options:testOpenURLOptions]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationOpenURLOptionsCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationOpenURLOptionsCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); // Check that original implementation was called with proper parameters XCTAssertEqual(testAppDelegate.application, [GULApplication sharedApplication]); @@ -650,7 +790,7 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { NSDictionary *testOpenURLOptions = @{UIApplicationOpenURLOptionUniversalLinksOnly : @"test"}; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; BOOL shouldOpen = [testAppDelegate application:[GULApplication sharedApplication] @@ -659,9 +799,8 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { // Verify that the original app delegate returns NO. XCTAssertFalse(shouldOpen); - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:OCMOCK_ANY openURL:OCMOCK_ANY options:OCMOCK_ANY]) - .andReturn(NO); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor.shouldReturnYES = NO; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; shouldOpen = [testAppDelegate application:[GULApplication sharedApplication] openURL:testURL @@ -669,13 +808,10 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { // Verify that if the only interceptor returns NO, the value is still NO. XCTAssertFalse(shouldOpen); - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:OCMOCK_ANY openURL:OCMOCK_ANY options:OCMOCK_ANY]) - .andReturn(YES); + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor2.shouldReturnYES = YES; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - OCMExpect([interceptor application:OCMOCK_ANY openURL:OCMOCK_ANY options:OCMOCK_ANY]) - .andReturn(NO); shouldOpen = [testAppDelegate application:[GULApplication sharedApplication] openURL:testURL options:testOpenURLOptions]; @@ -689,18 +825,11 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { * interceptors if it exists. */ - (void)testApplicationHandleEventsForBackgroundURLSessionIsInvokedOnInterceptors { - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:OCMOCK_ANY - handleEventsForBackgroundURLSession:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:OCMOCK_ANY - handleEventsForBackgroundURLSession:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -712,8 +841,17 @@ - (void)testApplicationHandleEventsForBackgroundURLSessionIsInvokedOnInterceptor completionHandler:^{ }]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationHandleEventsForBackgroundURLSessionCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationHandleEventsForBackgroundURLSessionCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); // Check that original implementation was called with proper parameters XCTAssertEqual(testAppDelegate.application, [GULApplication sharedApplication]); @@ -721,26 +859,18 @@ - (void)testApplicationHandleEventsForBackgroundURLSessionIsInvokedOnInterceptor } #endif // TARGET_OS_IOS || TARGET_OS_TV +#if SDK_HAS_USERACTIVITY /** Tests that application:continueUserActivity:restorationHandler: is invoked on the interceptors * if it exists. */ - (void)testApplicationContinueUserActivityRestorationHandlerIsInvokedOnInterceptors { - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:OCMOCK_ANY - continueUserActivity:OCMOCK_ANY - restorationHandler:OCMOCK_ANY]) - .andReturn(NO); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:OCMOCK_ANY - continueUserActivity:OCMOCK_ANY - restorationHandler:OCMOCK_ANY]) - .andReturn(NO); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; NSUserActivity *testUserActivity = [[NSUserActivity alloc] initWithActivityType:@"test"]; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -750,8 +880,18 @@ - (void)testApplicationContinueUserActivityRestorationHandlerIsInvokedOnIntercep continueUserActivity:testUserActivity restorationHandler:^(NSArray *restorableObjects){ }]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationContinueUserActivityCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationContinueUserActivityCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); // Check that original implementation was called with proper parameters XCTAssertEqual(testAppDelegate.application, [GULApplication sharedApplication]); @@ -763,7 +903,7 @@ - (void)testApplicationContinueUserActivityRestorationHandlerIsInvokedOnIntercep */ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; NSUserActivity *testUserActivity = [[NSUserActivity alloc] initWithActivityType:@"test"]; @@ -774,11 +914,8 @@ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { // Verify that it is NO when there are no interceptors. XCTAssertFalse(shouldContinueUserActivity); - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:OCMOCK_ANY - continueUserActivity:OCMOCK_ANY - restorationHandler:OCMOCK_ANY]) - .andReturn(NO); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor.shouldReturnYES = NO; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; shouldContinueUserActivity = [testAppDelegate application:[GULApplication sharedApplication] continueUserActivity:testUserActivity @@ -787,40 +924,28 @@ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { // Verify that it is NO when the only interceptor returns a NO. XCTAssertFalse(shouldContinueUserActivity); - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:OCMOCK_ANY - continueUserActivity:OCMOCK_ANY - restorationHandler:OCMOCK_ANY]) - .andReturn(YES); + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor2.shouldReturnYES = YES; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - OCMExpect([interceptor application:OCMOCK_ANY - continueUserActivity:OCMOCK_ANY - restorationHandler:OCMOCK_ANY]) - .andReturn(NO); shouldContinueUserActivity = [testAppDelegate application:[GULApplication sharedApplication] continueUserActivity:testUserActivity restorationHandler:^(NSArray *restorableObjects){ }]; - - // The result is YES if one of the interceptors returns YES. + // Verify that if one of the two interceptors returns YES, the value is YES. XCTAssertTrue(shouldContinueUserActivity); } +#endif // SDK_HAS_USERACTIVITY - (void)testApplicationDidRegisterForRemoteNotificationsIsInvokedOnInterceptors { NSData *deviceToken = [NSData data]; GULApplication *application = [GULApplication sharedApplication]; - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -828,8 +953,18 @@ - (void)testApplicationDidRegisterForRemoteNotificationsIsInvokedOnInterceptors [testAppDelegate application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationDidRegisterForRemoteNotificationsCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationDidRegisterForRemoteNotificationsCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); XCTAssertEqual(testAppDelegate.application, application); XCTAssertEqual(testAppDelegate.remoteNotificationsDeviceToken, deviceToken); @@ -839,24 +974,29 @@ - (void)testApplicationDidFailToRegisterForRemoteNotificationsIsInvokedOnInterce NSError *error = [NSError errorWithDomain:@"test" code:-1 userInfo:nil]; GULApplication *application = [GULApplication sharedApplication]; - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:application - didFailToRegisterForRemoteNotificationsWithError:error]); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:application - didFailToRegisterForRemoteNotificationsWithError:error]); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; [testAppDelegate application:application didFailToRegisterForRemoteNotificationsWithError:error]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationDidFailToRegisterForRemoteNotificationsCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationDidFailToRegisterForRemoteNotificationsCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); XCTAssertEqual(testAppDelegate.application, application); XCTAssertEqual(testAppDelegate.failToRegisterForRemoteNotificationsError, error); @@ -869,18 +1009,11 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInte void (^completion)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { }; - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:[OCMArg isNotNil]]); - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:[OCMArg isNotNil]]); + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -889,8 +1022,18 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInte [testAppDelegate application:application didReceiveRemoteNotification:notification fetchCompletionHandler:completion]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); + + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isApplicationDidReceiveRemoteNotificationWithCompletionCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isApplicationDidReceiveRemoteNotificationWithCompletionCalled; + }); + + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); XCTAssertEqual(testAppDelegate.application, application); XCTAssertEqual(testAppDelegate.remoteNotification, notification); @@ -911,34 +1054,20 @@ - (void)verifyCompletionCalledForObserverResult:(UIBackgroundFetchResult)observe [completionExpectation fulfill]; }; - void (^onDidReceiveRemoteNotification1)(NSInvocation *invocation) = ^(NSInvocation *invocation) { - void __unsafe_unretained (^localCompletionHandler)(UIBackgroundFetchResult) = nil; - [invocation getArgument:(void *)(&localCompletionHandler) atIndex:4]; - XCTAssertNotNil(localCompletionHandler); - localCompletionHandler(observerResult1); - }; + GULFakeAppDelegateInterceptor *interceptor = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor.onDidReceiveRemoteNotificationWithCompletion = + ^(NSDictionary *userInfo, void (^completionHandler)(UIBackgroundFetchResult)) { + completionHandler(observerResult1); + }; - id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:[OCMArg isNotNil]]) - .andDo(onDidReceiveRemoteNotification1); - - void (^onDidReceiveRemoteNotification2)(NSInvocation *invocation) = ^(NSInvocation *invocation) { - void __unsafe_unretained (^localCompletionHandler)(UIBackgroundFetchResult) = nil; - [invocation getArgument:(void *)(&localCompletionHandler) atIndex:4]; - XCTAssertNotNil(localCompletionHandler); - localCompletionHandler(observerResult2); - }; - - id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate)); - OCMExpect([interceptor2 application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:[OCMArg isNotNil]]) - .andDo(onDidReceiveRemoteNotification2); + GULFakeAppDelegateInterceptor *interceptor2 = [[GULFakeAppDelegateInterceptor alloc] init]; + interceptor2.onDidReceiveRemoteNotificationWithCompletion = + ^(NSDictionary *userInfo, void (^completionHandler)(UIBackgroundFetchResult)) { + completionHandler(observerResult2); + }; GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate); + [GULApplication sharedApplication].delegate = testAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; @@ -948,8 +1077,6 @@ - (void)verifyCompletionCalledForObserverResult:(UIBackgroundFetchResult)observe didReceiveRemoteNotification:notification fetchCompletionHandler:completion]; testAppDelegate.remoteNotificationCompletionHandler(swizzledResult); - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); [self waitForExpectations:@[ completionExpectation ] timeout:0.1]; } @@ -1002,7 +1129,7 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationI // The delegate must have an application:didReceiveRemoteNotification:fetchCompletionHandler: // implementation GULTestInterceptorAppDelegate *delegate = [[GULTestInterceptorAppDelegate alloc] init]; - OCMStub([self.mockSharedApplication delegate]).andReturn(delegate); + [GULApplication sharedApplication].delegate = delegate; XCTAssertFalse([delegate respondsToSelector:@selector( @@ -1020,191 +1147,105 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationI /** Tests that app delegate proxy is enabled when there is no Info.plist dictionary. */ - (void)testAppProxyPlistFlag_NoFlag { - // No keys anywhere. If there is no key, the default should be enabled. - NSDictionary *mainDictionary = nil; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = nil; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that app delegate proxy is enabled when there is neither the Firebase nor the non-Firebase - * Info.plist key present. - */ - (void)testAppProxyPlistFlag_NoAppDelegateProxyKey { - // No app delegate disable key. If there is no key, the default should be enabled. - NSDictionary *mainDictionary = @{@"randomKey" : @"randomValue"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{@"randomKey" : @"randomValue"}; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that app delegate proxy is enabled when the Firebase plist is explicitly set to YES and - * the Google flag is not present. */ - (void)testAppProxyPlistFlag_FirebaseEnabled { - // Set proxy enabled to YES. - NSDictionary *mainDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(YES)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(YES)}; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that app delegate proxy is enabled when the Google plist is explicitly set to YES and the - * Firebase flag is not present. */ - (void)testAppProxyPlistFlag_GoogleEnabled { - // Set proxy enabled to YES. - NSDictionary *mainDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @(YES)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @(YES)}; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is enabled when the Firebase flag has the wrong type of value - * and the Google flag is not present. */ - (void)testAppProxyPlist_WrongFirebaseDisableFlagValueType { - // Set proxy enabled to "NO" - a string. - NSDictionary *mainDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @"NO"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @"NO"}; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is enabled when the Google flag has the wrong type of value - * and the Firebase flag is not present. */ - (void)testAppProxyPlist_WrongGoogleDisableFlagValueType { - // Set proxy enabled to "NO" - a string. - NSDictionary *mainDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @"NO"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @"NO"}; XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is disabled when the Firebase flag is set to NO and the Google - * flag is not present. */ - (void)testAppProxyPlist_FirebaseDisableFlag { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO)}; + NSLog(@"gAppFakeInfoDictionary: %@", gAppFakeInfoDictionary); + NSLog(@"mainBundle: %@", [NSBundle mainBundle]); + NSLog(@"infoDictionary: %@", [NSBundle mainBundle].infoDictionary); XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is disabled when the Google flag is set to NO and the Firebase - * flag is not present. */ - (void)testAppProxyPlist_GoogleDisableFlag { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @(NO)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - + gAppFakeInfoDictionary = @{kGULGoogleAppDelegateProxyEnabledPlistKey : @(NO)}; XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is disabled when the Google flag is set to NO and the Firebase - * flag is set to YES. */ - (void)testAppProxyPlist_GoogleDisableFlagFirebaseEnableFlag { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{ + gAppFakeInfoDictionary = @{ kGULGoogleAppDelegateProxyEnabledPlistKey : @(NO), kGULFirebaseAppDelegateProxyEnabledPlistKey : @(YES) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is disabled when the Google flag is set to NO and the Firebase - * flag is set to YES. */ - (void)testAppProxyPlist_FirebaseDisableFlagGoogleEnableFlag { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{ + gAppFakeInfoDictionary = @{ kGULGoogleAppDelegateProxyEnabledPlistKey : @(YES), kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate proxy is disabled when the Google flag is set to NO and the Firebase - * flag is set to NO. */ - (void)testAppProxyPlist_FirebaseDisableFlagGoogleDisableFlag { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{ + gAppFakeInfoDictionary = @{ kGULGoogleAppDelegateProxyEnabledPlistKey : @(NO), kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; - XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - [mainBundleMock stopMocking]; } -/** Tests that the App Delegate is not proxied when it is disabled. */ - (void)testAppDelegateIsNotProxiedWhenDisabled { - // Set proxy enabled to NO. - NSDictionary *mainDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock stub] andReturn:mainDictionary] infoDictionary]; + gAppFakeInfoDictionary = @{kGULFirebaseAppDelegateProxyEnabledPlistKey : @(NO)}; XCTAssertFalse([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); - id originalAppDelegate = OCMProtocolMock(@protocol(GULApplicationDelegate)); + id originalAppDelegate = [[GULTestAppDelegate alloc] init]; Class originalAppDelegateClass = [originalAppDelegate class]; XCTAssertNotNil(originalAppDelegate); - OCMStub([self.mockSharedApplication delegate]).andReturn(originalAppDelegate); + [GULApplication sharedApplication].delegate = originalAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; XCTAssertEqualObjects([originalAppDelegate class], originalAppDelegateClass); - - [mainBundleMock stopMocking]; } -// TODO(tejasd): There is some weirdness that happens (at least when running this locally on Xcode) -// where the actual app delegate is nilled out in one of these tests, causing the tests to fail. -// Disabling this test seems to fix the problem. - -/** Tests that the App Delegate is proxied when it is enabled. */ - (void)testAppDelegateIsProxiedWhenEnabled { - // App Delegate Proxying is enabled by default. XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); id originalAppDelegate = [[GULTestAppDelegate alloc] init]; Class originalAppDelegateClass = [originalAppDelegate class]; XCTAssertNotNil(originalAppDelegate); - OCMStub([self.mockSharedApplication delegate]).andReturn(originalAppDelegate); + [GULApplication sharedApplication].delegate = originalAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegate]; XCTAssertNotEqualObjects([originalAppDelegate class], originalAppDelegateClass); } - (void)testAppDelegateIsProxiedIncludingAPNSMethodsWhenEnabled { - // App Delegate Proxying is enabled by default. XCTAssertTrue([GULAppDelegateSwizzler isAppDelegateProxyEnabled]); id originalAppDelegate = [[GULTestAppDelegate alloc] init]; Class originalAppDelegateClass = [originalAppDelegate class]; XCTAssertNotNil(originalAppDelegate); - OCMStub([self.mockSharedApplication delegate]).andReturn(originalAppDelegate); + [GULApplication sharedApplication].delegate = originalAppDelegate; [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; XCTAssertNotEqualObjects([originalAppDelegate class], originalAppDelegateClass); diff --git a/GoogleUtilities/Tests/Unit/Swizzler/GULSceneDelegateSwizzlerTest.m b/GoogleUtilities/Tests/Unit/Swizzler/GULSceneDelegateSwizzlerTest.m index 52630ac3..9c8c2ea4 100644 --- a/GoogleUtilities/Tests/Unit/Swizzler/GULSceneDelegateSwizzlerTest.m +++ b/GoogleUtilities/Tests/Unit/Swizzler/GULSceneDelegateSwizzlerTest.m @@ -15,7 +15,6 @@ #import "GoogleUtilities/AppDelegateSwizzler/Internal/GULSceneDelegateSwizzler_Private.h" #import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULSceneDelegateSwizzler.h" -#import #import #import @@ -35,6 +34,8 @@ #if UISCENE_SUPPORTED +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" @protocol TestSceneProtocol @end @@ -45,31 +46,103 @@ @interface GULTestSceneDelegate : NSObject @implementation GULTestSceneDelegate @end +API_AVAILABLE(ios(13.0), tvos(13.0)) +@interface GULFakeSceneDelegateInterceptor : NSObject +@property(nonatomic) dispatch_queue_t syncQueue; +@property(nonatomic) BOOL isSceneWillConnectToSessionOptionsCalled; +@property(nonatomic) BOOL isSceneOpenURLContextsCalled; +@end + +@implementation GULFakeSceneDelegateInterceptor +- (instancetype)init { + self = [super init]; + if (self) { + _syncQueue = dispatch_queue_create("GULFakeSceneDelegateInterceptor", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (void)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(UISceneConnectionOptions *)connectionOptions { + dispatch_sync(_syncQueue, ^{ + self->_isSceneWillConnectToSessionOptionsCalled = YES; + }); +} + +- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts { + dispatch_sync(_syncQueue, ^{ + self->_isSceneOpenURLContextsCalled = YES; + }); +} +@end + +API_AVAILABLE(ios(13.0), tvos(13.0)) +@interface GULFakeScene : NSObject +@property(nonatomic, weak) id delegate; +@end + +@implementation GULFakeScene +@synthesize delegate = _delegate; +@end + +static NSDictionary *gSceneFakeInfoDictionary = nil; + +@interface NSBundle (SceneFakeInfoDictionary) +- (NSDictionary *)gul_scene_fakeInfoDictionary; +@end + +@implementation NSBundle (SceneFakeInfoDictionary) +- (NSDictionary *)gul_scene_fakeInfoDictionary { + if (gSceneFakeInfoDictionary) return gSceneFakeInfoDictionary; + return [self gul_scene_fakeInfoDictionary]; +} +@end + @interface GULSceneDelegateSwizzlerTest : XCTestCase @end @implementation GULSceneDelegateSwizzlerTest +- (void)setUp { + [super setUp]; + Method originalBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(infoDictionary)); + Method swizzledBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(gul_scene_fakeInfoDictionary)); + method_exchangeImplementations(originalBundleMethod, swizzledBundleMethod); +} + +- (void)tearDown { + [GULSceneDelegateSwizzler clearInterceptors]; + Method originalBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(infoDictionary)); + Method swizzledBundleMethod = + class_getInstanceMethod([NSBundle class], @selector(gul_scene_fakeInfoDictionary)); + method_exchangeImplementations(originalBundleMethod, swizzledBundleMethod); + gSceneFakeInfoDictionary = nil; + [super tearDown]; +} + - (void)testProxySceneDelegateWithNoSceneDelegate { if (@available(iOS 13, tvOS 13, *)) { - id mockSharedScene = OCMClassMock([UIScene class]); - OCMStub([mockSharedScene delegate]).andReturn(nil); - XCTAssertNoThrow([GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:mockSharedScene]); - [mockSharedScene stopMocking]; - mockSharedScene = nil; + GULFakeScene *mockSharedScene = [[GULFakeScene alloc] init]; + mockSharedScene.delegate = nil; + XCTAssertNoThrow( + [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:(UIScene *)mockSharedScene]); } } - (void)testProxySceneDelegate { if (@available(iOS 13, tvOS 13, *)) { GULTestSceneDelegate *realSceneDelegate = [[GULTestSceneDelegate alloc] init]; - id mockSharedScene = OCMClassMock([UIScene class]); - OCMStub([mockSharedScene delegate]).andReturn(realSceneDelegate); + GULFakeScene *mockSharedScene = [[GULFakeScene alloc] init]; + mockSharedScene.delegate = realSceneDelegate; size_t sizeBefore = class_getInstanceSize([GULTestSceneDelegate class]); Class realSceneDelegateClassBefore = [realSceneDelegate class]; - [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:mockSharedScene]; + [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:(UIScene *)mockSharedScene]; XCTAssertTrue([realSceneDelegate isKindOfClass:[GULTestSceneDelegate class]]); @@ -88,31 +161,25 @@ - (void)testProxySceneDelegate { // Make sure that the class has changed. XCTAssertNotEqualObjects([realSceneDelegate class], realSceneDelegateClassBefore); - - [mockSharedScene stopMocking]; - mockSharedScene = nil; } } - (void)testProxyProxiedSceneDelegate { if (@available(iOS 13, tvOS 13, *)) { GULTestSceneDelegate *realSceneDelegate = [[GULTestSceneDelegate alloc] init]; - id mockSharedScene = OCMClassMock([UIScene class]); - OCMStub([mockSharedScene delegate]).andReturn(realSceneDelegate); + GULFakeScene *mockSharedScene = [[GULFakeScene alloc] init]; + mockSharedScene.delegate = realSceneDelegate; // Proxy the scene delegate for the 1st time. - [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:mockSharedScene]; + [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:(UIScene *)mockSharedScene]; Class realSceneDelegateClassBefore = [realSceneDelegate class]; // Proxy the scene delegate for the 2nd time. - [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:mockSharedScene]; + [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:(UIScene *)mockSharedScene]; // Make sure that the class isn't changed. XCTAssertEqualObjects([realSceneDelegate class], realSceneDelegateClassBefore); - - [mockSharedScene stopMocking]; - mockSharedScene = nil; } } @@ -121,26 +188,29 @@ - (void)testSceneOpenURLContextsIsInvokedOnInterceptors { NSSet *urlContexts = [NSSet set]; GULTestSceneDelegate *realSceneDelegate = [[GULTestSceneDelegate alloc] init]; - id mockSharedScene = OCMClassMock([UIScene class]); - OCMStub([mockSharedScene delegate]).andReturn(realSceneDelegate); + GULFakeScene *mockSharedScene = [[GULFakeScene alloc] init]; + mockSharedScene.delegate = realSceneDelegate; - id interceptor = OCMProtocolMock(@protocol(TestSceneProtocol)); - OCMExpect([interceptor scene:mockSharedScene openURLContexts:urlContexts]); + GULFakeSceneDelegateInterceptor *interceptor = [[GULFakeSceneDelegateInterceptor alloc] init]; - id interceptor2 = OCMProtocolMock(@protocol(TestSceneProtocol)); - OCMExpect([interceptor2 scene:mockSharedScene openURLContexts:urlContexts]); + GULFakeSceneDelegateInterceptor *interceptor2 = [[GULFakeSceneDelegateInterceptor alloc] init]; - [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:mockSharedScene]; + [GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:(UIScene *)mockSharedScene]; [GULSceneDelegateSwizzler registerSceneDelegateInterceptor:interceptor]; [GULSceneDelegateSwizzler registerSceneDelegateInterceptor:interceptor2]; - [realSceneDelegate scene:mockSharedScene openURLContexts:urlContexts]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); - - [mockSharedScene stopMocking]; - mockSharedScene = nil; + [realSceneDelegate scene:(UIScene *)mockSharedScene openURLContexts:urlContexts]; + __block BOOL isCalled1 = NO; + __block BOOL isCalled2 = NO; + dispatch_sync(interceptor.syncQueue, ^{ + isCalled1 = interceptor.isSceneOpenURLContextsCalled; + }); + dispatch_sync(interceptor2.syncQueue, ^{ + isCalled2 = interceptor2.isSceneOpenURLContextsCalled; + }); + XCTAssertTrue(isCalled1); + XCTAssertTrue(isCalled2); } } @@ -168,12 +238,10 @@ - (void)testNotificationCenterRegister { - (void)testAppProxyPlistFlag_NoFlag { // No keys anywhere. If there is no key, the default should be enabled. NSDictionary *mainDictionary = nil; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that scene delegate proxy is enabled when there is neither the Firebase nor the @@ -182,12 +250,10 @@ - (void)testAppProxyPlistFlag_NoFlag { - (void)testAppProxyPlistFlag_NoSceneDelegateProxyKey { // No scene delegate disable key. If there is no key, the default should be enabled. NSDictionary *mainDictionary = @{@"randomKey" : @"randomValue"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that scene delegate proxy is enabled when the Firebase plist is explicitly set to YES and @@ -195,12 +261,10 @@ - (void)testAppProxyPlistFlag_NoSceneDelegateProxyKey { - (void)testAppProxyPlistFlag_FirebaseEnabled { // Set proxy enabled to YES. NSDictionary *mainDictionary = @{kGULFirebaseSceneDelegateProxyEnabledPlistKey : @(YES)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that scene delegate proxy is enabled when the Google plist is explicitly set to YES and @@ -208,12 +272,10 @@ - (void)testAppProxyPlistFlag_FirebaseEnabled { - (void)testAppProxyPlistFlag_GoogleEnabled { // Set proxy enabled to YES. NSDictionary *mainDictionary = @{kGULGoogleSceneDelegateProxyEnabledPlistKey : @(YES)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is enabled when the Firebase flag has the wrong type of @@ -221,12 +283,10 @@ - (void)testAppProxyPlistFlag_GoogleEnabled { - (void)testAppProxyPlist_WrongFirebaseDisableFlagValueType { // Set proxy enabled to "NO" - a string. NSDictionary *mainDictionary = @{kGULFirebaseSceneDelegateProxyEnabledPlistKey : @"NO"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is enabled when the Google flag has the wrong type of value @@ -234,12 +294,10 @@ - (void)testAppProxyPlist_WrongFirebaseDisableFlagValueType { - (void)testAppProxyPlist_WrongGoogleDisableFlagValueType { // Set proxy enabled to "NO" - a string. NSDictionary *mainDictionary = @{kGULGoogleSceneDelegateProxyEnabledPlistKey : @"NO"}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertTrue([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is disabled when the Firebase flag is set to NO and the @@ -247,12 +305,10 @@ - (void)testAppProxyPlist_WrongGoogleDisableFlagValueType { - (void)testAppProxyPlist_FirebaseDisableFlag { // Set proxy enabled to NO. NSDictionary *mainDictionary = @{kGULFirebaseSceneDelegateProxyEnabledPlistKey : @(NO)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertFalse([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is disabled when the Google flag is set to NO and the @@ -260,12 +316,10 @@ - (void)testAppProxyPlist_FirebaseDisableFlag { - (void)testAppProxyPlist_GoogleDisableFlag { // Set proxy enabled to NO. NSDictionary *mainDictionary = @{kGULGoogleSceneDelegateProxyEnabledPlistKey : @(NO)}; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertFalse([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is disabled when the Google flag is set to NO and the @@ -276,12 +330,10 @@ - (void)testAppProxyPlist_GoogleDisableFlagFirebaseEnableFlag { kGULGoogleSceneDelegateProxyEnabledPlistKey : @(NO), kGULFirebaseSceneDelegateProxyEnabledPlistKey : @(YES) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertFalse([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is disabled when the Google flag is set to NO and the @@ -292,12 +344,10 @@ - (void)testAppProxyPlist_FirebaseDisableFlagGoogleEnableFlag { kGULGoogleSceneDelegateProxyEnabledPlistKey : @(YES), kGULFirebaseSceneDelegateProxyEnabledPlistKey : @(NO) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertFalse([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } /** Tests that the scene delegate proxy is disabled when the Google flag is set to NO and the @@ -308,14 +358,13 @@ - (void)testAppProxyPlist_FirebaseDisableFlagGoogleDisableFlag { kGULGoogleSceneDelegateProxyEnabledPlistKey : @(NO), kGULFirebaseSceneDelegateProxyEnabledPlistKey : @(NO) }; - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - [[[mainBundleMock expect] andReturn:mainDictionary] infoDictionary]; + gSceneFakeInfoDictionary = mainDictionary; XCTAssertFalse([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]); - [mainBundleMock stopMocking]; - mainBundleMock = nil; + gSceneFakeInfoDictionary = nil; } @end +#pragma clang diagnostic pop #endif // UISCENE_SUPPORTED diff --git a/GoogleUtilities/Tests/Unit/UserDefaults/GULUserDefaultsTests.m b/GoogleUtilities/Tests/Unit/UserDefaults/GULUserDefaultsTests.m index ee55926d..f1077a00 100644 --- a/GoogleUtilities/Tests/Unit/UserDefaults/GULUserDefaultsTests.m +++ b/GoogleUtilities/Tests/Unit/UserDefaults/GULUserDefaultsTests.m @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import #import #import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h" diff --git a/Package.swift b/Package.swift index fdc1ddfd..f34cf058 100644 --- a/Package.swift +++ b/Package.swift @@ -59,7 +59,6 @@ let package = Package( ), ], dependencies: [ - // TODO: restore OCMock when https://github.com/erikdoe/ocmock/pull/537 // gets merged to fix Xcode 15.3 builds. .package( url: "https://github.com/paulb777/ocmock.git", @@ -210,7 +209,6 @@ let package = Package( .testTarget( name: "UtilitiesUnit", dependencies: [ - .product(name: "OCMock", package: "OCMock"), "GoogleUtilities-AppDelegateSwizzler", "GoogleUtilities-Environment", "GoogleUtilities-Logger", From 26c0ffb6a157d0de2946ceeb568f57f82bf9ad3f Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Mon, 3 Aug 2026 21:00:24 -0400 Subject: [PATCH 2/5] gemfile --- Gemfile.lock | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8a30e2bc..29b487d8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,18 +5,18 @@ GEM base64 nkf rexml - activesupport (7.0.10) + activesupport (7.2.3.1) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1) - mutex_m + minitest (>= 5.1, < 6) securerandom (>= 0.3) - tzinfo (~> 2.0) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) @@ -73,9 +73,10 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.7) + connection_pool (3.0.2) cork (0.3.0) colored2 (~> 3.1) - danger (9.6.1) + danger (9.6.0) base64 (~> 0.2) claide (~> 1.0) claide-plugins (>= 0.9.2) @@ -93,19 +94,25 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - faraday (2.0.0) - ruby2_keywords (>= 0.0.4) - faraday-http-cache (2.5.1) + faraday (2.14.3) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-http-cache (2.7.0) faraday (>= 0.8) + faraday-net_http (3.4.4) + net-http (~> 0.5) ffi (1.16.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - git (1.19.1) + git (4.3.2) + activesupport (>= 5.0) addressable (~> 2.8) - rchardet (~> 1.8) + process_executer (~> 4.0) + rchardet (~> 1.9) httpclient (2.8.3) - i18n (1.14.8) + i18n (1.15.2) concurrent-ruby (~> 1.0) json (2.7.2) kramdown (2.5.2) @@ -113,29 +120,32 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) logger (1.7.0) - minitest (5.26.1) + minitest (5.27.0) molinillo (0.8.0) - mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) + net-http (0.9.1) + uri (>= 0.11.1) netrc (0.11.0) nkf (0.2.0) octokit (10.0.0) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) + process_executer (4.0.4) + track_open_instances (~> 0.1) pstore (0.2.1) public_suffix (4.0.7) - rchardet (1.8.0) + rchardet (1.10.2) rexml (3.4.4) ruby-macho (2.5.1) - ruby2_keywords (0.0.5) sawyer (0.9.3) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - securerandom (0.3.2) + securerandom (0.4.1) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) + track_open_instances (0.1.15) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) @@ -143,6 +153,7 @@ GEM unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.2.0) + uri (1.1.1) xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) From 37f2fe5c38013c88a32e36927749313a0705ee0f Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Mon, 3 Aug 2026 21:04:36 -0400 Subject: [PATCH 3/5] Remove other files and deps --- .../URLSession/FIRURLSessionOCMockStub.h | 35 ------------------ .../URLSession/FIRURLSessionOCMockStub.m | 36 ------------------- Package.swift | 8 +---- 3 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h delete mode 100644 GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m diff --git a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h b/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h deleted file mode 100644 index 2895b03c..00000000 --- a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * 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 - -NS_ASSUME_NONNULL_BEGIN - -typedef BOOL (^FIRRequestValidationBlock)(NSURLRequest *request); - -@interface FIRURLSessionOCMockStub : NSObject - -+ (id)stubURLSessionDataTaskWithResponse:(nullable NSHTTPURLResponse *)response - body:(nullable NSData *)body - error:(nullable NSError *)error - URLSessionMock:(id)URLSessionMock - requestValidationBlock:(nullable FIRRequestValidationBlock)requestValidationBlock; - -+ (NSHTTPURLResponse *)HTTPResponseWithCode:(NSInteger)statusCode; - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m b/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m deleted file mode 100644 index d927fbb8..00000000 --- a/GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.m +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * 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 "GoogleUtilities/Tests/Unit/Shared/URLSession/FIRURLSessionOCMockStub.h" - -@implementation FIRURLSessionOCMockStub - -+ (id)stubURLSessionDataTaskWithResponse:(NSHTTPURLResponse *)response - body:(NSData *)body - error:(NSError *)error - URLSessionMock:(id)URLSessionMock - requestValidationBlock:(FIRRequestValidationBlock)requestValidationBlock { - return nil; -} - -+ (NSHTTPURLResponse *)HTTPResponseWithCode:(NSInteger)statusCode { - return [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://localhost"] - statusCode:statusCode - HTTPVersion:@"HTTP/1.1" - headerFields:nil]; -} - -@end diff --git a/Package.swift b/Package.swift index f34cf058..3be2a752 100644 --- a/Package.swift +++ b/Package.swift @@ -58,13 +58,7 @@ let package = Package( targets: ["GoogleUtilities-UserDefaults"] ), ], - dependencies: [ - // gets merged to fix Xcode 15.3 builds. - .package( - url: "https://github.com/paulb777/ocmock.git", - revision: "173955e93e6ee6999a10729ab67e4b4efdd1db6d" - ), - ], + dependencies: [], targets: [ .target( name: "GoogleUtilities-AppDelegateSwizzler", From f8da28a0f63decc0d4b313ee208f789804c3442e Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 4 Aug 2026 12:30:37 -0400 Subject: [PATCH 4/5] Apply suggestion from @ncooke3 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index a603bb50..47b322c9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.5 +3.4.1 From 6e9be56134e799f2084b75becc90fac2d336f1da Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 4 Aug 2026 12:30:47 -0400 Subject: [PATCH 5/5] Apply suggestion from @ncooke3 --- GoogleUtilities.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index 37604135..262858b3 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -152,7 +152,7 @@ other Google CocoaPods. They're not intended for direct public usage. 'GoogleUtilities/Tests/Unit/Environment/**/*.[mh]' ] unit_tests.requires_app_host = true - end + end s.test_spec 'unit-swift' do |unit_tests_swift| unit_tests_swift.scheme = { :code_coverage => true }