Update to Glean v50 and adopt new APIs

This now uses Glean directly through the org.mozilla.telemetry:glean
package, instead of its Android Components release.
This is required to avoid a dependency cycle:
* The migration component in a-c requires Glean and a-s
* a-s requires Glean from a-c

But there can only be one Glean in the build.
So in order to not need to release individual components manually we
switch a-s to directly consume Glean.
Then we can update a-s and get that into a-c together with a Glean
update there.
This commit is contained in:
Jan-Erik Rediger 2022-05-13 14:38:12 +02:00
Родитель 2689788cec
Коммит 402afc0775
16 изменённых файлов: 84 добавлений и 82 удалений

Просмотреть файл

@ -8,6 +8,7 @@ buildscript {
jna_version = '5.8.0'
android_gradle_plugin_version = '4.2.2'
android_components_version = '94.0.0'
glean_version = '50.0.1'
androidx_annotation_version = '1.2.0'
androidx_core_version = '1.3.2'
androidx_test_core_version = '1.4.0'
@ -75,7 +76,7 @@ buildscript {
// Since the Glean version depends on the Android components version,
// it is very important to use a modern version of Glean and, ideally,
// let this come from the embedding product itself.
classpath "org.mozilla.components:tooling-glean-gradle:$android_components_version"
classpath "org.mozilla.telemetry:glean-gradle-plugin:$glean_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -124,7 +125,7 @@ ext.libsGitSha = "git --git-dir=${rootProject.rootDir}/.git diff --name-only mai
// downloaded libs.
// XX: Since we switched the libs directory to be `darwin-x86-64` and `darwin-aarch64` in https://github.com/mozilla/application-services/pull/4792
// we have mimatch with the libs built on CI, since they have the libraries under `darwin`.
// we have mimatch with the libs built on CI, since they have the libraries under `darwin`.
// Temporarily, we are disabling downloaded libs
// we should enable them once our CI builds libs the same way as our local environments
// see: https://github.com/mozilla/application-services/issues/4828

Просмотреть файл

@ -7,17 +7,18 @@ apply from: "$rootDir/publish.gradle"
// Needs to happen before `dependencies` in order for the variables
// exposed by the plugin to be available for this project.
ext.gleanNamespace = "mozilla.telemetry.glean"
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
dependencies {
// Part of the public API.
api project(':sync15')
implementation "org.mozilla.components:service-glean:$android_components_version"
implementation "org.mozilla.telemetry:glean:$glean_version"
testImplementation "androidx.test:core-ktx:$androidx_test_core_version"
testImplementation "androidx.work:work-testing:$androidx_work_testing_version"
testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:$project.ext.glean_version"
testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:$glean_version"
testImplementation project(":syncmanager")
}

Просмотреть файл

@ -16,8 +16,8 @@ import org.mozilla.appservices.logins.GleanMetrics.LoginsStore as LoginsStoreMet
* instantiate anything from these classes, and it's on us to fix any bustage
* on version updates.
*/
import mozilla.components.service.glean.private.CounterMetricType
import mozilla.components.service.glean.private.LabeledMetricType
import mozilla.telemetry.glean.private.CounterMetricType
import mozilla.telemetry.glean.private.LabeledMetricType
/**
* An artifact of the uniffi conversion - a thin-ish wrapper around a

Просмотреть файл

@ -6,7 +6,7 @@ package mozilla.appservices.logins
import androidx.test.core.app.ApplicationProvider
import mozilla.appservices.Megazord
import mozilla.appservices.syncmanager.SyncManager
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.telemetry.glean.testing.GleanTestRule
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@ -89,8 +89,8 @@ class DatabaseLoginsStorageTest {
fun testMetricsGathering() {
val store = createTestStore()
assert(!LoginsStoreMetrics.writeQueryCount.testHasValue())
assert(!LoginsStoreMetrics.writeQueryErrorCount["invalid_record"].testHasValue())
assertNull(LoginsStoreMetrics.writeQueryCount.testGetValue())
assertNull(LoginsStoreMetrics.writeQueryErrorCount["invalid_record"].testGetValue())
val login = store.add(
LoginEntry(
@ -110,7 +110,7 @@ class DatabaseLoginsStorageTest {
)
assertEquals(LoginsStoreMetrics.writeQueryCount.testGetValue(), 1)
assert(!LoginsStoreMetrics.writeQueryErrorCount["invalid_record"].testHasValue())
assertNull(LoginsStoreMetrics.writeQueryErrorCount["invalid_record"].testGetValue())
// N.B. this is invalid due to `formActionOrigin` being an invalid url.
val invalid = LoginEntry(
@ -137,14 +137,14 @@ class DatabaseLoginsStorageTest {
assertEquals(LoginsStoreMetrics.writeQueryCount.testGetValue(), 2)
assertEquals(LoginsStoreMetrics.writeQueryErrorCount["invalid_record"].testGetValue(), 1)
assert(!LoginsStoreMetrics.readQueryCount.testHasValue())
assert(!LoginsStoreMetrics.readQueryErrorCount["storage_error"].testHasValue())
assertNull(LoginsStoreMetrics.readQueryCount.testGetValue())
assertNull(LoginsStoreMetrics.readQueryErrorCount["storage_error"].testGetValue())
val record = store.get(login.record.id)!!
assertEquals(record.fields.origin, "https://www.example.com")
assertEquals(LoginsStoreMetrics.readQueryCount.testGetValue(), 1)
assert(!LoginsStoreMetrics.readQueryErrorCount["storage_error"].testHasValue())
assertNull(LoginsStoreMetrics.readQueryErrorCount["storage_error"].testGetValue())
finishAndClose(store)
}
@ -231,7 +231,7 @@ class DatabaseLoginsStorageTest {
assertEquals(3, LoginsStoreMetrics.migrationNumProcessed.testGetValue())
assertEquals(2, LoginsStoreMetrics.migrationNumFailed.testGetValue())
assertEquals(1, LoginsStoreMetrics.migrationNumSucceeded.testGetValue())
assertEquals(53, LoginsStoreMetrics.migrationTotalDuration.testGetValue())
assertEquals(53L, LoginsStoreMetrics.migrationTotalDuration.testGetValue())
// Note the truncation of the first error string.
assertEquals(listOf("Invalid login: Login has illegal field: Origin is ", "Invalid login: Origin is empty"), LoginsStoreMetrics.migrationErrors.testGetValue())
}

Просмотреть файл

@ -165,7 +165,7 @@ func recordMigrationMetrics(jsonString: String) {
GleanMetrics.LoginsStoreMigration.numFailed.add(failed)
}
if let duration = metrics["total_duration"] as? UInt64 {
if let duration = metrics["total_duration"] as? Int64 {
GleanMetrics.LoginsStoreMigration.totalDuration.setRawNanos(duration * 1_000_000)
}

Просмотреть файл

@ -6,6 +6,7 @@ apply from: "$rootDir/build-scripts/component-common.gradle"
apply from: "$rootDir/publish.gradle"
ext.gleanYamlFiles = ["${project.projectDir}/../metrics.yaml"]
ext.gleanNamespace = "mozilla.telemetry.glean"
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
dependencies {
@ -13,7 +14,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "org.mozilla.components:service-glean:$android_components_version"
implementation "org.mozilla.telemetry:glean:$glean_version"
implementation "androidx.core:core-ktx:$androidx_core_version"
testImplementation "androidx.test:core-ktx:$androidx_test_core_version"

Просмотреть файл

@ -18,7 +18,7 @@ import androidx.annotation.WorkerThread
import androidx.core.content.pm.PackageInfoCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mozilla.components.service.glean.Glean
import mozilla.telemetry.glean.Glean
import org.json.JSONObject
import org.mozilla.experiments.nimbus.GleanMetrics.NimbusEvents
import org.mozilla.experiments.nimbus.internal.AppContext

Просмотреть файл

@ -9,13 +9,12 @@ import android.util.Log
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asCoroutineDispatcher
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Response
import mozilla.components.service.glean.BuildInfo
import mozilla.components.service.glean.Glean
import mozilla.components.service.glean.config.Configuration
import mozilla.components.service.glean.net.ConceptFetchHttpUploader
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.telemetry.glean.BuildInfo
import mozilla.telemetry.glean.Glean
import mozilla.telemetry.glean.config.Configuration
import mozilla.telemetry.glean.testing.GleanTestRule
import mozilla.telemetry.glean.net.PingUploader
import mozilla.telemetry.glean.net.HttpStatus
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@ -32,6 +31,7 @@ import org.mozilla.experiments.nimbus.internal.EnrollmentChangeEvent
import org.mozilla.experiments.nimbus.internal.EnrollmentChangeEventType
import org.robolectric.RobolectricTestRunner
import java.util.concurrent.Executors
import java.util.Calendar
@RunWith(RobolectricTestRunner::class)
class NimbusTests {
@ -70,19 +70,19 @@ class NimbusTests {
@Before
fun setupGlean() {
val buildInfo = BuildInfo(versionCode = "0.0.1", versionName = "0.0.1")
val buildInfo = BuildInfo(versionCode = "0.0.1", versionName = "0.0.1", buildDate = Calendar.getInstance())
// Glean needs to be initialized for the experiments API to accept enrollment events, so we
// init it with a mock client so we don't upload anything.
val mockClient: Client = mock()
`when`(mockClient.fetch(any())).thenReturn(
Response("URL", 200, mock(), mock())
val mockClient: PingUploader = mock()
`when`(mockClient.upload(any(), any(), any())).thenReturn(
HttpStatus(200)
)
Glean.initialize(
context,
true,
Configuration(
httpClient = ConceptFetchHttpUploader(lazy { mockClient })
httpClient = mockClient
),
buildInfo
)
@ -142,7 +142,7 @@ class NimbusTests {
// Enrollment
assertTrue("Event must have a value", NimbusEvents.enrollment.testHasValue())
val enrollmentEvents = NimbusEvents.enrollment.testGetValue()
val enrollmentEvents = NimbusEvents.enrollment.testGetValue()!!
assertEquals("Event count must match", enrollmentEvents.count(), 1)
val enrollmentEventExtras = enrollmentEvents.first().extra!!
assertEquals(
@ -159,7 +159,7 @@ class NimbusTests {
// Unenrollment
assertTrue("Event must have a value", NimbusEvents.unenrollment.testHasValue())
val unenrollmentEvents = NimbusEvents.unenrollment.testGetValue()
val unenrollmentEvents = NimbusEvents.unenrollment.testGetValue()!!
assertEquals("Event count must match", unenrollmentEvents.count(), 1)
val unenrollmentEventExtras = unenrollmentEvents.first().extra!!
assertEquals(
@ -180,7 +180,7 @@ class NimbusTests {
// Disqualification
assertTrue("Event must have a value", NimbusEvents.disqualification.testHasValue())
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()!!
assertEquals("Event count must match", disqualificationEvents.count(), 1)
val disqualificationEventExtras = disqualificationEvents.first().extra!!
assertEquals(
@ -217,7 +217,7 @@ class NimbusTests {
// Use the Glean test API to check that the valid event is present
assertTrue("Event must have a value", NimbusEvents.exposure.testHasValue())
val enrollmentEvents = NimbusEvents.exposure.testGetValue()
val enrollmentEvents = NimbusEvents.exposure.testGetValue()!!
assertEquals("Event count must match", enrollmentEvents.count(), 1)
val enrollmentEventExtras = enrollmentEvents.first().extra!!
assertEquals(
@ -238,7 +238,7 @@ class NimbusTests {
// Verify the invalid event was ignored by checking again that the valid event is still the only
// event, and that it hasn't changed any of its extra properties.
assertTrue("Event must have a value", NimbusEvents.exposure.testHasValue())
val enrollmentEventsTryTwo = NimbusEvents.exposure.testGetValue()
val enrollmentEventsTryTwo = NimbusEvents.exposure.testGetValue()!!
assertEquals("Event count must match", enrollmentEventsTryTwo.count(), 1)
val enrollmentEventExtrasTryTwo = enrollmentEventsTryTwo.first().extra!!
assertEquals(
@ -274,7 +274,7 @@ class NimbusTests {
// Use the Glean test API to check that the valid event is present
assertTrue("Event must have a value", NimbusEvents.disqualification.testHasValue())
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()!!
assertEquals("Event count must match", disqualificationEvents.count(), 1)
val enrollmentEventExtras = disqualificationEvents.first().extra!!
assertEquals(
@ -306,7 +306,7 @@ class NimbusTests {
// Use the Glean test API to check that the valid event is present
assertTrue("Event must have a value", NimbusEvents.disqualification.testHasValue())
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()
val disqualificationEvents = NimbusEvents.disqualification.testGetValue()!!
assertEquals("Event count must match", disqualificationEvents.count(), 1)
val enrollmentEventExtras = disqualificationEvents.first().extra!!
assertEquals(

Просмотреть файл

@ -94,7 +94,7 @@ extension Nimbus: FeaturesInterface {
internal func recordExperimentTelemetry(_ experiments: [EnrolledExperiment]) {
for experiment in experiments {
Glean.shared.setExperimentActive(
experimentId: experiment.slug,
experiment.slug,
branch: experiment.branchSlug,
extra: ["enrollmentId": experiment.enrollmentId]
)

Просмотреть файл

@ -7,17 +7,18 @@ apply from: "$rootDir/build-scripts/component-common.gradle"
apply from: "$rootDir/build-scripts/protobuf-common.gradle"
apply from: "$rootDir/publish.gradle"
ext.gleanNamespace = "mozilla.telemetry.glean"
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
dependencies {
// Part of the public API.
api project(':sync15')
implementation "org.mozilla.components:service-glean:$android_components_version"
implementation "org.mozilla.telemetry:glean:$glean_version"
testImplementation "androidx.test:core-ktx:$androidx_test_core_version"
testImplementation "androidx.work:work-testing:$androidx_work_testing_version"
testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:$project.ext.glean_version"
testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:$glean_version"
testImplementation project(':syncmanager')
}

Просмотреть файл

@ -29,8 +29,8 @@ import mozilla.appservices.places.uniffi.InsertableBookmarkItem
import mozilla.appservices.places.uniffi.InsertableBookmarkSeparator
import mozilla.appservices.places.uniffi.BookmarkUpdateInfo
import mozilla.appservices.sync15.SyncTelemetryPing
import mozilla.components.service.glean.private.CounterMetricType
import mozilla.components.service.glean.private.LabeledMetricType
import mozilla.telemetry.glean.private.CounterMetricType
import mozilla.telemetry.glean.private.LabeledMetricType
import org.json.JSONObject
import java.lang.ref.WeakReference
import org.mozilla.appservices.places.GleanMetrics.PlacesManager as PlacesManagerMetrics

Просмотреть файл

@ -13,7 +13,7 @@ import mozilla.appservices.places.uniffi.FrecencyThresholdOption
import mozilla.appservices.syncmanager.SyncManager
import mozilla.appservices.places.uniffi.PlacesException
import mozilla.appservices.places.uniffi.BookmarkItem
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.telemetry.glean.testing.GleanTestRule
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
@ -379,15 +379,15 @@ class PlacesConnectionTest {
@Test
fun testHistoryMetricsGathering() {
assert(!PlacesManagerMetrics.writeQueryCount.testHasValue())
assert(!PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testHasValue())
assertNull(PlacesManagerMetrics.writeQueryCount.testGetValue())
assertNull(PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testGetValue())
db.noteObservation(VisitObservation(url = "https://www.example.com/2a", visitType = VisitTransition.REDIRECT_TEMPORARY, at = 130000))
db.noteObservation(VisitObservation(url = "https://www.example.com/2b", visitType = VisitTransition.LINK, at = 150000))
db.noteObservation(VisitObservation(url = "https://www.example.com/3", visitType = VisitTransition.LINK, at = 200000))
assertEquals(3, PlacesManagerMetrics.writeQueryCount.testGetValue())
assert(!PlacesManagerMetrics.writeQueryErrorCount["__other__"].testHasValue())
assertNull(PlacesManagerMetrics.writeQueryErrorCount["__other__"].testGetValue())
try {
db.noteObservation(VisitObservation(url = "4", visitType = VisitTransition.REDIRECT_TEMPORARY, at = 160000))
@ -397,16 +397,15 @@ class PlacesConnectionTest {
}
assertEquals(4, PlacesManagerMetrics.writeQueryCount.testGetValue())
assert(PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testHasValue())
assertEquals(1, PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testGetValue())
assert(!PlacesManagerMetrics.readQueryCount.testHasValue())
assert(!PlacesManagerMetrics.readQueryErrorCount["__other__"].testHasValue())
assertNull(PlacesManagerMetrics.readQueryCount.testGetValue())
assertNull(PlacesManagerMetrics.readQueryErrorCount["__other__"].testGetValue())
db.getVisitInfos(125000, 225000)
assertEquals(1, PlacesManagerMetrics.readQueryCount.testGetValue())
assert(!PlacesManagerMetrics.readQueryErrorCount["__other__"].testHasValue())
assertNull(PlacesManagerMetrics.readQueryErrorCount["__other__"].testGetValue())
db.deleteVisit("https://www.example.com/2a", 130000)
@ -414,13 +413,13 @@ class PlacesConnectionTest {
assertEquals(2, infos.size)
assertEquals(5, PlacesManagerMetrics.writeQueryCount.testGetValue())
assert(!PlacesManagerMetrics.writeQueryErrorCount["_other_"].testHasValue())
assertNull(PlacesManagerMetrics.writeQueryErrorCount["_other_"].testGetValue())
}
@Test
fun testBookmarksMetricsGathering() {
assert(!PlacesManagerMetrics.writeQueryCount.testHasValue())
assert(!PlacesManagerMetrics.writeQueryErrorCount["unknown_bookmark_item"].testHasValue())
assertNull(PlacesManagerMetrics.writeQueryCount.testGetValue())
assertNull(PlacesManagerMetrics.writeQueryErrorCount["unknown_bookmark_item"].testGetValue())
val itemGUID = db.createBookmarkItem(
parentGUID = BookmarkRoot.Unfiled.id,
@ -429,7 +428,7 @@ class PlacesConnectionTest {
)
assertEquals(1, PlacesManagerMetrics.writeQueryCount.testGetValue())
assert(!PlacesManagerMetrics.writeQueryErrorCount["unknown_bookmark_item"].testHasValue())
assertNull(PlacesManagerMetrics.writeQueryErrorCount["unknown_bookmark_item"].testGetValue())
try {
db.createBookmarkItem(
@ -443,16 +442,15 @@ class PlacesConnectionTest {
}
assertEquals(2, PlacesManagerMetrics.writeQueryCount.testGetValue())
assert(PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testHasValue())
assertEquals(1, PlacesManagerMetrics.writeQueryErrorCount["url_parse_failed"].testGetValue())
assert(!PlacesManagerMetrics.readQueryCount.testHasValue())
assert(!PlacesManagerMetrics.readQueryErrorCount["__other__"].testHasValue())
assertNull(PlacesManagerMetrics.readQueryCount.testGetValue())
assertNull(PlacesManagerMetrics.readQueryErrorCount["__other__"].testGetValue())
db.getBookmark(itemGUID)
assertEquals(1, PlacesManagerMetrics.readQueryCount.testGetValue())
assert(!PlacesManagerMetrics.readQueryErrorCount["__other__"].testHasValue())
assertNull(PlacesManagerMetrics.readQueryErrorCount["__other__"].testGetValue())
val folderGUID = db.createFolder(
parentGUID = BookmarkRoot.Unfiled.id,

Просмотреть файл

@ -915,7 +915,7 @@
repositoryURL = "https://github.com/mozilla/glean-swift";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 43.0.0;
minimumVersion = 50.0.1;
};
};
/* End XCRemoteSwiftPackageReference section */

Просмотреть файл

@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/mozilla/glean-swift",
"state": {
"branch": null,
"revision": "e8fef341610de380b55cd5a057f9a3adf7cebb44",
"version": "43.0.2"
"revision": "07b706286cf153f5e0b85f489d861a2b49c7ed2d",
"version": "50.0.1"
}
}
]

