Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions UnitTests/Mocks/MPListenerControllerMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,67 @@ class MPListenerControllerMock: NSObject, MPListenerControllerProtocol {
onAPICalledExpectation?.fulfill()
}
}

extension MPListenerControllerMock {

/// Verifies that the listener was called with the expected selector and parameters.
/// Automatically checks `onAPICalledCalled`, the selector, and up to three parameters.
func assertCalled(
_ expectedSelector: Selector,
param1 expectedParam1: NSObject? = nil,
param2 expectedParam2: NSObject? = nil,
param3 expectedParam3: NSObject? = nil,
file: StaticString = #file,
line: UInt = #line
) {
XCTAssertTrue(onAPICalledCalled, "Expected onAPICalled to be called", file: file, line: line)

guard let actualSelector = onAPICalledApiName else {
XCTFail("Expected API name \(expectedSelector), but none was recorded", file: file, line: line)
return
}
XCTAssertEqual(
NSStringFromSelector(actualSelector),
NSStringFromSelector(expectedSelector),
"Expected API selector to match",
file: file,
line: line
)

if let expectedParam1 {
XCTAssertEqual(
onAPICalledParameter1,
expectedParam1,
"Expected param1 to match for \(expectedSelector)",
file: file,
line: line
)
}

if let expectedParam2 {
XCTAssertEqual(
onAPICalledParameter2,
expectedParam2,
"Expected param2 to match for \(expectedSelector)",
file: file,
line: line
)
}

if let expectedParam3 {
XCTAssertEqual(
onAPICalledParameter3,
expectedParam3,
"Expected param3 to match for \(expectedSelector)",
file: file,
line: line
)
}
}

/// Verifies that no API call occurred.
func assertNotCalled(file: StaticString = #file, line: UInt = #line) {
XCTAssertFalse(onAPICalledCalled, "Expected onAPICalled NOT to be called", file: file, line: line)
XCTAssertTrue(onAPICalledApiNames.isEmpty, "Expected no recorded API names", file: file, line: line)
}
}
66 changes: 55 additions & 11 deletions UnitTests/SwiftTests/MParticle/MParticleErrorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,88 @@ import XCTest

final class MParticleErrorTests: MParticleTestBase {

// MARK: - Properties

var errorMessage = "error"

// MARK: - logErrorCallback

func test_logErrorCallback_logsMessage_whenSuccess() {
mparticle.logErrorCallback([:], execStatus: .success, message: "error")

assertReceivedMessage("Logged error with message: error")
mparticle.logErrorCallback([:], execStatus: .success, message: errorMessage)
assertReceivedMessage("Logged error with message: \(errorMessage)")
}

func test_logErrorCallback_doesNotLog_whenFail() {
mparticle.logErrorCallback([:], execStatus: .fail, message: "error")

mparticle.logErrorCallback([:], execStatus: .fail, message: errorMessage)
XCTAssertNil(receivedMessage)
}

// MARK: - logExceptionCallback

func test_logExceptionCallback_logsDetails_whenSuccess() {
mparticle.logExceptionCallback(exception, execStatus: .success, message: "exception", topmostContext: nil)

assertReceivedMessage("Logged exception name: exception, reason: Test, topmost context: (null)")
}

func test_logExceptionCallback_doesNotLog_whenFail() {
mparticle.logExceptionCallback(exception, execStatus: .fail, message: "exception", topmostContext: nil)

XCTAssertNil(receivedMessage)
}

// MARK: - logCrashCallback

func test_logCrashCallback_logsMessage_whenSuccess() {
mparticle.logCrashCallback(.success, message: "Message")
assertReceivedMessage("Logged crash with message: Message")
mparticle.logCrashCallback(.success, message: errorMessage)
assertReceivedMessage("Logged crash with message: \(errorMessage)")
}

// MARK: - logNetworkPerformanceCallback

func test_logNetworkPerformanceCallback_logsMessage_whenSuccess() {
mparticle.logNetworkPerformanceCallback(.success)

assertReceivedMessage("Logged network performance measurement")
}

func test_logNetworkPerformanceCallback_doesNotLog_whenFail() {
mparticle.logNetworkPerformanceCallback(.fail)

XCTAssertNil(receivedMessage)
}

// MARK: - logError

func test_logError_withoutEventInfo_invokesBackendWithNil() {
mparticle.logError(errorMessage)

XCTAssertTrue(executor.executeOnMessageQueueAsync)
listenerController.assertCalled(#selector(mparticle.logError(_:eventInfo:)))

XCTAssertTrue(backendController.logErrorCalled)
XCTAssertNil(backendController.logErrorExceptionParam)
XCTAssertNil(backendController.logErrorTopmostContextParam)
XCTAssertNil(backendController.logErrorEventInfoParam)

backendController.logErrorCompletionHandler?(errorMessage, .success)
assertReceivedMessage("Logged error with message: \(errorMessage)")
}

func test_logError_withNilMessage_logsRequirementMessage() {
errorMessage = ""
mparticle.logError(errorMessage, eventInfo: keyValueDict)
assertReceivedMessage("'message' is required for logError:eventInfo:")
}

func test_logError_withMessage_invokesBackend() {
mparticle.logError(errorMessage, eventInfo: keyValueDict)

XCTAssertTrue(executor.executeOnMessageQueueAsync)
listenerController.assertCalled(#selector(mparticle.logError(_:eventInfo:)))

XCTAssertTrue(backendController.logErrorCalled)
XCTAssertNil(backendController.logErrorExceptionParam)
XCTAssertNil(backendController.logErrorTopmostContextParam)
XCTAssertEqual(backendController.logErrorEventInfoParam as? [String : String], keyValueDict)

backendController.logErrorCompletionHandler?(errorMessage, .success)
assertReceivedMessage("Logged error with message: \(errorMessage)")
}
}
2 changes: 1 addition & 1 deletion mParticle-Apple-SDK/mParticle.m
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ - (void)logErrorCallback:(NSDictionary<NSString *,id> * _Nullable)eventInfo exec
}

- (void)logError:(NSString *)message eventInfo:(NSDictionary<NSString *, id> *)eventInfo {
if (!message) {
if ([message isEqual: @""]) {
NSString *message = [NSString stringWithFormat:@"'message' is required for %@", NSStringFromSelector(_cmd)];
[logger error:message];
return;
Expand Down