From 931586e587c6f10645c725a10b6b8d9e949f92c6 Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Mon, 20 Jan 2025 16:22:28 -0500 Subject: [PATCH 1/6] Ensure we send to both places for telemetry --- .../UnifiedAdsCallbackTelemetry.swift | 14 ++++++++ .../Telemetry/SponsoredTileTelemetry.swift | 34 +++++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift index ad1bcb478884..c8a67115d0b9 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift @@ -26,11 +26,13 @@ final class DefaultUnifiedAdsCallbackTelemetry: UnifiedAdsCallbackTelemetry { func sendImpressionTelemetry(tile: SponsoredTile, position: Int) { let impressionURL = tile.impressionURL sendTelemetry(urlString: impressionURL, position: position) + sendLegacyImpressionTelemetry(tile: tile, position: position) } func sendClickTelemetry(tile: SponsoredTile, position: Int) { let clickURL = tile.clickURL sendTelemetry(urlString: clickURL, position: position) + sendLegacyClickTelemetry(tile: tile, position: position) } private func sendTelemetry(urlString: String, position: Int) { @@ -67,4 +69,16 @@ final class DefaultUnifiedAdsCallbackTelemetry: UnifiedAdsCallbackTelemetry { } } } + + // MARK: Legacy telemetry + // FXIOS-11121 While we are migrating to the new Unified Ads telemetry system, we should + // keep sending the legacy telemetry Glean pings + + private func sendLegacyImpressionTelemetry(tile: SponsoredTile, position: Int) { + SponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) + } + + private func sendLegacyClickTelemetry(tile: SponsoredTile, position: Int) { + SponsoredTileTelemetry.sendClickTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) + } } diff --git a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift index 7ef2a313e456..6c2bfad78993 100644 --- a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift +++ b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift @@ -11,29 +11,51 @@ struct SponsoredTileTelemetry { // Source is only new tab at the moment, more source could be added later static let source = "newtab" - static func sendImpressionTelemetry(tile: SponsoredTile, position: Int) { + /// Send Sponsored tile impression telemetry with Glean Pings + /// - Parameters: + /// - tile: The sponsored tile + /// - position: The position of the sponsored tile in the top sites collection view + /// - isUnifiedAdsEnabled: Whether the unified ads is enabled, if enabled some information isn't set on the ping + static func sendImpressionTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { let extra = GleanMetrics.TopSites.ContileImpressionExtra( position: Int32(position), source: SponsoredTileTelemetry.source ) GleanMetrics.TopSites.contileImpression.record(extra) - GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) + // Some information isn't set on the ping when unified ads is enabled + if !isUnifiedAdsEnabled { + GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) + GleanMetrics.TopSites.contileReportingUrl.set(tile.impressionURL) + } + GleanMetrics.TopSites.contileAdvertiser.set(tile.title) - GleanMetrics.TopSites.contileReportingUrl.set(tile.impressionURL) GleanMetrics.Pings.shared.topsitesImpression.submit() } - static func sendClickTelemetry(tile: SponsoredTile, position: Int) { + /// Send Sponsored tile click telemetry with Glean Pings + /// - Parameters: + /// - tile: The sponsored tile + /// - position: The position of the sponsored tile in the top sites collection view + /// - isUnifiedAdsEnabled: Whether the unified ads is enabled, if enabled some information isn't set on the ping + static func sendClickTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { let extra = GleanMetrics.TopSites.ContileClickExtra( position: Int32(position), source: SponsoredTileTelemetry.source ) GleanMetrics.TopSites.contileClick.record(extra) - GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) + // Some information isn't set on the ping when unified ads is enabled + if !isUnifiedAdsEnabled { + GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) + GleanMetrics.TopSites.contileReportingUrl.set(tile.clickURL) + } + GleanMetrics.TopSites.contileAdvertiser.set(tile.title) - GleanMetrics.TopSites.contileReportingUrl.set(tile.clickURL) GleanMetrics.Pings.shared.topsitesImpression.submit() } } From e1de22e6c57219afefbf5921abeededef4e41308 Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Tue, 21 Jan 2025 12:04:22 -0500 Subject: [PATCH 2/6] Add tests, remove static from SponsoredTileTelemetry todo so --- .../Client/AdjustTelemetryHelper.swift | 2 +- .../TopSites/TopSiteState.swift | 2 +- .../UnifiedAdsCallbackTelemetry.swift | 13 ++- .../Frontend/Home/TopSites/TopSite.swift | 11 -- .../Home/TopSites/TopSitesViewModel.swift | 15 ++- .../Client/Telemetry/GleanWrapper.swift | 20 +++- .../Telemetry/SponsoredTileTelemetry.swift | 72 +++++++++--- .../UnifiedAdsCallbackTelemetryTests.swift | 42 ++++++- .../ClientTests/Mocks/MockGleanWrapper.swift | 32 ++++- .../SponsoredTileTelemetryTests.swift | 110 +++++++----------- 10 files changed, 198 insertions(+), 121 deletions(-) diff --git a/firefox-ios/Client/AdjustTelemetryHelper.swift b/firefox-ios/Client/AdjustTelemetryHelper.swift index 0db7a0578f83..5e4ff36680a0 100644 --- a/firefox-ios/Client/AdjustTelemetryHelper.swift +++ b/firefox-ios/Client/AdjustTelemetryHelper.swift @@ -54,7 +54,7 @@ // telemetry.record(network: network) // } // -// gleanWrapper.submitPing() +// gleanWrapper.submit(GleanMetrics.Pings.shared.firstSession) // } //} // swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSiteState.swift b/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSiteState.swift index 24a51456b069..d77e556f1a29 100644 --- a/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSiteState.swift +++ b/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSiteState.swift @@ -54,7 +54,7 @@ final class TopSiteState: Hashable, Equatable { // Only sending sponsored tile impressions for now guard let tile = site as? SponsoredTile else { return } - SponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position) + DefaultSponsoredTileTelemetry().sendImpressionTelemetry(tile: tile, position: position) } func getTelemetrySiteType() -> String { diff --git a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift index c8a67115d0b9..f412e6909b40 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift @@ -12,15 +12,18 @@ protocol UnifiedAdsCallbackTelemetry { } final class DefaultUnifiedAdsCallbackTelemetry: UnifiedAdsCallbackTelemetry { - private var networking: ContileNetworking - private var logger: Logger + private let networking: ContileNetworking + private let logger: Logger + private let sponsoredTileTelemetry: SponsoredTileTelemetry init( networking: ContileNetworking = DefaultContileNetwork(with: NetworkUtils.defaultURLSession()), - logger: Logger = DefaultLogger.shared + logger: Logger = DefaultLogger.shared, + sponsoredTileTelemetry: SponsoredTileTelemetry = DefaultSponsoredTileTelemetry() ) { self.networking = networking self.logger = logger + self.sponsoredTileTelemetry = sponsoredTileTelemetry } func sendImpressionTelemetry(tile: SponsoredTile, position: Int) { @@ -75,10 +78,10 @@ final class DefaultUnifiedAdsCallbackTelemetry: UnifiedAdsCallbackTelemetry { // keep sending the legacy telemetry Glean pings private func sendLegacyImpressionTelemetry(tile: SponsoredTile, position: Int) { - SponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) + sponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) } private func sendLegacyClickTelemetry(tile: SponsoredTile, position: Int) { - SponsoredTileTelemetry.sendClickTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) + sponsoredTileTelemetry.sendClickTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: true) } } diff --git a/firefox-ios/Client/Frontend/Home/TopSites/TopSite.swift b/firefox-ios/Client/Frontend/Home/TopSites/TopSite.swift index 47909051ee54..838c5b72bd71 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/TopSite.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/TopSite.swift @@ -52,17 +52,6 @@ final class TopSite: FeatureFlaggable { // MARK: Telemetry - func impressionTracking(position: Int, unifiedAdsTelemetry: UnifiedAdsCallbackTelemetry) { - // Only sending sponsored tile impressions for now - guard let tile = site as? SponsoredTile else { return } - - if featureFlags.isFeatureEnabled(.unifiedAds, checking: .buildOnly) { - unifiedAdsTelemetry.sendImpressionTelemetry(tile: tile, position: position) - } else { - SponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position) - } - } - func getTelemetrySiteType() -> String { if isPinned && isGoogleGUID { return "google" diff --git a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift index d8ca692fe1d5..a0b5c0cfc25b 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift @@ -33,11 +33,13 @@ class TopSitesViewModel { private let googleTopSiteManager: GoogleTopSiteManager private var wallpaperManager: WallpaperManager private let unifiedAdsTelemetry: UnifiedAdsCallbackTelemetry + private let sponsoredTileTelemetry: SponsoredTileTelemetry init(profile: Profile, isZeroSearch: Bool = false, theme: Theme, wallpaperManager: WallpaperManager, + sponsoredTileTelemetry: SponsoredTileTelemetry = DefaultSponsoredTileTelemetry(), unifiedAdsTelemetry: UnifiedAdsCallbackTelemetry = DefaultUnifiedAdsCallbackTelemetry()) { self.profile = profile self.isZeroSearch = isZeroSearch @@ -52,6 +54,7 @@ class TopSitesViewModel { topSitesDataAdaptor = adaptor self.wallpaperManager = wallpaperManager self.unifiedAdsTelemetry = unifiedAdsTelemetry + self.sponsoredTileTelemetry = sponsoredTileTelemetry adaptor.delegate = self } @@ -64,7 +67,15 @@ class TopSitesViewModel { func sendImpressionTelemetry(_ homeTopSite: TopSite, position: Int) { guard !hasSentImpressionForTile(homeTopSite) else { return } - homeTopSite.impressionTracking(position: position, unifiedAdsTelemetry: unifiedAdsTelemetry) + + // Only sending sponsored tile impressions for now + guard let tile = homeTopSite.site as? SponsoredTile else { return } + + if featureFlags.isFeatureEnabled(.unifiedAds, checking: .buildOnly) { + unifiedAdsTelemetry.sendImpressionTelemetry(tile: tile, position: position) + } else { + sponsoredTileTelemetry.sendImpressionTelemetry(tile: tile, position: position) + } } private func topSitePressTracking(homeTopSite: TopSite, position: Int) { @@ -95,7 +106,7 @@ class TopSitesViewModel { if featureFlags.isFeatureEnabled(.unifiedAds, checking: .buildOnly) { unifiedAdsTelemetry.sendClickTelemetry(tile: tile, position: position) } else { - SponsoredTileTelemetry.sendClickTelemetry(tile: tile, position: position) + sponsoredTileTelemetry.sendClickTelemetry(tile: tile, position: position) } } } diff --git a/firefox-ios/Client/Telemetry/GleanWrapper.swift b/firefox-ios/Client/Telemetry/GleanWrapper.swift index 5a1698b1ec01..b40effddb0ed 100644 --- a/firefox-ios/Client/Telemetry/GleanWrapper.swift +++ b/firefox-ios/Client/Telemetry/GleanWrapper.swift @@ -8,7 +8,6 @@ import Glean protocol GleanWrapper { func handleDeeplinkUrl(url: URL) func setUpload(isEnabled: Bool) - func submitPing() // MARK: Glean Metrics @@ -20,6 +19,7 @@ protocol GleanWrapper { func recordLabel(for metric: LabeledMetricType, label: String) func setBoolean(for metric: BooleanMetricType, value: Bool) func recordQuantity(for metric: QuantityMetricType, value: Int64) + func recordUrl(for metric: UrlMetricType, value: String) func incrementNumerator(for metric: RateMetricType, amount: Int32) func incrementDenominator(for metric: RateMetricType, amount: Int32) @@ -31,6 +31,10 @@ protocol GleanWrapper { timerId: GleanTimerId) func stopAndAccumulateTiming(for metric: TimingDistributionMetricType, timerId: GleanTimerId) + + // MARK: Pings + + func submit(ping: Ping) where ReasonCodesEnum: ReasonCodes } /// Glean wrapper to abstract Glean from our application @@ -49,10 +53,6 @@ struct DefaultGleanWrapper: GleanWrapper { glean.setCollectionEnabled(isEnabled) } - func submitPing() { - GleanMetrics.Pings.shared.firstSession.submit() - } - // MARK: Glean Metrics func recordEvent(for metric: EventMetricType, @@ -88,6 +88,10 @@ struct DefaultGleanWrapper: GleanWrapper { metric.set(value) } + func recordUrl(for metric: UrlMetricType, value: String) { + metric.set(value) + } + // MARK: RateMetricType func incrementNumerator(for metric: RateMetricType, amount: Int32) { @@ -113,4 +117,10 @@ struct DefaultGleanWrapper: GleanWrapper { timerId: GleanTimerId) { metric.stopAndAccumulate(timerId) } + + // MARK: Pings + + func submit(ping: Ping) where ReasonCodesEnum: ReasonCodes { + ping.submit() + } } diff --git a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift index 6c2bfad78993..4fd7896d59a4 100644 --- a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift +++ b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift @@ -7,32 +7,63 @@ import Glean // Telemetry for the Sponsored tiles located in the Top sites on the Firefox home page // Using Pings to send the telemetry events -struct SponsoredTileTelemetry { +protocol SponsoredTileTelemetry { + func sendImpressionTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool) + func sendClickTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool) +} + +extension SponsoredTileTelemetry { + func sendImpressionTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { + sendImpressionTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: isUnifiedAdsEnabled) + } + + func sendClickTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { + sendClickTelemetry(tile: tile, position: position, isUnifiedAdsEnabled: isUnifiedAdsEnabled) + } +} + +struct DefaultSponsoredTileTelemetry: SponsoredTileTelemetry { // Source is only new tab at the moment, more source could be added later static let source = "newtab" + private let gleanWrapper: GleanWrapper + + init(gleanWrapper: GleanWrapper = DefaultGleanWrapper()) { + self.gleanWrapper = gleanWrapper + } /// Send Sponsored tile impression telemetry with Glean Pings /// - Parameters: /// - tile: The sponsored tile /// - position: The position of the sponsored tile in the top sites collection view /// - isUnifiedAdsEnabled: Whether the unified ads is enabled, if enabled some information isn't set on the ping - static func sendImpressionTelemetry(tile: SponsoredTile, - position: Int, - isUnifiedAdsEnabled: Bool = false) { + func sendImpressionTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { let extra = GleanMetrics.TopSites.ContileImpressionExtra( position: Int32(position), - source: SponsoredTileTelemetry.source + source: DefaultSponsoredTileTelemetry.source ) - GleanMetrics.TopSites.contileImpression.record(extra) + gleanWrapper.recordEvent(for: GleanMetrics.TopSites.contileImpression, extras: extra) // Some information isn't set on the ping when unified ads is enabled if !isUnifiedAdsEnabled { - GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) - GleanMetrics.TopSites.contileReportingUrl.set(tile.impressionURL) + gleanWrapper.recordQuantity(for: GleanMetrics.TopSites.contileTileId, + value: Int64(tile.tileId)) + gleanWrapper.recordUrl(for: GleanMetrics.TopSites.contileReportingUrl, + value: tile.impressionURL) } - GleanMetrics.TopSites.contileAdvertiser.set(tile.title) - GleanMetrics.Pings.shared.topsitesImpression.submit() + gleanWrapper.recordString(for: GleanMetrics.TopSites.contileAdvertiser, + value: tile.title) + gleanWrapper.submit(ping: GleanMetrics.Pings.shared.topsitesImpression) } /// Send Sponsored tile click telemetry with Glean Pings @@ -40,22 +71,25 @@ struct SponsoredTileTelemetry { /// - tile: The sponsored tile /// - position: The position of the sponsored tile in the top sites collection view /// - isUnifiedAdsEnabled: Whether the unified ads is enabled, if enabled some information isn't set on the ping - static func sendClickTelemetry(tile: SponsoredTile, - position: Int, - isUnifiedAdsEnabled: Bool = false) { + func sendClickTelemetry(tile: SponsoredTile, + position: Int, + isUnifiedAdsEnabled: Bool = false) { let extra = GleanMetrics.TopSites.ContileClickExtra( position: Int32(position), - source: SponsoredTileTelemetry.source + source: DefaultSponsoredTileTelemetry.source ) - GleanMetrics.TopSites.contileClick.record(extra) + gleanWrapper.recordEvent(for: GleanMetrics.TopSites.contileClick, extras: extra) // Some information isn't set on the ping when unified ads is enabled if !isUnifiedAdsEnabled { - GleanMetrics.TopSites.contileTileId.set(Int64(tile.tileId)) - GleanMetrics.TopSites.contileReportingUrl.set(tile.clickURL) + gleanWrapper.recordQuantity(for: GleanMetrics.TopSites.contileTileId, + value: Int64(tile.tileId)) + gleanWrapper.recordUrl(for: GleanMetrics.TopSites.contileReportingUrl, + value: tile.clickURL) } - GleanMetrics.TopSites.contileAdvertiser.set(tile.title) - GleanMetrics.Pings.shared.topsitesImpression.submit() + gleanWrapper.recordString(for: GleanMetrics.TopSites.contileAdvertiser, + value: tile.title) + gleanWrapper.submit(ping: GleanMetrics.Pings.shared.topsitesImpression) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift index 851d660058ec..363e68b488fd 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +import Glean import XCTest @testable import Client @@ -10,16 +11,19 @@ import XCTest class UnifiedAdsCallbackTelemetryTests: XCTestCase { private var networking: MockContileNetworking! private var logger: MockLogger! + private var gleanWrapper: MockGleanWrapper! override func setUp() { super.setUp() networking = MockContileNetworking() logger = MockLogger() + gleanWrapper = MockGleanWrapper() } override func tearDown() { networking = nil logger = nil + gleanWrapper = nil super.tearDown() } @@ -39,10 +43,46 @@ class UnifiedAdsCallbackTelemetryTests: XCTestCase { XCTAssertEqual(logger.savedMessage, "The unified ads telemetry call failed: \(tile.clickURL)") } + func testLegacyImpressionTelemetry() { + let subject = createSubject() + subject.sendImpressionTelemetry(tile: tile, position: 1) + + XCTAssertEqual(gleanWrapper.recordQuantityCalled, 0) + XCTAssertEqual(gleanWrapper.recordStringCalled, 1) + XCTAssertEqual(gleanWrapper.recordUrlCalled, 0) + XCTAssertEqual(gleanWrapper.recordEventCalled, 1) + XCTAssertEqual(gleanWrapper.submitPingCalled, 1) + guard let savedPing = gleanWrapper.savedPing as? Ping else { + XCTFail("savedPing is not of type Ping") + return + } + XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) + XCTAssertEqual(gleanWrapper.savedEvents?.count, 2) + } + + func testLegacyClickTelemetry() { + let subject = createSubject() + subject.sendClickTelemetry(tile: tile, position: 1) + + XCTAssertEqual(gleanWrapper.recordQuantityCalled, 0) + XCTAssertEqual(gleanWrapper.recordStringCalled, 1) + XCTAssertEqual(gleanWrapper.recordUrlCalled, 0) + XCTAssertEqual(gleanWrapper.recordEventCalled, 1) + XCTAssertEqual(gleanWrapper.submitPingCalled, 1) + guard let savedPing = gleanWrapper.savedPing as? Ping else { + XCTFail("savedPing is not of type Ping") + return + } + XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) + XCTAssertEqual(gleanWrapper.savedEvents?.count, 2) + } + // MARK: - Helper functions func createSubject(file: StaticString = #filePath, line: UInt = #line) -> UnifiedAdsCallbackTelemetry { - let subject = DefaultUnifiedAdsCallbackTelemetry(networking: networking, logger: logger) + let subject = DefaultUnifiedAdsCallbackTelemetry(networking: networking, + logger: logger, + gleanWrapper: gleanWrapper) trackForMemoryLeaks(subject, file: file, line: line) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift index d93c09b2246b..3b38024d38a9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift @@ -8,7 +8,6 @@ import UIKit class MockGleanWrapper: GleanWrapper { var handleDeeplinkUrlCalled = 0 - var submitPingCalled = 0 var setUploadEnabledCalled = 0 var recordEventCalled = 0 var recordEventNoExtraCalled = 0 @@ -17,13 +16,17 @@ class MockGleanWrapper: GleanWrapper { var recordLabelCalled = 0 var setBooleanCalled = 0 var recordQuantityCalled = 0 + var recordUrlCalled = 0 var incrementNumeratorCalled = 0 var incrementDenominatorCalled = 0 var startTimingCalled = 0 var cancelTimingCalled = 0 var stopAndAccumulateCalled = 0 + var submitPingCalled = 0 var savedEvent: Any? + var savedEvents: [Any]? = [] var savedExtras: Any? + var savedPing: Any? var savedHandleDeeplinkUrl: URL? var savedSetUploadIsEnabled: Bool? @@ -34,10 +37,6 @@ class MockGleanWrapper: GleanWrapper { savedHandleDeeplinkUrl = url } - func submitPing() { - submitPingCalled += 1 - } - func setUpload(isEnabled: Bool) { setUploadEnabledCalled += 1 savedSetUploadIsEnabled = isEnabled @@ -47,51 +46,67 @@ class MockGleanWrapper: GleanWrapper { extras: EventExtras) where ExtraObject: EventExtras { savedEvent = metric savedExtras = extras + savedEvents?.append(metric) recordEventCalled += 1 } func recordEvent(for metric: EventMetricType) where NoExtras: EventExtras { savedEvent = metric + savedEvents?.append(metric) recordEventNoExtraCalled += 1 } func incrementCounter(for metric: CounterMetricType) { savedEvent = metric + savedEvents?.append(metric) incrementCounterCalled += 1 } func recordString(for metric: StringMetricType, value: String) { savedEvent = metric + savedEvents?.append(metric) recordStringCalled += 1 } func recordLabel(for metric: LabeledMetricType, label: String) { savedEvent = metric + savedEvents?.append(metric) recordLabelCalled += 1 } func setBoolean(for metric: BooleanMetricType, value: Bool) { savedEvent = metric + savedEvents?.append(metric) setBooleanCalled += 1 } func recordQuantity(for metric: QuantityMetricType, value: Int64) { savedEvent = metric + savedEvents?.append(metric) recordQuantityCalled += 1 } + func recordUrl(for metric: UrlMetricType, value: String) { + savedEvent = metric + savedEvents?.append(metric) + recordUrlCalled += 1 + } + func incrementNumerator(for metric: RateMetricType, amount: Int32) { savedEvent = metric + savedEvents?.append(metric) incrementNumeratorCalled += 1 } func incrementDenominator(for metric: RateMetricType, amount: Int32) { savedEvent = metric + savedEvents?.append(metric) incrementDenominatorCalled += 1 } func startTiming(for metric: TimingDistributionMetricType) -> GleanTimerId { savedEvent = metric + savedEvents?.append(metric) startTimingCalled += 1 return savedTimerId } @@ -99,12 +114,19 @@ class MockGleanWrapper: GleanWrapper { func cancelTiming(for metric: TimingDistributionMetricType, timerId: GleanTimerId) { savedEvent = metric + savedEvents?.append(metric) cancelTimingCalled += 1 } func stopAndAccumulateTiming(for metric: TimingDistributionMetricType, timerId: GleanTimerId) { savedEvent = metric + savedEvents?.append(metric) stopAndAccumulateCalled += 1 } + + func submit(ping: Ping) where ReasonCodesEnum: ReasonCodes { + savedPing = ping + submitPingCalled += 1 + } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift index 2a8742d6e6b6..3fb87db35634 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift @@ -8,14 +8,18 @@ import Glean import XCTest class SponsoredTileTelemetryTests: XCTestCase { + private var gleanWrapper: MockGleanWrapper! + override func setUp() { super.setUp() + gleanWrapper = MockGleanWrapper() clearTest() } override func tearDown() { - super.tearDown() clearTest() + gleanWrapper = nil + super.tearDown() } // MARK: Impression @@ -25,28 +29,20 @@ class SponsoredTileTelemetryTests: XCTestCase { let contile = ContileProviderMock.defaultSuccessData[0] let topSite = SponsoredTile(contile: contile) - let expectation = expectation(description: "The top sites ping was sent") - GleanMetrics.Pings.shared.topsitesImpression.testBeforeNextSubmit { _ in - self.testEventMetricRecordingSuccess(metric: GleanMetrics.TopSites.contileImpression) - - self.testQuantityMetricSuccess(metric: GleanMetrics.TopSites.contileTileId, - expectedValue: 1, - failureMessage: "Should have contile id of \(contile.id)") - - self.testStringMetricSuccess(metric: GleanMetrics.TopSites.contileAdvertiser, - expectedValue: contile.name, - failureMessage: "Should have contile advertiser of \(contile.name)") - - self.testUrlMetricSuccess(metric: GleanMetrics.TopSites.contileReportingUrl, - expectedValue: contile.impressionUrl, - failureMessage: "Should have contile url of \(contile.impressionUrl)") - - expectation.fulfill() + let subject = createSubject() + subject.sendImpressionTelemetry(tile: topSite, position: 2) + + XCTAssertEqual(gleanWrapper.recordQuantityCalled, 1) + XCTAssertEqual(gleanWrapper.recordStringCalled, 1) + XCTAssertEqual(gleanWrapper.recordUrlCalled, 1) + XCTAssertEqual(gleanWrapper.recordEventCalled, 1) + XCTAssertEqual(gleanWrapper.submitPingCalled, 1) + guard let savedPing = gleanWrapper.savedPing as? Ping else { + XCTFail("savedPing is not of type Ping") + return } - - SponsoredTileTelemetry.sendImpressionTelemetry(tile: topSite, position: 2) - - waitForExpectations(timeout: 5.0) + XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) + XCTAssertEqual(gleanWrapper.savedEvents?.count, 4) } // MARK: Click @@ -56,62 +52,34 @@ class SponsoredTileTelemetryTests: XCTestCase { let contile = ContileProviderMock.defaultSuccessData[1] let topSite = SponsoredTile(contile: contile) - let expectation = expectation(description: "The top sites ping was sent") - GleanMetrics.Pings.shared.topsitesImpression.testBeforeNextSubmit { _ in - self.testEventMetricRecordingSuccess(metric: GleanMetrics.TopSites.contileClick) - - self.testQuantityMetricSuccess(metric: GleanMetrics.TopSites.contileTileId, - expectedValue: 2, - failureMessage: "Should have contile id of \(contile.id)") - - self.testStringMetricSuccess(metric: GleanMetrics.TopSites.contileAdvertiser, - expectedValue: contile.name, - failureMessage: "Should have contile advertiser of \(contile.name)") - - self.testUrlMetricSuccess(metric: GleanMetrics.TopSites.contileReportingUrl, - expectedValue: contile.clickUrl, - failureMessage: "Should have contile url of \(contile.clickUrl)") - expectation.fulfill() + let subject = createSubject() + subject.sendClickTelemetry(tile: topSite, position: 3) + + XCTAssertEqual(gleanWrapper.recordQuantityCalled, 1) + XCTAssertEqual(gleanWrapper.recordStringCalled, 1) + XCTAssertEqual(gleanWrapper.recordUrlCalled, 1) + XCTAssertEqual(gleanWrapper.recordEventCalled, 1) + XCTAssertEqual(gleanWrapper.submitPingCalled, 1) + guard let savedPing = gleanWrapper.savedPing as? Ping else { + XCTFail("savedPing is not of type Ping") + return } - - SponsoredTileTelemetry.sendClickTelemetry(tile: topSite, position: 3) - - waitForExpectations(timeout: 5.0) + XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) + XCTAssertEqual(gleanWrapper.savedEvents?.count, 4) } - // MARK: ContextId - func testContextIdImpressionTopSite() { - TelemetryContextualIdentifier.setupContextId() - let contile = ContileProviderMock.defaultSuccessData[0] - let topSite = SponsoredTile(contile: contile) - - let expectation = expectation(description: "The top sites ping was sent") - GleanMetrics.Pings.shared.topsitesImpression.testBeforeNextSubmit { _ in - guard let contextId = TelemetryContextualIdentifier.contextId, - let uuid = UUID(uuidString: contextId) else { - XCTFail("Expected contextId to be configured") - return - } - - self.testUuidMetricSuccess(metric: GleanMetrics.TopSites.contextId, - expectedValue: uuid, - failureMessage: "Should have contextId of \(uuid)") - expectation.fulfill() - } + // MARK: Helper methods - SponsoredTileTelemetry.sendImpressionTelemetry(tile: topSite, position: 2) - waitForExpectations(timeout: 5.0) + func createSubject() -> SponsoredTileTelemetry { + return DefaultSponsoredTileTelemetry(gleanWrapper: gleanWrapper) } - // MARK: Helper methods - func clearTest() { - // Due to changes allow certain custom pings to implement their own opt-out - // independent of Glean, custom pings may need to be registered manually in - // tests in order to puth them in a state in which they can collect data. - Glean.shared.registerPings(GleanMetrics.Pings.shared) - - Glean.shared.resetGlean(clearStores: true) TelemetryContextualIdentifier.clearUserDefaults() } + + /// Helper function to cast a value to `AnyHashable`. + private func asAnyHashable(_ value: T) -> AnyHashable? { + return value as? AnyHashable + } } From 98279e1dcfe165ae43b674036532986483792fcb Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Tue, 21 Jan 2025 12:15:05 -0500 Subject: [PATCH 3/6] Fix --- .../Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift | 3 ++- .../Tests/ClientTests/SponsoredTileTelemetryTests.swift | 5 ----- .../Tests/ClientTests/XCTestCaseExtensions.swift | 5 +++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift index 363e68b488fd..4e78b503ca1c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift @@ -80,9 +80,10 @@ class UnifiedAdsCallbackTelemetryTests: XCTestCase { // MARK: - Helper functions func createSubject(file: StaticString = #filePath, line: UInt = #line) -> UnifiedAdsCallbackTelemetry { + let sponsoredTileTelemetry = DefaultSponsoredTileTelemetry(gleanWrapper: gleanWrapper) let subject = DefaultUnifiedAdsCallbackTelemetry(networking: networking, logger: logger, - gleanWrapper: gleanWrapper) + sponsoredTileTelemetry: sponsoredTileTelemetry) trackForMemoryLeaks(subject, file: file, line: line) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift index 3fb87db35634..fff3f6627686 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift @@ -77,9 +77,4 @@ class SponsoredTileTelemetryTests: XCTestCase { func clearTest() { TelemetryContextualIdentifier.clearUserDefaults() } - - /// Helper function to cast a value to `AnyHashable`. - private func asAnyHashable(_ value: T) -> AnyHashable? { - return value as? AnyHashable - } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/XCTestCaseExtensions.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/XCTestCaseExtensions.swift index 6417a607e63f..a71d7417e299 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/XCTestCaseExtensions.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/XCTestCaseExtensions.swift @@ -33,4 +33,9 @@ extension XCTestCase { let returnValue = try await asyncMethod() return try XCTUnwrap(returnValue, file: file, line: line) } + + /// Helper function to cast a value to `AnyHashable`. + func asAnyHashable(_ value: T) -> AnyHashable? { + return value as? AnyHashable + } } From 5e129fea2a298d4581733ed0d231fd3f1b0e5bb2 Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Tue, 21 Jan 2025 14:14:14 -0500 Subject: [PATCH 4/6] Remove savedEvent and replace with SavedEvents --- .../Tests/ClientTests/Mocks/MockGleanWrapper.swift | 14 -------------- .../PrivateBrowsingTelemetryTests.swift | 4 ++-- .../TabTray/InactiveTabsTelemetryTests.swift | 10 +++++----- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift index 3b38024d38a9..79eb3545830e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockGleanWrapper.swift @@ -23,7 +23,6 @@ class MockGleanWrapper: GleanWrapper { var cancelTimingCalled = 0 var stopAndAccumulateCalled = 0 var submitPingCalled = 0 - var savedEvent: Any? var savedEvents: [Any]? = [] var savedExtras: Any? var savedPing: Any? @@ -44,68 +43,57 @@ class MockGleanWrapper: GleanWrapper { func recordEvent(for metric: EventMetricType, extras: EventExtras) where ExtraObject: EventExtras { - savedEvent = metric savedExtras = extras savedEvents?.append(metric) recordEventCalled += 1 } func recordEvent(for metric: EventMetricType) where NoExtras: EventExtras { - savedEvent = metric savedEvents?.append(metric) recordEventNoExtraCalled += 1 } func incrementCounter(for metric: CounterMetricType) { - savedEvent = metric savedEvents?.append(metric) incrementCounterCalled += 1 } func recordString(for metric: StringMetricType, value: String) { - savedEvent = metric savedEvents?.append(metric) recordStringCalled += 1 } func recordLabel(for metric: LabeledMetricType, label: String) { - savedEvent = metric savedEvents?.append(metric) recordLabelCalled += 1 } func setBoolean(for metric: BooleanMetricType, value: Bool) { - savedEvent = metric savedEvents?.append(metric) setBooleanCalled += 1 } func recordQuantity(for metric: QuantityMetricType, value: Int64) { - savedEvent = metric savedEvents?.append(metric) recordQuantityCalled += 1 } func recordUrl(for metric: UrlMetricType, value: String) { - savedEvent = metric savedEvents?.append(metric) recordUrlCalled += 1 } func incrementNumerator(for metric: RateMetricType, amount: Int32) { - savedEvent = metric savedEvents?.append(metric) incrementNumeratorCalled += 1 } func incrementDenominator(for metric: RateMetricType, amount: Int32) { - savedEvent = metric savedEvents?.append(metric) incrementDenominatorCalled += 1 } func startTiming(for metric: TimingDistributionMetricType) -> GleanTimerId { - savedEvent = metric savedEvents?.append(metric) startTimingCalled += 1 return savedTimerId @@ -113,14 +101,12 @@ class MockGleanWrapper: GleanWrapper { func cancelTiming(for metric: TimingDistributionMetricType, timerId: GleanTimerId) { - savedEvent = metric savedEvents?.append(metric) cancelTimingCalled += 1 } func stopAndAccumulateTiming(for metric: TimingDistributionMetricType, timerId: GleanTimerId) { - savedEvent = metric savedEvents?.append(metric) stopAndAccumulateCalled += 1 } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift index a3d1757c4579..d566f568de3e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift @@ -21,7 +21,7 @@ final class PrivateBrowsingTelemetryTests: XCTestCase { subject.sendDataClearanceTappedTelemetry(didConfirm: true) let savedEvent = try XCTUnwrap( - gleanWrapper.savedEvent as? EventMetricType + gleanWrapper.savedEvents?[0] as? EventMetricType ) let savedExtras = try XCTUnwrap( gleanWrapper.savedExtras as? GleanMetrics.PrivateBrowsing.DataClearanceIconTappedExtra @@ -41,7 +41,7 @@ final class PrivateBrowsingTelemetryTests: XCTestCase { subject.sendDataClearanceTappedTelemetry(didConfirm: false) let savedEvent = try XCTUnwrap( - gleanWrapper.savedEvent as? EventMetricType + gleanWrapper.savedEvents?[0] as? EventMetricType ) let savedExtras = try XCTUnwrap( gleanWrapper.savedExtras as? GleanMetrics.PrivateBrowsing.DataClearanceIconTappedExtra diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/InactiveTabsTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/InactiveTabsTelemetryTests.swift index 1eac50e0e790..363567caaf58 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/InactiveTabsTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/InactiveTabsTelemetryTests.swift @@ -26,7 +26,7 @@ final class InactiveTabsTelemetryTests: XCTestCase { func testRecordInactiveTab_WhenSectionShown_ThenGleanIsCalled() throws { subject?.sectionShown() - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? CounterMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? CounterMetricType) let expectedMetricType = type(of: GleanMetrics.InactiveTabsTray.inactiveTabShown) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -37,7 +37,7 @@ final class InactiveTabsTelemetryTests: XCTestCase { func testRecordInactiveTab_WhenClosedAllTabs_ThenGleanIsCalled() throws { subject?.closedAllTabs() - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? CounterMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? CounterMetricType) let expectedMetricType = type(of: GleanMetrics.InactiveTabsTray.inactiveTabsCloseAllBtn) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -48,7 +48,7 @@ final class InactiveTabsTelemetryTests: XCTestCase { func testRecordInactiveTab_WhenTabOpened_ThenGleanIsCalled() throws { subject?.tabOpened() - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? CounterMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? CounterMetricType) let expectedMetricType = type(of: GleanMetrics.InactiveTabsTray.openInactiveTab) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -59,7 +59,7 @@ final class InactiveTabsTelemetryTests: XCTestCase { func testRecordInactiveTab_WhenTabSwipedClosed_ThenGleanIsCalled() throws { subject?.tabSwipedToClose() - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? CounterMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? CounterMetricType) let expectedMetricType = type(of: GleanMetrics.InactiveTabsTray.inactiveTabSwipeClose) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -71,7 +71,7 @@ final class InactiveTabsTelemetryTests: XCTestCase { subject?.sectionToggled(hasExpanded: true) let savedMetric = try XCTUnwrap( - gleanWrapper.savedEvent as? EventMetricType + gleanWrapper.savedEvents?[0] as? EventMetricType ) let expectedMetricType = type(of: GleanMetrics.InactiveTabsTray.toggleInactiveTabTray) let resultMetricType = type(of: savedMetric) From 9b31849dfd4ace5f8e9eaea38815eea523842884 Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Tue, 21 Jan 2025 14:26:36 -0500 Subject: [PATCH 5/6] Add event check on array --- .../UnifiedAdsCallbackTelemetryTests.swift | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift index 4e78b503ca1c..cde8026e5b40 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/UnifiedAdsCallbackTelemetryTests.swift @@ -43,7 +43,7 @@ class UnifiedAdsCallbackTelemetryTests: XCTestCase { XCTAssertEqual(logger.savedMessage, "The unified ads telemetry call failed: \(tile.clickURL)") } - func testLegacyImpressionTelemetry() { + func testLegacyImpressionTelemetry() throws { let subject = createSubject() subject.sendImpressionTelemetry(tile: tile, position: 1) @@ -58,9 +58,26 @@ class UnifiedAdsCallbackTelemetryTests: XCTestCase { } XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) XCTAssertEqual(gleanWrapper.savedEvents?.count, 2) + + // Ensuring we call the right metrics type + let firstSavedMetric = try XCTUnwrap( + gleanWrapper.savedEvents?[0] as? EventMetricType + ) + let expectedFirstMetricType = type(of: GleanMetrics.TopSites.contileImpression) + let firstResultMetricType = type(of: firstSavedMetric) + let debugMessage = TelemetryDebugMessage(expectedMetric: expectedFirstMetricType, + resultMetric: firstResultMetricType) + XCTAssert(firstResultMetricType == expectedFirstMetricType, debugMessage.text) + + let secondSavedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[1] as? StringMetricType) + let expectedSecondMetricType = type(of: GleanMetrics.TopSites.contileAdvertiser) + let secondResultMetricType = type(of: secondSavedMetric) + let secondDebugMessage = TelemetryDebugMessage(expectedMetric: expectedSecondMetricType, + resultMetric: secondResultMetricType) + XCTAssert(secondResultMetricType == expectedSecondMetricType, secondDebugMessage.text) } - func testLegacyClickTelemetry() { + func testLegacyClickTelemetry() throws { let subject = createSubject() subject.sendClickTelemetry(tile: tile, position: 1) @@ -75,6 +92,23 @@ class UnifiedAdsCallbackTelemetryTests: XCTestCase { } XCTAssertEqual(asAnyHashable(savedPing), asAnyHashable(GleanMetrics.Pings.shared.topsitesImpression)) XCTAssertEqual(gleanWrapper.savedEvents?.count, 2) + + // Ensuring we call the right metrics type + let firstSavedMetric = try XCTUnwrap( + gleanWrapper.savedEvents?[0] as? EventMetricType + ) + let expectedFirstMetricType = type(of: GleanMetrics.TopSites.contileClick) + let firstResultMetricType = type(of: firstSavedMetric) + let debugMessage = TelemetryDebugMessage(expectedMetric: expectedFirstMetricType, + resultMetric: firstResultMetricType) + XCTAssert(firstResultMetricType == expectedFirstMetricType, debugMessage.text) + + let secondSavedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[1] as? StringMetricType) + let expectedSecondMetricType = type(of: GleanMetrics.TopSites.contileAdvertiser) + let secondResultMetricType = type(of: secondSavedMetric) + let secondDebugMessage = TelemetryDebugMessage(expectedMetric: expectedSecondMetricType, + resultMetric: secondResultMetricType) + XCTAssert(secondResultMetricType == expectedSecondMetricType, secondDebugMessage.text) } // MARK: - Helper functions From 6ffa440ca969cde7082c13c87a0bce5c3c4e123e Mon Sep 17 00:00:00 2001 From: Laurie Marceau Date: Tue, 21 Jan 2025 14:36:14 -0500 Subject: [PATCH 6/6] Adjust for latest main --- .../Library/Bookmarks/BookmarksTelemetryTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarksTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarksTelemetryTests.swift index 90699fcd6704..a3aeb9389062 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarksTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarksTelemetryTests.swift @@ -26,7 +26,7 @@ final class BookmarksTelemetryTests: XCTestCase { func testRecordBookmark_WhenAddedBookmark_ThenGleanIsCalled() throws { subject?.addBookmark(eventLabel: .bookmarksPanel) - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? LabeledMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? LabeledMetricType) let expectedMetricType = type(of: GleanMetrics.Bookmarks.add) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -37,7 +37,7 @@ final class BookmarksTelemetryTests: XCTestCase { func testRecordBookmark_WhenDeletedBookmark_ThenGleanIsCalled() throws { subject?.deleteBookmark(eventLabel: .bookmarksPanel) - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? LabeledMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? LabeledMetricType) let expectedMetricType = type(of: GleanMetrics.Bookmarks.delete) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -48,7 +48,7 @@ final class BookmarksTelemetryTests: XCTestCase { func testRecordBookmark_WhenOpenedSite_ThenGleanIsCalled() throws { subject?.openBookmarksSite(eventLabel: BookmarksTelemetry.EventLabel.bookmarksPanel) - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? LabeledMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? LabeledMetricType) let expectedMetricType = type(of: GleanMetrics.Bookmarks.open) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -59,7 +59,7 @@ final class BookmarksTelemetryTests: XCTestCase { func testRecordBookmark_WhenEditedSite_ThenGleanIsCalled() throws { subject?.editBookmark(eventLabel: .bookmarksPanel) - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? LabeledMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? LabeledMetricType) let expectedMetricType = type(of: GleanMetrics.Bookmarks.edit) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType) @@ -70,7 +70,7 @@ final class BookmarksTelemetryTests: XCTestCase { func testRecordBookmark_WhenAddedFolder_ThenGleanIsCalled() throws { subject?.addBookmarkFolder() - let savedMetric = try XCTUnwrap(gleanWrapper.savedEvent as? EventMetricType) + let savedMetric = try XCTUnwrap(gleanWrapper.savedEvents?[0] as? EventMetricType) let expectedMetricType = type(of: GleanMetrics.Bookmarks.folderAdd) let resultMetricType = type(of: savedMetric) let debugMessage = TelemetryDebugMessage(expectedMetric: expectedMetricType, resultMetric: resultMetricType)