// // Copyright 2022 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 XCTest #if SWIFT_PACKAGE import FirebaseSessionsObjC #endif // SWIFT_PACKAGE @testable import FirebaseSessions /// /// This is the parent class for tests that test against the Sessions internal /// API or test Sessions `init` functionality. /// /// Test cases should only go in subclasses because all tests in the parent /// class will be run by subclasses. /// class FirebaseSessionsTestsBase: XCTestCase { let testAppID = "testAppID" // Mocks var mockCoordinator: MockSessionCoordinator! var mockAppInfo: MockApplicationInfo! var mockSettings: MockSettingsProtocol! // Non-mock dependencies var initiator: SessionInitiator! var generator: SessionGenerator! // Class under test var sessions: Sessions! // Example subscribers var mockCrashlyticsSubscriber: MockSubscriber! var mockPerformanceSubscriber: MockSubscriber! var allSubscribers: [SessionsSubscriber] { return [mockCrashlyticsSubscriber, mockPerformanceSubscriber] } // Mock controllers var pausedClock = Date(timeIntervalSince1970: 1_635_739_200) override func setUp() { // Reset the subscribers between tests mockCrashlyticsSubscriber = MockSubscriber(name: SessionsSubscriberName.Crashlytics) mockPerformanceSubscriber = MockSubscriber(name: SessionsSubscriberName.Performance) } /// This function forms the basis for all tests of type `FirebaseSessionsTestsBase`. It's written /// so that you don't need to setup expectations for each test individually. /// /// It has 4 parts: /// - `subscriberSDKs` tells the test to expect the list of subscriber SDKs /// - `preSessionsInit` is before Sessions or any Subscriber SDKs start up. This is a good /// place to mock variables in any dependencies (eg. Settings, or mocking any Subscribers /// themselves) /// - `postSessionsInit` is after Sessions has started up, but before logging any events. This /// is a good place for Subscribers to call register on the Sessions SDK /// - `postLogEvent` is called whenever an event is logged via the Sessions SDK. This is where /// most assertions will happen. func runSessionsSDK(subscriberSDKs: [SessionsSubscriber], preSessionsInit: (MockSettingsProtocol) -> Void, postSessionsInit: () -> Void, postLogEvent: @escaping (Result, [SessionsSubscriber]) -> Void) { // This class is static, so we need to clear global state SessionsDependencies.dependencies.removeAll() for subscriberSDK in subscriberSDKs { SessionsDependencies.addDependency(name: subscriberSDK.sessionsSubscriberName) } // Setup an expectation so we can wait for loggedEventCallback to be called. // We need the expectation because the callback is called in the background let loggedEventExpectation = XCTestExpectation(description: "Called loggedEventCallback") mockCoordinator = MockSessionCoordinator() mockAppInfo = MockApplicationInfo() mockSettings = MockSettingsProtocol() // Allow tests to configure settings before it is used during // initialization of other classes preSessionsInit(mockSettings) generator = SessionGenerator(collectEvents: Sessions .shouldCollectEvents(settings: mockSettings)) initiator = SessionInitiator(settings: mockSettings, currentTimeProvider: { self.pausedClock }) sessions = Sessions(appID: testAppID, sessionGenerator: generator, coordinator: mockCoordinator, initiator: initiator, appInfo: mockAppInfo, settings: mockSettings) { result in // Provide the result for tests to test against postLogEvent(result, subscriberSDKs) // Fulfil the expectation so the test can continue loggedEventExpectation.fulfill() } // Execute test cases after Sessions is initialized. This is a good // place register Subscriber SDKs postSessionsInit() // Wait for the Sessions SDK to log the session before finishing // the test. wait(for: [loggedEventExpectation], timeout: 3) } func assertSuccess(result: Result) { switch result { case .success(()): break case let .failure(error): XCTFail("Expected success but got failure with error: \(error)") } } func assertFailure(result: Result, expectedError: FirebaseSessionsError) { switch result { case .success(()): XCTFail("Expected failure but got success") case let .failure(error): XCTAssertEqual(error, expectedError) } } // Do not add tests to this class because they will be run by all subclasses }