diff --git a/UnitTests/MParticleTestsSwift.swift b/UnitTests/MParticleTestsSwift.swift deleted file mode 100644 index c939ed51..00000000 --- a/UnitTests/MParticleTestsSwift.swift +++ /dev/null @@ -1,1458 +0,0 @@ -import XCTest -#if MPARTICLE_LOCATION_DISABLE - import mParticle_Apple_SDK_NoLocation -#else - import mParticle_Apple_SDK -#endif - -enum LogKitBatchData { - static let invalidJSON = #"{"invalid": "json""# - static let singleEvent = #"{"events":[{"id":1}]}"# - static let multiEvent = #"{"events":[{"id":1},{"id":2}]}"# - static let parsedSingleEvent: [String: Any] = [ - "events": [ - ["id": 1] - ] - ] -} - -class MParticleTestsSwift: XCTestCase { - var receivedMessage: String? - var mparticle: MParticle! - var listenerController: MPListenerControllerMock! - var kitContainer: MPKitContainerMock! - var executor: ExecutorMock! - var backendController: MPBackendControllerMock! - var state: MPStateMachineMock! - var notificationController: MPNotificationControllerMock! - var appEnvironmentProvier: AppEnvironmentProviderMock! - var appNotificationHandler: MPAppNotificationHandlerMock! - var persistenceController: MPPersistenceControllerMock! - var settingsProvider: SettingsProviderMock! - var options: MParticleOptions! - var userDefaults: MPUserDefaultsMock! - var dataPlanFilter: MPDataPlanFilterMock! - var kit: MPKitMock! - - let testName: String = "test" - let keyValueDict: [String: String] = ["key": "value"] - let responseKeyValueDict: [String: String] = ["responseKey": "responseValue"] - - let token = "abcd1234".data(using: .utf8)! - - let error = NSError(domain: "test", code: 1) - let exception = NSException(name: NSExceptionName("Test"), reason: "Test", userInfo: nil) - - let url = URL(string: "https://example.com")! - - var event: MPEvent! - var transformedEvent: MPEvent! - var baseEvent: MPBaseEvent! - var transformedBaseEvent: MPBaseEvent! - var commerceEvent: MPCommerceEvent! - var transformedCommerceEvent: MPCommerceEvent! - - func customLogger(_ message: String) { - receivedMessage = message - } - - override func setUp() { - super.setUp() - mparticle = MParticle.sharedInstance() - mparticle = MParticle() - mparticle.logLevel = .verbose - mparticle.customLogger = customLogger - listenerController = MPListenerControllerMock() - listenerController.onAPICalledExpectation = XCTestExpectation() - mparticle.listenerController = listenerController - - kitContainer = MPKitContainerMock() - mparticle.setKitContainer(kitContainer) - - executor = ExecutorMock() - mparticle.setExecutor(executor) - - backendController = MPBackendControllerMock() - mparticle.backendController = backendController - - state = MPStateMachineMock() - mparticle.stateMachine = state - - notificationController = MPNotificationControllerMock() - mparticle.notificationController = notificationController - - appEnvironmentProvier = AppEnvironmentProviderMock() - mparticle.appEnvironmentProvider = appEnvironmentProvier - - appNotificationHandler = MPAppNotificationHandlerMock() - mparticle.appNotificationHandler = appNotificationHandler - - persistenceController = MPPersistenceControllerMock() - mparticle.persistenceController = persistenceController - - settingsProvider = SettingsProviderMock() - mparticle.settingsProvider = settingsProvider - - dataPlanFilter = MPDataPlanFilterMock() - mparticle.dataPlanFilter = dataPlanFilter - - options = MParticleOptions() - - userDefaults = MPUserDefaultsMock() - - kit = MPKitMock() - - event = MPEvent(name: testName, type: .other)! - event.customAttributes = keyValueDict - - transformedEvent = MPEvent(name: testName, type: .addToCart)! - event.customAttributes = keyValueDict - - baseEvent = MPBaseEvent(eventType: .other)! - transformedBaseEvent = MPBaseEvent(eventType: .addToCart)! - - commerceEvent = MPCommerceEvent(action: .addToCart)! - transformedCommerceEvent = MPCommerceEvent(action: .removeFromCart)! - } - - override func tearDown() { - super.tearDown() - receivedMessage = nil - mparticle.dataPlanFilter = nil - } - - func testSetOptOutCompletionSuccess() { - mparticle.setOptOutCompletion(.success, optOut: true) - assertReceivedMessage("Set Opt Out: 1") - } - - func testSetOptOutCompletionFailure() { - mparticle.setOptOutCompletion(.fail, optOut: true) - assertReceivedMessage("Set Opt Out Failed: 1") - } - - func testSetOptOutOptOutValueIsDifferentItShouldBeChangedAndDeliveredToBackendController() { - XCTAssertFalse(state.optOut) - mparticle.optOut = true - XCTAssertTrue(state.optOut) - XCTAssertTrue(backendController.setOptOutCalled) - XCTAssertEqual(backendController.setOptOutOptOutStatusParam, true) - XCTAssertNotNil(backendController.setOptOutCompletionHandler) - backendController.setOptOutCompletionHandler?(true, .success) - } - - func testIdentifyNoDispatchCallbackNoErrorDefferedKitAvailable() { - mparticle.deferredKitConfiguration_PRIVATE = [[String: String]]() - let expectedApiResult = MPIdentityApiResult() - let expectation = XCTestExpectation() - options.onIdentifyComplete = { apiResult, error in - XCTAssertTrue(expectedApiResult === apiResult) - XCTAssertNil(error) - - expectation.fulfill() - } - mparticle.identifyNoDispatchCallback(expectedApiResult, error: nil, options: options) - - wait(for: [expectation], timeout: 1.0) - XCTAssertNil(receivedMessage) - XCTAssertNil(mparticle.deferredKitConfiguration_PRIVATE) - } - - func testIdentifyNoDispatchCallbackWithErrorDefferedKitAvailable() { - mparticle.deferredKitConfiguration_PRIVATE = [[String: String]]() - let expectedApiResult = MPIdentityApiResult() - let expectation = XCTestExpectation() - options.onIdentifyComplete = { apiResult, _ in - XCTAssertTrue(expectedApiResult === apiResult) - XCTAssertTrue(self.error == self.error) - - expectation.fulfill() - } - mparticle.identifyNoDispatchCallback(expectedApiResult, error: error, options: options) - - wait(for: [expectation], timeout: 0.1) - assertReceivedMessage("Identify request failed with error: \(error)") - XCTAssertNil(mparticle.deferredKitConfiguration_PRIVATE) - } - - func testConfigureDefaultConfigurationExistOptionParametersAreNotSet() { - mparticle.backendController = MPBackendController_PRIVATE() - mparticle.configure(with: options) - XCTAssertEqual(mparticle.backendController.sessionTimeout, 0.0) - XCTAssertEqual(mparticle.backendController.uploadInterval, 60.0) - XCTAssertEqual(mparticle.customUserAgent, nil) - XCTAssertEqual(mparticle.collectUserAgent, true) - XCTAssertEqual(mparticle.trackNotifications, true) - } - - func testConfigureWhenDefaultConfigurationExists() { - let settings: NSMutableDictionary = [ - "session_timeout": NSNumber(value: 2.0), - "upload_interval": NSNumber(value: 3.0), - "custom_user_agent": "custom_user_agent", - "collect_user_agent": false, - "track_notifications": false, - "enable_location_tracking": true, - "location_tracking_accuracy": 100.0, - "location_tracking_distance_filter": 10.0, - ] - settingsProvider.configSettings = settings - mparticle.settingsProvider = settingsProvider - mparticle.backendController = MPBackendController_PRIVATE() - mparticle.configure(with: options) - - XCTAssertEqual(mparticle.backendController.sessionTimeout, 2.0) - XCTAssertEqual(mparticle.backendController.uploadInterval, 3.0) - XCTAssertEqual(mparticle.customUserAgent, "custom_user_agent") - XCTAssertEqual(mparticle.collectUserAgent, false) - XCTAssertEqual(mparticle.trackNotifications, false) - } - - func testStartWithKeyCallbackFirstRun() { - XCTAssertFalse(mparticle.initialized) - - mparticle.start(withKeyCallback: true, options: options, userDefaults: userDefaults) - - XCTAssertTrue(mparticle.initialized) - XCTAssertNil(mparticle.settingsProvider.configSettings) - - XCTAssertNotNil(userDefaults.setMPObjectValueParam) - XCTAssertEqual(userDefaults.setMPObjectKeyParam, "firstrun") - XCTAssertEqual(userDefaults.setMPObjectUserIdParam, 0) - XCTAssertTrue(userDefaults.synchronizeCalled) - } - - func testStartWithKeyCallbackNotFirstRunWithIdentityRequest() { - let user = mparticle.identity.currentUser - options.identifyRequest = MPIdentityApiRequest(user: user!) - - mparticle.start(withKeyCallback: false, options: options, userDefaults: userDefaults as MPUserDefaultsProtocol) - - XCTAssertTrue(mparticle.initialized) - XCTAssertNil(mparticle.settingsProvider.configSettings) - - XCTAssertFalse(userDefaults.setMPObjectCalled) - XCTAssertFalse(userDefaults.synchronizeCalled) - } - - func testBeginTimedEventCompletionHandlerDataFilterNotSet() { - mparticle.dataPlanFilter = nil - XCTAssertNil(mparticle.dataPlanFilter) - - mparticle.beginTimedEventCompletionHandler(event, execStatus: .success) - assertReceivedMessage("Began timed event", event: event) - } - - func testBeginTimedEventCompletionHandlerDataFilterSetDataFilterReturnNil() { - mparticle.beginTimedEventCompletionHandler(event, execStatus: .success) - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertEqual(dataPlanFilter.transformEventEventParam, event) - assertReceivedMessage("Blocked timed event begin from kits", event: event) - } - - func testLogEventCallbackDataFilterNotSet() { - mparticle.dataPlanFilter = nil - XCTAssertNil(mparticle.dataPlanFilter) - mparticle.logEventCallback(event, execStatus: .success) - - XCTAssertNil(receivedMessage) - } - - func testLogEventCallbackDataFilterSetDataFilterReturnNil() { - mparticle.logEventCallback(event, execStatus: .success) - - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertEqual(dataPlanFilter.transformEventEventParam, event) - - assertReceivedMessage("Blocked timed event end from kits", event: event) - } - - func testLogScreenCallbackDataFilterNotSet() { - mparticle.dataPlanFilter = nil - XCTAssertNil(mparticle.dataPlanFilter) - mparticle.logScreenCallback(event, execStatus: .success) - - assertReceivedMessage("Logged screen event", event: event) - } - - func testLogScreenCallbackDataFilterSetDataFilterReturnNil() { - mparticle.logScreenCallback(event, execStatus: .success) - - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) - XCTAssertEqual(dataPlanFilter.transformEventForScreenEventScreenEventParam, event) - - assertReceivedMessage("Blocked screen event from kits", event: event) - - } - - func testLogErrorCallbackSuccess() { - mparticle.logErrorCallback([:], execStatus: .success, message: "error") - - assertReceivedMessage("Logged error with message: error") - } - - func testLogErrorCallbackFail() { - mparticle.logErrorCallback([:], execStatus: .fail, message: "error") - - XCTAssertNil(receivedMessage) - } - - func testLogExceptionCallbackSuccess() { - mparticle.logExceptionCallback(exception, execStatus: .success, message: "exception", topmostContext: nil) - - assertReceivedMessage("Logged exception name: exception, reason: Test, topmost context: (null)") - } - - func testLogExceptionCallbackFail() { - mparticle.logExceptionCallback(exception, execStatus: .fail, message: "exception", topmostContext: nil) - - XCTAssertNil(receivedMessage) - } - - func testLogCrashCallbackSuccess() { - mparticle.logCrashCallback(.success, message: "Message") - assertReceivedMessage("Logged crash with message: Message") - } - - func testLogCommerceEventCallbackSuccess() { - mparticle.logCommerceEventCallback(commerceEvent, execStatus: .success) - - XCTAssertNil(receivedMessage) - } - - func testLogCommerceEventCallbackFail() { - mparticle.logCommerceEventCallback(commerceEvent, execStatus: .fail) - - assertReceivedMessage("Failed to log commerce event", event: commerceEvent) - } - - func testLogNetworkPerformanceCallbackSuccess() { - mparticle.logNetworkPerformanceCallback(.success) - - assertReceivedMessage("Logged network performance measurement") - } - - func testLogNetworkPerformanceCallbackFail() { - mparticle.logNetworkPerformanceCallback(.fail) - - XCTAssertNil(receivedMessage) - } - - func testSetSharedInstance() { - MParticle.setSharedInstance(mparticle) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "setSharedInstance:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === mparticle) - } - - func testBeginTimedEventDependenciesReceiveCorrectParametersAndHandlerExecutedWithoutErrors() { - mparticle.beginTimedEvent(event) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "beginTimedEvent:") - XCTAssertEqual(listenerController.onAPICalledParameter1, event) - XCTAssertTrue(backendController.beginTimedEventCalled) - XCTAssertEqual(backendController.beginTimedEventEventParam, event) - XCTAssertNotNil(backendController.beginTimedEventCompletionHandler) - backendController.beginTimedEventCompletionHandler?(event, .success) - XCTAssertNotNil(receivedMessage) - } - - func testSessionDidBegin() { - kitContainer.forwardSDKCallExpectation = XCTestExpectation() - mparticle.sessionDidBegin(MPSession()) - - wait(for: [kitContainer.forwardSDKCallExpectation!], timeout: 1.0) - - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "beginSession") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .sessionStart) - XCTAssertNil(kitContainer.forwardSDKCallEventParam) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testSessionDidEnd() { - kitContainer.forwardSDKCallExpectation = XCTestExpectation() - mparticle.sessionDidEnd(MPSession()) - - wait(for: [kitContainer.forwardSDKCallExpectation!], timeout: 1.0) - - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "endSession") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .sessionEnd) - XCTAssertNil(kitContainer.forwardSDKCallEventParam) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testResetForSwitchingWorkspaces() { - let expectation = XCTestExpectation() - - mparticle.reset { - expectation.fulfill() - } - - wait(for: [expectation], timeout: 1.0) - - XCTAssertTrue(kitContainer.flushSerializedKitsCalled) - XCTAssertTrue(kitContainer.removeAllSideloadedKitsCalled) - XCTAssertEqual(persistenceController.resetDatabaseCalled, true) - XCTAssertTrue(backendController.unproxyOriginalAppDelegateCalled) - } - - func testBeginSessionTempSessionAvailableSessionTempSessionShouldNotBeCreated() { - backendController.session = nil - backendController.tempSessionReturnValue = MParticleSession() - mparticle.beginSession() - XCTAssertFalse(backendController.createTempSessionCalled) - } - - func testBeginSessionSessionAvailableSessionTempSessionShouldNotBeCreated() { - backendController.session = MPSession() - backendController.tempSessionReturnValue = nil - mparticle.beginSession() - XCTAssertFalse(backendController.createTempSessionCalled) - } - - func testBeginSessionSessionUnavailable() { - backendController.session = nil - backendController.tempSessionReturnValue = nil - mparticle.beginSession() - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(backendController.createTempSessionCalled) - XCTAssertTrue(backendController.beginSessionCalled) - XCTAssertEqual(backendController.beginSessionIsManualParam, true) - XCTAssertNotNil(backendController.beginSessionDateParam) - } - - func testEndSessionNoSession() { - backendController.session = nil - mparticle.endSession() - XCTAssertEqual(executor.executeOnMessageQueueAsync, true) - XCTAssertFalse(backendController.endSessionWithIsManualCalled) - } - - func testEndSessionWithSession() { - backendController.session = MPSession() - mparticle.endSession() - XCTAssertEqual(executor.executeOnMessageQueueAsync, true) - XCTAssertTrue(backendController.endSessionWithIsManualCalled) - XCTAssertEqual(backendController.endSessionIsManualParam, true) - } - - func testForwardLogInstall() { - mparticle.forwardLogInstall() - XCTAssertEqual(executor.executeOnMainAsync, true) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "forwardLogInstall") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) - XCTAssertNil(kitContainer.forwardSDKCallEventParam) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testForwardLogUpdate() { - mparticle.forwardLogUpdate() - XCTAssertEqual(executor.executeOnMainAsync, true) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "forwardLogUpdate") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) - XCTAssertNil(kitContainer.forwardSDKCallEventParam) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testIndentityReturnsTheSameObject() { - let identity = mparticle.identity - XCTAssertTrue(identity === mparticle.identity) - } - - func testRoktReturnsTheSameObject() { - let rokt = mparticle.rokt - XCTAssertTrue(rokt === mparticle.rokt) - } - - func testSessionTimeoutReturnsValueFromBackendController() { - mparticle.backendController.sessionTimeout = 100 - XCTAssertEqual(mparticle.sessionTimeout, 100) - } - - func testUniqueIdentifierRwturnedFromStateMachine() { - state.consumerInfo.uniqueIdentifier = "test" - XCTAssertEqual(mparticle.uniqueIdentifier, "test") - } - - func testSetUploadIntervalChangeValueInBackendControllerWhenIntervalGreaterThenOne() { - mparticle.setUploadInterval(3) - XCTAssertEqual(backendController.uploadInterval, 3) - } - - func testSetUploadIntervalNotChangeValueInBackendControllerWhenIntervalLessThenOne() { - mparticle.setUploadInterval(0.1) - XCTAssertEqual(backendController.uploadInterval, 0.0) - } - - func testUploadIntervalGetFromBackendController() { - backendController.uploadInterval = 100 - XCTAssertEqual(mparticle.uploadInterval, 100) - } - - func testUserAttributesForUserIdRequestDataFromBackendController() { - backendController.userAttributesReturnValue = ["key": "value"] - let dictionary = mparticle.userAttributes(forUserId: 1) - XCTAssertEqual(dictionary?["key"] as? String, "value") - XCTAssertTrue(backendController.userAttributesCalled) - XCTAssertEqual(backendController.userAttributesUserIdParam, 1) - } - - func testConfigureWithOptionsNoSettings() { - mparticle.configure(with: .init()) - XCTAssertEqual(backendController.sessionTimeout, 0.0) - XCTAssertEqual(backendController.uploadInterval, 0.0) - XCTAssertNil(mparticle.customUserAgent) - XCTAssertTrue(mparticle.collectUserAgent) - XCTAssertTrue(mparticle.trackNotifications) -#if os(iOS) -#if !MPARTICLE_LOCATION_DISABLE - XCTAssertNil(listenerController.onAPICalledApiName) -#endif -#endif - } - - func testConfigureWithOptionsWithSettingsAndOptionNotSet() { - settingsProvider.configSettings = [ - "session_timeout": 100, - "upload_interval": 50, - "custom_user_agent": "agent", - "collect_user_agent": false, - "track_notifications": false, - "enable_location_tracking": true, - ] - options.isSessionTimeoutSet = false - options.isUploadIntervalSet = false - options.isCollectUserAgentSet = false - options.isCollectUserAgentSet = false - options.isTrackNotificationsSet = false - mparticle.configure(with: .init()) - XCTAssertEqual(backendController.sessionTimeout, 100.0) - XCTAssertEqual(backendController.uploadInterval, 50.0) - XCTAssertEqual(mparticle.customUserAgent, "agent") - XCTAssertFalse(mparticle.collectUserAgent) - XCTAssertFalse(mparticle.trackNotifications) - -#if os(iOS) -#if !MPARTICLE_LOCATION_DISABLE - XCTAssertEqual(listenerController.onAPICalledApiName?.description, - "beginLocationTracking:minDistance:authorizationRequest:") -#endif -#endif - } - - // MARK: - logEvent - - func testLogEventCalledLogCustomEvent() { - mparticle.logEvent(event) - wait(for: [listenerController.onAPICalledExpectation!], timeout: 0.1) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") - } - - func testLogEventCalledLogCommerceEvent() { - mparticle.logEvent(commerceEvent) - wait(for: [listenerController.onAPICalledExpectation!], timeout: 0.1) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") - } - - func testLogEventWithFilterReturningNil_blocksEvent() { - dataPlanFilter.transformEventForBaseEventReturnValue = nil - - mparticle.logEvent(baseEvent) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === baseEvent) - - // Verify backend was called - XCTAssertTrue(backendController.logBaseEventCalled) - XCTAssertTrue(backendController.logBaseEventEventParam === baseEvent) - let completion = backendController.logBaseEventCompletionHandler! - XCTAssertNotNil(completion) - completion(baseEvent, .success) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - - // Verify filter transform event - XCTAssertTrue(dataPlanFilter.transformEventForBaseEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForBaseEventParam === baseEvent) - - // Logger should record the blocked event message - assertReceivedMessage("Blocked base event from kits", event: baseEvent) - } - - func testLogBaseEventWithFilterReturningEvent_forwardsTransformedEvent() { - dataPlanFilter.transformEventForBaseEventReturnValue = transformedBaseEvent - - mparticle.logEvent(baseEvent) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === baseEvent) - - // Verify backend was called - XCTAssertTrue(backendController.logBaseEventCalled) - XCTAssertTrue(backendController.logBaseEventEventParam === baseEvent) - let completion = backendController.logBaseEventCompletionHandler! - XCTAssertNotNil(completion) - completion(baseEvent, .success) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(executor.executeOnMainAsync) - - // Verify filter transformed event - XCTAssertTrue(dataPlanFilter.transformEventForBaseEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForBaseEventParam === baseEvent) - - // Verify kit container forwarded transformed event - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBaseEvent:") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) - XCTAssertTrue(kitContainer.forwardSDKCallEventParam === transformedBaseEvent) - } - - // MARK: - logCustomEvent - - func testLogCustomEventWithNilEvent_logsError() { - mparticle.logCustomEvent(nil) - assertReceivedMessage("Cannot log nil event!") - } - - func testLogCustomEventWithFilterReturningNil_blocksEvent() { - dataPlanFilter.transformEventReturnValue = nil - - mparticle.logCustomEvent(event) - - // Verify event timing ended - XCTAssertNil(event.endTime) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === event) - - // Verify backend was called - XCTAssertTrue(backendController.logEventCalled) - XCTAssertTrue(backendController.logEventEventParam === event) - XCTAssertNotNil(backendController.logEventCompletionHandler) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - - // Verify filter transform event - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) - - // Logger should record the blocked event message - assertReceivedMessage("Blocked custom event from kits", event: event) - } - - func testLogCustomEventWithFilterReturningEvent_forwardsTransformedEvent() { - dataPlanFilter.transformEventReturnValue = transformedEvent - - mparticle.logCustomEvent(event) - - // Verify event timing ended - XCTAssertNil(event.endTime) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === event) - - // Verify backend was called - XCTAssertTrue(backendController.logEventCalled) - XCTAssertTrue(backendController.logEventEventParam === event) - XCTAssertNotNil(backendController.logEventCompletionHandler) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(executor.executeOnMainAsync) - - // Verify filter transformed event - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) - - // Verify kit container forwarded transformed event - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logEvent:") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .event) - XCTAssertTrue(kitContainer.forwardSDKCallEventParam === transformedEvent) - } - - // MARK: - logScreen - - func testLogScreenEvent_dataPlanFilterReturnsNil_blocksEvent() { - mparticle.logScreenEvent(event) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === event) - - XCTAssertTrue(backendController.logScreenCalled) - XCTAssertTrue(backendController.logScreenEventParam === event) - XCTAssertNotNil(backendController.logScreenCompletionHandler) - - backendController.logScreenCompletionHandler?(event, .success) - - assertReceivedMessage("Blocked screen event from kits", event: event) - } - - func testLogScreenEvent_tracesFullExecutionFlow() { - dataPlanFilter.transformEventForScreenEventReturnValue = event - - mparticle.logScreenEvent(event) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === event) - - XCTAssertTrue(backendController.logScreenCalled) - XCTAssertTrue(backendController.logScreenEventParam === event) - XCTAssertNotNil(backendController.logScreenCompletionHandler) - - backendController.logScreenCompletionHandler?(event, .success) - - assertReceivedMessage("Logged screen event", event: event) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === event) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") - XCTAssertTrue(kitContainer.forwardSDKCallEventParam === event) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testLogScreenWrapper_withNilScreenName_logsErrorAndReturns() { - mparticle.logScreen("", eventInfo: event.customAttributes) - - assertReceivedMessage("Screen name is required.") - XCTAssertFalse(backendController.eventWithNameCalled) - XCTAssertFalse(executor.executeOnMessageQueueAsync) - XCTAssertFalse(listenerController.onAPICalledCalled) - XCTAssertFalse(backendController.logScreenCalled) - } - - func testLogScreenWrapper_callsLogScreen() { - backendController.eventWithNameReturnValue = event - dataPlanFilter.transformEventForScreenEventReturnValue = event - - mparticle.logScreen(event.name, eventInfo: event.customAttributes) - - XCTAssertTrue(backendController.eventWithNameCalled) - XCTAssertEqual(backendController.eventWithNameEventNameParam, testName) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === event) - XCTAssertTrue(backendController.logScreenCalled) - XCTAssertNotNil(backendController.logScreenCompletionHandler) - backendController.logScreenCompletionHandler!(event, .success) - - assertReceivedMessage("Logged screen event", event: event) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === event) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") - XCTAssertTrue(kitContainer.forwardSDKCallEventParam === event) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - func testLogScreen_withNoExistingEvent_createsNewEventOfTypeNavigation() { - backendController.eventWithNameReturnValue = nil - let mockMPNavEvent = MPEvent(name: testName, type: .navigation)! - mockMPNavEvent.customAttributes = keyValueDict - mockMPNavEvent.shouldUploadEvent = true - - dataPlanFilter.transformEventForScreenEventReturnValue = mockMPNavEvent - - mparticle.logScreen(testName, eventInfo: keyValueDict, shouldUploadEvent: true) - - XCTAssertTrue(backendController.eventWithNameCalled) - XCTAssertEqual(backendController.eventWithNameEventNameParam, testName) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") - XCTAssertEqual(listenerController.onAPICalledParameter1, mockMPNavEvent) - XCTAssertTrue(backendController.logScreenCalled) - XCTAssertNotNil(backendController.logScreenCompletionHandler) - backendController.logScreenCompletionHandler!(mockMPNavEvent, .success) - - assertReceivedMessage("Logged screen event", event: mockMPNavEvent) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === mockMPNavEvent) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") - XCTAssertTrue(kitContainer.forwardSDKCallEventParam === mockMPNavEvent) - XCTAssertNil(kitContainer.forwardSDKCallParametersParam) - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) - XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) - } - - // MARK: - logKitBatch - - func testLogKitBatch_withNilBatch_doesNotExecuteOrForward() { - mparticle.logKitBatch(nil) - - assertReceivedMessage("Cannot log nil batch!") - XCTAssertFalse(executor.executeOnMessageQueueAsync) - XCTAssertFalse(kitContainer.hasKitBatchingKitsCalled) - XCTAssertFalse(executor.executeOnMainAsync) - XCTAssertFalse(kitContainer.forwardSDKCallCalled) - XCTAssertFalse(persistenceController.saveCalled) - } - - func testLogKitBatch_noBatchingKits_andKitsInitialized_doesNothing() { - kitContainer.kitsInitialized = true - kitContainer.hasKitBatchingKitsReturnValue = false - - mparticle.logKitBatch(LogKitBatchData.singleEvent) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertFalse(executor.executeOnMainAsync) - XCTAssertFalse(kitContainer.forwardSDKCallCalled) - XCTAssertFalse(persistenceController.saveCalled) - } - - func testLogKitBatch_kitsNotInitialized_defersWorkUntilInitialization() { - kitContainer.kitsInitialized = false - kitContainer.hasKitBatchingKitsReturnValue = true - - mparticle.logKitBatch(LogKitBatchData.singleEvent) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - - // Should queue deferred block, not execute immediately - XCTAssertFalse(executor.executeOnMainAsync) - XCTAssertFalse(kitContainer.forwardSDKCallCalled) - XCTAssertFalse(persistenceController.saveCalled) - - // Simulate kits becoming initialized - kitContainer.kitsInitialized = true - mparticle.executeKitsInitializedBlocks() - - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "executeKitsInitializedBlocks") - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") - XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) - XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) - } - - func testLogKitBatch_withBatchingKits_forwardsParsedBatch_andPersistsForwardRecords() { - kitContainer.kitsInitialized = true - kitContainer.hasKitBatchingKitsReturnValue = true - - mparticle.logKitBatch(LogKitBatchData.multiEvent) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") - XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) - XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) - } - - func testLogKitBatch_invokesKitHandler_andPersistsForwardRecords() { - kitContainer.kitsInitialized = true - kitContainer.hasKitBatchingKitsReturnValue = true - - let forwardRecord = MPForwardRecord() - kit.logBatchReturnValue = [forwardRecord] - - mparticle.logKitBatch(LogKitBatchData.singleEvent) - - guard let kitHandler = kitContainer.forwardSDKCallKitHandlerParam else { - XCTFail("Expected kitHandler closure to be captured") - return - } - - // Simulate invoking the handler - let config = MPKitConfiguration() - kitHandler(kit, LogKitBatchData.parsedSingleEvent, config) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") - XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) - XCTAssertTrue(kit.logBatchCalled) - XCTAssertEqual(kit.logBatchParam?["events"] as? [[String: Int]], - LogKitBatchData.parsedSingleEvent.values.first as? [[String : Int]]) - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(persistenceController.saveCalled) - XCTAssertTrue(persistenceController.saveForwardRecordParam === forwardRecord) - } - - func testLogKitBatch_invalidJSON_stillForwardsWithNilBatch() { - kitContainer.kitsInitialized = true - kitContainer.hasKitBatchingKitsReturnValue = true - - mparticle.logKitBatch(LogKitBatchData.invalidJSON) - - XCTAssertTrue(executor.executeOnMessageQueueAsync) - XCTAssertTrue(executor.executeOnMainAsync) - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") - XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 0) - XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) - } - - // MARK: - logCommerceEvent - - func testLogCommerceEvent_assignsTimestampWhenNil() { - commerceEvent.setTimestamp(nil) - - mparticle.logCommerceEvent(commerceEvent) - - XCTAssertNotNil(commerceEvent.timestamp) - XCTAssertTrue(backendController.logCommerceEventCalled) - XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) - XCTAssertTrue(executor.executeOnMessageQueueAsync) - } - - func testLogCommerceEventWithFilterReturningNil_blocksEvent() { - dataPlanFilter.transformEventForCommerceEventParam = nil - - mparticle.logCommerceEvent(commerceEvent) - - // Verify event timestamp added - XCTAssertNotNil(commerceEvent.timestamp) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) - - // Verify backend was called - XCTAssertTrue(backendController.logCommerceEventCalled) - XCTAssertTrue(backendController.logCommerceEventParam === commerceEvent) - let completion = backendController.logCommerceEventCompletionHandler! - XCTAssertNotNil(completion) - completion(commerceEvent, .success) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - - // Verify filter transform event - XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventParam === commerceEvent) - - // Logger should record the blocked event message - assertReceivedMessage("Blocked commerce event from kits", event: commerceEvent) - } - - func testLogCommerceEventWithFilterReturningEvent_forwardsTransformedEvent() { - dataPlanFilter.transformEventForCommerceEventReturnValue = transformedCommerceEvent - - mparticle.logCommerceEvent(commerceEvent) - - // Verify event timestamp added - XCTAssertNotNil(commerceEvent.timestamp) - - // Verify listener was called - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") - XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) - - // Verify backend was called - XCTAssertTrue(backendController.logCommerceEventCalled) - XCTAssertTrue(backendController.logCommerceEventParam === commerceEvent) - let completion = backendController.logCommerceEventCompletionHandler! - XCTAssertNotNil(completion) - completion(commerceEvent, .success) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMessageQueueAsync) - - // Verify filter transformed event - XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventParam === commerceEvent) - - // Verify kit container forwarded transformed event - XCTAssertTrue(kitContainer.forwardCommerceEventCallCalled) - XCTAssertTrue(kitContainer.forwardCommerceEventCallCommerceEventParam === transformedCommerceEvent) - } - - // MARK: - logLTVIncrease - - func testLogLTVIncrease_withNameAndInfo_createsEventAndCallsBackend() { - let amount = 42.0 - let name = "name" - let info: [String: Any] = ["source": "in_app", "currency": "USD"] - - mparticle.logLTVIncrease(amount, eventName: name, eventInfo: info) - - // Assert event was passed through - let loggedEvent = backendController.logEventEventParam! - XCTAssertNotNil(loggedEvent) - XCTAssertEqual(loggedEvent.name, name) - XCTAssertEqual(loggedEvent.type, .transaction) - - // Custom attributes should include amount and method name - let attrs = loggedEvent.customAttributes! - XCTAssertEqual(attrs["$Amount"] as? Double, amount) - XCTAssertEqual(attrs["$MethodName"] as? String, "LogLTVIncrease") - - // Check that the eventInfo entries were added - XCTAssertEqual(attrs["source"] as? String, "in_app") - XCTAssertEqual(attrs["currency"] as? String, "USD") - XCTAssertEqual(attrs.count, 4) - - // Listener controller should be notified - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logLTVIncrease:eventName:eventInfo:") - - // Backend completion handler should be stored - XCTAssertTrue(backendController.logEventCalled) - let completion = backendController.logEventCompletionHandler! - XCTAssertNotNil(completion) - completion(loggedEvent, .success) - } - - func testLogLTVIncrease_withoutEventInfo_defaultsToNilInfo() { - let amount = 12.5 - let name = "name" - - mparticle.logLTVIncrease(amount, eventName: name) - - // Assert event was passed through - let loggedEvent = backendController.logEventEventParam! - XCTAssertNotNil(loggedEvent) - XCTAssertEqual(loggedEvent.name, name) - XCTAssertEqual(loggedEvent.type, .transaction) - - // Custom attributes should only be amount and method name - let attrs = loggedEvent.customAttributes! - XCTAssertEqual(attrs["$Amount"] as? Double, amount) - XCTAssertEqual(attrs["$MethodName"] as? String, "LogLTVIncrease") - XCTAssertEqual(attrs.count, 2) - - // Listener controller should be notified - XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logLTVIncrease:eventName:eventInfo:") - XCTAssertEqual(listenerController.onAPICalledParameter1 as? Double, amount) - XCTAssertEqual(listenerController.onAPICalledParameter2 as? String, name) - XCTAssertNil(listenerController.onAPICalledParameter3) - - // Backend completion handler should be stored - XCTAssertTrue(backendController.logEventCalled) - let completion = backendController.logEventCompletionHandler! - XCTAssertNotNil(completion) - completion(loggedEvent, .success) - } - - func testLogLTVIncreaseCallback_withSuccessExecStatus_noDataPlanFilter_forwardsEvent() { - dataPlanFilter.transformEventReturnValue = nil - - mparticle.logLTVIncreaseCallback(event, execStatus: .success) - - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) - - assertReceivedMessage("Blocked LTV increase event from kits", event: event) - } - - func testLogLTVIncreaseCallback_withSuccessExecStatus_filterReturnsTransformedEvent_forwardsTransformedEvent() { - dataPlanFilter.transformEventReturnValue = transformedEvent - - mparticle.logLTVIncreaseCallback(event, execStatus: .success) - - // Verify filter transformed event - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMainAsync) - - // Verify kit container forwarded transformed event - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logLTVIncrease:event:") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) - XCTAssertNil(kitContainer.forwardSDKCallEventParam) - } - - // MARK: Error, Exception, and Crash Handling - - func testLeaveBreadcrumbCallback_withDataFilterNotSet_forwardsTransformedEvent() { - mparticle.dataPlanFilter = nil - XCTAssertNil(mparticle.dataPlanFilter) - - mparticle.leaveBreadcrumbCallback(event, execStatus: .success) - - // Verify executor usage - XCTAssertTrue(executor.executeOnMainAsync) - - // Verify kit container forwarded transformed event - XCTAssertTrue(kitContainer.forwardSDKCallCalled) - XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "leaveBreadcrumb:") - XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .breadcrumb) - XCTAssertNotNil(kitContainer.forwardSDKCallEventParam) - - assertReceivedMessage("Left breadcrumb", event: event) - } - - func testLeaveBreadcrumbCallback_withDataFilterSet_andDataFilterReturnNil() { - mparticle.leaveBreadcrumbCallback(event, execStatus: .success) - - XCTAssertTrue(dataPlanFilter.transformEventCalled) - XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) - - assertReceivedMessage("Blocked breadcrumb event from kits", event: event) - } - - func testLeaveBreadcrumbCallback_execStatusFail_noLoggedMessages() { - mparticle.leaveBreadcrumbCallback(event, execStatus: .fail) - - XCTAssertNil(receivedMessage) - } - - func testLeaveBreadcrumb_eventNamePassed_backendControllerReceiveCorrectName() { - mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) - XCTAssertEqual(backendController.eventWithNameEventNameParam, event.name) - } - - func testLeaveBreadcrumb_eventNamePassed_backendControllerReturnsNilEvent_newEventCreated() { - mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.name, event.name) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.type, .other) - XCTAssertNotNil(backendController.leaveBreadcrumbEventParam?.timestamp) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam!.customAttributes! as NSObject, event.customAttributes! as NSObject) - XCTAssertNotNil(backendController.leaveBreadcrumbCompletionHandler) - } - - func testLeaveBreadcrumb_eventNamePassed_backendControllerReturnsEvent_eventModified() { - backendController.eventSet?.add(event as Any) - mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.name, event.name) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.type, .other) - XCTAssertNotNil(backendController.leaveBreadcrumbEventParam?.timestamp) - XCTAssertEqual(backendController.leaveBreadcrumbEventParam!.customAttributes! as NSObject, event.customAttributes! as NSObject) - XCTAssertNotNil(backendController.leaveBreadcrumbCompletionHandler) - } - - func testLeaveBreadcrumb_eventNamePassed_CallbackCallsCallbackFunction() { - mparticle.dataPlanFilter = nil - mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) - backendController.leaveBreadcrumbCompletionHandler?(event, .success) - assertReceivedMessage("Left breadcrumb", event: event) - - } - - // MARK: - Application Notification Tests -#if os(iOS) - func testPushNotificationToken_returnsDeviceToken_whenNotAppExtension() { - notificationController.deviceTokenReturnValue = token - - appEnvironmentProvier.isAppExtensionReturnValue = false - - let token = mparticle.pushNotificationToken - - XCTAssertEqual(token, self.token) - XCTAssertTrue(notificationController.deviceTokenCalled) - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - } - - func testPushNotificationToken_returnsNil_whenAppExtension() { - notificationController.deviceTokenReturnValue = token - - appEnvironmentProvier.isAppExtensionReturnValue = true - - let token = mparticle.pushNotificationToken - - XCTAssertNil(token) - XCTAssertFalse(notificationController.deviceTokenCalled) - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - } - - func testSetPushNotificationToken_setsToken_whenNotAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.pushNotificationToken = token - - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertTrue(notificationController.setDeviceTokenCalled) - XCTAssertEqual(notificationController.setDeviceTokenParam, token) - } - - func testSetPushNotificationToken_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.pushNotificationToken = token - - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(notificationController.setDeviceTokenCalled) - } - - func testDidReceiveRemoteNotification_doesNothing_whenProxiedAppDelegateExists() { - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - mparticle.didReceiveRemoteNotification([:]) - - XCTAssertFalse(appNotificationHandler.didReceiveRemoteNotificationCalled) - XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) - } - - func testDidReceiveRemoteNotification_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.didReceiveRemoteNotification(keyValueDict) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.didReceiveRemoteNotificationCalled) - } - - - - func testDidReceiveRemoteNotification_forwardsToHandler_whenNotAppExtension_andNoProxiedDelegate() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.didReceiveRemoteNotification(keyValueDict) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertTrue(appNotificationHandler.didReceiveRemoteNotificationCalled) - XCTAssertEqual(appNotificationHandler.didReceiveRemoteNotificationParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) - } - - func testDidFailToRegisterForRemoteNotificationsWithError_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.didFailToRegisterForRemoteNotificationsWithError(error) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) - } - - func testDidFailToRegisterForRemoteNotificationsWithError_doesNothing_whenProxiedDelegateSet() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - mparticle.didFailToRegisterForRemoteNotificationsWithError(error) - - XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) - } - - func testDidFailToRegisterForRemoteNotificationsWithError_forwardsToHandler_whenNotAppExtension_andNoProxiedDelegate() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.didFailToRegisterForRemoteNotificationsWithError(error) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertTrue(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) - XCTAssertEqual(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorParam as NSError?, error) - } - - func testDidRegisterForRemoteNotificationsWithDeviceToken_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) - } - - func testDidRegisterForRemoteNotificationsWithDeviceToken_doesNothing_whenProxiedDelegateExists() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) - - XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) - } - - func testDidRegisterForRemoteNotificationsWithDeviceToken_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertTrue(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) - XCTAssertEqual(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenParam, token) - } - - func testHandleActionWithIdentifierForRemoteNotification_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) - } - - func testHandleActionWithIdentifierForRemoteNotification_doesNothing_whenProxiedDelegateExists() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) - - XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) - } - - func testHandleActionWithIdentifierForRemoteNotification_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) - - XCTAssertTrue(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) - XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationIdentifierParam, testName) - XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationUserInfoParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) - } - - func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_doesNothing_whenAppExtension() { - appEnvironmentProvier.isAppExtensionReturnValue = true - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) - } - - func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_doesNothing_whenProxiedDelegateExists() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) - - XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) - } - - func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { - appEnvironmentProvier.isAppExtensionReturnValue = false - - mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) - - XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) - XCTAssertTrue(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) - XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoIdentifierParam, testName) - XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoUserInfoParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) - XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoResponseInfoParam?[responseKeyValueDict.keys.first!] as? String, responseKeyValueDict.values.first) - } -#endif - - func testOpenURLSourceApplication_doesNothing_whenProxiedDelegateExists() { - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - let url = URL(string: "https://example.com")! - let sourceApp = "com.example.app" - let annotation = "annotation" - mparticle.open(url, sourceApplication: sourceApp, annotation: annotation) - - XCTAssertFalse(appNotificationHandler.openURLWithSourceApplicationAndAnnotationCalled) - } - - func testOpenURLSourceApplication_callsHandler_whenNoProxiedDelegate() { - let sourceApp = "com.example.app" - let annotation = "annotation" - - mparticle.open(url, sourceApplication: sourceApp, annotation: annotation) - - XCTAssertTrue(appNotificationHandler.openURLWithSourceApplicationAndAnnotationCalled) - XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationURLParam, url) - XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationSourceApplicationParam, sourceApp) - XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationAnnotationParam as! String, annotation) - } - - func testOpenURLOptions_callsHandler_whenNoProxiedDelegate_andIOSVersion9OrHigher() { - let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] - - mparticle.open(url, options: options) - - XCTAssertTrue(appNotificationHandler.openURLWithOptionsCalled) - XCTAssertEqual(appNotificationHandler.openURLWithOptionsURLParam, url) - XCTAssertEqual(appNotificationHandler.openURLWithOptionsOptionsParam?["UIApplicationOpenURLOptionsSourceApplicationKey"] as? String, "com.example.app") - } - - func testOpenURLOptions_doesNothing_whenProxiedDelegateExists() { - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] - mparticle.open(url, options: options) - - XCTAssertFalse(appNotificationHandler.openURLWithOptionsCalled) - } - - func testOpenURLOptions_doesNothing_whenSystemVersionBelow9() { - let currentDevice = UIDevice.current - let origSelector = NSSelectorFromString("systemVersion") - let mockedVersion: @convention(block) () -> String = { "8.4" } - let imp = imp_implementationWithBlock(mockedVersion) - class_replaceMethod(object_getClass(currentDevice), origSelector, imp, "@@:") - - let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] - mparticle.open(url, options: options) - - XCTAssertFalse(appNotificationHandler.openURLWithOptionsCalled) - } - - func testContinueUserActivity_returnsFalseAndDoesNotCallHandler_whenProxiedDelegateExists() { - mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") - - let activity = NSUserActivity(activityType: "com.example.test") - - let result = mparticle.continue(activity) { _ in } - - XCTAssertFalse(result) - XCTAssertFalse(appNotificationHandler.continueUserActivityCalled) - } - - func testContinueUserActivity_returnsFalse_whenHandlerReturnsFalse() { - let activity = NSUserActivity(activityType: "com.example.test") - appNotificationHandler.continueUserActivityReturnValue = false - - let result = mparticle.continue(activity) { _ in } - - XCTAssertTrue(appNotificationHandler.continueUserActivityCalled) - XCTAssertNotNil(appNotificationHandler.continueUserActivityRestorationHandlerParam) - XCTAssertEqual(appNotificationHandler.continueUserActivityUserActivityParam, activity) - XCTAssertFalse(result) - } -} - -// MARK: - Helpers - -extension MParticleTestsSwift { - func assertReceivedMessage( - _ expectedSuffix: String, - event: T? = nil, - file: StaticString = #file, - line: UInt = #line - ) { - guard let message = receivedMessage else { - XCTFail("Expected a received message but got nil", file: file, line: line) - return - } - - let prefix = "mParticle -> " - let expected: String - - if let event = event { - expected = "\(prefix)\(expectedSuffix): \(event)" - } else { - expected = "\(prefix)\(expectedSuffix)" - } - - XCTAssertEqual( - message.trimmingCharacters(in: .whitespacesAndNewlines), - expected.trimmingCharacters(in: .whitespacesAndNewlines), - file: file, - line: line - ) - } -} diff --git a/UnitTests/SwiftTests/Core/MParticleConfigurationTests.swift b/UnitTests/SwiftTests/Core/MParticleConfigurationTests.swift new file mode 100644 index 00000000..7c0ac760 --- /dev/null +++ b/UnitTests/SwiftTests/Core/MParticleConfigurationTests.swift @@ -0,0 +1,134 @@ +// +// MParticleConfigurationTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleConfigurationTests: MParticleTestBase { + + func testStartWithKeyCallbackFirstRun() { + XCTAssertFalse(mparticle.initialized) + + mparticle.start(withKeyCallback: true, options: options, userDefaults: userDefaults) + + XCTAssertTrue(mparticle.initialized) + XCTAssertNil(mparticle.settingsProvider.configSettings) + + XCTAssertNotNil(userDefaults.setMPObjectValueParam) + XCTAssertEqual(userDefaults.setMPObjectKeyParam, "firstrun") + XCTAssertEqual(userDefaults.setMPObjectUserIdParam, 0) + XCTAssertTrue(userDefaults.synchronizeCalled) + } + + func testStartWithKeyCallbackNotFirstRunWithIdentityRequest() { + let user = mparticle.identity.currentUser + options.identifyRequest = MPIdentityApiRequest(user: user!) + + mparticle.start(withKeyCallback: false, options: options, userDefaults: userDefaults as MPUserDefaultsProtocol) + + XCTAssertTrue(mparticle.initialized) + XCTAssertNil(mparticle.settingsProvider.configSettings) + + XCTAssertFalse(userDefaults.setMPObjectCalled) + XCTAssertFalse(userDefaults.synchronizeCalled) + } + + func testConfigureDefaultConfigurationExistOptionParametersAreNotSet() { + mparticle.backendController = MPBackendController_PRIVATE() + mparticle.configure(with: options) + XCTAssertEqual(mparticle.backendController.sessionTimeout, 0.0) + XCTAssertEqual(mparticle.backendController.uploadInterval, 60.0) + XCTAssertEqual(mparticle.customUserAgent, nil) + XCTAssertEqual(mparticle.collectUserAgent, true) + XCTAssertEqual(mparticle.trackNotifications, true) + } + + func testConfigureWhenDefaultConfigurationExists() { + let settings: NSMutableDictionary = [ + "session_timeout": NSNumber(value: 2.0), + "upload_interval": NSNumber(value: 3.0), + "custom_user_agent": "custom_user_agent", + "collect_user_agent": false, + "track_notifications": false, + "enable_location_tracking": true, + "location_tracking_accuracy": 100.0, + "location_tracking_distance_filter": 10.0, + ] + settingsProvider.configSettings = settings + mparticle.settingsProvider = settingsProvider + mparticle.backendController = MPBackendController_PRIVATE() + mparticle.configure(with: options) + + XCTAssertEqual(mparticle.backendController.sessionTimeout, 2.0) + XCTAssertEqual(mparticle.backendController.uploadInterval, 3.0) + XCTAssertEqual(mparticle.customUserAgent, "custom_user_agent") + XCTAssertEqual(mparticle.collectUserAgent, false) + XCTAssertEqual(mparticle.trackNotifications, false) + } + + func testConfigureWithOptionsNoSettings() { + mparticle.configure(with: .init()) + XCTAssertEqual(backendController.sessionTimeout, 0.0) + XCTAssertEqual(backendController.uploadInterval, 0.0) + XCTAssertNil(mparticle.customUserAgent) + XCTAssertTrue(mparticle.collectUserAgent) + XCTAssertTrue(mparticle.trackNotifications) +#if os(iOS) +#if !MPARTICLE_LOCATION_DISABLE + XCTAssertNil(listenerController.onAPICalledApiName) +#endif +#endif + } + + func testConfigureWithOptionsWithSettingsAndOptionNotSet() { + settingsProvider.configSettings = [ + "session_timeout": 100, + "upload_interval": 50, + "custom_user_agent": "agent", + "collect_user_agent": false, + "track_notifications": false, + "enable_location_tracking": true, + ] + options.isSessionTimeoutSet = false + options.isUploadIntervalSet = false + options.isCollectUserAgentSet = false + options.isCollectUserAgentSet = false + options.isTrackNotificationsSet = false + mparticle.configure(with: .init()) + XCTAssertEqual(backendController.sessionTimeout, 100.0) + XCTAssertEqual(backendController.uploadInterval, 50.0) + XCTAssertEqual(mparticle.customUserAgent, "agent") + XCTAssertFalse(mparticle.collectUserAgent) + XCTAssertFalse(mparticle.trackNotifications) + +#if os(iOS) +#if !MPARTICLE_LOCATION_DISABLE + XCTAssertEqual(listenerController.onAPICalledApiName?.description, + "beginLocationTracking:minDistance:authorizationRequest:") +#endif +#endif + } + + func testResetForSwitchingWorkspaces() { + let expectation = XCTestExpectation() + + mparticle.reset { + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1.0) + + XCTAssertTrue(kitContainer.flushSerializedKitsCalled) + XCTAssertTrue(kitContainer.removeAllSideloadedKitsCalled) + XCTAssertEqual(persistenceController.resetDatabaseCalled, true) + XCTAssertTrue(backendController.unproxyOriginalAppDelegateCalled) + } +} diff --git a/UnitTests/SwiftTests/Core/MParticleIdentityTests.swift b/UnitTests/SwiftTests/Core/MParticleIdentityTests.swift new file mode 100644 index 00000000..0e7ebe7b --- /dev/null +++ b/UnitTests/SwiftTests/Core/MParticleIdentityTests.swift @@ -0,0 +1,52 @@ +// +// MParticleIdentityTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import Foundation + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleIdentityTests: MParticleTestBase { + + func testIdentifyNoDispatchCallbackNoErrorDefferedKitAvailable() { + mparticle.deferredKitConfiguration_PRIVATE = [[String: String]]() + let expectedApiResult = MPIdentityApiResult() + let expectation = XCTestExpectation() + options.onIdentifyComplete = { apiResult, error in + XCTAssertTrue(expectedApiResult === apiResult) + XCTAssertNil(error) + + expectation.fulfill() + } + mparticle.identifyNoDispatchCallback(expectedApiResult, error: nil, options: options) + + wait(for: [expectation], timeout: 1.0) + XCTAssertNil(receivedMessage) + XCTAssertNil(mparticle.deferredKitConfiguration_PRIVATE) + } + + func testIdentifyNoDispatchCallbackWithErrorDefferedKitAvailable() { + mparticle.deferredKitConfiguration_PRIVATE = [[String: String]]() + let expectedApiResult = MPIdentityApiResult() + let expectation = XCTestExpectation() + options.onIdentifyComplete = { apiResult, _ in + XCTAssertTrue(expectedApiResult === apiResult) + XCTAssertTrue(self.error == self.error) + + expectation.fulfill() + } + mparticle.identifyNoDispatchCallback(expectedApiResult, error: error, options: options) + + wait(for: [expectation], timeout: 0.1) + assertReceivedMessage("Identify request failed with error: \(error)") + XCTAssertNil(mparticle.deferredKitConfiguration_PRIVATE) + } +} diff --git a/UnitTests/SwiftTests/Core/MParticleRuntimeValuesTests.swift b/UnitTests/SwiftTests/Core/MParticleRuntimeValuesTests.swift new file mode 100644 index 00000000..39bdbdf8 --- /dev/null +++ b/UnitTests/SwiftTests/Core/MParticleRuntimeValuesTests.swift @@ -0,0 +1,85 @@ +// +// MParticleRuntimeValuesTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleRuntimeValuesTests: MParticleTestBase { + + func testSetSharedInstance() { + MParticle.setSharedInstance(mparticle) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "setSharedInstance:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === mparticle) + } + + func testSetOptOutCompletionSuccess() { + mparticle.setOptOutCompletion(.success, optOut: true) + assertReceivedMessage("Set Opt Out: 1") + } + + func testSetOptOutCompletionFailure() { + mparticle.setOptOutCompletion(.fail, optOut: true) + assertReceivedMessage("Set Opt Out Failed: 1") + } + + func testIndentityReturnsTheSameObject() { + let identity = mparticle.identity + XCTAssertTrue(identity === mparticle.identity) + } + + func testRoktReturnsTheSameObject() { + let rokt = mparticle.rokt + XCTAssertTrue(rokt === mparticle.rokt) + } + + func testSessionTimeoutReturnsValueFromBackendController() { + mparticle.backendController.sessionTimeout = 100 + XCTAssertEqual(mparticle.sessionTimeout, 100) + } + + func testUniqueIdentifierRwturnedFromStateMachine() { + state.consumerInfo.uniqueIdentifier = "test" + XCTAssertEqual(mparticle.uniqueIdentifier, "test") + } + + func testSetUploadIntervalChangeValueInBackendControllerWhenIntervalGreaterThenOne() { + mparticle.setUploadInterval(3) + XCTAssertEqual(backendController.uploadInterval, 3) + } + + func testSetUploadIntervalNotChangeValueInBackendControllerWhenIntervalLessThenOne() { + mparticle.setUploadInterval(0.1) + XCTAssertEqual(backendController.uploadInterval, 0.0) + } + + func testUploadIntervalGetFromBackendController() { + backendController.uploadInterval = 100 + XCTAssertEqual(mparticle.uploadInterval, 100) + } + + func testUserAttributesForUserIdRequestDataFromBackendController() { + backendController.userAttributesReturnValue = ["key": "value"] + let dictionary = mparticle.userAttributes(forUserId: 1) + XCTAssertEqual(dictionary?["key"] as? String, "value") + XCTAssertTrue(backendController.userAttributesCalled) + XCTAssertEqual(backendController.userAttributesUserIdParam, 1) + } + + func testSetOptOutOptOutValueIsDifferentItShouldBeChangedAndDeliveredToBackendController() { + XCTAssertFalse(state.optOut) + mparticle.optOut = true + XCTAssertTrue(state.optOut) + XCTAssertTrue(backendController.setOptOutCalled) + XCTAssertEqual(backendController.setOptOutOptOutStatusParam, true) + XCTAssertNotNil(backendController.setOptOutCompletionHandler) + backendController.setOptOutCompletionHandler?(true, .success) + } +} diff --git a/UnitTests/SwiftTests/Core/MParticleSessionTests.swift b/UnitTests/SwiftTests/Core/MParticleSessionTests.swift new file mode 100644 index 00000000..4644e902 --- /dev/null +++ b/UnitTests/SwiftTests/Core/MParticleSessionTests.swift @@ -0,0 +1,86 @@ +// +// MParticleSessionTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import Foundation + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleSessionTests: MParticleTestBase { + + func testBeginSessionTempSessionAvailableSessionTempSessionShouldNotBeCreated() { + backendController.session = nil + backendController.tempSessionReturnValue = MParticleSession() + mparticle.beginSession() + XCTAssertFalse(backendController.createTempSessionCalled) + } + + func testBeginSessionSessionAvailableSessionTempSessionShouldNotBeCreated() { + backendController.session = MPSession() + backendController.tempSessionReturnValue = nil + mparticle.beginSession() + XCTAssertFalse(backendController.createTempSessionCalled) + } + + func testBeginSessionSessionUnavailable() { + backendController.session = nil + backendController.tempSessionReturnValue = nil + mparticle.beginSession() + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(backendController.createTempSessionCalled) + XCTAssertTrue(backendController.beginSessionCalled) + XCTAssertEqual(backendController.beginSessionIsManualParam, true) + XCTAssertNotNil(backendController.beginSessionDateParam) + } + + func testEndSessionNoSession() { + backendController.session = nil + mparticle.endSession() + XCTAssertEqual(executor.executeOnMessageQueueAsync, true) + XCTAssertFalse(backendController.endSessionWithIsManualCalled) + } + + func testEndSessionWithSession() { + backendController.session = MPSession() + mparticle.endSession() + XCTAssertEqual(executor.executeOnMessageQueueAsync, true) + XCTAssertTrue(backendController.endSessionWithIsManualCalled) + XCTAssertEqual(backendController.endSessionIsManualParam, true) + } + + func testSessionDidBegin() { + kitContainer.forwardSDKCallExpectation = XCTestExpectation() + mparticle.sessionDidBegin(MPSession()) + + wait(for: [kitContainer.forwardSDKCallExpectation!], timeout: 1.0) + + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "beginSession") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .sessionStart) + XCTAssertNil(kitContainer.forwardSDKCallEventParam) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } + + func testSessionDidEnd() { + kitContainer.forwardSDKCallExpectation = XCTestExpectation() + mparticle.sessionDidEnd(MPSession()) + + wait(for: [kitContainer.forwardSDKCallExpectation!], timeout: 1.0) + + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "endSession") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .sessionEnd) + XCTAssertNil(kitContainer.forwardSDKCallEventParam) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } +} diff --git a/UnitTests/SwiftTests/Diagnostics/MParticleBreadcrumbTests.swift b/UnitTests/SwiftTests/Diagnostics/MParticleBreadcrumbTests.swift new file mode 100644 index 00000000..ea52c63b --- /dev/null +++ b/UnitTests/SwiftTests/Diagnostics/MParticleBreadcrumbTests.swift @@ -0,0 +1,81 @@ +// +// MParticleBreadcrumbTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleBreadcrumbTests: MParticleTestBase { + + func testLeaveBreadcrumbCallback_withDataFilterNotSet_forwardsTransformedEvent() { + mparticle.dataPlanFilter = nil + XCTAssertNil(mparticle.dataPlanFilter) + + mparticle.leaveBreadcrumbCallback(event, execStatus: .success) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMainAsync) + + // Verify kit container forwarded transformed event + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "leaveBreadcrumb:") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .breadcrumb) + XCTAssertNotNil(kitContainer.forwardSDKCallEventParam) + + assertReceivedMessage("Left breadcrumb", event: event) + } + + func testLeaveBreadcrumbCallback_withDataFilterSet_andDataFilterReturnNil() { + mparticle.leaveBreadcrumbCallback(event, execStatus: .success) + + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) + + assertReceivedMessage("Blocked breadcrumb event from kits", event: event) + } + + func testLeaveBreadcrumbCallback_execStatusFail_noLoggedMessages() { + mparticle.leaveBreadcrumbCallback(event, execStatus: .fail) + + XCTAssertNil(receivedMessage) + } + + func testLeaveBreadcrumb_eventNamePassed_backendControllerReceiveCorrectName() { + mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) + XCTAssertEqual(backendController.eventWithNameEventNameParam, event.name) + } + + func testLeaveBreadcrumb_eventNamePassed_backendControllerReturnsNilEvent_newEventCreated() { + mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.name, event.name) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.type, .other) + XCTAssertNotNil(backendController.leaveBreadcrumbEventParam?.timestamp) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam!.customAttributes! as NSObject, event.customAttributes! as NSObject) + XCTAssertNotNil(backendController.leaveBreadcrumbCompletionHandler) + } + + func testLeaveBreadcrumb_eventNamePassed_backendControllerReturnsEvent_eventModified() { + backendController.eventSet?.add(event as Any) + mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.name, event.name) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam?.type, .other) + XCTAssertNotNil(backendController.leaveBreadcrumbEventParam?.timestamp) + XCTAssertEqual(backendController.leaveBreadcrumbEventParam!.customAttributes! as NSObject, event.customAttributes! as NSObject) + XCTAssertNotNil(backendController.leaveBreadcrumbCompletionHandler) + } + + func testLeaveBreadcrumb_eventNamePassed_CallbackCallsCallbackFunction() { + mparticle.dataPlanFilter = nil + mparticle.leaveBreadcrumb(event.name, eventInfo: event.customAttributes) + backendController.leaveBreadcrumbCompletionHandler?(event, .success) + assertReceivedMessage("Left breadcrumb", event: event) + + } +} diff --git a/UnitTests/SwiftTests/Diagnostics/MParticleErrorTests.swift b/UnitTests/SwiftTests/Diagnostics/MParticleErrorTests.swift new file mode 100644 index 00000000..95c40b73 --- /dev/null +++ b/UnitTests/SwiftTests/Diagnostics/MParticleErrorTests.swift @@ -0,0 +1,57 @@ +// +// MParticleErrorTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleErrorTests: MParticleTestBase { + + func testLogErrorCallbackSuccess() { + mparticle.logErrorCallback([:], execStatus: .success, message: "error") + + assertReceivedMessage("Logged error with message: error") + } + + func testLogErrorCallbackFail() { + mparticle.logErrorCallback([:], execStatus: .fail, message: "error") + + XCTAssertNil(receivedMessage) + } + + func testLogExceptionCallbackSuccess() { + mparticle.logExceptionCallback(exception, execStatus: .success, message: "exception", topmostContext: nil) + + assertReceivedMessage("Logged exception name: exception, reason: Test, topmost context: (null)") + } + + func testLogExceptionCallbackFail() { + mparticle.logExceptionCallback(exception, execStatus: .fail, message: "exception", topmostContext: nil) + + XCTAssertNil(receivedMessage) + } + + func testLogCrashCallbackSuccess() { + mparticle.logCrashCallback(.success, message: "Message") + assertReceivedMessage("Logged crash with message: Message") + } + + func testLogNetworkPerformanceCallbackSuccess() { + mparticle.logNetworkPerformanceCallback(.success) + + assertReceivedMessage("Logged network performance measurement") + } + + func testLogNetworkPerformanceCallbackFail() { + mparticle.logNetworkPerformanceCallback(.fail) + + XCTAssertNil(receivedMessage) + } +} diff --git a/UnitTests/SwiftTests/Events/MParticleCommerceEventTests.swift b/UnitTests/SwiftTests/Events/MParticleCommerceEventTests.swift new file mode 100644 index 00000000..382bc744 --- /dev/null +++ b/UnitTests/SwiftTests/Events/MParticleCommerceEventTests.swift @@ -0,0 +1,100 @@ +// +// MParticleCommerceEventTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleCommerceEventTests: MParticleTestBase { + + func testLogCommerceEvent_assignsTimestampWhenNil() { + commerceEvent.setTimestamp(nil) + + mparticle.logCommerceEvent(commerceEvent) + + XCTAssertNotNil(commerceEvent.timestamp) + XCTAssertTrue(backendController.logCommerceEventCalled) + XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) + XCTAssertTrue(executor.executeOnMessageQueueAsync) + } + + func testLogCommerceEventWithFilterReturningNil_blocksEvent() { + dataPlanFilter.transformEventForCommerceEventParam = nil + + mparticle.logCommerceEvent(commerceEvent) + + // Verify event timestamp added + XCTAssertNotNil(commerceEvent.timestamp) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) + + // Verify backend was called + XCTAssertTrue(backendController.logCommerceEventCalled) + XCTAssertTrue(backendController.logCommerceEventParam === commerceEvent) + let completion = backendController.logCommerceEventCompletionHandler! + XCTAssertNotNil(completion) + completion(commerceEvent, .success) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + + // Verify filter transform event + XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventParam === commerceEvent) + + // Logger should record the blocked event message + assertReceivedMessage("Blocked commerce event from kits", event: commerceEvent) + } + + func testLogCommerceEventWithFilterReturningEvent_forwardsTransformedEvent() { + dataPlanFilter.transformEventForCommerceEventReturnValue = transformedCommerceEvent + + mparticle.logCommerceEvent(commerceEvent) + + // Verify event timestamp added + XCTAssertNotNil(commerceEvent.timestamp) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === commerceEvent) + + // Verify backend was called + XCTAssertTrue(backendController.logCommerceEventCalled) + XCTAssertTrue(backendController.logCommerceEventParam === commerceEvent) + let completion = backendController.logCommerceEventCompletionHandler! + XCTAssertNotNil(completion) + completion(commerceEvent, .success) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + + // Verify filter transformed event + XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForCommerceEventParam === commerceEvent) + + // Verify kit container forwarded transformed event + XCTAssertTrue(kitContainer.forwardCommerceEventCallCalled) + XCTAssertTrue(kitContainer.forwardCommerceEventCallCommerceEventParam === transformedCommerceEvent) + } + + func testLogCommerceEventCallbackSuccess() { + mparticle.logCommerceEventCallback(commerceEvent, execStatus: .success) + + XCTAssertNil(receivedMessage) + } + + func testLogCommerceEventCallbackFail() { + mparticle.logCommerceEventCallback(commerceEvent, execStatus: .fail) + + assertReceivedMessage("Failed to log commerce event", event: commerceEvent) + } +} diff --git a/UnitTests/SwiftTests/Events/MParticleCustomEventTests.swift b/UnitTests/SwiftTests/Events/MParticleCustomEventTests.swift new file mode 100644 index 00000000..e82f3d99 --- /dev/null +++ b/UnitTests/SwiftTests/Events/MParticleCustomEventTests.swift @@ -0,0 +1,81 @@ +// +// MParticleCustomEventTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleCustomEventTests: MParticleTestBase { + + func testLogCustomEventWithNilEvent_logsError() { + mparticle.logCustomEvent(nil) + assertReceivedMessage("Cannot log nil event!") + } + + func testLogCustomEventWithFilterReturningNil_blocksEvent() { + dataPlanFilter.transformEventReturnValue = nil + + mparticle.logCustomEvent(event) + + // Verify event timing ended + XCTAssertNil(event.endTime) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === event) + + // Verify backend was called + XCTAssertTrue(backendController.logEventCalled) + XCTAssertTrue(backendController.logEventEventParam === event) + XCTAssertNotNil(backendController.logEventCompletionHandler) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + + // Verify filter transform event + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) + + // Logger should record the blocked event message + assertReceivedMessage("Blocked custom event from kits", event: event) + } + + func testLogCustomEventWithFilterReturningEvent_forwardsTransformedEvent() { + dataPlanFilter.transformEventReturnValue = transformedEvent + + mparticle.logCustomEvent(event) + + // Verify event timing ended + XCTAssertNil(event.endTime) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === event) + + // Verify backend was called + XCTAssertTrue(backendController.logEventCalled) + XCTAssertTrue(backendController.logEventEventParam === event) + XCTAssertNotNil(backendController.logEventCompletionHandler) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(executor.executeOnMainAsync) + + // Verify filter transformed event + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) + + // Verify kit container forwarded transformed event + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logEvent:") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .event) + XCTAssertTrue(kitContainer.forwardSDKCallEventParam === transformedEvent) + } +} diff --git a/UnitTests/SwiftTests/Events/MParticleEventTests.swift b/UnitTests/SwiftTests/Events/MParticleEventTests.swift new file mode 100644 index 00000000..14fc7d24 --- /dev/null +++ b/UnitTests/SwiftTests/Events/MParticleEventTests.swift @@ -0,0 +1,103 @@ +// +// MParticleEventTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleEventTests: MParticleTestBase { + + func testLogEventCalledLogCustomEvent() { + mparticle.logEvent(event) + wait(for: [listenerController.onAPICalledExpectation!], timeout: 0.1) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCustomEvent:") + } + + func testLogEventCalledLogCommerceEvent() { + mparticle.logEvent(commerceEvent) + wait(for: [listenerController.onAPICalledExpectation!], timeout: 0.1) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logCommerceEvent:") + } + + func testLogEventWithFilterReturningNil_blocksEvent() { + dataPlanFilter.transformEventForBaseEventReturnValue = nil + + mparticle.logEvent(baseEvent) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === baseEvent) + + // Verify backend was called + XCTAssertTrue(backendController.logBaseEventCalled) + XCTAssertTrue(backendController.logBaseEventEventParam === baseEvent) + let completion = backendController.logBaseEventCompletionHandler! + XCTAssertNotNil(completion) + completion(baseEvent, .success) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + + // Verify filter transform event + XCTAssertTrue(dataPlanFilter.transformEventForBaseEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForBaseEventParam === baseEvent) + + // Logger should record the blocked event message + assertReceivedMessage("Blocked base event from kits", event: baseEvent) + } + + func testLogBaseEventWithFilterReturningEvent_forwardsTransformedEvent() { + dataPlanFilter.transformEventForBaseEventReturnValue = transformedBaseEvent + + mparticle.logEvent(baseEvent) + + // Verify listener was called + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === baseEvent) + + // Verify backend was called + XCTAssertTrue(backendController.logBaseEventCalled) + XCTAssertTrue(backendController.logBaseEventEventParam === baseEvent) + let completion = backendController.logBaseEventCompletionHandler! + XCTAssertNotNil(completion) + completion(baseEvent, .success) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(executor.executeOnMainAsync) + + // Verify filter transformed event + XCTAssertTrue(dataPlanFilter.transformEventForBaseEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForBaseEventParam === baseEvent) + + // Verify kit container forwarded transformed event + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBaseEvent:") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) + XCTAssertTrue(kitContainer.forwardSDKCallEventParam === transformedBaseEvent) + } + + func testLogEventCallbackDataFilterNotSet() { + mparticle.dataPlanFilter = nil + XCTAssertNil(mparticle.dataPlanFilter) + mparticle.logEventCallback(event, execStatus: .success) + + XCTAssertNil(receivedMessage) + } + + func testLogEventCallbackDataFilterSetDataFilterReturnNil() { + mparticle.logEventCallback(event, execStatus: .success) + + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertEqual(dataPlanFilter.transformEventEventParam, event) + + assertReceivedMessage("Blocked timed event end from kits", event: event) + } +} diff --git a/UnitTests/SwiftTests/Events/MParticleScreenEventTests.swift b/UnitTests/SwiftTests/Events/MParticleScreenEventTests.swift new file mode 100644 index 00000000..335aa46c --- /dev/null +++ b/UnitTests/SwiftTests/Events/MParticleScreenEventTests.swift @@ -0,0 +1,147 @@ +// +// MParticleScreenEventTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleScreenEventTests: MParticleTestBase { + + func testLogScreenEvent_dataPlanFilterReturnsNil_blocksEvent() { + mparticle.logScreenEvent(event) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === event) + + XCTAssertTrue(backendController.logScreenCalled) + XCTAssertTrue(backendController.logScreenEventParam === event) + XCTAssertNotNil(backendController.logScreenCompletionHandler) + + backendController.logScreenCompletionHandler?(event, .success) + + assertReceivedMessage("Blocked screen event from kits", event: event) + } + + func testLogScreenEvent_tracesFullExecutionFlow() { + dataPlanFilter.transformEventForScreenEventReturnValue = event + + mparticle.logScreenEvent(event) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === event) + + XCTAssertTrue(backendController.logScreenCalled) + XCTAssertTrue(backendController.logScreenEventParam === event) + XCTAssertNotNil(backendController.logScreenCompletionHandler) + + backendController.logScreenCompletionHandler?(event, .success) + + assertReceivedMessage("Logged screen event", event: event) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === event) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") + XCTAssertTrue(kitContainer.forwardSDKCallEventParam === event) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } + + func testLogScreenWrapper_withNilScreenName_logsErrorAndReturns() { + mparticle.logScreen("", eventInfo: event.customAttributes) + + assertReceivedMessage("Screen name is required.") + XCTAssertFalse(backendController.eventWithNameCalled) + XCTAssertFalse(executor.executeOnMessageQueueAsync) + XCTAssertFalse(listenerController.onAPICalledCalled) + XCTAssertFalse(backendController.logScreenCalled) + } + + func testLogScreenWrapper_callsLogScreen() { + backendController.eventWithNameReturnValue = event + dataPlanFilter.transformEventForScreenEventReturnValue = event + + mparticle.logScreen(event.name, eventInfo: event.customAttributes) + + XCTAssertTrue(backendController.eventWithNameCalled) + XCTAssertEqual(backendController.eventWithNameEventNameParam, testName) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") + XCTAssertTrue(listenerController.onAPICalledParameter1 === event) + XCTAssertTrue(backendController.logScreenCalled) + XCTAssertNotNil(backendController.logScreenCompletionHandler) + backendController.logScreenCompletionHandler!(event, .success) + + assertReceivedMessage("Logged screen event", event: event) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === event) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") + XCTAssertTrue(kitContainer.forwardSDKCallEventParam === event) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } + + func testLogScreen_withNoExistingEvent_createsNewEventOfTypeNavigation() { + backendController.eventWithNameReturnValue = nil + let mockMPNavEvent = MPEvent(name: testName, type: .navigation)! + mockMPNavEvent.customAttributes = keyValueDict + mockMPNavEvent.shouldUploadEvent = true + + dataPlanFilter.transformEventForScreenEventReturnValue = mockMPNavEvent + + mparticle.logScreen(testName, eventInfo: keyValueDict, shouldUploadEvent: true) + + XCTAssertTrue(backendController.eventWithNameCalled) + XCTAssertEqual(backendController.eventWithNameEventNameParam, testName) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logScreenEvent:") + XCTAssertEqual(listenerController.onAPICalledParameter1, mockMPNavEvent) + XCTAssertTrue(backendController.logScreenCalled) + XCTAssertNotNil(backendController.logScreenCompletionHandler) + backendController.logScreenCompletionHandler!(mockMPNavEvent, .success) + + assertReceivedMessage("Logged screen event", event: mockMPNavEvent) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventScreenEventParam === mockMPNavEvent) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logScreen:") + XCTAssertTrue(kitContainer.forwardSDKCallEventParam === mockMPNavEvent) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .screenView) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } + + func testLogScreenCallbackDataFilterNotSet() { + mparticle.dataPlanFilter = nil + XCTAssertNil(mparticle.dataPlanFilter) + mparticle.logScreenCallback(event, execStatus: .success) + + assertReceivedMessage("Logged screen event", event: event) + } + + func testLogScreenCallbackDataFilterSetDataFilterReturnNil() { + mparticle.logScreenCallback(event, execStatus: .success) + + XCTAssertTrue(dataPlanFilter.transformEventForScreenEventCalled) + XCTAssertEqual(dataPlanFilter.transformEventForScreenEventScreenEventParam, event) + + assertReceivedMessage("Blocked screen event from kits", event: event) + + } +} diff --git a/UnitTests/SwiftTests/Events/MParticleTimedEventTests.swift b/UnitTests/SwiftTests/Events/MParticleTimedEventTests.swift new file mode 100644 index 00000000..d84031ad --- /dev/null +++ b/UnitTests/SwiftTests/Events/MParticleTimedEventTests.swift @@ -0,0 +1,42 @@ +// +// MParticleTimedEventTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleTimedEventTests: MParticleTestBase { + + func testBeginTimedEventCompletionHandlerDataFilterNotSet() { + mparticle.dataPlanFilter = nil + XCTAssertNil(mparticle.dataPlanFilter) + + mparticle.beginTimedEventCompletionHandler(event, execStatus: .success) + assertReceivedMessage("Began timed event", event: event) + } + + func testBeginTimedEventCompletionHandlerDataFilterSetDataFilterReturnNil() { + mparticle.beginTimedEventCompletionHandler(event, execStatus: .success) + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertEqual(dataPlanFilter.transformEventEventParam, event) + assertReceivedMessage("Blocked timed event begin from kits", event: event) + } + + func testBeginTimedEventDependenciesReceiveCorrectParametersAndHandlerExecutedWithoutErrors() { + mparticle.beginTimedEvent(event) + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "beginTimedEvent:") + XCTAssertEqual(listenerController.onAPICalledParameter1, event) + XCTAssertTrue(backendController.beginTimedEventCalled) + XCTAssertEqual(backendController.beginTimedEventEventParam, event) + XCTAssertNotNil(backendController.beginTimedEventCompletionHandler) + backendController.beginTimedEventCompletionHandler?(event, .success) + XCTAssertNotNil(receivedMessage) + } +} diff --git a/UnitTests/SwiftTests/Infra/MParticleBackendControllerDelegateTests.swift b/UnitTests/SwiftTests/Infra/MParticleBackendControllerDelegateTests.swift new file mode 100644 index 00000000..ce7d2399 --- /dev/null +++ b/UnitTests/SwiftTests/Infra/MParticleBackendControllerDelegateTests.swift @@ -0,0 +1,38 @@ +// +// MParticleBackendControllerDelegateTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleBackendControllerDelegateTests: MParticleTestBase { + + func testForwardLogInstall() { + mparticle.forwardLogInstall() + XCTAssertEqual(executor.executeOnMainAsync, true) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "forwardLogInstall") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) + XCTAssertNil(kitContainer.forwardSDKCallEventParam) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } + + func testForwardLogUpdate() { + mparticle.forwardLogUpdate() + XCTAssertEqual(executor.executeOnMainAsync, true) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "forwardLogUpdate") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) + XCTAssertNil(kitContainer.forwardSDKCallEventParam) + XCTAssertNil(kitContainer.forwardSDKCallParametersParam) + XCTAssertNil(kitContainer.forwardSDKCallUserInfoParam) + } +} diff --git a/UnitTests/SwiftTests/Infra/MParticleKitBatchTests.swift b/UnitTests/SwiftTests/Infra/MParticleKitBatchTests.swift new file mode 100644 index 00000000..c377a944 --- /dev/null +++ b/UnitTests/SwiftTests/Infra/MParticleKitBatchTests.swift @@ -0,0 +1,123 @@ +// +// MParticleKitBatchTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleKitBatchTests: MParticleTestBase { + + func testLogKitBatch_withNilBatch_doesNotExecuteOrForward() { + mparticle.logKitBatch(nil) + + assertReceivedMessage("Cannot log nil batch!") + XCTAssertFalse(executor.executeOnMessageQueueAsync) + XCTAssertFalse(kitContainer.hasKitBatchingKitsCalled) + XCTAssertFalse(executor.executeOnMainAsync) + XCTAssertFalse(kitContainer.forwardSDKCallCalled) + XCTAssertFalse(persistenceController.saveCalled) + } + + func testLogKitBatch_noBatchingKits_andKitsInitialized_doesNothing() { + kitContainer.kitsInitialized = true + kitContainer.hasKitBatchingKitsReturnValue = false + + mparticle.logKitBatch(LogKitBatchData.singleEvent) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertFalse(executor.executeOnMainAsync) + XCTAssertFalse(kitContainer.forwardSDKCallCalled) + XCTAssertFalse(persistenceController.saveCalled) + } + + func testLogKitBatch_kitsNotInitialized_defersWorkUntilInitialization() { + kitContainer.kitsInitialized = false + kitContainer.hasKitBatchingKitsReturnValue = true + + mparticle.logKitBatch(LogKitBatchData.singleEvent) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + + // Should queue deferred block, not execute immediately + XCTAssertFalse(executor.executeOnMainAsync) + XCTAssertFalse(kitContainer.forwardSDKCallCalled) + XCTAssertFalse(persistenceController.saveCalled) + + // Simulate kits becoming initialized + kitContainer.kitsInitialized = true + mparticle.executeKitsInitializedBlocks() + + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "executeKitsInitializedBlocks") + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") + XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) + XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) + } + + func testLogKitBatch_withBatchingKits_forwardsParsedBatch_andPersistsForwardRecords() { + kitContainer.kitsInitialized = true + kitContainer.hasKitBatchingKitsReturnValue = true + + mparticle.logKitBatch(LogKitBatchData.multiEvent) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") + XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) + XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) + } + + func testLogKitBatch_invokesKitHandler_andPersistsForwardRecords() { + kitContainer.kitsInitialized = true + kitContainer.hasKitBatchingKitsReturnValue = true + + let forwardRecord = MPForwardRecord() + kit.logBatchReturnValue = [forwardRecord] + + mparticle.logKitBatch(LogKitBatchData.singleEvent) + + guard let kitHandler = kitContainer.forwardSDKCallKitHandlerParam else { + XCTFail("Expected kitHandler closure to be captured") + return + } + + // Simulate invoking the handler + let config = MPKitConfiguration() + kitHandler(kit, LogKitBatchData.parsedSingleEvent, config) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") + XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 1) + XCTAssertTrue(kit.logBatchCalled) + XCTAssertEqual(kit.logBatchParam?["events"] as? [[String: Int]], + LogKitBatchData.parsedSingleEvent.values.first as? [[String : Int]]) + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(persistenceController.saveCalled) + XCTAssertTrue(persistenceController.saveForwardRecordParam === forwardRecord) + } + + func testLogKitBatch_invalidJSON_stillForwardsWithNilBatch() { + kitContainer.kitsInitialized = true + kitContainer.hasKitBatchingKitsReturnValue = true + + mparticle.logKitBatch(LogKitBatchData.invalidJSON) + + XCTAssertTrue(executor.executeOnMessageQueueAsync) + XCTAssertTrue(executor.executeOnMainAsync) + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logBatch:") + XCTAssertEqual(kitContainer.forwardSDKCallBatchParam?.count, 0) + XCTAssertNotNil(kitContainer.forwardSDKCallKitHandlerParam) + } +} diff --git a/UnitTests/SwiftTests/LTV/MParticleLTVTests.swift b/UnitTests/SwiftTests/LTV/MParticleLTVTests.swift new file mode 100644 index 00000000..4a7c12d2 --- /dev/null +++ b/UnitTests/SwiftTests/LTV/MParticleLTVTests.swift @@ -0,0 +1,111 @@ +// +// MParticleLTVTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +final class MParticleLTVTests: MParticleTestBase { + + func testLogLTVIncrease_withNameAndInfo_createsEventAndCallsBackend() { + let amount = 42.0 + let name = "name" + let info: [String: Any] = ["source": "in_app", "currency": "USD"] + + mparticle.logLTVIncrease(amount, eventName: name, eventInfo: info) + + // Assert event was passed through + let loggedEvent = backendController.logEventEventParam! + XCTAssertNotNil(loggedEvent) + XCTAssertEqual(loggedEvent.name, name) + XCTAssertEqual(loggedEvent.type, .transaction) + + // Custom attributes should include amount and method name + let attrs = loggedEvent.customAttributes! + XCTAssertEqual(attrs["$Amount"] as? Double, amount) + XCTAssertEqual(attrs["$MethodName"] as? String, "LogLTVIncrease") + + // Check that the eventInfo entries were added + XCTAssertEqual(attrs["source"] as? String, "in_app") + XCTAssertEqual(attrs["currency"] as? String, "USD") + XCTAssertEqual(attrs.count, 4) + + // Listener controller should be notified + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logLTVIncrease:eventName:eventInfo:") + + // Backend completion handler should be stored + XCTAssertTrue(backendController.logEventCalled) + let completion = backendController.logEventCompletionHandler! + XCTAssertNotNil(completion) + completion(loggedEvent, .success) + } + + func testLogLTVIncrease_withoutEventInfo_defaultsToNilInfo() { + let amount = 12.5 + let name = "name" + + mparticle.logLTVIncrease(amount, eventName: name) + + // Assert event was passed through + let loggedEvent = backendController.logEventEventParam! + XCTAssertNotNil(loggedEvent) + XCTAssertEqual(loggedEvent.name, name) + XCTAssertEqual(loggedEvent.type, .transaction) + + // Custom attributes should only be amount and method name + let attrs = loggedEvent.customAttributes! + XCTAssertEqual(attrs["$Amount"] as? Double, amount) + XCTAssertEqual(attrs["$MethodName"] as? String, "LogLTVIncrease") + XCTAssertEqual(attrs.count, 2) + + // Listener controller should be notified + XCTAssertEqual(listenerController.onAPICalledApiName?.description, "logLTVIncrease:eventName:eventInfo:") + XCTAssertEqual(listenerController.onAPICalledParameter1 as? Double, amount) + XCTAssertEqual(listenerController.onAPICalledParameter2 as? String, name) + XCTAssertNil(listenerController.onAPICalledParameter3) + + // Backend completion handler should be stored + XCTAssertTrue(backendController.logEventCalled) + let completion = backendController.logEventCompletionHandler! + XCTAssertNotNil(completion) + completion(loggedEvent, .success) + } + + func testLogLTVIncreaseCallback_withSuccessExecStatus_noDataPlanFilter_forwardsEvent() { + dataPlanFilter.transformEventReturnValue = nil + + mparticle.logLTVIncreaseCallback(event, execStatus: .success) + + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) + + assertReceivedMessage("Blocked LTV increase event from kits", event: event) + } + + func testLogLTVIncreaseCallback_withSuccessExecStatus_filterReturnsTransformedEvent_forwardsTransformedEvent() { + dataPlanFilter.transformEventReturnValue = transformedEvent + + mparticle.logLTVIncreaseCallback(event, execStatus: .success) + + // Verify filter transformed event + XCTAssertTrue(dataPlanFilter.transformEventCalled) + XCTAssertTrue(dataPlanFilter.transformEventEventParam === event) + + // Verify executor usage + XCTAssertTrue(executor.executeOnMainAsync) + + // Verify kit container forwarded transformed event + XCTAssertTrue(kitContainer.forwardSDKCallCalled) + XCTAssertEqual(kitContainer.forwardSDKCallSelectorParam?.description, "logLTVIncrease:event:") + XCTAssertEqual(kitContainer.forwardSDKCallMessageTypeParam, .unknown) + XCTAssertNil(kitContainer.forwardSDKCallEventParam) + } +} + diff --git a/UnitTests/SwiftTests/Platform/MParticleOpenURLTests.swift b/UnitTests/SwiftTests/Platform/MParticleOpenURLTests.swift new file mode 100644 index 00000000..6b7d3cd5 --- /dev/null +++ b/UnitTests/SwiftTests/Platform/MParticleOpenURLTests.swift @@ -0,0 +1,75 @@ +// +// MParticleOpenURLTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +import ObjectiveC.runtime + +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +#if os(iOS) +final class MParticleOpenURLTests: MParticleTestBase { + + func testOpenURLSourceApplication_doesNothing_whenProxiedDelegateExists() { + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + let url = URL(string: "https://example.com")! + let sourceApp = "com.example.app" + let annotation = "annotation" + mparticle.open(url, sourceApplication: sourceApp, annotation: annotation) + + XCTAssertFalse(appNotificationHandler.openURLWithSourceApplicationAndAnnotationCalled) + } + + func testOpenURLSourceApplication_callsHandler_whenNoProxiedDelegate() { + let sourceApp = "com.example.app" + let annotation = "annotation" + + mparticle.open(url, sourceApplication: sourceApp, annotation: annotation) + + XCTAssertTrue(appNotificationHandler.openURLWithSourceApplicationAndAnnotationCalled) + XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationURLParam, url) + XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationSourceApplicationParam, sourceApp) + XCTAssertEqual(appNotificationHandler.openURLWithSourceApplicationAndAnnotationAnnotationParam as! String, annotation) + } + + func testOpenURLOptions_callsHandler_whenNoProxiedDelegate_andIOSVersion9OrHigher() { + let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] + + mparticle.open(url, options: options) + + XCTAssertTrue(appNotificationHandler.openURLWithOptionsCalled) + XCTAssertEqual(appNotificationHandler.openURLWithOptionsURLParam, url) + XCTAssertEqual(appNotificationHandler.openURLWithOptionsOptionsParam?["UIApplicationOpenURLOptionsSourceApplicationKey"] as? String, "com.example.app") + } + + func testOpenURLOptions_doesNothing_whenProxiedDelegateExists() { + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] + mparticle.open(url, options: options) + + XCTAssertFalse(appNotificationHandler.openURLWithOptionsCalled) + } + + func testOpenURLOptions_doesNothing_whenSystemVersionBelow9() { + let currentDevice = UIDevice.current + let origSelector = NSSelectorFromString("systemVersion") + let mockedVersion: @convention(block) () -> String = { "8.4" } + let imp = imp_implementationWithBlock(mockedVersion) + class_replaceMethod(object_getClass(currentDevice), origSelector, imp, "@@:") + + let options = ["UIApplicationOpenURLOptionsSourceApplicationKey": "com.example.app"] + mparticle.open(url, options: options) + + XCTAssertFalse(appNotificationHandler.openURLWithOptionsCalled) + } +} +#endif diff --git a/UnitTests/SwiftTests/Platform/MParticlePushNotificationTokenTests.swift b/UnitTests/SwiftTests/Platform/MParticlePushNotificationTokenTests.swift new file mode 100644 index 00000000..8a07dc07 --- /dev/null +++ b/UnitTests/SwiftTests/Platform/MParticlePushNotificationTokenTests.swift @@ -0,0 +1,61 @@ +// +// MParticlePushNotificationTokenTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +#if os(iOS) +final class MParticlePushNotificationTokenTests: MParticleTestBase { + + func testPushNotificationToken_returnsDeviceToken_whenNotAppExtension() { + notificationController.deviceTokenReturnValue = token + + appEnvironmentProvier.isAppExtensionReturnValue = false + + let token = mparticle.pushNotificationToken + + XCTAssertEqual(token, self.token) + XCTAssertTrue(notificationController.deviceTokenCalled) + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + } + + func testPushNotificationToken_returnsNil_whenAppExtension() { + notificationController.deviceTokenReturnValue = token + + appEnvironmentProvier.isAppExtensionReturnValue = true + + let token = mparticle.pushNotificationToken + + XCTAssertNil(token) + XCTAssertFalse(notificationController.deviceTokenCalled) + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + } + + func testSetPushNotificationToken_setsToken_whenNotAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.pushNotificationToken = token + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertTrue(notificationController.setDeviceTokenCalled) + XCTAssertEqual(notificationController.setDeviceTokenParam, token) + } + + func testSetPushNotificationToken_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.pushNotificationToken = token + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(notificationController.setDeviceTokenCalled) + } +} +#endif diff --git a/UnitTests/SwiftTests/Platform/MParticleRemoteNotificationTests.swift b/UnitTests/SwiftTests/Platform/MParticleRemoteNotificationTests.swift new file mode 100644 index 00000000..c85a743f --- /dev/null +++ b/UnitTests/SwiftTests/Platform/MParticleRemoteNotificationTests.swift @@ -0,0 +1,171 @@ +// +// MParticleRemoteNotificationTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +#if os(iOS) +final class MParticleRemoteNotificationTests: MParticleTestBase { + + func testDidReceiveRemoteNotification_doesNothing_whenProxiedAppDelegateExists() { + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + mparticle.didReceiveRemoteNotification([:]) + + XCTAssertFalse(appNotificationHandler.didReceiveRemoteNotificationCalled) + XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) + } + + func testDidReceiveRemoteNotification_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.didReceiveRemoteNotification(keyValueDict) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.didReceiveRemoteNotificationCalled) + } + + + + func testDidReceiveRemoteNotification_forwardsToHandler_whenNotAppExtension_andNoProxiedDelegate() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.didReceiveRemoteNotification(keyValueDict) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertTrue(appNotificationHandler.didReceiveRemoteNotificationCalled) + XCTAssertEqual(appNotificationHandler.didReceiveRemoteNotificationParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) + } + + func testDidFailToRegisterForRemoteNotificationsWithError_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.didFailToRegisterForRemoteNotificationsWithError(error) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) + } + + func testDidFailToRegisterForRemoteNotificationsWithError_doesNothing_whenProxiedDelegateSet() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + mparticle.didFailToRegisterForRemoteNotificationsWithError(error) + + XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) + } + + func testDidFailToRegisterForRemoteNotificationsWithError_forwardsToHandler_whenNotAppExtension_andNoProxiedDelegate() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.didFailToRegisterForRemoteNotificationsWithError(error) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertTrue(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorCalled) + XCTAssertEqual(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithErrorParam as NSError?, error) + } + + func testDidRegisterForRemoteNotificationsWithDeviceToken_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) + } + + func testDidRegisterForRemoteNotificationsWithDeviceToken_doesNothing_whenProxiedDelegateExists() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) + + XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) + } + + func testDidRegisterForRemoteNotificationsWithDeviceToken_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.didRegisterForRemoteNotifications(withDeviceToken: token) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertTrue(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenCalled) + XCTAssertEqual(appNotificationHandler.didFailToRegisterForRemoteNotificationsWithDeviceTokenParam, token) + } + + func testHandleActionWithIdentifierForRemoteNotification_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) + } + + func testHandleActionWithIdentifierForRemoteNotification_doesNothing_whenProxiedDelegateExists() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) + + XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) + } + + func testHandleActionWithIdentifierForRemoteNotification_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict) + + XCTAssertTrue(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationCalled) + XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationIdentifierParam, testName) + XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationUserInfoParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) + } + + func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_doesNothing_whenAppExtension() { + appEnvironmentProvier.isAppExtensionReturnValue = true + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) + } + + func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_doesNothing_whenProxiedDelegateExists() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) + + XCTAssertFalse(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertFalse(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) + } + + func testHandleActionWithIdentifierForRemoteNotificationWithResponseInfo_callsHandler_whenNotAppExtension_andNoProxiedDelegate() { + appEnvironmentProvier.isAppExtensionReturnValue = false + + mparticle.handleAction(withIdentifier: testName, forRemoteNotification: keyValueDict, withResponseInfo: responseKeyValueDict) + + XCTAssertTrue(appEnvironmentProvier.isAppExtensionCalled) + XCTAssertTrue(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoCalled) + XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoIdentifierParam, testName) + XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoUserInfoParam?[keyValueDict.keys.first!] as? String, keyValueDict.values.first) + XCTAssertEqual(appNotificationHandler.handleActionWithIdentifierForRemoteNotificationWithResponseInfoResponseInfoParam?[responseKeyValueDict.keys.first!] as? String, responseKeyValueDict.values.first) + } +} +#endif + diff --git a/UnitTests/SwiftTests/Platform/MParticleUserActivityTests.swift b/UnitTests/SwiftTests/Platform/MParticleUserActivityTests.swift new file mode 100644 index 00000000..dbcaff0c --- /dev/null +++ b/UnitTests/SwiftTests/Platform/MParticleUserActivityTests.swift @@ -0,0 +1,43 @@ +// +// MParticleUserActivityTests.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +import ObjectiveC.runtime +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +#if os(iOS) +final class MParticleUserActivityTests: MParticleTestBase { + + func testContinueUserActivity_returnsFalseAndDoesNotCallHandler_whenProxiedDelegateExists() { + mparticle.setValue(NSNumber(value: true), forKey: "proxiedAppDelegate") + + let activity = NSUserActivity(activityType: "com.example.test") + + let result = mparticle.continue(activity) { _ in } + + XCTAssertFalse(result) + XCTAssertFalse(appNotificationHandler.continueUserActivityCalled) + } + + func testContinueUserActivity_returnsFalse_whenHandlerReturnsFalse() { + let activity = NSUserActivity(activityType: "com.example.test") + appNotificationHandler.continueUserActivityReturnValue = false + + let result = mparticle.continue(activity) { _ in } + + XCTAssertTrue(appNotificationHandler.continueUserActivityCalled) + XCTAssertNotNil(appNotificationHandler.continueUserActivityRestorationHandlerParam) + XCTAssertEqual(appNotificationHandler.continueUserActivityUserActivityParam, activity) + XCTAssertFalse(result) + } +} +#endif + diff --git a/UnitTests/SwiftTests/Shared/LogKitBatchData.swift b/UnitTests/SwiftTests/Shared/LogKitBatchData.swift new file mode 100644 index 00000000..a704ded7 --- /dev/null +++ b/UnitTests/SwiftTests/Shared/LogKitBatchData.swift @@ -0,0 +1,19 @@ +// +// LogKitBatchData.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import Foundation + +enum LogKitBatchData { + static let invalidJSON = #"{"invalid": "json""# + static let singleEvent = #"{"events":[{"id":1}]}"# + static let multiEvent = #"{"events":[{"id":1},{"id":2}]}"# + static let parsedSingleEvent: [String: Any] = [ + "events": [ + ["id": 1] + ] + ] +} diff --git a/UnitTests/SwiftTests/Shared/MParticleTestBase.swift b/UnitTests/SwiftTests/Shared/MParticleTestBase.swift new file mode 100644 index 00000000..7d967a3c --- /dev/null +++ b/UnitTests/SwiftTests/Shared/MParticleTestBase.swift @@ -0,0 +1,146 @@ +// +// MParticleTestBase.swift +// mParticle-Apple-SDK +// +// Created by Nick Dimitrakas on 11/3/25. +// + +import XCTest +#if MPARTICLE_LOCATION_DISABLE + import mParticle_Apple_SDK_NoLocation +#else + import mParticle_Apple_SDK +#endif + +class MParticleTestBase: XCTestCase { + var receivedMessage: String? + var mparticle: MParticle! + var listenerController: MPListenerControllerMock! + var kitContainer: MPKitContainerMock! + var executor: ExecutorMock! + var backendController: MPBackendControllerMock! + var state: MPStateMachineMock! + var notificationController: MPNotificationControllerMock! + var appEnvironmentProvier: AppEnvironmentProviderMock! + var appNotificationHandler: MPAppNotificationHandlerMock! + var persistenceController: MPPersistenceControllerMock! + var settingsProvider: SettingsProviderMock! + var options: MParticleOptions! + var userDefaults: MPUserDefaultsMock! + var dataPlanFilter: MPDataPlanFilterMock! + var kit: MPKitMock! + + let testName: String = "test" + let keyValueDict: [String: String] = ["key": "value"] + let responseKeyValueDict: [String: String] = ["responseKey": "responseValue"] + + let token = "abcd1234".data(using: .utf8)! + let error = NSError(domain: "test", code: 1) + let exception = NSException(name: NSExceptionName("Test"), reason: "Test", userInfo: nil) + let url = URL(string: "https://example.com")! + + var event: MPEvent! + var transformedEvent: MPEvent! + var baseEvent: MPBaseEvent! + var transformedBaseEvent: MPBaseEvent! + var commerceEvent: MPCommerceEvent! + var transformedCommerceEvent: MPCommerceEvent! + + func customLogger(_ message: String) { + receivedMessage = message + } + + override func setUp() { + super.setUp() + mparticle = MParticle.sharedInstance() + mparticle = MParticle() + mparticle.logLevel = .verbose + mparticle.customLogger = customLogger + listenerController = MPListenerControllerMock() + listenerController.onAPICalledExpectation = XCTestExpectation() + mparticle.listenerController = listenerController + + kitContainer = MPKitContainerMock() + mparticle.setKitContainer(kitContainer) + + executor = ExecutorMock() + mparticle.setExecutor(executor) + + backendController = MPBackendControllerMock() + mparticle.backendController = backendController + + state = MPStateMachineMock() + mparticle.stateMachine = state + + notificationController = MPNotificationControllerMock() + mparticle.notificationController = notificationController + + appEnvironmentProvier = AppEnvironmentProviderMock() + mparticle.appEnvironmentProvider = appEnvironmentProvier + + appNotificationHandler = MPAppNotificationHandlerMock() + mparticle.appNotificationHandler = appNotificationHandler + + persistenceController = MPPersistenceControllerMock() + mparticle.persistenceController = persistenceController + + settingsProvider = SettingsProviderMock() + mparticle.settingsProvider = settingsProvider + + dataPlanFilter = MPDataPlanFilterMock() + mparticle.dataPlanFilter = dataPlanFilter + + options = MParticleOptions() + + userDefaults = MPUserDefaultsMock() + + kit = MPKitMock() + + event = MPEvent(name: testName, type: .other)! + event.customAttributes = keyValueDict + + transformedEvent = MPEvent(name: testName, type: .addToCart)! + event.customAttributes = keyValueDict + + baseEvent = MPBaseEvent(eventType: .other)! + transformedBaseEvent = MPBaseEvent(eventType: .addToCart)! + + commerceEvent = MPCommerceEvent(action: .addToCart)! + transformedCommerceEvent = MPCommerceEvent(action: .removeFromCart)! + } + + override func tearDown() { + super.tearDown() + receivedMessage = nil + mparticle.dataPlanFilter = nil + } +} + +extension MParticleTestBase { + func assertReceivedMessage( + _ expectedSuffix: String, + event: T? = nil, + file: StaticString = #file, + line: UInt = #line + ) { + guard let message = receivedMessage else { + XCTFail("Expected a received message but got nil", file: file, line: line) + return + } + let prefix = "mParticle -> " + let expected: String = { + if let event = event { + return "\(prefix)\(expectedSuffix): \(event)" + } else { + return "\(prefix)\(expectedSuffix)" + } + }() + + XCTAssertEqual( + message.trimmingCharacters(in: .whitespacesAndNewlines), + expected.trimmingCharacters(in: .whitespacesAndNewlines), + file: file, + line: line + ) + } +} diff --git a/mParticle-Apple-SDK.xcodeproj/project.pbxproj b/mParticle-Apple-SDK.xcodeproj/project.pbxproj index cf3b7f5a..b1e1b7ce 100644 --- a/mParticle-Apple-SDK.xcodeproj/project.pbxproj +++ b/mParticle-Apple-SDK.xcodeproj/project.pbxproj @@ -37,8 +37,6 @@ 356D4A5D2E58B25900CB69FE /* MPDataPlanFilterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356D4A5B2E58B25500CB69FE /* MPDataPlanFilterMock.swift */; }; 356D4A5F2E5CAB0000CB69FE /* MPListenerControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356D4A5E2E5CAB0000CB69FE /* MPListenerControllerMock.swift */; }; 356D4A602E5CAB0000CB69FE /* MPListenerControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356D4A5E2E5CAB0000CB69FE /* MPListenerControllerMock.swift */; }; - 359BAFE82E55ED8900A8A704 /* MParticleTestsSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359BAFE72E55ED7D00A8A704 /* MParticleTestsSwift.swift */; }; - 359BAFE92E55ED8900A8A704 /* MParticleTestsSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359BAFE72E55ED7D00A8A704 /* MParticleTestsSwift.swift */; }; 359BAFF92E56330200A8A704 /* SettingsProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 359BAFF82E56330200A8A704 /* SettingsProvider.h */; }; 359BAFFA2E56330200A8A704 /* SettingsProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 359BAFF82E56330200A8A704 /* SettingsProvider.h */; }; 359BAFFC2E56335300A8A704 /* SettingsProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 359BAFFB2E56335300A8A704 /* SettingsProvider.m */; }; @@ -508,6 +506,46 @@ 53FDD1BE2AE871AF003D5FA1 /* MPIHasher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FDD1BC2AE871AF003D5FA1 /* MPIHasher.swift */; }; 7231B80D2EB3C4AC001565E5 /* MPKitMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B80C2EB3C4AC001565E5 /* MPKitMock.swift */; }; 7231B80E2EB3C4AC001565E5 /* MPKitMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B80C2EB3C4AC001565E5 /* MPKitMock.swift */; }; + 7231B81A2EB94DCD001565E5 /* MParticleTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8192EB94DCD001565E5 /* MParticleTestBase.swift */; }; + 7231B81B2EB94DCD001565E5 /* MParticleTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8192EB94DCD001565E5 /* MParticleTestBase.swift */; }; + 7231B81D2EB94EAA001565E5 /* MParticleConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B81C2EB94EAA001565E5 /* MParticleConfigurationTests.swift */; }; + 7231B81E2EB94EAA001565E5 /* MParticleConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B81C2EB94EAA001565E5 /* MParticleConfigurationTests.swift */; }; + 7231B8202EB958DB001565E5 /* MParticleIdentityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B81F2EB958DB001565E5 /* MParticleIdentityTests.swift */; }; + 7231B8212EB958DC001565E5 /* MParticleIdentityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B81F2EB958DB001565E5 /* MParticleIdentityTests.swift */; }; + 7231B8232EB95940001565E5 /* LogKitBatchData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8222EB95940001565E5 /* LogKitBatchData.swift */; }; + 7231B8242EB95940001565E5 /* LogKitBatchData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8222EB95940001565E5 /* LogKitBatchData.swift */; }; + 7231B8262EB95991001565E5 /* MParticleSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8252EB95991001565E5 /* MParticleSessionTests.swift */; }; + 7231B8272EB95991001565E5 /* MParticleSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8252EB95991001565E5 /* MParticleSessionTests.swift */; }; + 7231B8292EB95B04001565E5 /* MParticleRuntimeValuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8282EB95B04001565E5 /* MParticleRuntimeValuesTests.swift */; }; + 7231B82A2EB95B04001565E5 /* MParticleRuntimeValuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8282EB95B04001565E5 /* MParticleRuntimeValuesTests.swift */; }; + 7231B82C2EB95D75001565E5 /* MParticleCustomEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B82B2EB95D75001565E5 /* MParticleCustomEventTests.swift */; }; + 7231B82D2EB95D75001565E5 /* MParticleCustomEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B82B2EB95D75001565E5 /* MParticleCustomEventTests.swift */; }; + 7231B82F2EB95D96001565E5 /* MParticleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B82E2EB95D96001565E5 /* MParticleEventTests.swift */; }; + 7231B8302EB95D96001565E5 /* MParticleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B82E2EB95D96001565E5 /* MParticleEventTests.swift */; }; + 7231B8322EB95EDD001565E5 /* MParticleScreenEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8312EB95EDD001565E5 /* MParticleScreenEventTests.swift */; }; + 7231B8332EB95EDD001565E5 /* MParticleScreenEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8312EB95EDD001565E5 /* MParticleScreenEventTests.swift */; }; + 7231B8352EB95F9F001565E5 /* MParticleBreadcrumbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8342EB95F9F001565E5 /* MParticleBreadcrumbTests.swift */; }; + 7231B8362EB95F9F001565E5 /* MParticleBreadcrumbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8342EB95F9F001565E5 /* MParticleBreadcrumbTests.swift */; }; + 7231B8392EB960C8001565E5 /* MParticleTimedEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8382EB960C8001565E5 /* MParticleTimedEventTests.swift */; }; + 7231B83A2EB960C8001565E5 /* MParticleTimedEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8382EB960C8001565E5 /* MParticleTimedEventTests.swift */; }; + 7231B83C2EB961F2001565E5 /* MParticleLTVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B83B2EB961F2001565E5 /* MParticleLTVTests.swift */; }; + 7231B83D2EB961F2001565E5 /* MParticleLTVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B83B2EB961F2001565E5 /* MParticleLTVTests.swift */; }; + 7231B83F2EB9627D001565E5 /* MParticleErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B83E2EB9627D001565E5 /* MParticleErrorTests.swift */; }; + 7231B8402EB9627D001565E5 /* MParticleErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B83E2EB9627D001565E5 /* MParticleErrorTests.swift */; }; + 7231B8452EB9630E001565E5 /* MParticleCommerceEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8442EB9630E001565E5 /* MParticleCommerceEventTests.swift */; }; + 7231B8462EB9630E001565E5 /* MParticleCommerceEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8442EB9630E001565E5 /* MParticleCommerceEventTests.swift */; }; + 7231B84C2EB963B3001565E5 /* MParticleKitBatchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B84B2EB963B3001565E5 /* MParticleKitBatchTests.swift */; }; + 7231B84D2EB963B3001565E5 /* MParticleKitBatchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B84B2EB963B3001565E5 /* MParticleKitBatchTests.swift */; }; + 7231B84F2EB96455001565E5 /* MParticleUserActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B84E2EB96455001565E5 /* MParticleUserActivityTests.swift */; }; + 7231B8502EB96455001565E5 /* MParticleUserActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B84E2EB96455001565E5 /* MParticleUserActivityTests.swift */; }; + 7231B8522EB964A1001565E5 /* MParticleOpenURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8512EB964A1001565E5 /* MParticleOpenURLTests.swift */; }; + 7231B8532EB964A1001565E5 /* MParticleOpenURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8512EB964A1001565E5 /* MParticleOpenURLTests.swift */; }; + 7231B8552EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8542EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift */; }; + 7231B8562EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8542EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift */; }; + 7231B8582EB96598001565E5 /* MParticleRemoteNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8572EB96598001565E5 /* MParticleRemoteNotificationTests.swift */; }; + 7231B8592EB96598001565E5 /* MParticleRemoteNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B8572EB96598001565E5 /* MParticleRemoteNotificationTests.swift */; }; + 7231B85D2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B85C2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift */; }; + 7231B85E2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7231B85C2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift */; }; 727801DE2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 727801DD2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift */; }; 727801DF2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 727801DD2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift */; }; 727801E12EB0FECA00BDAD98 /* AppEnvironmentProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 727801E02EB0FECA00BDAD98 /* AppEnvironmentProviderMock.swift */; }; @@ -604,7 +642,6 @@ 356D4A582E58B09D00CB69FE /* SettingsProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsProviderMock.swift; sourceTree = ""; }; 356D4A5B2E58B25500CB69FE /* MPDataPlanFilterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDataPlanFilterMock.swift; sourceTree = ""; }; 356D4A5E2E5CAB0000CB69FE /* MPListenerControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPListenerControllerMock.swift; sourceTree = ""; }; - 359BAFE72E55ED7D00A8A704 /* MParticleTestsSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleTestsSwift.swift; sourceTree = ""; }; 359BAFEA2E55EE0C00A8A704 /* MParticle+PrivateMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MParticle+PrivateMethods.h"; sourceTree = ""; }; 359BAFF82E56330200A8A704 /* SettingsProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsProvider.h; sourceTree = ""; }; 359BAFFB2E56335300A8A704 /* SettingsProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsProvider.m; sourceTree = ""; }; @@ -856,6 +893,26 @@ 53E20DC62CBFFCD200146A97 /* NSArray+MPCaseInsensitiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSArray+MPCaseInsensitiveTests.swift"; sourceTree = ""; }; 53FDD1BC2AE871AF003D5FA1 /* MPIHasher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPIHasher.swift; sourceTree = ""; }; 7231B80C2EB3C4AC001565E5 /* MPKitMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPKitMock.swift; sourceTree = ""; }; + 7231B8192EB94DCD001565E5 /* MParticleTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleTestBase.swift; sourceTree = ""; }; + 7231B81C2EB94EAA001565E5 /* MParticleConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleConfigurationTests.swift; sourceTree = ""; }; + 7231B81F2EB958DB001565E5 /* MParticleIdentityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleIdentityTests.swift; sourceTree = ""; }; + 7231B8222EB95940001565E5 /* LogKitBatchData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogKitBatchData.swift; sourceTree = ""; }; + 7231B8252EB95991001565E5 /* MParticleSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleSessionTests.swift; sourceTree = ""; }; + 7231B8282EB95B04001565E5 /* MParticleRuntimeValuesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleRuntimeValuesTests.swift; sourceTree = ""; }; + 7231B82B2EB95D75001565E5 /* MParticleCustomEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleCustomEventTests.swift; sourceTree = ""; }; + 7231B82E2EB95D96001565E5 /* MParticleEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleEventTests.swift; sourceTree = ""; }; + 7231B8312EB95EDD001565E5 /* MParticleScreenEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleScreenEventTests.swift; sourceTree = ""; }; + 7231B8342EB95F9F001565E5 /* MParticleBreadcrumbTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleBreadcrumbTests.swift; sourceTree = ""; }; + 7231B8382EB960C8001565E5 /* MParticleTimedEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleTimedEventTests.swift; sourceTree = ""; }; + 7231B83B2EB961F2001565E5 /* MParticleLTVTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleLTVTests.swift; sourceTree = ""; }; + 7231B83E2EB9627D001565E5 /* MParticleErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleErrorTests.swift; sourceTree = ""; }; + 7231B8442EB9630E001565E5 /* MParticleCommerceEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleCommerceEventTests.swift; sourceTree = ""; }; + 7231B84B2EB963B3001565E5 /* MParticleKitBatchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleKitBatchTests.swift; sourceTree = ""; }; + 7231B84E2EB96455001565E5 /* MParticleUserActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleUserActivityTests.swift; sourceTree = ""; }; + 7231B8512EB964A1001565E5 /* MParticleOpenURLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleOpenURLTests.swift; sourceTree = ""; }; + 7231B8542EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticlePushNotificationTokenTests.swift; sourceTree = ""; }; + 7231B8572EB96598001565E5 /* MParticleRemoteNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleRemoteNotificationTests.swift; sourceTree = ""; }; + 7231B85C2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MParticleBackendControllerDelegateTests.swift; sourceTree = ""; }; 727801DD2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPNotificationControllerMock.swift; sourceTree = ""; }; 727801E02EB0FECA00BDAD98 /* AppEnvironmentProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppEnvironmentProviderMock.swift; sourceTree = ""; }; 727801E32EB1131B00BDAD98 /* MPAppNotificationHandlerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPAppNotificationHandlerMock.swift; sourceTree = ""; }; @@ -1407,6 +1464,22 @@ isa = PBXGroup; children = ( 53A79C8829CE019F00E7489F /* OCMock.xcframework */, + 7231B85B2EB969E1001565E5 /* Diagnostics */, + 7231B85A2EB969C2001565E5 /* LTV */, + 7231B8472EB96373001565E5 /* Infra */, + 7231B8372EB9607C001565E5 /* Platform */, + 7231B8152EB94A67001565E5 /* Shared */, + 7231B8162EB94A6C001565E5 /* Core */, + 7231B8172EB94A71001565E5 /* Events */, + 359BAFFE2E575AF500A8A704 /* SettingsProviderTests.swift */, + 35329FF42E54CB84009AC4FD /* MParticleOptions+MParticlePrivateTests.swift */, + 35329FEB2E54C480009AC4FD /* MPNetworkOptions+MParticlePrivateTests.swift */, + 35E3FCD12E549AED00DB5B18 /* MParticleSession+MParticlePrivateTests.swift */, + 35E3FCC22E53B5C200DB5B18 /* MPAttributionResult+MParticlePrivateTests.swift */, + 72FEBD162E86FE2D00B8341F /* MPIdentityTests.swift */, + 72D356522E8460020012A0C2 /* MPEventTests.swift */, + 53E20DC62CBFFCD200146A97 /* NSArray+MPCaseInsensitiveTests.swift */, + D3961CE32CC0C2A0003B3194 /* NSString+MPPercentEscapeTests.swift */, ); path = Libraries; sourceTree = ""; @@ -1427,6 +1500,75 @@ name = Frameworks; sourceTree = ""; }; + 7231B8152EB94A67001565E5 /* Shared */ = { + isa = PBXGroup; + children = ( + 7231B8192EB94DCD001565E5 /* MParticleTestBase.swift */, + 7231B8222EB95940001565E5 /* LogKitBatchData.swift */, + ); + path = Shared; + sourceTree = ""; + }; + 7231B8162EB94A6C001565E5 /* Core */ = { + isa = PBXGroup; + children = ( + 7231B81C2EB94EAA001565E5 /* MParticleConfigurationTests.swift */, + 7231B81F2EB958DB001565E5 /* MParticleIdentityTests.swift */, + 7231B8252EB95991001565E5 /* MParticleSessionTests.swift */, + 7231B8282EB95B04001565E5 /* MParticleRuntimeValuesTests.swift */, + ); + path = Core; + sourceTree = ""; + }; + 7231B8172EB94A71001565E5 /* Events */ = { + isa = PBXGroup; + children = ( + 7231B82B2EB95D75001565E5 /* MParticleCustomEventTests.swift */, + 7231B82E2EB95D96001565E5 /* MParticleEventTests.swift */, + 7231B8312EB95EDD001565E5 /* MParticleScreenEventTests.swift */, + 7231B8382EB960C8001565E5 /* MParticleTimedEventTests.swift */, + 7231B8442EB9630E001565E5 /* MParticleCommerceEventTests.swift */, + ); + path = Events; + sourceTree = ""; + }; + 7231B8372EB9607C001565E5 /* Platform */ = { + isa = PBXGroup; + children = ( + 7231B84E2EB96455001565E5 /* MParticleUserActivityTests.swift */, + 7231B8512EB964A1001565E5 /* MParticleOpenURLTests.swift */, + 7231B8542EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift */, + 7231B8572EB96598001565E5 /* MParticleRemoteNotificationTests.swift */, + ); + path = Platform; + sourceTree = ""; + }; + 7231B8472EB96373001565E5 /* Infra */ = { + isa = PBXGroup; + children = ( + 7231B84B2EB963B3001565E5 /* MParticleKitBatchTests.swift */, + 7231B85C2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift */, + ); + path = Infra; + sourceTree = ""; + }; + 7231B85A2EB969C2001565E5 /* LTV */ = { + isa = PBXGroup; + children = ( + 7231B83B2EB961F2001565E5 /* MParticleLTVTests.swift */, + ); + path = LTV; + sourceTree = ""; + }; + 7231B85B2EB969E1001565E5 /* Diagnostics */ = { + isa = PBXGroup; + children = ( + 7231B8342EB95F9F001565E5 /* MParticleBreadcrumbTests.swift */, + 7231B83E2EB9627D001565E5 /* MParticleErrorTests.swift */, + ); + path = Diagnostics; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1815,6 +1957,7 @@ 7231B80E2EB3C4AC001565E5 /* MPKitMock.swift in Sources */, 534CD26E29CE2CE1008452B3 /* MPNetworkCommunicationTests.m in Sources */, 534CD26F29CE2CE1008452B3 /* MPAliasRequestTests.m in Sources */, + 7231B8232EB95940001565E5 /* LogKitBatchData.swift in Sources */, 534CD27029CE2CE1008452B3 /* MPAliasResponseTests.m in Sources */, 534CD27129CE2CE1008452B3 /* MPKitSecondTestClassNoStartImmediately.m in Sources */, 534CD27229CE2CE1008452B3 /* MPConsentSerializationTests.m in Sources */, @@ -1822,6 +1965,7 @@ 534CD27429CE2CE1008452B3 /* BracketTests.mm in Sources */, 351308242E676F18002A3AD6 /* MPPersistenceControllerMock.swift in Sources */, 356752902E5F7FF900DEEE23 /* MPBackendControllerMock.swift in Sources */, + 7231B8322EB95EDD001565E5 /* MParticleScreenEventTests.swift in Sources */, 534CD27529CE2CE1008452B3 /* MPIdentityTests.m in Sources */, 35E3FCD32E549AF500DB5B18 /* MParticleSession+MParticlePrivateTests.swift in Sources */, D3CEDAC62C9DB0E1001B32DF /* MPDateFormatter.swift in Sources */, @@ -1829,10 +1973,13 @@ 534CD27629CE2CE1008452B3 /* MPUserAttributeChangeTests.m in Sources */, 534CD27729CE2CE1008452B3 /* MPForwardRecordTests.m in Sources */, D3961CE52CC0C2A0003B3194 /* NSString+MPPercentEscapeTests.swift in Sources */, + 7231B8552EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift in Sources */, + 7231B84F2EB96455001565E5 /* MParticleUserActivityTests.swift in Sources */, 534CD27829CE2CE1008452B3 /* MPPersistenceControllerTests.mm in Sources */, 534CD27929CE2CE1008452B3 /* MPMessageTests.m in Sources */, 534CD27A29CE2CE1008452B3 /* MPDataPlanFilterTests.m in Sources */, 534CD27B29CE2CE1008452B3 /* MPZipTests.m in Sources */, + 7231B8522EB964A1001565E5 /* MParticleOpenURLTests.swift in Sources */, 53E20DC82CBFFCD200146A97 /* NSArray+MPCaseInsensitiveTests.swift in Sources */, 534CD27C29CE2CE1008452B3 /* MPIntegrationAttributesTest.m in Sources */, 356752932E60928B00DEEE23 /* MPStateMachineMock.swift in Sources */, @@ -1843,6 +1990,7 @@ 727801E12EB0FECA00BDAD98 /* AppEnvironmentProviderMock.swift in Sources */, 351308202E6729DA002A3AD6 /* MPKitContainerMock.swift in Sources */, 534CD28029CE2CE1008452B3 /* MPResponseEventsTest.m in Sources */, + 7231B83D2EB961F2001565E5 /* MParticleLTVTests.swift in Sources */, 534CD28129CE2CE1008452B3 /* MPConsentStateTests.m in Sources */, 35329FF52E54CB8C009AC4FD /* MParticleOptions+MParticlePrivateTests.swift in Sources */, 534CD28229CE2CE1008452B3 /* HasherTests.m in Sources */, @@ -1850,41 +1998,54 @@ 534CD28329CE2CE1008452B3 /* MParticleWebViewTests.m in Sources */, 534CD28429CE2CE1008452B3 /* MPCustomModuleTests.m in Sources */, 727801E42EB1131B00BDAD98 /* MPAppNotificationHandlerMock.swift in Sources */, + 7231B83A2EB960C8001565E5 /* MParticleTimedEventTests.swift in Sources */, + 7231B85E2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift in Sources */, 534CD28629CE2CE1008452B3 /* MPKitActivityTests.m in Sources */, + 7231B83F2EB9627D001565E5 /* MParticleErrorTests.swift in Sources */, 534CD28729CE2CE1008452B3 /* MPKitTestClassSideloaded.m in Sources */, + 7231B81A2EB94DCD001565E5 /* MParticleTestBase.swift in Sources */, 534CD28829CE2CE1008452B3 /* MPDeviceTests.m in Sources */, 534CD28929CE2CE1008452B3 /* MPKitRegisterTests.m in Sources */, + 7231B81D2EB94EAA001565E5 /* MParticleConfigurationTests.swift in Sources */, 534CD28A29CE2CE1008452B3 /* MPStateMachineTests.m in Sources */, 534CD28B29CE2CE1008452B3 /* MPCCPAConsentTests.m in Sources */, 534CD28C29CE2CE1008452B3 /* MPUserDefaultsTests.m in Sources */, + 7231B82F2EB95D96001565E5 /* MParticleEventTests.swift in Sources */, 534CD28D29CE2CE1008452B3 /* MPUploadBuilderTests.m in Sources */, 356D4A5F2E5CAB0000CB69FE /* MPListenerControllerMock.swift in Sources */, 534CD28E29CE2CE1008452B3 /* MPBaseTestCase.m in Sources */, - 359BAFE92E55ED8900A8A704 /* MParticleTestsSwift.swift in Sources */, 35329FEC2E54C483009AC4FD /* MPNetworkOptions+MParticlePrivateTests.swift in Sources */, + 7231B8352EB95F9F001565E5 /* MParticleBreadcrumbTests.swift in Sources */, 534CD28F29CE2CE1008452B3 /* MPIdentityApiRequestTests.m in Sources */, 534CD29029CE2CE1008452B3 /* MPKitAppsFlyerTest.m in Sources */, + 7231B82A2EB95B04001565E5 /* MParticleRuntimeValuesTests.swift in Sources */, 534CD29129CE2CE1008452B3 /* MPEventTests.m in Sources */, 534CD29229CE2CE1008452B3 /* MPMessageBuilderTests.m in Sources */, + 7231B84D2EB963B3001565E5 /* MParticleKitBatchTests.swift in Sources */, 534CD29329CE2CE1008452B3 /* MPKitTestClass.m in Sources */, 534CD29429CE2CE1008452B3 /* MPKitContainerTests.m in Sources */, 72FEBD182E86FE3B00B8341F /* MPIdentityTests.swift in Sources */, 534CD29529CE2CE1008452B3 /* MPBase_Attribute_Event_ProjectionTests.m in Sources */, 534CD29629CE2CE1008452B3 /* MPConsumerInfoTests.m in Sources */, 359BB0062E57A56800A8A704 /* MPUserDefaultsMock.swift in Sources */, + 7231B8582EB96598001565E5 /* MParticleRemoteNotificationTests.swift in Sources */, 7E15B2062D94617900C1FF3E /* MPRoktTests.m in Sources */, 534CD29729CE2CE1008452B3 /* MPURLRequestBuilderTests.m in Sources */, + 7231B8212EB958DC001565E5 /* MParticleIdentityTests.swift in Sources */, 727801DE2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift in Sources */, 534CD29829CE2CE1008452B3 /* MPForwardQueueItemTests.m in Sources */, 534CD29929CE2CE1008452B3 /* MPDateFormatterTests.m in Sources */, 534CD29A29CE2CE1008452B3 /* MPResponseConfigTests.m in Sources */, 534CD29B29CE2CE1008452B3 /* MPCommerceEventTests.m in Sources */, 534CD29C29CE2CE1008452B3 /* MParticleUserTests.m in Sources */, + 7231B8272EB95991001565E5 /* MParticleSessionTests.swift in Sources */, 534CD29D29CE2CE1008452B3 /* MPLaunchInfoTests.m in Sources */, 534CD29E29CE2CE1008452B3 /* MPConvertJSTests.m in Sources */, 534CD29F29CE2CE1008452B3 /* MPAppNotificationHandlerTests.m in Sources */, + 7231B82D2EB95D75001565E5 /* MParticleCustomEventTests.swift in Sources */, 531BCF3B2B28A83E00F5C573 /* MPIdentityCachingTests.m in Sources */, 534CD2A029CE2CE1008452B3 /* MParticleTests.m in Sources */, + 7231B8452EB9630E001565E5 /* MParticleCommerceEventTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2015,6 +2176,7 @@ 7231B80D2EB3C4AC001565E5 /* MPKitMock.swift in Sources */, 53A79CBE29CE019F00E7489F /* MPNetworkCommunicationTests.m in Sources */, 53A79CF429CE019F00E7489F /* MPAliasRequestTests.m in Sources */, + 7231B8242EB95940001565E5 /* LogKitBatchData.swift in Sources */, 53A79CF129CE019F00E7489F /* MPAliasResponseTests.m in Sources */, 53A79CC529CE019F00E7489F /* MPKitSecondTestClassNoStartImmediately.m in Sources */, 53A79CE429CE019F00E7489F /* MPConsentSerializationTests.m in Sources */, @@ -2022,6 +2184,7 @@ 351308252E676F18002A3AD6 /* MPPersistenceControllerMock.swift in Sources */, 3567528F2E5F7FF900DEEE23 /* MPBackendControllerMock.swift in Sources */, 53A79CF029CE019F00E7489F /* BracketTests.mm in Sources */, + 7231B8332EB95EDD001565E5 /* MParticleScreenEventTests.swift in Sources */, 35E3FCD22E549AF500DB5B18 /* MParticleSession+MParticlePrivateTests.swift in Sources */, D3CEDAC52C9DB0E0001B32DF /* MPDateFormatter.swift in Sources */, D3CEDAC02C9C5B5B001B32DF /* MPGDPRConsent.swift in Sources */, @@ -2029,10 +2192,13 @@ 53A79CE529CE019F00E7489F /* MPUserAttributeChangeTests.m in Sources */, D3961CE42CC0C2A0003B3194 /* NSString+MPPercentEscapeTests.swift in Sources */, 53A79CF229CE019F00E7489F /* MPForwardRecordTests.m in Sources */, + 7231B8562EB9652C001565E5 /* MParticlePushNotificationTokenTests.swift in Sources */, + 7231B8502EB96455001565E5 /* MParticleUserActivityTests.swift in Sources */, 53A79CB829CE019F00E7489F /* MPPersistenceControllerTests.mm in Sources */, 53A79CE929CE019F00E7489F /* MPMessageTests.m in Sources */, 53A79CDA29CE019F00E7489F /* MPDataPlanFilterTests.m in Sources */, 53E20DC72CBFFCD200146A97 /* NSArray+MPCaseInsensitiveTests.swift in Sources */, + 7231B8532EB964A1001565E5 /* MParticleOpenURLTests.swift in Sources */, 53A79CEC29CE019F00E7489F /* MPZipTests.m in Sources */, 356752942E60928B00DEEE23 /* MPStateMachineMock.swift in Sources */, 53A79CCC29CE019F00E7489F /* MPIntegrationAttributesTest.m in Sources */, @@ -2043,6 +2209,7 @@ 727801E22EB0FECA00BDAD98 /* AppEnvironmentProviderMock.swift in Sources */, 53A79CEE29CE019F00E7489F /* NSNumber+MPFormatterTests.m in Sources */, 53A79CD929CE019F00E7489F /* MPResponseEventsTest.m in Sources */, + 7231B83C2EB961F2001565E5 /* MParticleLTVTests.swift in Sources */, 35329FF62E54CB8C009AC4FD /* MParticleOptions+MParticlePrivateTests.swift in Sources */, 53A79CC729CE019F00E7489F /* MPConsentStateTests.m in Sources */, 35E3FCC42E53B5C600DB5B18 /* MPAttributionResult+MParticlePrivateTests.swift in Sources */, @@ -2050,41 +2217,54 @@ 53A79CB929CE019F00E7489F /* MParticleWebViewTests.m in Sources */, 53A79CED29CE019F00E7489F /* MPCustomModuleTests.m in Sources */, 727801E52EB1131B00BDAD98 /* MPAppNotificationHandlerMock.swift in Sources */, + 7231B8392EB960C8001565E5 /* MParticleTimedEventTests.swift in Sources */, + 7231B85D2EB96D88001565E5 /* MParticleBackendControllerDelegateTests.swift in Sources */, 53A79CCE29CE019F00E7489F /* MPKitActivityTests.m in Sources */, + 7231B8402EB9627D001565E5 /* MParticleErrorTests.swift in Sources */, 53A79CBA29CE019F00E7489F /* MPKitTestClassSideloaded.m in Sources */, + 7231B81B2EB94DCD001565E5 /* MParticleTestBase.swift in Sources */, 53A79CEF29CE019F00E7489F /* MPDeviceTests.m in Sources */, 53A79CF829CE019F00E7489F /* MPKitRegisterTests.m in Sources */, + 7231B81E2EB94EAA001565E5 /* MParticleConfigurationTests.swift in Sources */, 53A79CDB29CE019F00E7489F /* MPStateMachineTests.m in Sources */, 53A79CD629CE019F00E7489F /* MPCCPAConsentTests.m in Sources */, 53A79CE129CE019F00E7489F /* MPUserDefaultsTests.m in Sources */, + 7231B8302EB95D96001565E5 /* MParticleEventTests.swift in Sources */, 356D4A602E5CAB0000CB69FE /* MPListenerControllerMock.swift in Sources */, 53A79CCD29CE019F00E7489F /* MPUploadBuilderTests.m in Sources */, - 359BAFE82E55ED8900A8A704 /* MParticleTestsSwift.swift in Sources */, 35329FED2E54C483009AC4FD /* MPNetworkOptions+MParticlePrivateTests.swift in Sources */, 53A79CE829CE019F00E7489F /* MPBaseTestCase.m in Sources */, + 7231B8362EB95F9F001565E5 /* MParticleBreadcrumbTests.swift in Sources */, 53A79CC029CE019F00E7489F /* MPIdentityApiRequestTests.m in Sources */, 53A79CE329CE019F00E7489F /* MPKitAppsFlyerTest.m in Sources */, + 7231B8292EB95B04001565E5 /* MParticleRuntimeValuesTests.swift in Sources */, 53A79CC929CE019F00E7489F /* MPEventTests.m in Sources */, 53A79CC429CE019F00E7489F /* MPMessageBuilderTests.m in Sources */, + 7231B84C2EB963B3001565E5 /* MParticleKitBatchTests.swift in Sources */, 53A79CC329CE019F00E7489F /* MPKitTestClass.m in Sources */, 53A79CD529CE019F00E7489F /* MPKitContainerTests.m in Sources */, 72FEBD172E86FE3B00B8341F /* MPIdentityTests.swift in Sources */, 53A79CBC29CE019F00E7489F /* MPBase_Attribute_Event_ProjectionTests.m in Sources */, 53A79CEA29CE019F00E7489F /* MPConsumerInfoTests.m in Sources */, 359BB0072E57A56800A8A704 /* MPUserDefaultsMock.swift in Sources */, + 7231B8592EB96598001565E5 /* MParticleRemoteNotificationTests.swift in Sources */, 7E15B2072D94617900C1FF3E /* MPRoktTests.m in Sources */, 53A79CDD29CE019F00E7489F /* MPURLRequestBuilderTests.m in Sources */, + 7231B8202EB958DB001565E5 /* MParticleIdentityTests.swift in Sources */, 727801DF2EB0FE8400BDAD98 /* MPNotificationControllerMock.swift in Sources */, 53A79CDE29CE019F00E7489F /* MPForwardQueueItemTests.m in Sources */, 53A79CDF29CE019F00E7489F /* MPDateFormatterTests.m in Sources */, 53A79CD729CE019F00E7489F /* MPResponseConfigTests.m in Sources */, 53A79CBB29CE019F00E7489F /* MPCommerceEventTests.m in Sources */, 53A79CEB29CE019F00E7489F /* MParticleUserTests.m in Sources */, + 7231B8262EB95991001565E5 /* MParticleSessionTests.swift in Sources */, 53A79CE229CE019F00E7489F /* MPLaunchInfoTests.m in Sources */, 53A79CD429CE019F00E7489F /* MPConvertJSTests.m in Sources */, 53A79CF729CE019F00E7489F /* MPAppNotificationHandlerTests.m in Sources */, + 7231B82C2EB95D75001565E5 /* MParticleCustomEventTests.swift in Sources */, 531BCF3A2B28A83E00F5C573 /* MPIdentityCachingTests.m in Sources */, 53A79CD829CE019F00E7489F /* MParticleTests.m in Sources */, + 7231B8462EB9630E001565E5 /* MParticleCommerceEventTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };