diff --git a/Core/DailyPixel.swift b/Core/DailyPixel.swift index 98d0f037e9..e21be88fa4 100644 --- a/Core/DailyPixel.swift +++ b/Core/DailyPixel.swift @@ -18,6 +18,7 @@ // import Foundation +import Persistence /// A variant of pixel that is fired at most once per day. /// @@ -52,6 +53,8 @@ public final class DailyPixel { error: Swift.Error? = nil, withAdditionalParameters params: [String: String] = [:], includedParameters: [Pixel.QueryParameters] = [.appVersion], + pixelFiring: PixelFiring.Type = Pixel.self, + dailyPixelStore: KeyValueStoring = DailyPixel.storage, onComplete: @escaping (Swift.Error?) -> Void = { _ in }) { var key: String = pixel.name @@ -61,13 +64,13 @@ public final class DailyPixel { key.append(":\(createSortedStringOfValues(from: errorParams))") } - if !hasBeenFiredToday(forKey: key, dailyPixelStorage: storage) { - Pixel.fire(pixel: pixel, - error: error, - includedParameters: includedParameters, - withAdditionalParameters: params, - onComplete: onComplete) - updatePixelLastFireDate(forKey: key) + if !hasBeenFiredToday(forKey: key, dailyPixelStore: dailyPixelStore) { + pixelFiring.fire(pixel: pixel, + error: error, + includedParameters: includedParameters, + withAdditionalParameters: params, + onComplete: onComplete) + updatePixelLastFireDate(forKey: key, dailyPixelStore: dailyPixelStore) } else { onComplete(Error.alreadyFired) } @@ -80,12 +83,14 @@ public final class DailyPixel { error: Swift.Error? = nil, withAdditionalParameters params: [String: String] = [:], includedParameters: [Pixel.QueryParameters] = [.appVersion], + pixelFiring: PixelFiring.Type = Pixel.self, + dailyPixelStore: KeyValueStoring = DailyPixel.storage, onDailyComplete: @escaping (Swift.Error?) -> Void = { _ in }, onCountComplete: @escaping (Swift.Error?) -> Void = { _ in }) { let key: String = pixel.name - if !hasBeenFiredToday(forKey: key, dailyPixelStorage: storage) { - Pixel.fire( + if !hasBeenFiredToday(forKey: key, dailyPixelStore: dailyPixelStore) { + pixelFiring.fire( pixelNamed: pixel.name + "_d", withAdditionalParameters: params, includedParameters: includedParameters, @@ -94,12 +99,12 @@ public final class DailyPixel { } else { onDailyComplete(Error.alreadyFired) } - updatePixelLastFireDate(forKey: key) + updatePixelLastFireDate(forKey: key, dailyPixelStore: dailyPixelStore) var newParams = params if let error { newParams.appendErrorPixelParams(error: error) } - Pixel.fire( + pixelFiring.fire( pixelNamed: pixel.name + "_c", withAdditionalParameters: newParams, includedParameters: includedParameters, @@ -107,12 +112,12 @@ public final class DailyPixel { ) } - private static func updatePixelLastFireDate(forKey key: String) { - storage.set(Date(), forKey: key) + private static func updatePixelLastFireDate(forKey key: String, dailyPixelStore: KeyValueStoring) { + dailyPixelStore.set(Date(), forKey: key) } - private static func hasBeenFiredToday(forKey key: String, dailyPixelStorage: UserDefaults) -> Bool { - if let lastFireDate = dailyPixelStorage.object(forKey: key) as? Date { + private static func hasBeenFiredToday(forKey key: String, dailyPixelStore: KeyValueStoring) -> Bool { + if let lastFireDate = dailyPixelStore.object(forKey: key) as? Date { return Date().isSameDay(lastFireDate) } return false diff --git a/Core/PixelFiring.swift b/Core/PixelFiring.swift index 17d5cd2c20..52ddc011d7 100644 --- a/Core/PixelFiring.swift +++ b/Core/PixelFiring.swift @@ -27,8 +27,19 @@ public protocol PixelFiring { includedParameters: [Pixel.QueryParameters], onComplete: @escaping (Error?) -> Void) + static func fire(pixel: Pixel.Event, + error: Error?, + includedParameters: [Pixel.QueryParameters], + withAdditionalParameters params: [String: String], + onComplete: @escaping (Error?) -> Void) + static func fire(_ pixel: Pixel.Event, withAdditionalParameters params: [String: String]) + + static func fire(pixelNamed pixelName: String, + withAdditionalParameters params: [String: String], + includedParameters: [Pixel.QueryParameters], + onComplete: @escaping (Error?) -> Void) } extension Pixel: PixelFiring { @@ -36,8 +47,8 @@ extension Pixel: PixelFiring { withAdditionalParameters params: [String: String], includedParameters: [Pixel.QueryParameters], onComplete: @escaping (Error?) -> Void) { - - Pixel.fire(pixel: pixel, + + Self.fire(pixel: pixel, withAdditionalParameters: params, includedParameters: includedParameters, onComplete: onComplete) @@ -45,6 +56,17 @@ extension Pixel: PixelFiring { public static func fire(_ pixel: Pixel.Event, withAdditionalParameters params: [String: String]) { - Pixel.fire(pixel: pixel, withAdditionalParameters: params) + Self.fire(pixel: pixel, withAdditionalParameters: params) + } + + public static func fire(pixelNamed pixelName: String, + withAdditionalParameters params: [String: String], + includedParameters: [Pixel.QueryParameters], + onComplete: @escaping (Error?) -> Void) { + Self.fire(pixelNamed: pixelName, + withAdditionalParameters: params, + allowedQueryReservedCharacters: nil, + includedParameters: includedParameters, + onComplete: onComplete) } } diff --git a/DuckDuckGoTests/AdAttributionPixelReporterTests.swift b/DuckDuckGoTests/AdAttributionPixelReporterTests.swift index eb21f4507b..0a553d07b0 100644 --- a/DuckDuckGoTests/AdAttributionPixelReporterTests.swift +++ b/DuckDuckGoTests/AdAttributionPixelReporterTests.swift @@ -44,7 +44,7 @@ final class AdAttributionPixelReporterTests: XCTestCase { let result = await sut.reportAttributionIfNeeded() - XCTAssertEqual(PixelFiringMock.lastPixel, .appleAdAttribution) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.appleAdAttribution.name) XCTAssertTrue(result) } @@ -55,7 +55,7 @@ final class AdAttributionPixelReporterTests: XCTestCase { await fetcherStorage.markAttributionReportSuccessful() let result = await sut.reportAttributionIfNeeded() - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertFalse(result) } @@ -65,7 +65,7 @@ final class AdAttributionPixelReporterTests: XCTestCase { let result = await sut.reportAttributionIfNeeded() - XCTAssertEqual(PixelFiringMock.lastPixel?.name, "m_apple-ad-attribution") + XCTAssertEqual(PixelFiringMock.lastPixelName, "m_apple-ad-attribution") XCTAssertTrue(result) } @@ -130,7 +130,7 @@ final class AdAttributionPixelReporterTests: XCTestCase { let result = await sut.reportAttributionIfNeeded() - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertTrue(fetcherStorage.wasAttributionReportSuccessful) XCTAssertTrue(result) } @@ -141,7 +141,7 @@ final class AdAttributionPixelReporterTests: XCTestCase { let result = await sut.reportAttributionIfNeeded() - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertFalse(fetcherStorage.wasAttributionReportSuccessful) XCTAssertFalse(result) } diff --git a/DuckDuckGoTests/DailyPixelTests.swift b/DuckDuckGoTests/DailyPixelTests.swift index c03c635fb3..374e3e83bc 100644 --- a/DuckDuckGoTests/DailyPixelTests.swift +++ b/DuckDuckGoTests/DailyPixelTests.swift @@ -21,44 +21,31 @@ import XCTest import OHHTTPStubs import OHHTTPStubsSwift import Networking +import TestUtils +import Persistence @testable import Core final class DailyPixelTests: XCTestCase { - - let host = "improving.duckduckgo.com" - - let dailyPixelStorage = UserDefaults(suiteName: "com.duckduckgo.daily.pixel.storage")! - override func setUp() { - super.setUp() - - Pixel.isDryRun = false - } + let mockStore = MockKeyValueStore() override func tearDown() { - Pixel.isDryRun = true - - HTTPStubs.removeAllStubs() - resetDailyPixelStorage() super.tearDown() - } - - private func resetDailyPixelStorage() { - dailyPixelStorage.dictionaryRepresentation().keys.forEach(dailyPixelStorage.removeObject(forKey:)) + + PixelFiringMock.tearDown() } func testThatDailyPixelFiresCorrectlyForTheFirstTime() { let expectation = XCTestExpectation() - - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - - DailyPixel.fire(pixel: .forgetAllPressedBrowsing) { error in + + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - + + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) wait(for: [expectation], timeout: 3.0) } @@ -66,38 +53,40 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 2 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - - DailyPixel.fire(pixel: .forgetAllPressedBrowsing) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - DailyPixel.fire(pixel: .forgetAllPressedBrowsing) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNotNil(error) XCTAssertEqual(error as? DailyPixel.Error, .alreadyFired) expectation.fulfill() } - + + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + wait(for: [expectation], timeout: 3.0) } func testThatDailyPixelWillFireIfFiredPreviouslyOnDifferentDay() { let expectation = XCTestExpectation() - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - updateLastFireDateToYesterday(for: .forgetAllPressedBrowsing) - DailyPixel.fire(pixel: .forgetAllPressedBrowsing) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - + + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + wait(for: [expectation], timeout: 3.0) } @@ -105,23 +94,28 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 2 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - let error = NSError(domain: "test", code: 0, userInfo: nil) - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNotNil(error) XCTAssertEqual(error as? DailyPixel.Error, .alreadyFired) expectation.fulfill() } + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + XCTAssertEqual(PixelFiringMock.lastPixelInfo?.error as? NSError, error) + wait(for: [expectation], timeout: 3.0) } @@ -129,23 +123,31 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 2 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - let error1 = NSError(domain: "test1", code: 1, userInfo: nil) let error2 = NSError(domain: "test2", code: 2, userInfo: nil) - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error1) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error1, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error2) { error in + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + XCTAssertEqual(PixelFiringMock.lastPixelInfo?.error as? NSError, error1) + + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error2, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + XCTAssertEqual(PixelFiringMock.lastPixelInfo?.error as? NSError, error2) + wait(for: [expectation], timeout: 3.0) } @@ -154,35 +156,56 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 4 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - let error1 = NSError(domain: "test1", code: 1, userInfo: nil) let error2 = NSError(domain: "test1", code: 2, userInfo: nil) - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error1) { error in + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error1, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } + + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + XCTAssertEqual(PixelFiringMock.lastPixelInfo?.error as? NSError, error1) - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error2) { error in + + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error2, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNil(error) expectation.fulfill() } - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error1) { error in + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.forgetAllPressedBrowsing.name) + XCTAssertEqual(PixelFiringMock.lastPixelInfo?.error as? NSError, error2) + + PixelFiringMock.tearDown() + + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error1, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNotNil(error) XCTAssertEqual(error as? DailyPixel.Error, .alreadyFired) expectation.fulfill() } - DailyPixel.fire(pixel: .forgetAllPressedBrowsing, error: error2) { error in + XCTAssertNil(PixelFiringMock.lastPixelName) + + DailyPixel.fire(pixel: .forgetAllPressedBrowsing, + error: error2, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore) { error in XCTAssertNotNil(error) XCTAssertEqual(error as? DailyPixel.Error, .alreadyFired) expectation.fulfill() } + XCTAssertNil(PixelFiringMock.lastPixelName) + wait(for: [expectation], timeout: 3.0) } @@ -190,12 +213,10 @@ final class DailyPixelTests: XCTestCase { let countExpectation = XCTestExpectation() let dailyExpectation = XCTestExpectation() - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNil(error) dailyExpectation.fulfill() @@ -207,6 +228,10 @@ final class DailyPixelTests: XCTestCase { } ) + XCTAssertEqual(PixelFiringMock.allPixelsFired.count, 2) + XCTAssertEqual(PixelFiringMock.allPixelsFired[0].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_d") + XCTAssertEqual(PixelFiringMock.allPixelsFired[1].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + wait(for: [countExpectation, dailyExpectation], timeout: 3.0) } @@ -214,12 +239,10 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 2 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNil(error) expectation.fulfill() @@ -228,6 +251,8 @@ final class DailyPixelTests: XCTestCase { DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNotNil(error) XCTAssertEqual(error as? DailyPixel.Error, .alreadyFired) @@ -235,6 +260,11 @@ final class DailyPixelTests: XCTestCase { } ) + XCTAssertEqual(PixelFiringMock.allPixelsFired.count, 3) + XCTAssertEqual(PixelFiringMock.allPixelsFired[0].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_d") + XCTAssertEqual(PixelFiringMock.allPixelsFired[1].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + XCTAssertEqual(PixelFiringMock.allPixelsFired[2].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + wait(for: [expectation], timeout: 3.0) } @@ -242,12 +272,12 @@ final class DailyPixelTests: XCTestCase { let countExpectation = XCTestExpectation() let dailyExpectation = XCTestExpectation() - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(error: TestError.testError) - } + PixelFiringMock.expectedFireError = TestError.testError DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNotNil(error) dailyExpectation.fulfill() @@ -258,6 +288,10 @@ final class DailyPixelTests: XCTestCase { } ) + XCTAssertEqual(PixelFiringMock.allPixelsFired.count, 2) + XCTAssertEqual(PixelFiringMock.allPixelsFired[0].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_d") + XCTAssertEqual(PixelFiringMock.allPixelsFired[1].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + wait(for: [countExpectation, dailyExpectation], timeout: 3.0) } @@ -265,12 +299,10 @@ final class DailyPixelTests: XCTestCase { let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 2 - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onCountComplete: { error in XCTAssertNil(error) expectation.fulfill() @@ -279,32 +311,41 @@ final class DailyPixelTests: XCTestCase { DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onCountComplete: { error in XCTAssertNil(error) expectation.fulfill() } ) + XCTAssertEqual(PixelFiringMock.allPixelsFired.count, 3) + XCTAssertEqual(PixelFiringMock.allPixelsFired[0].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_d") + XCTAssertEqual(PixelFiringMock.allPixelsFired[1].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + XCTAssertEqual(PixelFiringMock.allPixelsFired[2].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + wait(for: [expectation], timeout: 3.0) } func testThatDailyPixelWithCountWillFireIfFiredPreviouslyOnDifferentDay() { let expectation = XCTestExpectation() - stub(condition: isHost(host)) { _ -> HTTPStubsResponse in - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - updateLastFireDateToYesterday(for: .forgetAllPressedBrowsing) DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNil(error) expectation.fulfill() } ) + XCTAssertEqual(PixelFiringMock.allPixelsFired.count, 2) + XCTAssertEqual(PixelFiringMock.allPixelsFired[0].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_d") + XCTAssertEqual(PixelFiringMock.allPixelsFired[1].pixelName, Pixel.Event.forgetAllPressedBrowsing.name + "_c") + wait(for: [expectation], timeout: 3.0) } @@ -321,6 +362,8 @@ final class DailyPixelTests: XCTestCase { DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onCountComplete: { error in XCTAssertNil(error) expectation.fulfill() @@ -343,6 +386,8 @@ final class DailyPixelTests: XCTestCase { DailyPixel.fireDailyAndCount( pixel: .forgetAllPressedBrowsing, + pixelFiring: PixelFiringMock.self, + dailyPixelStore: mockStore, onDailyComplete: { error in XCTAssertNil(error) expectation.fulfill() @@ -354,7 +399,7 @@ final class DailyPixelTests: XCTestCase { private func updateLastFireDateToYesterday(for pixel: Pixel.Event) { let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) - dailyPixelStorage.set(yesterday, forKey: pixel.name) + mockStore.set(yesterday, forKey: pixel.name) } private enum TestError: Error { diff --git a/DuckDuckGoTests/MockPixelFiring.swift b/DuckDuckGoTests/MockPixelFiring.swift index 5794dbe0b5..8eb632c92a 100644 --- a/DuckDuckGoTests/MockPixelFiring.swift +++ b/DuckDuckGoTests/MockPixelFiring.swift @@ -21,25 +21,41 @@ import Foundation import Core struct PixelInfo { - let pixel: Pixel.Event? + let pixelName: String? + let error: Error? let params: [String: String]? let includedParams: [Pixel.QueryParameters]? + + init(pixelName: String?, + error: Error? = nil, + params: [String: String]?, + includedParams: [Pixel.QueryParameters]?) { + self.pixelName = pixelName + self.error = error + self.params = params + self.includedParams = includedParams + } } final actor PixelFiringMock: PixelFiring, PixelFiringAsync, DailyPixelFiring { + static var expectedFireError: Error? + static var allPixelsFired = [PixelInfo]() + static var lastPixelInfo: PixelInfo? static var lastDailyPixelInfo: PixelInfo? static var lastParams: [String: String]? { lastPixelInfo?.params } - static var lastPixel: Pixel.Event? { lastPixelInfo?.pixel } + static var lastPixelName: String? { lastPixelInfo?.pixelName } static var lastIncludedParams: [Pixel.QueryParameters]? { lastPixelInfo?.includedParams } static func fire(pixel: Pixel.Event, withAdditionalParameters params: [String: String], includedParameters: [Pixel.QueryParameters]) async throws { - lastPixelInfo = PixelInfo(pixel: pixel, params: params, includedParams: includedParameters) + let info = PixelInfo(pixelName: pixel.name, params: params, includedParams: includedParameters) + lastPixelInfo = info + allPixelsFired.append(info) if let expectedFireError { throw expectedFireError @@ -50,27 +66,63 @@ final actor PixelFiringMock: PixelFiring, PixelFiringAsync, DailyPixelFiring { withAdditionalParameters params: [String: String], includedParameters: [Pixel.QueryParameters], onComplete: @escaping (Error?) -> Void) { - lastPixelInfo = PixelInfo(pixel: pixel, params: params, includedParams: includedParameters) + let info = PixelInfo(pixelName: pixel.name, params: params, includedParams: includedParameters) + lastPixelInfo = info + allPixelsFired.append(info) - if let expectedFireError { - onComplete(expectedFireError) - } + onComplete(expectedFireError) } static func fire(_ pixel: Pixel.Event, withAdditionalParameters params: [String: String]) { - lastPixelInfo = PixelInfo(pixel: pixel, params: params, includedParams: nil) + let info = PixelInfo(pixelName: pixel.name, params: params, includedParams: nil) + lastPixelInfo = info + allPixelsFired.append(info) + } + + static func fire(pixel: Pixel.Event, + error: (any Error)?, + includedParameters: [Pixel.QueryParameters], + withAdditionalParameters params: [String: String], + onComplete: @escaping ((any Error)?) -> Void) { + + let info = PixelInfo(pixelName: pixel.name, error: error, params: params, includedParams: includedParameters) + lastPixelInfo = info + allPixelsFired.append(info) + + onComplete(expectedFireError) + } + + static func fire(pixelNamed pixelName: String, + withAdditionalParameters params: [String: String], + includedParameters: [Pixel.QueryParameters], + onComplete: @escaping ((any Error)?) -> Void) { + + let info = PixelInfo(pixelName: pixelName, params: params, includedParams: includedParameters) + lastPixelInfo = info + allPixelsFired.append(info) + + onComplete(expectedFireError) } + // DailyPixelFiring + static func fireDaily(_ pixel: Pixel.Event) { - lastDailyPixelInfo = PixelInfo(pixel: pixel, params: nil, includedParams: nil) + let info = PixelInfo(pixelName: pixel.name, params: nil, includedParams: nil) + lastDailyPixelInfo = info + allPixelsFired.append(info) } static func fireDaily(_ pixel: Pixel.Event, withAdditionalParameters params: [String: String]) { - lastDailyPixelInfo = PixelInfo(pixel: pixel, params: params, includedParams: nil) + let info = PixelInfo(pixelName: pixel.name, params: params, includedParams: nil) + lastDailyPixelInfo = info + allPixelsFired.append(info) } + // - + static func tearDown() { + allPixelsFired = [] lastPixelInfo = nil lastDailyPixelInfo = nil expectedFireError = nil diff --git a/DuckDuckGoTests/NewTabPageFavoritesModelTests.swift b/DuckDuckGoTests/NewTabPageFavoritesModelTests.swift index 387eca3131..09a02148ca 100644 --- a/DuckDuckGoTests/NewTabPageFavoritesModelTests.swift +++ b/DuckDuckGoTests/NewTabPageFavoritesModelTests.swift @@ -20,6 +20,7 @@ import XCTest import Combine import Bookmarks +@testable import Core @testable import DuckDuckGo final class NewTabPageFavoritesModelTests: XCTestCase { @@ -35,7 +36,7 @@ final class NewTabPageFavoritesModelTests: XCTestCase { XCTAssertTrue(sut.isCollapsed) sut.toggleCollapse() - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageFavoritesSeeMore) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageFavoritesSeeMore.name) } func testFiresPixelWhenCollapsingList() { @@ -46,7 +47,7 @@ final class NewTabPageFavoritesModelTests: XCTestCase { XCTAssertFalse(sut.isCollapsed) sut.toggleCollapse() - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageFavoritesSeeLess) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageFavoritesSeeLess.name) } func testFiresPixelsOnFavoriteSelected() { @@ -54,8 +55,8 @@ final class NewTabPageFavoritesModelTests: XCTestCase { sut.favoriteSelected(Favorite(id: "", title: "", domain: "", urlObject: URL(string: "https://foo.bar"))) - XCTAssertEqual(PixelFiringMock.lastPixel, .favoriteLaunchedNTP) - XCTAssertEqual(PixelFiringMock.lastDailyPixelInfo?.pixel, .favoriteLaunchedNTPDaily) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.favoriteLaunchedNTP.name) + XCTAssertEqual(PixelFiringMock.lastDailyPixelInfo?.pixelName, Pixel.Event.favoriteLaunchedNTPDaily.name) } func testFiresPixelOnFavoriteDeleted() { @@ -66,7 +67,7 @@ final class NewTabPageFavoritesModelTests: XCTestCase { sut.deleteFavorite(favorite) - XCTAssertEqual(PixelFiringMock.lastPixel, .homeScreenDeleteFavorite) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.homeScreenDeleteFavorite.name) } func testFiresPixelOnFavoriteEdited() { @@ -77,7 +78,7 @@ final class NewTabPageFavoritesModelTests: XCTestCase { sut.editFavorite(favorite) - XCTAssertEqual(PixelFiringMock.lastPixel, .homeScreenEditFavorite) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.homeScreenEditFavorite.name) } func testFiresPixelOnTappingPlaceholder() { @@ -85,7 +86,7 @@ final class NewTabPageFavoritesModelTests: XCTestCase { sut.placeholderTapped() - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageFavoritesPlaceholderTapped) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageFavoritesPlaceholderTapped.name) } func testPrefixFavoritesCreatesRemainingPlaceholders() { diff --git a/DuckDuckGoTests/NewTabPageMessagesModelTests.swift b/DuckDuckGoTests/NewTabPageMessagesModelTests.swift index 617973b4f4..d24d9edfa8 100644 --- a/DuckDuckGoTests/NewTabPageMessagesModelTests.swift +++ b/DuckDuckGoTests/NewTabPageMessagesModelTests.swift @@ -140,7 +140,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { await model.onDidClose(.close) - XCTAssertEqual(PixelFiringMock.lastPixel, .remoteMessageDismissed) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.remoteMessageDismissed.name) XCTAssertEqual(PixelFiringMock.lastParams, [PixelParameters.message: "foo"]) } @@ -154,7 +154,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.action(isShare: false)) - XCTAssertEqual(PixelFiringMock.lastPixel, .remoteMessageActionClicked) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.remoteMessageActionClicked.name) XCTAssertEqual(PixelFiringMock.lastParams, [PixelParameters.message: "foo"]) } @@ -168,7 +168,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.primaryAction(isShare: false)) - XCTAssertEqual(PixelFiringMock.lastPixel, .remoteMessagePrimaryActionClicked) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.remoteMessagePrimaryActionClicked.name) XCTAssertEqual(PixelFiringMock.lastParams, [PixelParameters.message: "foo"]) } @@ -182,7 +182,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.secondaryAction(isShare: false)) - XCTAssertEqual(PixelFiringMock.lastPixel, .remoteMessageSecondaryActionClicked) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.remoteMessageSecondaryActionClicked.name) XCTAssertEqual(PixelFiringMock.lastParams, [PixelParameters.message: "foo"]) } @@ -197,7 +197,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { await model.onDidClose(.close) - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertNil(PixelFiringMock.lastParams) } @@ -211,7 +211,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.action(isShare: false)) - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertNil(PixelFiringMock.lastParams) } @@ -225,7 +225,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.primaryAction(isShare: false)) - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertNil(PixelFiringMock.lastParams) } @@ -239,7 +239,7 @@ final class NewTabPageMessagesModelTests: XCTestCase { let model = try XCTUnwrap(sut.homeMessageViewModels.first) await model.onDidClose(.secondaryAction(isShare: false)) - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) XCTAssertNil(PixelFiringMock.lastParams) } diff --git a/DuckDuckGoTests/NewTabPageSectionsSettingsModelTests.swift b/DuckDuckGoTests/NewTabPageSectionsSettingsModelTests.swift index bcc4b14177..b45c9ca692 100644 --- a/DuckDuckGoTests/NewTabPageSectionsSettingsModelTests.swift +++ b/DuckDuckGoTests/NewTabPageSectionsSettingsModelTests.swift @@ -18,6 +18,7 @@ // import XCTest +@testable import Core @testable import DuckDuckGo final class NewTabPageSectionsSettingsModelTests: XCTestCase { @@ -35,7 +36,7 @@ final class NewTabPageSectionsSettingsModelTests: XCTestCase { setting?.isEnabled.wrappedValue = true - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageCustomizeSectionOn("favorites")) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageCustomizeSectionOn("favorites").name) } func testFiresPixelWhenItemDisabled() { @@ -47,7 +48,7 @@ final class NewTabPageSectionsSettingsModelTests: XCTestCase { setting?.isEnabled.wrappedValue = false - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageCustomizeSectionOff("favorites")) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageCustomizeSectionOff("favorites").name) } func testFiresPixelWhenItemReordered() { @@ -55,7 +56,7 @@ final class NewTabPageSectionsSettingsModelTests: XCTestCase { sut.moveItems(from: IndexSet(integer: 0), to: 1) - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageSectionReordered) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageSectionReordered.name) } private func createSUT() -> NewTabPageSectionsSettingsModel { diff --git a/DuckDuckGoTests/NewTabPageShortcutsSettingsModelTests.swift b/DuckDuckGoTests/NewTabPageShortcutsSettingsModelTests.swift index 012f2b3b23..62964ea0ef 100644 --- a/DuckDuckGoTests/NewTabPageShortcutsSettingsModelTests.swift +++ b/DuckDuckGoTests/NewTabPageShortcutsSettingsModelTests.swift @@ -20,6 +20,7 @@ import XCTest import BrowserServicesKit +@testable import Core @testable import DuckDuckGo final class NewTabPageShortcutsSettingsModelTests: XCTestCase { @@ -37,7 +38,7 @@ final class NewTabPageShortcutsSettingsModelTests: XCTestCase { passwordsSettings.isEnabled.wrappedValue = true - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageCustomizeShortcutAdded("passwords")) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageCustomizeShortcutAdded("passwords").name) } func testFiresPixelWhenItemDisabled() throws { @@ -49,7 +50,7 @@ final class NewTabPageShortcutsSettingsModelTests: XCTestCase { passwordsSettings.isEnabled.wrappedValue = false - XCTAssertEqual(PixelFiringMock.lastPixel, .newTabPageCustomizeShortcutRemoved("passwords")) + XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.newTabPageCustomizeShortcutRemoved("passwords").name) } private func createSUT() -> NewTabPageShortcutsSettingsModel { diff --git a/DuckDuckGoTests/NewTabPageViewModelTests.swift b/DuckDuckGoTests/NewTabPageViewModelTests.swift index 4c42df78c9..08fa19dfb5 100644 --- a/DuckDuckGoTests/NewTabPageViewModelTests.swift +++ b/DuckDuckGoTests/NewTabPageViewModelTests.swift @@ -18,6 +18,7 @@ // import XCTest +@testable import Core @testable import DuckDuckGo final class NewTabPageViewModelTests: XCTestCase { @@ -68,7 +69,7 @@ final class NewTabPageViewModelTests: XCTestCase { sut.dismissIntroMessage() - XCTAssertEqual(.newTabPageMessageDismissed, PixelFiringMock.lastPixel) + XCTAssertEqual(Pixel.Event.newTabPageMessageDismissed.name, PixelFiringMock.lastPixelName) } func testFiresPixelWhenIntroMessageDisplayed() { @@ -76,7 +77,7 @@ final class NewTabPageViewModelTests: XCTestCase { sut.introMessageDisplayed() - XCTAssertEqual(.newTabPageMessageDisplayed, PixelFiringMock.lastPixel) + XCTAssertEqual(Pixel.Event.newTabPageMessageDisplayed.name, PixelFiringMock.lastPixelName) } func testFiresPixelOnNewTabPageCustomize() { @@ -84,6 +85,6 @@ final class NewTabPageViewModelTests: XCTestCase { sut.customizeNewTabPage() - XCTAssertEqual(.newTabPageCustomize, PixelFiringMock.lastPixel) + XCTAssertEqual(Pixel.Event.newTabPageCustomize.name, PixelFiringMock.lastPixelName) } } diff --git a/DuckDuckGoTests/PixelTests.swift b/DuckDuckGoTests/PixelTests.swift index dc257c5192..28a9b29158 100644 --- a/DuckDuckGoTests/PixelTests.swift +++ b/DuckDuckGoTests/PixelTests.swift @@ -47,19 +47,13 @@ class PixelTests: XCTestCase { let date = Date(timeIntervalSince1970: 0) let now = Date(timeIntervalSince1970: 1) - - stub(condition: { request -> Bool in - if let url = request.url { - XCTAssertEqual("1.0", url.getParameter(named: "dur")) - return true - } - - XCTFail("Did not found param dur") - return true - }, response: { _ -> HTTPStubsResponse in + + stub(condition: isHost(host) && isPath("/t/ml_ios_phone")) { request -> HTTPStubsResponse in + XCTAssertEqual("1.0", request.url?.getParameter(named: "dur")) + expectation.fulfill() return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - }) + } let pixel = TimedPixel(.appLaunch, date: date) pixel.fire(now) @@ -164,7 +158,7 @@ class PixelTests: XCTestCase { expectation.fulfill() } - wait(for: [expectation], timeout: 1.0) + wait(for: [expectation], timeout: 5.0) } func testPixelDebouncePreventsFiringWithinInterval() { diff --git a/DuckDuckGoTests/SyncUI/SyncManagementViewModelTests.swift b/DuckDuckGoTests/SyncUI/SyncManagementViewModelTests.swift index af65520b66..9bc66824af 100644 --- a/DuckDuckGoTests/SyncUI/SyncManagementViewModelTests.swift +++ b/DuckDuckGoTests/SyncUI/SyncManagementViewModelTests.swift @@ -18,6 +18,7 @@ // import XCTest +import Combine @testable import SyncUI /// To be fleshed out when UI is settled @@ -44,7 +45,7 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate func waitForInvocation() { let expectation = expectation(description: "Inv") - let cancellable = monitor.$functionCalls.dropFirst().sink { val in + let cancellable = monitor.didChange.dropFirst().sink { val in print(val) expectation.fulfill() } @@ -88,7 +89,8 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate // async functions selector description apparently contain 'WithCompletionHandler' monitor.assert(#selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"), calls: 1) monitor.assertCalls([ - #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1 + #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1, + #selector(showSyncWithAnotherDevice).description: 1 ]) } @@ -131,7 +133,8 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate // async functions selector description apparently contain 'WithCompletionHandler' monitor.assert(#selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"), calls: 1) monitor.assertCalls([ - #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1 + #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1, + #selector(shareRecoveryPDF).description: 1 ]) } @@ -164,7 +167,8 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate // async functions selector description apparently contain 'WithCompletionHandler' monitor.assert(#selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"), calls: 1) monitor.assertCalls([ - #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1 + #selector(authenticateUser).description.dropping(suffix: "WithCompletionHandler:"): 1, + #selector(showRecoverData).description: 1 ]) } // MARK: Delegate functions @@ -264,7 +268,12 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate private class Monitor { - @Published var functionCalls = [String: Int]() + public var didChange = PassthroughSubject() + var functionCalls = [String: Int]() { + didSet { + didChange.send() + } + } /// Whatever is passed as function is used as the key, the same key should be used for assertions. /// Use `String.cleaningFunctionName()` with `#function` but be aware that overloaded function names will not be tracked accurately. diff --git a/DuckDuckGoTests/UsageSegmentationTests.swift b/DuckDuckGoTests/UsageSegmentationTests.swift index 6fbde4eece..15bf6cd46f 100644 --- a/DuckDuckGoTests/UsageSegmentationTests.swift +++ b/DuckDuckGoTests/UsageSegmentationTests.swift @@ -48,13 +48,13 @@ final class UsageSegmentationTests: XCTestCase { sut.processATB(todayAtb, withInstallAtb: installAtb, andActivityType: .appUse) XCTAssertEqual([installAtb, todayAtb], appUseAtbs) XCTAssertEqual([], searchAtbs) - XCTAssertNotNil(PixelFiringMock.lastPixel) + XCTAssertNotNil(PixelFiringMock.lastPixelName) // Then a search sut.processATB(todayAtb, withInstallAtb: installAtb, andActivityType: .search) XCTAssertEqual([installAtb, todayAtb], appUseAtbs) XCTAssertEqual([installAtb, todayAtb], searchAtbs) - XCTAssertNotNil(PixelFiringMock.lastPixel) + XCTAssertNotNil(PixelFiringMock.lastPixelName) // Then another search shouldn't change anything else sut.processATB(todayAtb, withInstallAtb: installAtb, andActivityType: .search) @@ -74,7 +74,7 @@ final class UsageSegmentationTests: XCTestCase { sut.processATB(atb, withInstallAtb: installAtb, andActivityType: .search) XCTAssertEqual(searchAtbs, [installAtb, atb]) - XCTAssertNil(PixelFiringMock.lastPixel) + XCTAssertNil(PixelFiringMock.lastPixelName) } @@ -106,11 +106,11 @@ final class UsageSegmentationTests: XCTestCase { assertWhenNewATBReceivedWithInstallAtb_ThenBothStoredAndPixelFired(.search) } - func testWhenSearchActvityATBReceivedTwice_ThenNotStoredAndNoPixelFired() { + func testWhenSearchActivityATBReceivedTwice_ThenNotStoredAndNoPixelFired() { assertWhenATBReceivedTwice_ThenNotStoredAndNoPixelFired(.search) } - func testWhenAppActvityATBReceivedTwice_ThenNotStoredAndNoPixelFired() { + func testWhenAppActivityATBReceivedTwice_ThenNotStoredAndNoPixelFired() { assertWhenATBReceivedTwice_ThenNotStoredAndNoPixelFired(.appUse) } @@ -123,7 +123,7 @@ final class UsageSegmentationTests: XCTestCase { sut.processATB(atb, withInstallAtb: installAtb, andActivityType: activityType) XCTAssertEqual(activityType == .appUse ? appUseAtbs : searchAtbs, [installAtb]) - XCTAssertEqual(Pixel.Event.usageSegments, PixelFiringMock.lastPixel, file: file, line: line) + XCTAssertEqual(Pixel.Event.usageSegments.name, PixelFiringMock.lastPixelName, file: file, line: line) XCTAssertEqual([], PixelFiringMock.lastPixelInfo?.includedParams) } @@ -135,7 +135,7 @@ final class UsageSegmentationTests: XCTestCase { sut.processATB(atb, withInstallAtb: installAtb, andActivityType: activityType) XCTAssertEqual(activityType == .appUse ? appUseAtbs : searchAtbs, [installAtb, atb], file: file, line: line) - XCTAssertEqual(Pixel.Event.usageSegments, PixelFiringMock.lastPixel, file: file, line: line) + XCTAssertEqual(Pixel.Event.usageSegments.name, PixelFiringMock.lastPixelName, file: file, line: line) XCTAssertEqual([], PixelFiringMock.lastPixelInfo?.includedParams) } @@ -154,7 +154,7 @@ final class UsageSegmentationTests: XCTestCase { sut.processATB(atb, withInstallAtb: installAtb, andActivityType: activityType) XCTAssertEqual(activityType == .appUse ? appUseAtbs : searchAtbs, [installAtb, atb], file: file, line: line) - XCTAssertNil(PixelFiringMock.lastPixel, file: file, line: line) + XCTAssertNil(PixelFiringMock.lastPixelName, file: file, line: line) } private func makeSubject() -> UsageSegmenting {