From 1a927980f71fb0def8d56fecca5982d955d66155 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 22:25:19 +0100 Subject: [PATCH 1/6] Pixels automatic naming prefixing fixed --- Sources/PixelKit/DebugEvent.swift | 92 +++++++++++++++++++ Sources/PixelKit/NonStandardEvent.swift | 41 +++++++++ Sources/PixelKit/PixelKit.swift | 23 ++++- Sources/PixelKit/PixelKitEvent.swift | 74 --------------- .../XCTestCase+PixelKit.swift | 24 ++--- .../PixelKitParametersTests.swift | 2 +- Tests/PixelKitTests/PixelKitTests.swift | 11 ++- 7 files changed, 167 insertions(+), 100 deletions(-) create mode 100644 Sources/PixelKit/DebugEvent.swift create mode 100644 Sources/PixelKit/NonStandardEvent.swift diff --git a/Sources/PixelKit/DebugEvent.swift b/Sources/PixelKit/DebugEvent.swift new file mode 100644 index 000000000..5aff63d17 --- /dev/null +++ b/Sources/PixelKit/DebugEvent.swift @@ -0,0 +1,92 @@ +// +// DebugEvent.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// Implementation of ``PixelKitEvent`` with specific logic for debug events. +public final class DebugEvent: PixelKitEvent { + public enum EventType { + case assertionFailure(message: String, file: StaticString, line: UInt) + case custom(_ event: PixelKitEvent) + } + + public let eventType: EventType + public let error: Error? + + public init(eventType: EventType, error: Error? = nil) { + self.eventType = eventType + self.error = error + } + + public init(_ event: PixelKitEvent, error: Error? = nil) { + self.eventType = .custom(event) + self.error = error + } + + public var name: String { + switch eventType { + case .assertionFailure: + return "assertion_failure" + case .custom(let event): + return event.name + } + } + + public var parameters: [String: String]? { + var params: [String: String] + + if case let .custom(event) = eventType, + let eventParams = event.parameters { + params = eventParams + } else { + params = [String: String]() + } + + if let errorWithUserInfo = error as? ErrorWithPixelParameters { + params = errorWithUserInfo.errorParameters + } + + if case let .assertionFailure(message, file, line) = eventType { + params[PixelKit.Parameters.assertionMessage] = message + params[PixelKit.Parameters.assertionFile] = String(file) + params[PixelKit.Parameters.assertionLine] = String(line) + } + + if let error = error { + let nsError = error as NSError + + params[PixelKit.Parameters.errorCode] = "\(nsError.code)" + params[PixelKit.Parameters.errorDomain] = nsError.domain + + if let underlyingError = nsError.userInfo["NSUnderlyingError"] as? NSError { + params[PixelKit.Parameters.underlyingErrorCode] = "\(underlyingError.code)" + params[PixelKit.Parameters.underlyingErrorDomain] = underlyingError.domain + } + + if let sqlErrorCode = nsError.userInfo["SQLiteResultCode"] as? NSNumber { + params[PixelKit.Parameters.underlyingErrorSQLiteCode] = "\(sqlErrorCode.intValue)" + } + + if let sqlExtendedErrorCode = nsError.userInfo["SQLiteExtendedResultCode"] as? NSNumber { + params[PixelKit.Parameters.underlyingErrorSQLiteExtendedCode] = "\(sqlExtendedErrorCode.intValue)" + } + } + + return params + } +} diff --git a/Sources/PixelKit/NonStandardEvent.swift b/Sources/PixelKit/NonStandardEvent.swift new file mode 100644 index 000000000..274b68e64 --- /dev/null +++ b/Sources/PixelKit/NonStandardEvent.swift @@ -0,0 +1,41 @@ +// +// NonStandardEvent.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// This custom event is used for special cases, like pixels with non-standard names and uses, these pixels are sent as is and the names remain unchanged +public final class NonStandardEvent: PixelKitEventV2 { + + let event: PixelKitEventV2 + + public init(_ event: PixelKitEventV2) { + self.event = event + } + + public var name: String { + event.name + } + + public var parameters: [String: String]? { + event.parameters + } + + public var error: Error? { + event.error + } +} diff --git a/Sources/PixelKit/PixelKit.swift b/Sources/PixelKit/PixelKit.swift index d647b2883..6af7381c1 100644 --- a/Sources/PixelKit/PixelKit.swift +++ b/Sources/PixelKit/PixelKit.swift @@ -186,6 +186,9 @@ public final class PixelKit { headers[Header.moreInfo] = "See " + Self.duckDuckGoMorePrivacyInfo.absoluteString headers[Header.client] = "macOS" + // The event name can't contain `.` + reportErrorIf(pixel: pixelName, contains: ".") + switch frequency { case .standard: reportErrorIf(pixel: pixelName, endsWith: "_u") @@ -204,6 +207,7 @@ public final class PixelKit { reportErrorIf(pixel: pixelName, endsWith: "_d") guard pixelName.hasSuffix("_u") else { assertionFailure("Unique pixel: must end with _u") + onComplete(false, nil) return } if !pixelHasBeenFiredEver(pixelName) { @@ -253,6 +257,14 @@ public final class PixelKit { } } + /// If the pixel name contains the forbiddenString then an error is logged or an assertion failure is fired in debug + func reportErrorIf(pixel: String, contains forbiddenString: String) { + if pixel.contains(forbiddenString) { + logger.error("Pixel \(pixel, privacy: .public) must not contain \(forbiddenString, privacy: .public)") + assertionFailure("Pixel \(pixel) must not contain \(forbiddenString)") + } + } + private func printDebugInfo(pixelName: String, frequency: Frequency, parameters: [String: String], skipped: Bool = false) { let params = parameters.filter { key, _ in !["test"].contains(key) } logger.debug("👾[\(frequency.description, privacy: .public)-\(skipped ? "Skipped" : "Fired", privacy: .public)] \(pixelName, privacy: .public) \(params, privacy: .public)") @@ -279,11 +291,14 @@ public final class PixelKit { private func prefixedName(for event: Event) -> String { if event.name.hasPrefix("m_mac_") { + // Can be a debug event or not, if already prefixed the name remains unchanged return event.name - } - - if let debugEvent = event as? DebugEvent { + } else if let debugEvent = event as? DebugEvent { + // Is a Debug event not already prefixed return "m_mac_debug_\(debugEvent.name)" + } else if let nonStandardEvent = event as? NonStandardEvent { + // Special kind of pixel event that don't follow the standard naming conventions + return nonStandardEvent.name } else { return "m_mac_\(event.name)" } @@ -328,7 +343,7 @@ public final class PixelKit { let error = event.error { // For v2 events we only consider the error specified in the event - // and purposedly ignore the parameter in this call. + // and purposely ignore the parameter in this call. // This is to encourage moving the error over to the protocol error // instead of still relying on the parameter of this call. newError = error diff --git a/Sources/PixelKit/PixelKitEvent.swift b/Sources/PixelKit/PixelKitEvent.swift index ca352f334..04573f0e7 100644 --- a/Sources/PixelKit/PixelKitEvent.swift +++ b/Sources/PixelKit/PixelKitEvent.swift @@ -24,77 +24,3 @@ public protocol PixelKitEvent { var name: String { get } var parameters: [String: String]? { get } } - -/// Implementation of ``PixelKitEvent`` with specific logic for debug events. -/// -public final class DebugEvent: PixelKitEvent { - public enum EventType { - case assertionFailure(message: String, file: StaticString, line: UInt) - case custom(_ event: PixelKitEvent) - } - - public let eventType: EventType - public let error: Error? - - public init(eventType: EventType, error: Error? = nil) { - self.eventType = eventType - self.error = error - } - - public init(_ event: PixelKitEvent, error: Error? = nil) { - self.eventType = .custom(event) - self.error = error - } - - public var name: String { - switch eventType { - case .assertionFailure: - return "assertion_failure" - case .custom(let event): - return event.name - } - } - - public var parameters: [String: String]? { - var params: [String: String] - - if case let .custom(event) = eventType, - let eventParams = event.parameters { - params = eventParams - } else { - params = [String: String]() - } - - if let errorWithUserInfo = error as? ErrorWithPixelParameters { - params = errorWithUserInfo.errorParameters - } - - if case let .assertionFailure(message, file, line) = eventType { - params[PixelKit.Parameters.assertionMessage] = message - params[PixelKit.Parameters.assertionFile] = String(file) - params[PixelKit.Parameters.assertionLine] = String(line) - } - - if let error = error { - let nsError = error as NSError - - params[PixelKit.Parameters.errorCode] = "\(nsError.code)" - params[PixelKit.Parameters.errorDomain] = nsError.domain - - if let underlyingError = nsError.userInfo["NSUnderlyingError"] as? NSError { - params[PixelKit.Parameters.underlyingErrorCode] = "\(underlyingError.code)" - params[PixelKit.Parameters.underlyingErrorDomain] = underlyingError.domain - } - - if let sqlErrorCode = nsError.userInfo["SQLiteResultCode"] as? NSNumber { - params[PixelKit.Parameters.underlyingErrorSQLiteCode] = "\(sqlErrorCode.intValue)" - } - - if let sqlExtendedErrorCode = nsError.userInfo["SQLiteExtendedResultCode"] as? NSNumber { - params[PixelKit.Parameters.underlyingErrorSQLiteExtendedCode] = "\(sqlExtendedErrorCode.intValue)" - } - } - - return params - } -} diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index 78766b559..6550ed608 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -51,14 +51,6 @@ public extension XCTestCase { } } - static var pixelPlatformPrefix: String { -#if os(macOS) - return "m_mac_" -#elseif os(iOS) - return "m_" -#endif - } - /// These parameters are known to be expected just based on the event definition. /// /// They're not a complete list of parameters for the event, as the fire call may contain extra information @@ -146,23 +138,23 @@ public extension XCTestCase { } func expectedPixelNames(originalName: String, frequency: PixelKit.Frequency) -> [String] { - let expectedPixelNameWithoutSuffix = originalName.hasPrefix(Self.pixelPlatformPrefix) ? originalName : Self.pixelPlatformPrefix + originalName + let expectedPixelNameWithoutSuffix = originalName var expectedPixelNames: [String] = [] switch frequency { case .standard: - expectedPixelNames.append(expectedPixelNameWithoutSuffix) + expectedPixelNames.append(originalName) case .legacyInitial: - expectedPixelNames.append(expectedPixelNameWithoutSuffix) + expectedPixelNames.append(originalName) case .unique: - expectedPixelNames.append(expectedPixelNameWithoutSuffix) + expectedPixelNames.append(originalName) case .legacyDaily: - expectedPixelNames.append(expectedPixelNameWithoutSuffix) + expectedPixelNames.append(originalName) case .daily: - expectedPixelNames.append(expectedPixelNameWithoutSuffix.appending("_d")) + expectedPixelNames.append(originalName.appending("_d")) case .dailyAndCount: - expectedPixelNames.append(expectedPixelNameWithoutSuffix.appending("_d")) - expectedPixelNames.append(expectedPixelNameWithoutSuffix.appending("_c")) + expectedPixelNames.append(originalName.appending("_d")) + expectedPixelNames.append(originalName.appending("_c")) } return expectedPixelNames } diff --git a/Tests/PixelKitTests/PixelKitParametersTests.swift b/Tests/PixelKitTests/PixelKitParametersTests.swift index 4d661d333..4fa3e1f6c 100644 --- a/Tests/PixelKitTests/PixelKitParametersTests.swift +++ b/Tests/PixelKitTests/PixelKitParametersTests.swift @@ -66,7 +66,7 @@ final class PixelKitParametersTests: XCTestCase { fire(TestEvent.errorEvent(error: topLevelError), frequency: .standard, - and: .expect(pixelName: "m_mac_error_event", + and: .expect(pixelName: "error_event", error: topLevelError, underlyingErrors: [underlyingError2, underlyingError3]), file: #filePath, diff --git a/Tests/PixelKitTests/PixelKitTests.swift b/Tests/PixelKitTests/PixelKitTests.swift index cf51b5031..d64951239 100644 --- a/Tests/PixelKitTests/PixelKitTests.swift +++ b/Tests/PixelKitTests/PixelKitTests.swift @@ -36,6 +36,7 @@ final class PixelKitTests: XCTestCase { case dailyAndContinuousEvent case dailyAndContinuousEventWithoutParameters case uniqueEvent + case nameWithDot = "test.pixel.with.dot" var name: String { switch self { @@ -53,14 +54,14 @@ final class PixelKitTests: XCTestCase { "eventParam1": "eventParamValue1", "eventParam2": "eventParamValue2" ] - case .testEventWithoutParameters, .dailyEventWithoutParameters, .dailyAndContinuousEventWithoutParameters: + default: return nil } } var frequency: PixelKit.Frequency { switch self { - case .testEvent, .testEventWithoutParameters: + case .testEvent, .testEventWithoutParameters, .nameWithDot: return .standard case .uniqueEvent: return .unique @@ -96,7 +97,7 @@ final class PixelKitTests: XCTestCase { let userDefaults = userDefaults() // Set expectations - let expectedPixelName = "m_mac_\(event.name)" + let expectedPixelName = event.name let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") // Prepare mock to validate expectations @@ -140,7 +141,7 @@ final class PixelKitTests: XCTestCase { let userDefaults = userDefaults() // Set expectations - let expectedPixelName = "m_mac_\(event.name)_d" + let expectedPixelName = "\(event.name)_d" let expectedMoreInfoString = "See \(PixelKit.duckDuckGoMorePrivacyInfo)" let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") @@ -184,7 +185,7 @@ final class PixelKitTests: XCTestCase { let userDefaults = userDefaults() // Set expectations - let expectedPixelName = "m_mac_\(event.name)_d" + let expectedPixelName = "\(event.name)_d" let expectedMoreInfoString = "See \(PixelKit.duckDuckGoMorePrivacyInfo)" let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") fireCallbackCalled.expectedFulfillmentCount = 1 From 89806ca5bf9a4961bd420cb7effbc198ce9031b0 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 23:00:17 +0100 Subject: [PATCH 2/6] unit tests --- .../XCTestCase+PixelKit.swift | 3 +- Tests/PixelKitTests/PixelKitTests.swift | 114 +++++++++++++++--- 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index 6550ed608..778b16245 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -138,9 +138,8 @@ public extension XCTestCase { } func expectedPixelNames(originalName: String, frequency: PixelKit.Frequency) -> [String] { - let expectedPixelNameWithoutSuffix = originalName var expectedPixelNames: [String] = [] - + switch frequency { case .standard: expectedPixelNames.append(originalName) diff --git a/Tests/PixelKitTests/PixelKitTests.swift b/Tests/PixelKitTests/PixelKitTests.swift index d64951239..17ef2ce46 100644 --- a/Tests/PixelKitTests/PixelKitTests.swift +++ b/Tests/PixelKitTests/PixelKitTests.swift @@ -27,24 +27,38 @@ final class PixelKitTests: XCTestCase { } /// Test events for convenience - /// + private enum TestEvent: String, PixelKitEvent { + + case testEventPrefixed = "m_mac_testEventPrefixed" + case testEvent + + var name: String { + return rawValue + } + + var parameters: [String: String]? { + return nil + } + + var error: Error? { + return nil + } + } + + private enum TestEventV2: String, PixelKitEventV2 { + case testEvent case testEventWithoutParameters case dailyEvent case dailyEventWithoutParameters case dailyAndContinuousEvent case dailyAndContinuousEventWithoutParameters - case uniqueEvent + case uniqueEvent = "uniqueEvent_u" case nameWithDot = "test.pixel.with.dot" var name: String { - switch self { - case .uniqueEvent: - return "\(rawValue)_u" - default: - return rawValue - } + return rawValue } var parameters: [String: String]? { @@ -59,6 +73,10 @@ final class PixelKitTests: XCTestCase { } } + var error: Error? { + return nil + } + var frequency: PixelKit.Frequency { switch self { case .testEvent, .testEventWithoutParameters, .nameWithDot: @@ -84,7 +102,69 @@ final class PixelKitTests: XCTestCase { XCTFail("This callback should not be executed when doing a dry run") } - pixelKit.fire(TestEvent.testEvent) + pixelKit.fire(TestEventV2.testEvent) + } + + func testNonStandardEvent() { + func testReportBrokenSitePixel() { + fire(NonStandardEvent(TestEventV2.testEvent), + frequency: .standard, + and: .expect(pixelName: TestEventV2.testEvent.name), + file: #filePath, + line: #line) + } + } + + func testDebugEventPrefixed() { + let appVersion = "1.0.5" + let headers = ["a": "2", "b": "3", "c": "2000"] + let event = DebugEvent(TestEvent.testEventPrefixed) + let userDefaults = userDefaults() + + // Set expectations + let expectedPixelName = TestEvent.testEventPrefixed.name + let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") + + // Prepare mock to validate expectations + let pixelKit = PixelKit(dryRun: false, + appVersion: appVersion, + defaultHeaders: headers, + dailyPixelCalendar: nil, + defaults: userDefaults) { firedPixelName, firedHeaders, parameters, _, _, _ in + + fireCallbackCalled.fulfill() + XCTAssertEqual(expectedPixelName, firedPixelName) + } + // Run test + pixelKit.fire(event) + // Wait for expectations to be fulfilled + wait(for: [fireCallbackCalled], timeout: 0.5) + } + + func testDebugEventNotPrefixed() { + let appVersion = "1.0.5" + let headers = ["a": "2", "b": "3", "c": "2000"] + let event = DebugEvent(TestEvent.testEvent) + let userDefaults = userDefaults() + + // Set expectations + let expectedPixelName = "m_mac_debug_\(TestEvent.testEvent.name)" + let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") + + // Prepare mock to validate expectations + let pixelKit = PixelKit(dryRun: false, + appVersion: appVersion, + defaultHeaders: headers, + dailyPixelCalendar: nil, + defaults: userDefaults) { firedPixelName, firedHeaders, parameters, _, _, _ in + + fireCallbackCalled.fulfill() + XCTAssertEqual(expectedPixelName, firedPixelName) + } + // Run test + pixelKit.fire(event) + // Wait for expectations to be fulfilled + wait(for: [fireCallbackCalled], timeout: 0.5) } /// Tests firing a sample pixel and ensuring that all fields are properly set in the fire request callback. @@ -93,11 +173,11 @@ final class PixelKitTests: XCTestCase { // Prepare test parameters let appVersion = "1.0.5" let headers = ["a": "2", "b": "3", "c": "2000"] - let event = TestEvent.testEvent + let event = TestEventV2.testEvent let userDefaults = userDefaults() // Set expectations - let expectedPixelName = event.name + let expectedPixelName = "m_mac_\(event.name)" let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") // Prepare mock to validate expectations @@ -137,11 +217,11 @@ final class PixelKitTests: XCTestCase { // Prepare test parameters let appVersion = "1.0.5" let headers = ["a": "2", "b": "3", "c": "2000"] - let event = TestEvent.dailyEvent + let event = TestEventV2.dailyEvent let userDefaults = userDefaults() // Set expectations - let expectedPixelName = "\(event.name)_d" + let expectedPixelName = "m_mac_\(event.name)_d" let expectedMoreInfoString = "See \(PixelKit.duckDuckGoMorePrivacyInfo)" let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") @@ -181,11 +261,11 @@ final class PixelKitTests: XCTestCase { // Prepare test parameters let appVersion = "1.0.5" let headers = ["a": "2", "b": "3", "c": "2000"] - let event = TestEvent.dailyEvent + let event = TestEventV2.dailyEvent let userDefaults = userDefaults() // Set expectations - let expectedPixelName = "\(event.name)_d" + let expectedPixelName = "m_mac_\(event.name)_d" let expectedMoreInfoString = "See \(PixelKit.duckDuckGoMorePrivacyInfo)" let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") fireCallbackCalled.expectedFulfillmentCount = 1 @@ -227,7 +307,7 @@ final class PixelKitTests: XCTestCase { // Prepare test parameters let appVersion = "1.0.5" let headers = ["a": "2", "b": "3", "c": "2000"] - let event = TestEvent.dailyEvent + let event = TestEventV2.dailyEvent let userDefaults = userDefaults() let timeMachine = TimeMachine() @@ -271,7 +351,7 @@ final class PixelKitTests: XCTestCase { // Prepare test parameters let appVersion = "1.0.5" let headers = ["a": "2", "b": "3", "c": "2000"] - let event = TestEvent.uniqueEvent + let event = TestEventV2.uniqueEvent let userDefaults = userDefaults() let timeMachine = TimeMachine() From e0cce053c583c298c09a59387b131f38f2ee0579 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 23:27:36 +0100 Subject: [PATCH 3/6] test improved --- .../XCTestCase+PixelKit.swift | 6 ++++- .../PixelKitParametersTests.swift | 2 +- Tests/PixelKitTests/PixelKitTests.swift | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index 778b16245..71f413ad3 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -114,7 +114,11 @@ public extension XCTestCase { let firedParameters = Self.filterStandardPixelParameters(from: firedParameters) // Internal validations - XCTAssertTrue(expectedPixelNames.contains(firedPixelName), file: file, line: line) + var found = false + for expectedNameSuffix in expectedPixelNames { + found = firedPixelName.hasSuffix(expectedNameSuffix) + } + XCTAssertTrue(found) XCTAssertTrue(knownExpectedParameters.allSatisfy { (key, value) in firedParameters[key] == value }) diff --git a/Tests/PixelKitTests/PixelKitParametersTests.swift b/Tests/PixelKitTests/PixelKitParametersTests.swift index 4fa3e1f6c..4d661d333 100644 --- a/Tests/PixelKitTests/PixelKitParametersTests.swift +++ b/Tests/PixelKitTests/PixelKitParametersTests.swift @@ -66,7 +66,7 @@ final class PixelKitParametersTests: XCTestCase { fire(TestEvent.errorEvent(error: topLevelError), frequency: .standard, - and: .expect(pixelName: "error_event", + and: .expect(pixelName: "m_mac_error_event", error: topLevelError, underlyingErrors: [underlyingError2, underlyingError3]), file: #filePath, diff --git a/Tests/PixelKitTests/PixelKitTests.swift b/Tests/PixelKitTests/PixelKitTests.swift index 17ef2ce46..51d1834b7 100644 --- a/Tests/PixelKitTests/PixelKitTests.swift +++ b/Tests/PixelKitTests/PixelKitTests.swift @@ -167,6 +167,32 @@ final class PixelKitTests: XCTestCase { wait(for: [fireCallbackCalled], timeout: 0.5) } + func testDebugEventDaily() { + let appVersion = "1.0.5" + let headers = ["a": "2", "b": "3", "c": "2000"] + let event = DebugEvent(TestEvent.testEvent) + let userDefaults = userDefaults() + + // Set expectations + let expectedPixelName = "m_mac_debug_\(TestEvent.testEvent.name)_d" + let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") + + // Prepare mock to validate expectations + let pixelKit = PixelKit(dryRun: false, + appVersion: appVersion, + defaultHeaders: headers, + dailyPixelCalendar: nil, + defaults: userDefaults) { firedPixelName, firedHeaders, parameters, _, _, _ in + + fireCallbackCalled.fulfill() + XCTAssertEqual(expectedPixelName, firedPixelName) + } + // Run test + pixelKit.fire(event, frequency: .daily) + // Wait for expectations to be fulfilled + wait(for: [fireCallbackCalled], timeout: 0.5) + } + /// Tests firing a sample pixel and ensuring that all fields are properly set in the fire request callback. /// func testFiringASamplePixel() { From 50481e6d2e6d1a68108be023bdd0e042706f62c4 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 23:28:04 +0100 Subject: [PATCH 4/6] lint --- Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index 71f413ad3..ae27f8a58 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -143,7 +143,7 @@ public extension XCTestCase { func expectedPixelNames(originalName: String, frequency: PixelKit.Frequency) -> [String] { var expectedPixelNames: [String] = [] - + switch frequency { case .standard: expectedPixelNames.append(originalName) From 246e9ca4c1efcc55476e000e43b88e531e057eff Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 23:39:49 +0100 Subject: [PATCH 5/6] unit tests improved --- .../PixelKitTestingUtilities/XCTestCase+PixelKit.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index ae27f8a58..02bea7b28 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -116,12 +116,15 @@ public extension XCTestCase { // Internal validations var found = false for expectedNameSuffix in expectedPixelNames { - found = firedPixelName.hasSuffix(expectedNameSuffix) + if firedPixelName.hasSuffix(expectedNameSuffix) { + found = true + break + } } - XCTAssertTrue(found) + XCTAssertTrue(found, file: file, line: line) XCTAssertTrue(knownExpectedParameters.allSatisfy { (key, value) in firedParameters[key] == value - }) + }, file: file, line: line) if frequency == .dailyAndCount { XCTAssertTrue(firedPixelName.hasPrefix(expectations.pixelName)) From d4751757763f7379e54bd7bafd1f7b0d10e8af13 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 7 May 2024 23:47:49 +0100 Subject: [PATCH 6/6] lint fix --- Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift index 02bea7b28..c12e95213 100644 --- a/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift +++ b/Sources/PixelKitTestingUtilities/XCTestCase+PixelKit.swift @@ -115,11 +115,8 @@ public extension XCTestCase { // Internal validations var found = false - for expectedNameSuffix in expectedPixelNames { - if firedPixelName.hasSuffix(expectedNameSuffix) { + for expectedNameSuffix in expectedPixelNames where firedPixelName.hasSuffix(expectedNameSuffix) { found = true - break - } } XCTAssertTrue(found, file: file, line: line) XCTAssertTrue(knownExpectedParameters.allSatisfy { (key, value) in