From 7750eda0bd81f793ba22e5674a2e3b5c46485e01 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Wed, 16 Apr 2025 12:10:34 -0400 Subject: [PATCH 1/5] [Config] Create MockURLProtocol --- .../{URLSessionPartialMock.swift => MockURLProtocol.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename FirebaseRemoteConfig/Tests/Swift/FakeUtils/{URLSessionPartialMock.swift => MockURLProtocol.swift} (100%) diff --git a/FirebaseRemoteConfig/Tests/Swift/FakeUtils/URLSessionPartialMock.swift b/FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift similarity index 100% rename from FirebaseRemoteConfig/Tests/Swift/FakeUtils/URLSessionPartialMock.swift rename to FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift From cbba9328cea3a1b1278cc2b3ebaa7709e5c9378c Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Wed, 16 Apr 2025 12:14:09 -0400 Subject: [PATCH 2/5] Change implementation to MockURLProtocol --- .../Swift/FakeUtils/MockURLProtocol.swift | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift b/FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift index f6809a0560e..7761e4e523d 100644 --- a/FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift +++ b/FirebaseRemoteConfig/Tests/Swift/FakeUtils/MockURLProtocol.swift @@ -14,52 +14,49 @@ import Foundation -#if SWIFT_PACKAGE - import RemoteConfigFakeConsoleObjC -#endif - -// Create a partial mock by subclassing the URLSessionDataTask. -class URLSessionDataTaskMock: URLSessionDataTask, @unchecked Sendable { - private let closure: () -> Void - - init(closure: @escaping () -> Void) { - self.closure = closure +class MockURLProtocol: URLProtocol { + #if compiler(>=6) + nonisolated(unsafe) static var requestHandler: ((URLRequest) throws -> ( + Data, + HTTPURLResponse + ))? + #else + static var requestHandler: ((URLRequest) throws -> ( + Data, + HTTPURLResponse + ))? + #endif + + override class func canInit(with request: URLRequest) -> Bool { + #if os(watchOS) + print("MockURLProtocol cannot be used on watchOS.") + return false + #else + return true + #endif // os(watchOS) } - override func resume() { - closure() - } -} - -class URLSessionMock: URLSession, @unchecked Sendable { - typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void - - private let fakeConsole: FakeConsole - init(with fakeConsole: FakeConsole) { - self.fakeConsole = fakeConsole + override class func canonicalRequest(for request: URLRequest) -> URLRequest { + return request } - // Properties to control what gets returned to the URLSession callback. - // error could also be added here. - var data: Data? - var response: URLResponse? - var etag = "" - - override func dataTask(with request: URLRequest, - completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) - -> URLSessionDataTask { - let consoleValues = fakeConsole.get() - if etag == "" || consoleValues["state"] as! String == RCNFetchResponseKeyStateUpdate { - // Time string in microseconds to insure a different string from previous change. - etag = String(NSDate().timeIntervalSince1970) + override func startLoading() { + guard let requestHandler = MockURLProtocol.requestHandler else { + fatalError("`requestHandler` is nil.") } - let jsonData = try! JSONSerialization.data(withJSONObject: consoleValues) - let response = HTTPURLResponse(url: URL(fileURLWithPath: "fakeURL"), - statusCode: 200, - httpVersion: nil, - headerFields: ["etag": etag]) - return URLSessionDataTaskMock { - completionHandler(jsonData, response, nil) + guard let client = client else { + fatalError("`client` is nil.") + } + + do { + let (data, respopnse) = try requestHandler(request) + client.urlProtocol(self, didReceive: respopnse, cacheStoragePolicy: .notAllowed) + client.urlProtocol(self, didLoad: data) + client.urlProtocolDidFinishLoading(self) + } catch { + client.urlProtocol(self, didFailWithError: error) } } + + override func stopLoading() {} } From dec004a3d479a0c5b2b34b731dc895965766013a Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Wed, 16 Apr 2025 12:14:33 -0400 Subject: [PATCH 3/5] Add explicit self --- FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m index 1021d7014d1..9786af20cbf 100644 --- a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m +++ b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m @@ -1916,7 +1916,7 @@ - (void)testSetCustomSignalsMultipleTimes { [_configInstances[i] setCustomSignals:testSignals1 withCompletion:^(NSError *_Nullable error) { XCTAssertNil(error); - [_configInstances[i] + [self->_configInstances[i] setCustomSignals:testSignals2 withCompletion:^(NSError *_Nullable error) { XCTAssertNil(error); From 5f539d314d3bec1319325cd43d9985ad19df098e Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Wed, 16 Apr 2025 12:23:48 -0400 Subject: [PATCH 4/5] Migrate APITestBase.swift to use new MockURLProtocol --- .../Tests/Swift/SwiftAPI/APITestBase.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/FirebaseRemoteConfig/Tests/Swift/SwiftAPI/APITestBase.swift b/FirebaseRemoteConfig/Tests/Swift/SwiftAPI/APITestBase.swift index 8aa741f0437..d48600d0f24 100644 --- a/FirebaseRemoteConfig/Tests/Swift/SwiftAPI/APITestBase.swift +++ b/FirebaseRemoteConfig/Tests/Swift/SwiftAPI/APITestBase.swift @@ -88,7 +88,24 @@ class APITestBase: XCTestCase { config.configRealtime = RealtimeMocks.mockRealtime(config.configRealtime) } fakeConsole = FakeConsole() - config.configFetch.fetchSession = URLSessionMock(with: fakeConsole) + let configuration = URLSessionConfiguration.default + configuration.protocolClasses = [MockURLProtocol.self] + config.configFetch.fetchSession = URLSession(configuration: configuration) + + var etag = "" + MockURLProtocol.requestHandler = { request in + let consoleValues = self.fakeConsole.get() + if etag == "" || consoleValues["state"] as! String == RCNFetchResponseKeyStateUpdate { + // Time string in microseconds to insure a different string from previous change. + etag = String(NSDate().timeIntervalSince1970) + } + let jsonData = try! JSONSerialization.data(withJSONObject: consoleValues) + let response = HTTPURLResponse(url: URL(fileURLWithPath: "fakeURL"), + statusCode: 200, + httpVersion: nil, + headerFields: ["etag": etag]) + return (jsonData, response!) + } fakeConsole.config = [Constants.key1: Constants.value1, Constants.jsonKey: jsonValue, From 8c0938a2eacf5caf93b3d1e75548e10b502e66d5 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Wed, 16 Apr 2025 14:41:33 -0400 Subject: [PATCH 5/5] Remove duplicate flag --- scripts/build.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 2925f77d2da..9ad85b209a1 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -486,13 +486,12 @@ case "$product-$platform-$method" in ../../../FirebaseRemoteConfig/Tests/Swift/AccessToken.json # Integration tests are only run on iOS to minimize flake failures. - # TODO(ncooke3): Remove -sdk and -destination flags. + # TODO(ncooke3): Remove -sdk and -destination flags and replace with "${xcb_flags[@]}" RunXcodebuild \ -workspace 'gen/FirebaseRemoteConfig/FirebaseRemoteConfig.xcworkspace' \ -scheme "FirebaseRemoteConfig-Unit-swift-api-tests" \ -sdk 'iphonesimulator' \ -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.3.1' \ - "${xcb_flags[@]}" \ build \ test ;;