Просмотреть файл

@ -33,12 +33,12 @@ class LoginsTests: XCTestCase {
"""
recordMigrationMetrics(jsonString: json)
XCTAssertEqual(3, try GleanMetrics.LoginsStoreMigration.numProcessed.testGetValue())
XCTAssertEqual(2, try GleanMetrics.LoginsStoreMigration.numFailed.testGetValue())
XCTAssertEqual(1, try GleanMetrics.LoginsStoreMigration.numSucceeded.testGetValue())
XCTAssertEqual(53, try GleanMetrics.LoginsStoreMigration.totalDuration.testGetValue())
XCTAssertEqual(3, GleanMetrics.LoginsStoreMigration.numProcessed.testGetValue())
XCTAssertEqual(2, GleanMetrics.LoginsStoreMigration.numFailed.testGetValue())
XCTAssertEqual(1, GleanMetrics.LoginsStoreMigration.numSucceeded.testGetValue())
XCTAssertEqual(53, GleanMetrics.LoginsStoreMigration.totalDuration.testGetValue())
// Note the truncation of the first error string.
XCTAssertEqual(["Invalid login: Login has illegal field: Origin is ", "Invalid login: Origin is empty"], try GleanMetrics.LoginsStoreMigration.errors.testGetValue())
XCTAssertEqual(["Invalid login: Login has illegal field: Origin is ", "Invalid login: Origin is empty"], GleanMetrics.LoginsStoreMigration.errors.testGetValue())
}
}

Просмотреть файл

@ -198,7 +198,7 @@ class NimbusTests: XCTestCase {
)]
nimbus.recordExperimentTelemetry(enrolledExperiments)
XCTAssertTrue(Glean.shared.testIsExperimentActive(experimentId: "test-experiment"),
XCTAssertTrue(Glean.shared.testIsExperimentActive("test-experiment"),
"Experiment should be active")
// TODO: Below fails due to branch and extra being private members Glean
// We will need to change this if we want to remove glean as a submodule and instead
@ -245,8 +245,8 @@ class NimbusTests: XCTestCase {
// Use the Glean test API to check the recorded events
// Enrollment
XCTAssertTrue(GleanMetrics.NimbusEvents.enrollment.testHasValue(), "Enrollment event must exist")
let enrollmentEvents = try GleanMetrics.NimbusEvents.enrollment.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.enrollment.testGetValue(), "Enrollment event must exist")
let enrollmentEvents = GleanMetrics.NimbusEvents.enrollment.testGetValue()!
XCTAssertEqual(1, enrollmentEvents.count, "Enrollment event count must match")
let enrollmentEventExtras = enrollmentEvents.first!.extra
XCTAssertEqual("test-experiment", enrollmentEventExtras!["experiment"], "Enrollment event experiment must match")
@ -254,8 +254,8 @@ class NimbusTests: XCTestCase {
XCTAssertEqual("test-enrollment-id", enrollmentEventExtras!["enrollment_id"], "Enrollment event enrollment id must match")
// Unenrollment
XCTAssertTrue(GleanMetrics.NimbusEvents.unenrollment.testHasValue(), "Unenrollment event must exist")
let unenrollmentEvents = try GleanMetrics.NimbusEvents.unenrollment.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.unenrollment.testGetValue(), "Unenrollment event must exist")
let unenrollmentEvents = GleanMetrics.NimbusEvents.unenrollment.testGetValue()!
XCTAssertEqual(1, unenrollmentEvents.count, "Unenrollment event count must match")
let unenrollmentEventExtras = unenrollmentEvents.first!.extra
XCTAssertEqual("test-experiment", unenrollmentEventExtras!["experiment"], "Unenrollment event experiment must match")
@ -263,8 +263,8 @@ class NimbusTests: XCTestCase {
XCTAssertEqual("test-enrollment-id", unenrollmentEventExtras!["enrollment_id"], "Unenrollment event enrollment id must match")
// Disqualification
XCTAssertTrue(GleanMetrics.NimbusEvents.disqualification.testHasValue(), "Disqualification event must exist")
let disqualificationEvents = try GleanMetrics.NimbusEvents.disqualification.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.disqualification.testGetValue(), "Disqualification event must exist")
let disqualificationEvents = GleanMetrics.NimbusEvents.disqualification.testGetValue()!
XCTAssertEqual(1, disqualificationEvents.count, "Disqualification event count must match")
let disqualificationEventExtras = disqualificationEvents.first!.extra
XCTAssertEqual("test-experiment", disqualificationEventExtras!["experiment"], "Disqualification event experiment must match")
@ -283,14 +283,14 @@ class NimbusTests: XCTestCase {
try nimbus.applyPendingExperimentsOnThisThread()
// Assert that there are no events to start with
XCTAssertFalse(GleanMetrics.NimbusEvents.exposure.testHasValue(), "Event must have a value")
XCTAssertNil(GleanMetrics.NimbusEvents.exposure.testGetValue(), "Event must not have a value")
// Record a valid exposure event in Glean that matches the featureId from the test experiment
nimbus.recordExposureEvent(featureId: "aboutwelcome")
// Use the Glean test API to check that the valid event is present
XCTAssertTrue(GleanMetrics.NimbusEvents.exposure.testHasValue(), "Event must have a value")
let enrollmentEvents = try GleanMetrics.NimbusEvents.exposure.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.exposure.testGetValue(), "Event must have a value")
let enrollmentEvents = GleanMetrics.NimbusEvents.exposure.testGetValue()!
XCTAssertEqual(1, enrollmentEvents.count, "Event count must match")
let enrollmentEventExtras = enrollmentEvents.first!.extra
XCTAssertEqual("secure-gold", enrollmentEventExtras!["experiment"], "Experiment slug must match")
@ -306,7 +306,7 @@ class NimbusTests: XCTestCase {
// Verify the invalid event was ignored by checking again that the valid event is still the only
// event, and that it hasn't changed any of its extra properties.
let enrollmentEventsTryTwo = try GleanMetrics.NimbusEvents.exposure.testGetValue()
let enrollmentEventsTryTwo = GleanMetrics.NimbusEvents.exposure.testGetValue()!
XCTAssertEqual(1, enrollmentEventsTryTwo.count, "Event count must match")
let enrollmentEventExtrasTryTwo = enrollmentEventsTryTwo.first!.extra
XCTAssertEqual("secure-gold", enrollmentEventExtrasTryTwo!["experiment"], "Experiment slug must match")
@ -328,14 +328,14 @@ class NimbusTests: XCTestCase {
try nimbus.applyPendingExperimentsOnThisThread()
// Assert that there are no events to start with
XCTAssertFalse(GleanMetrics.NimbusEvents.exposure.testHasValue(), "Event must have a value")
XCTAssertNil(GleanMetrics.NimbusEvents.exposure.testGetValue(), "Event must not have a value")
// Opt out of the experiment, which should generate a "disqualification" event
try nimbus.optOutOnThisThread("secure-gold")
// Use the Glean test API to check that the valid event is present
XCTAssertTrue(GleanMetrics.NimbusEvents.disqualification.testHasValue(), "Event must have a value")
let disqualificationEvents = try GleanMetrics.NimbusEvents.disqualification.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.disqualification.testGetValue(), "Event must have a value")
let disqualificationEvents = GleanMetrics.NimbusEvents.disqualification.testGetValue()!
XCTAssertEqual(1, disqualificationEvents.count, "Event count must match")
let disqualificationEventExtras = disqualificationEvents.first!.extra
XCTAssertEqual("secure-gold", disqualificationEventExtras!["experiment"], "Experiment slug must match")
@ -357,15 +357,15 @@ class NimbusTests: XCTestCase {
try nimbus.applyPendingExperimentsOnThisThread()
// Assert that there are no events to start with
XCTAssertFalse(GleanMetrics.NimbusEvents.exposure.testHasValue(), "Event must have a value")
XCTAssertNil(GleanMetrics.NimbusEvents.exposure.testGetValue(), "Event must not have a value")
// Opt out of all experiments, which should generate a "disqualification" event for the enrolled
// experiment
try nimbus.setGlobalUserParticipationOnThisThread(false)
// Use the Glean test API to check that the valid event is present
XCTAssertTrue(GleanMetrics.NimbusEvents.disqualification.testHasValue(), "Event must have a value")
let disqualificationEvents = try GleanMetrics.NimbusEvents.disqualification.testGetValue()
XCTAssertNotNil(GleanMetrics.NimbusEvents.disqualification.testGetValue(), "Event must have a value")
let disqualificationEvents = GleanMetrics.NimbusEvents.disqualification.testGetValue()!
XCTAssertEqual(1, disqualificationEvents.count, "Event count must match")
let disqualificationEventExtras = disqualificationEvents.first!.extra
XCTAssertEqual("secure-gold", disqualificationEventExtras!["experiment"], "Experiment slug must match")