Skip to content

Commit

Permalink
Pass through ping schedule for Swift and Kotlin
Browse files Browse the repository at this point in the history
  • Loading branch information
badboy committed Oct 1, 2024
1 parent 6c76bf6 commit 6ac987d
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

[Full changelog](https://github.com/mozilla/glean/compare/v61.1.0...main)

* Kotlin
* Accept a ping schedule map on initialize ([#2967](https://github.com/mozilla/glean/pull/2967))
* Swift
* Accept a ping schedule map on initialize ([#2967](https://github.com/mozilla/glean/pull/2967))

# v61.1.0 (2024-09-24)

[Full changelog](https://github.com/mozilla/glean/compare/v61.0.0...v61.1.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ open class GleanInternalAPI internal constructor() {
enableEventTimestamps = configuration.enableEventTimestamps,
experimentationId = configuration.experimentationId,
enableInternalPings = configuration.enableInternalPings,
pingSchedule = emptyMap(),
pingSchedule = configuration.pingSchedule,
pingLifetimeThreshold = configuration.pingLifetimeThreshold.toULong(),
pingLifetimeMaxTime = configuration.pingLifetimeMaxTime.toULong(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import mozilla.telemetry.glean.net.PingUploader
* @property delayPingLifetimeIo Whether Glean should delay persistence of data from metrics with ping lifetime.
* @property pingLifetimeThreshold Write count threshold when to auto-flush. `0` disables it.
* @property pingLifetimeMaxTime After what time to auto-flush (in milliseconds). 0 disables it.
* @property pingSchedule A ping schedule map.
* Maps a ping name to a list of pings to schedule along with it.
* Only used if the ping's own ping schedule list is empty.
*/
data class Configuration @JvmOverloads constructor(
val serverEndpoint: String = DEFAULT_TELEMETRY_ENDPOINT,
Expand All @@ -44,6 +47,7 @@ data class Configuration @JvmOverloads constructor(
val delayPingLifetimeIo: Boolean = true,
val pingLifetimeThreshold: Int = 1000,
val pingLifetimeMaxTime: Int = 0,
val pingSchedule: Map<String, List<String>> = emptyMap(),
) {
companion object {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.telemetry.glean.scheduler

import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.telemetry.glean.Glean
import mozilla.telemetry.glean.delayMetricsPing
import mozilla.telemetry.glean.getContext
import mozilla.telemetry.glean.private.NoReasonCodes
import mozilla.telemetry.glean.private.PingType
import mozilla.telemetry.glean.resetGlean
import mozilla.telemetry.glean.testing.GleanTestRule
import mozilla.telemetry.glean.triggerWorkManager
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit

/**
* Testing behavior of custom pings.
*
* We already rely on the Rust side to test custom pings,
* but this enables us to test the upload mechanism specifically.
*
* Even if this seemingly duplicates some of the testing, this should be kept around.
*/
@RunWith(AndroidJUnit4::class)
class RidealongPingTest {
private val context = getContext()
private lateinit var server: MockWebServer

@get:Rule
val gleanRule = GleanTestRule(context)

@Before
fun setup() {
server = getMockWebServer()
}

@After
fun teardown() {
server.shutdown()
}

@Test
fun `sends a ride-along custom ping on baseline schedule`() {
delayMetricsPing(context)
resetGlean(
context,
Glean.configuration.copy(
serverEndpoint = "http://" + server.hostName + ":" + server.port,
pingSchedule = mapOf("baseline" to listOf("custom-ping")),
),
clearStores = true,
uploadEnabled = true,
)

// Define a new custom ping inline.
PingType<NoReasonCodes>(
name = "custom-ping",
includeClientId = true,
sendIfEmpty = true,
preciseTimestamps = true,
includeInfoSections = true,
enabled = true,
schedulesPings = emptyList(),
reasonCodes = emptyList(),
)

Glean.handleBackgroundEvent()
// Trigger it to upload
triggerWorkManager(context)

var request = server.takeRequest(2L, TimeUnit.SECONDS)!!
var docType = request.path!!.split("/")[3]
assertEquals("baseline", docType)

request = server.takeRequest(2L, TimeUnit.SECONDS)!!
docType = request.path!!.split("/")[3]
assertEquals("custom-ping", docType)
}
}
4 changes: 4 additions & 0 deletions glean-core/ios/Glean.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
CD0CADA427E216810015A997 /* glean.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD08C8527E21104007C8400 /* glean.swift */; };
CD0F7CC026F0F27900EDA6A4 /* UrlMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0F7CBF26F0F27900EDA6A4 /* UrlMetric.swift */; };
CD0F7CC226F0F28900EDA6A4 /* UrlMetricTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0F7CC126F0F28900EDA6A4 /* UrlMetricTests.swift */; };
CD3682F32CAC110300B02F04 /* RidealongPingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3682F22CAC10FE00B02F04 /* RidealongPingTests.swift */; };
CD38786D271DCCC700C097D8 /* libglean_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD38786C271DCCC700C097D8 /* libglean_ffi.a */; };
CD70CF932850D69500FC2014 /* Gzip in Frameworks */ = {isa = PBXBuildFile; productRef = CD70CF922850D69500FC2014 /* Gzip */; };
CD70CF982850D77200FC2014 /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = CD70CF972850D77200FC2014 /* OHHTTPStubs */; };
Expand Down Expand Up @@ -144,6 +145,7 @@
CD07A4DD2BC808AE007A0F1C /* ObjectMetric.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectMetric.swift; sourceTree = "<group>"; };
CD0F7CBF26F0F27900EDA6A4 /* UrlMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlMetric.swift; sourceTree = "<group>"; };
CD0F7CC126F0F28900EDA6A4 /* UrlMetricTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlMetricTests.swift; sourceTree = "<group>"; };
CD3682F22CAC10FE00B02F04 /* RidealongPingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RidealongPingTests.swift; sourceTree = "<group>"; };
CD387868271D9CD100C097D8 /* glean.udl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = glean.udl; path = ../../src/glean.udl; sourceTree = "<group>"; };
CD38786C271DCCC700C097D8 /* libglean_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libglean_ffi.a; path = ../../target/libglean_ffi.a; sourceTree = "<group>"; };
CD81DCF9282A8F9A00347965 /* RateMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateMetric.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -372,6 +374,7 @@
isa = PBXGroup;
children = (
60691AEA28DD0BF200BDF31A /* BaselinePingTests.swift */,
CD3682F22CAC10FE00B02F04 /* RidealongPingTests.swift */,
BF80AA5E2399305200A8B172 /* DeletionRequestPingTests.swift */,
BF80AA5A2399301200A8B172 /* HttpPingUploaderTests.swift */,
);
Expand Down Expand Up @@ -637,6 +640,7 @@
CD0F7CC226F0F28900EDA6A4 /* UrlMetricTests.swift in Sources */,
BFCBD6AB246D55CC0032096D /* TestUtils.swift in Sources */,
AC06529E26E034BF00D92D5E /* QuantityMetricTypeTest.swift in Sources */,
CD3682F32CAC110300B02F04 /* RidealongPingTests.swift in Sources */,
1F58921223C923C4007D2D80 /* MetricsPingSchedulerTests.swift in Sources */,
CD9DA7852BC809BE00E18F31 /* ObjectMetricTests.swift in Sources */,
1FD4527723395EEB00F4C7E8 /* UuidMetricTests.swift in Sources */,
Expand Down
8 changes: 7 additions & 1 deletion glean-core/ios/Glean/Config/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public struct Configuration {
let enableInternalPings: Bool
let pingLifetimeThreshold: Int
let pingLifetimeMaxTime: Int
let pingSchedule: [String: [String]]

struct Constants {
static let defaultTelemetryEndpoint = "https://incoming.telemetry.mozilla.org"
Expand All @@ -36,6 +37,9 @@ public struct Configuration {
/// * enableInternalPings Whether to enable internal pings.
/// * pingLifetimeThreshold Write count threshold when to auto-flush. `0` disables it.
/// * pingLifetimeMaxTime After what time to auto-flush (in milliseconds). 0 disables it.
/// * pingSchedule A ping schedule map.
/// Maps a ping name to a list of pings to schedule along with it.
/// Only used if the ping's own ping schedule list is empty.
public init(
maxEvents: Int32? = nil,
channel: String? = nil,
Expand All @@ -46,7 +50,8 @@ public struct Configuration {
experimentationId: String? = nil,
enableInternalPings: Bool = true,
pingLifetimeThreshold: Int = 0,
pingLifetimeMaxTime: Int = 0
pingLifetimeMaxTime: Int = 0,
pingSchedule: [String: [String]] = [:]
) {
self.serverEndpoint = serverEndpoint ?? Constants.defaultTelemetryEndpoint
self.maxEvents = maxEvents
Expand All @@ -58,5 +63,6 @@ public struct Configuration {
self.enableInternalPings = enableInternalPings
self.pingLifetimeThreshold = pingLifetimeThreshold
self.pingLifetimeMaxTime = pingLifetimeMaxTime
self.pingSchedule = pingSchedule
}
}
2 changes: 1 addition & 1 deletion glean-core/ios/Glean/Glean.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public class Glean {
enableEventTimestamps: configuration.enableEventTimestamps,
experimentationId: configuration.experimentationId,
enableInternalPings: configuration.enableInternalPings,
pingSchedule: [:],
pingSchedule: configuration.pingSchedule,
pingLifetimeThreshold: UInt64(configuration.pingLifetimeThreshold),
pingLifetimeMaxTime: UInt64(configuration.pingLifetimeMaxTime)
)
Expand Down
62 changes: 62 additions & 0 deletions glean-core/ios/GleanTests/RidealongPingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@testable import Glean
import OHHTTPStubs
import OHHTTPStubsSwift
import XCTest

final class RidealongPingTests: XCTestCase {
var expectation: XCTestExpectation?

override func tearDown() {
Glean.shared.testDestroyGleanHandle()
expectation = nil
tearDownStubs()
}

func testSendRidealongPingWithBaseline() {

let configuration = Configuration(pingSchedule: ["baseline": ["ridealong"]])
resetGleanDiscardingInitialPings(testCase: self, tag: "RidealongPingTests", configuration: configuration)

// Register ping _after_ Glean has been initialized to avoid this being sent multiple times.
_ = Ping<NoReasonCodes>(
name: "ridealong",
includeClientId: true,
sendIfEmpty: true,
preciseTimestamps: true,
includeInfoSections: true,
enabled: true,
schedulesPings: [],
reasonCodes: []
)

// We receive a baseline ping, and a ridealong ping.
// The order might vary.
var pingsToReceive = ["baseline", "ridealong"]

stubServerReceive { pingType, _ in
XCTAssertTrue(!pingsToReceive.isEmpty, "No more pings expected")
XCTAssertTrue(pingsToReceive.contains(pingType), "Expected ping types: \(pingsToReceive), got \(pingType)")
pingsToReceive.removeAll(where: { $0 == pingType })

if pingsToReceive.isEmpty {
DispatchQueue.main.async {
// let the response get processed before we mark the expectation fulfilled
self.expectation?.fulfill()
}
}
}

// Set up the expectation that will be fulfilled by the stub above
expectation = expectation(description: "Pings Received")

Glean.shared.submitPingByName("baseline")

waitForExpectations(timeout: 5.0) { error in
XCTAssertNil(error, "Test timed out waiting for upload: \(error!)")
}
}
}
7 changes: 5 additions & 2 deletions glean-core/ios/GleanTests/TestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ func stubServerReceive(callback: @escaping (String, [String: Any]?) -> Void) {
///
/// This also prevents outgoing network requests during unit tests while
/// still allowing us to use the default telemetry endpoint.
func resetGleanDiscardingInitialPings(testCase: XCTestCase, tag: String, clearStores: Bool = true) {
func resetGleanDiscardingInitialPings(testCase: XCTestCase,
tag: String,
clearStores: Bool = true,
configuration: Configuration = Configuration()) {
let expectation = testCase.expectation(description: "\(tag): Ping Received")

// We are using OHHTTPStubs combined with an XCTestExpectation in order to capture
Expand Down Expand Up @@ -85,7 +88,7 @@ func resetGleanDiscardingInitialPings(testCase: XCTestCase, tag: String, clearSt
let mps = MetricsPingScheduler(true)
mps.updateSentDate(Date())

Glean.shared.resetGlean(clearStores: clearStores)
Glean.shared.resetGlean(configuration: configuration, clearStores: clearStores)

testCase.waitForExpectations(timeout: 5.0) { error in
XCTAssertNil(error, "Test timed out waiting for upload: \(error!)")
Expand Down

0 comments on commit 6ac987d

Please sign in to comment.