Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
*/
const { TelemetryController } = ChromeUtils.importESModule(
);
const { AMTelemetry } = ChromeUtils.importESModule(
);
// We don't have an easy way to serve update manifests from a secure URL.
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
const EVENT_CATEGORY = "addonsManager";
const EVENT_METHODS_INSTALL = ["install", "update"];
const EVENT_METHODS_MANAGE = ["disable", "enable", "uninstall"];
const EVENT_METHODS = [...EVENT_METHODS_INSTALL, ...EVENT_METHODS_MANAGE];
const GLEAN_EVENT_NAMES = ["install", "update", "manage"];
const FAKE_INSTALL_TELEMETRY_INFO = {
source: "fake-install-source",
method: "fake-install-method",
};
add_setup(() => {
do_get_profile();
Services.fog.initializeFOG();
});
function getTelemetryEvents(includeMethods = EVENT_METHODS) {
const snapshot = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
ok(
snapshot.parent && !!snapshot.parent.length,
"Got parent telemetry events in the snapshot"
);
return snapshot.parent
.filter(([, category, method]) => {
const includeMethod = includeMethods
? includeMethods.includes(method)
: true;
return category === EVENT_CATEGORY && includeMethod;
})
.map(event => {
return {
method: event[2],
object: event[3],
value: event[4],
extra: event[5],
};
});
}
function assertNoTelemetryEvents() {
const snapshot = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
if (!snapshot.parent || snapshot.parent.length === 0) {
ok(true, "Got no parent telemetry events as expected");
return;
}
let filteredEvents = snapshot.parent.filter(([_timestamp, category]) => {
return category === EVENT_CATEGORY;
});
Assert.deepEqual(filteredEvents, [], "Got no AMTelemetry events as expected");
}
add_task(async function setup() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
// Thunderbird doesn't have one or more of the probes used in this test.
// Ensure the data is collected anyway.
Services.prefs.setBoolPref(
"toolkit.telemetry.testing.overrideProductsCheck",
true
);
// Telemetry test setup needed to ensure that the builtin events are defined
// and they can be collected and verified.
await TelemetryController.testSetup();
await promiseStartupManager();
});
// Test the basic install and management flows.
add_task(
{
// We need to enable this pref because some assertions verify that
// `installOrigins` is collected in some Telemetry events.
pref_set: [["extensions.install_origins.enabled", true]],
},
async function test_basic_telemetry_events() {
const EXTENSION_ID = "basic@test.extension";
const manifest = {
browser_specific_settings: { gecko: { id: EXTENSION_ID } },
};
let extension = ExtensionTestUtils.loadExtension({
manifest,
useAddonManager: "permanent",
amInstallTelemetryInfo: FAKE_INSTALL_TELEMETRY_INFO,
});
await extension.startup();
const addon = await promiseAddonByID(EXTENSION_ID);
info("Disabling the extension");
await addon.disable();
info("Set pending uninstall on the extension");
const onceAddonUninstalling = promiseAddonEvent("onUninstalling");
addon.uninstall(true);
await onceAddonUninstalling;
info("Cancel pending uninstall");
const oncePendingUninstallCancelled = promiseAddonEvent(
"onOperationCancelled"
);
addon.cancelUninstall();
await oncePendingUninstallCancelled;
info("Re-enabling the extension");
const onceAddonStarted = promiseWebExtensionStartup(EXTENSION_ID);
const onceAddonEnabled = promiseAddonEvent("onEnabled");
addon.enable();
await Promise.all([onceAddonEnabled, onceAddonStarted]);
await extension.unload();
let amEvents = getTelemetryEvents();
let gleanEvents = AddonTestUtils.getAMGleanEvents(GLEAN_EVENT_NAMES);
const amMethods = amEvents.map(evt => evt.method);
const expectedMethods = [
// These two install methods are related to the steps "started" and "completed".
"install",
"install",
// Sequence of disable and enable (pending uninstall and undo uninstall are not going to
// record any telemetry events).
"disable",
"enable",
// The final "uninstall" when the test extension is unloaded.
"uninstall",
];
Assert.deepEqual(
amMethods,
expectedMethods,
"Got the addonsManager telemetry events in the expected order"
);
Assert.deepEqual(
expectedMethods,
gleanEvents.map(evt => {
// Install events don't have a method, so use ducktyping to recognize
// them: they have a step, but unlike update events, no updated_from.
if (evt.step && !evt.updated_from) {
return "install";
}
return evt.method;
}),
"Got the addonsManager Glean events in the expected order."
);
const installEvents = amEvents.filter(evt => evt.method === "install");
const expectedInstallEvents = [
{
method: "install",
object: "extension",
value: "1",
extra: {
addon_id: "basic@test.extension",
step: "started",
install_origins: "0",
...FAKE_INSTALL_TELEMETRY_INFO,
},
},
{
method: "install",
object: "extension",
value: "1",
extra: {
addon_id: "basic@test.extension",
step: "completed",
install_origins: "0",
...FAKE_INSTALL_TELEMETRY_INFO,
},
},
];
Assert.deepEqual(
installEvents,
expectedInstallEvents,
"Got the expected addonsManager.install events"
);
let gleanInstall = {
addon_type: "extension",
addon_id: "basic@test.extension",
source: FAKE_INSTALL_TELEMETRY_INFO.source,
source_method: FAKE_INSTALL_TELEMETRY_INFO.method,
install_origins: "0",
};
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents("install"),
[
{ step: "started", ...gleanInstall },
{ step: "completed", ...gleanInstall },
],
"Got the expected addonsManager Glean events."
);
let gleanManage = {
addon_type: "extension",
addon_id: "basic@test.extension",
source: FAKE_INSTALL_TELEMETRY_INFO.source,
source_method: FAKE_INSTALL_TELEMETRY_INFO.method,
};
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents("manage"),
[
{ method: "disable", ...gleanManage },
{ method: "enable", ...gleanManage },
{ method: "uninstall", ...gleanManage },
],
"Got the expected addonsManager Glean events"
);
const manageEvents = amEvents.filter(evt =>
EVENT_METHODS_MANAGE.includes(evt.method)
);
const expectedExtra = FAKE_INSTALL_TELEMETRY_INFO;
const expectedManageEvents = [
{
method: "disable",
object: "extension",
value: "basic@test.extension",
extra: expectedExtra,
},
{
method: "enable",
object: "extension",
value: "basic@test.extension",
extra: expectedExtra,
},
{
method: "uninstall",
object: "extension",
value: "basic@test.extension",
extra: expectedExtra,
},
];
Assert.deepEqual(
manageEvents,
expectedManageEvents,
"Got the expected addonsManager.manage events"
);
Services.fog.testResetFOG();
// Verify that on every install flow, the value of the addonsManager.install Telemetry events
// is being incremented.
extension = ExtensionTestUtils.loadExtension({
manifest,
useAddonManager: "permanent",
amInstallTelemetryInfo: FAKE_INSTALL_TELEMETRY_INFO,
});
await extension.startup();
await extension.unload();
const eventsFromNewInstall = getTelemetryEvents();
equal(
eventsFromNewInstall.length,
3,
"Got the expected number of addonsManager install events"
);
equal(
3,
AddonTestUtils.getAMGleanEvents(GLEAN_EVENT_NAMES).length,
"Got the expected number of addonsManager Glean events."
);
equal(
2,
AddonTestUtils.getAMGleanEvents("install", { install_id: "2" }).length,
"Got the expected install_id for Glean install event."
);
const eventValues = eventsFromNewInstall
.filter(evt => evt.method === "install")
.map(evt => evt.value);
const expectedValues = ["2", "2"];
Assert.deepEqual(
eventValues,
expectedValues,
"Got the expected install id"
);
Services.fog.testResetFOG();
}
);
add_task(
{
// We need to enable this pref because some assertions verify that
// `installOrigins` is collected in some Telemetry events.
pref_set: [["extensions.install_origins.enabled", true]],
},
async function test_update_telemetry_events() {
const EXTENSION_ID = "basic@test.extension";
const testserver = AddonTestUtils.createHttpServer({
hosts: ["example.com"],
});
const updateUrl = `http://example.com/updates.json`;
const testAddon = AddonTestUtils.createTempWebExtensionFile({
manifest: {
version: "1.0",
browser_specific_settings: {
gecko: {
id: EXTENSION_ID,
update_url: updateUrl,
},
},
},
});
const testUserRequestedUpdate = AddonTestUtils.createTempWebExtensionFile({
manifest: {
version: "2.0",
browser_specific_settings: {
gecko: {
id: EXTENSION_ID,
update_url: updateUrl,
},
},
},
});
const testAppRequestedUpdate = AddonTestUtils.createTempWebExtensionFile({
manifest: {
version: "2.1",
browser_specific_settings: {
gecko: {
id: EXTENSION_ID,
update_url: updateUrl,
},
},
},
});
testserver.registerFile(
`/addons/${EXTENSION_ID}-2.0.xpi`,
testUserRequestedUpdate
);
testserver.registerFile(
`/addons/${EXTENSION_ID}-2.1.xpi`,
testAppRequestedUpdate
);
let updates = [
{
version: "2.0",
update_link: `http://example.com/addons/${EXTENSION_ID}-2.0.xpi`,
applications: {
gecko: {
strict_min_version: "1",
},
},
},
];
testserver.registerPathHandler("/updates.json", (request, response) => {
response.write(`{
"addons": {
"${EXTENSION_ID}": {
"updates": ${JSON.stringify(updates)}
}
}
}`);
});
await promiseInstallFile(testAddon, false, FAKE_INSTALL_TELEMETRY_INFO);
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
// User requested update.
await promiseFindAddonUpdates(
addon,
AddonManager.UPDATE_WHEN_USER_REQUESTED
);
let installs = await AddonManager.getAllInstalls();
await promiseCompleteAllInstalls(installs);
updates = [
{
version: "2.1",
update_link: `http://example.com/addons/${EXTENSION_ID}-2.1.xpi`,
applications: {
gecko: {
strict_min_version: "1",
},
},
},
];
// App requested update.
await promiseFindAddonUpdates(
addon,
AddonManager.UPDATE_WHEN_PERIODIC_UPDATE
);
let installs2 = await AddonManager.getAllInstalls();
await promiseCompleteAllInstalls(installs2);
updates = [
{
version: "2.1.1",
update_link: `http://example.com/addons/${EXTENSION_ID}-2.1.1-network-failure.xpi`,
applications: {
gecko: {
strict_min_version: "1",
},
},
},
];
// Update which fails to download.
await promiseFindAddonUpdates(
addon,
AddonManager.UPDATE_WHEN_PERIODIC_UPDATE
);
let installs3 = await AddonManager.getAllInstalls();
await promiseCompleteAllInstalls(installs3);
let amEvents = getTelemetryEvents();
const installEvents = amEvents
.filter(evt => evt.method === "install")
.map(evt => {
delete evt.value;
return evt;
});
const addon_id = "basic@test.extension";
const object = "extension";
let gleanInstall = {
addon_id,
addon_type: "extension",
install_origins: "0",
source: FAKE_INSTALL_TELEMETRY_INFO.source,
source_method: FAKE_INSTALL_TELEMETRY_INFO.method,
};
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents("install"),
[
{ step: "started", ...gleanInstall },
{ step: "completed", ...gleanInstall },
],
"Got the expected install Glean events."
);
Assert.deepEqual(
installEvents,
[
{
method: "install",
object,
extra: {
addon_id,
step: "started",
install_origins: "0",
...FAKE_INSTALL_TELEMETRY_INFO,
},
},
{
method: "install",
object,
extra: {
addon_id,
step: "completed",
install_origins: "0",
...FAKE_INSTALL_TELEMETRY_INFO,
},
},
],
"Got the expected addonsManager.install events"
);
const updateEvents = amEvents
.filter(evt => evt.method === "update")
.map(evt => {
delete evt.value;
return evt;
});
const method = "update";
const baseExtra = FAKE_INSTALL_TELEMETRY_INFO;
let glean = AddonTestUtils.getAMGleanEvents("update");
glean.forEach(e => delete e.download_time);
let gleanUpdate = {
addon_id,
addon_type: "extension",
source: FAKE_INSTALL_TELEMETRY_INFO.source,
source_method: FAKE_INSTALL_TELEMETRY_INFO.method,
};
Assert.deepEqual(
glean,
[
{ step: "started", updated_from: "user", ...gleanUpdate },
{ step: "download_started", updated_from: "user", ...gleanUpdate },
{ step: "download_completed", updated_from: "user", ...gleanUpdate },
{ step: "completed", updated_from: "user", ...gleanUpdate },
{ step: "started", updated_from: "app", ...gleanUpdate },
{ step: "download_started", updated_from: "app", ...gleanUpdate },
{ step: "download_completed", updated_from: "app", ...gleanUpdate },
{ step: "completed", updated_from: "app", ...gleanUpdate },
{ step: "started", updated_from: "app", ...gleanUpdate },
{ step: "download_started", updated_from: "app", ...gleanUpdate },
{
step: "download_failed",
updated_from: "app",
error: "ERROR_NETWORK_FAILURE",
...gleanUpdate,
},
],
"Got the expected Glean update events."
);
const expectedUpdateEvents = [
// User-requested update to the 2.1 version.
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "started",
updated_from: "user",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "download_started",
updated_from: "user",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "download_completed",
updated_from: "user",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "completed",
updated_from: "user",
},
},
// App-requested update to the 2.1 version.
{
method,
object,
extra: { ...baseExtra, addon_id, step: "started", updated_from: "app" },
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "download_started",
updated_from: "app",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "download_completed",
updated_from: "app",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "completed",
updated_from: "app",
},
},
// Broken update to the 2.1.1 version (on ERROR_NETWORK_FAILURE).
{
method,
object,
extra: { ...baseExtra, addon_id, step: "started", updated_from: "app" },
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
step: "download_started",
updated_from: "app",
},
},
{
method,
object,
extra: {
...baseExtra,
addon_id,
error: "ERROR_NETWORK_FAILURE",
step: "download_failed",
updated_from: "app",
},
},
];
AddonTestUtils.getAMGleanEvents("update")
.filter(e => ["download_completed", "download_failed"].includes(e.step))
.forEach(e =>
Assert.greater(
parseInt(e.download_time, 10),
0,
`At step ${e.step} download_time: ${e.download_time}`
)
);
for (let i = 0; i < updateEvents.length; i++) {
if (
["download_completed", "download_failed"].includes(
updateEvents[i].extra.step
)
) {
const download_time = parseInt(updateEvents[i].extra.download_time, 10);
ok(
!isNaN(download_time) && download_time > 0,
`Got a download_time extra in ${updateEvents[i].extra.step} events: ${download_time}`
);
delete updateEvents[i].extra.download_time;
}
Assert.deepEqual(
updateEvents[i],
expectedUpdateEvents[i],
"Got the expected addonsManager.update events"
);
}
equal(
updateEvents.length,
expectedUpdateEvents.length,
"Got the expected number of addonsManager.update events"
);
await addon.uninstall();
// Clear any AMTelemetry events related to the uninstalled extensions.
getTelemetryEvents();
Services.fog.testResetFOG();
}
);
add_task(async function test_no_telemetry_events_on_internal_sources() {
assertNoTelemetryEvents();
const INTERNAL_EXTENSION_ID = "internal@test.extension";
// Install an extension which has internal as its installation source,
// and expect it to do not appear in the collected telemetry events.
let internalExtension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id: INTERNAL_EXTENSION_ID } },
},
useAddonManager: "permanent",
amInstallTelemetryInfo: { source: "internal" },
});
await internalExtension.startup();
const internalAddon = await promiseAddonByID(INTERNAL_EXTENSION_ID);
info("Disabling the internal extension");
const onceInternalAddonDisabled = promiseAddonEvent("onDisabled");
internalAddon.disable();
await onceInternalAddonDisabled;
info("Re-enabling the internal extension");
const onceInternalAddonStarted = promiseWebExtensionStartup(
INTERNAL_EXTENSION_ID
);
const onceInternalAddonEnabled = promiseAddonEvent("onEnabled");
internalAddon.enable();
await Promise.all([onceInternalAddonEnabled, onceInternalAddonStarted]);
await internalExtension.unload();
assertNoTelemetryEvents();
});
add_task(async function test_collect_attribution_data_for_amo() {
assertNoTelemetryEvents();
// We pass the `source` value to `amInstallTelemetryInfo` in this test so the
// host could be anything in this variable below. Whether to collect
// attribution data for AMO is determined by the `source` value, not this
// host.
const addonId = "{28374a9a-676c-5640-bfa7-865cd4686ead}";
// This is the SHA256 hash of the `addonId` above.
const expectedHashedAddonId =
"cf815c9f45c249473d630705f89e64d359737a106a375bbb83be71e6d52dc234";
for (const { source, sourceURL, expectNoEvent, expectedAmoAttribution } of [
// Basic test.
{
source: "amo",
sourceURL: `${url}?utm_content=utm-content-value`,
expectedAmoAttribution: {
utm_content: "utm-content-value",
},
},
// No UTM parameters will produce an event without any attribution data.
{
source: "amo",
sourceURL: url,
expectedAmoAttribution: {},
},
// Invalid source URLs will produce an event without any attribution data.
{
source: "amo",
sourceURL: "invalid-url",
expectedAmoAttribution: {},
},
// No source URL.
{
source: "amo",
sourceURL: null,
expectedAmoAttribution: {},
},
{
source: "amo",
sourceURL: undefined,
expectedAmoAttribution: {},
},
// Ignore unsupported/bogus UTM parameters.
{
source: "amo",
sourceURL: [
`${url}?utm_content=utm-content-value`,
"utm_foo=invalid",
"utm_campaign=some-campaign",
"utm_term=invalid-too",
].join("&"),
expectedAmoAttribution: {
utm_campaign: "some-campaign",
utm_content: "utm-content-value",
},
},
{
source: "amo",
sourceURL: `${url}?foo=bar&q=azerty`,
expectedAmoAttribution: {},
},
// Long values are truncated.
{
source: "amo",
sourceURL: `${url}?utm_medium=${"a".repeat(100)}`,
expectedAmoAttribution: {
utm_medium: "a".repeat(40),
},
},
// Only collect the first value if the parameter is passed more than once.
{
source: "amo",
sourceURL: `${url}?utm_source=first-source&utm_source=second-source`,
expectedAmoAttribution: {
utm_source: "first-source",
},
},
// When source is "disco", we don't collect the UTM parameters.
{
source: "disco",
sourceURL: `${url}?utm_content=utm-content-value`,
expectedAmoAttribution: {},
},
// When source is neither "amo" nor "disco", we don't collect anything.
{
source: "link",
sourceURL: `${url}?utm_content=utm-content-value`,
expectNoEvent: true,
},
{
source: null,
sourceURL: `${url}?utm_content=utm-content-value`,
expectNoEvent: true,
},
{
source: undefined,
sourceURL: `${url}?utm_content=utm-content-value`,
expectNoEvent: true,
},
]) {
const extDefinition = {
useAddonManager: "permanent",
manifest: {
version: "1.0",
browser_specific_settings: { gecko: { id: addonId } },
},
amInstallTelemetryInfo: {
...FAKE_INSTALL_TELEMETRY_INFO,
sourceURL,
source,
},
};
let extension = ExtensionTestUtils.loadExtension(extDefinition);
await extension.startup();
const installStatsEvents = getTelemetryEvents(["install_stats"]);
let gleanEvents = AddonTestUtils.getAMGleanEvents(["installStats"]);
Services.fog.testResetFOG();
if (expectNoEvent === true) {
Assert.equal(
installStatsEvents.length,
0,
"no install_stats event should be recorded"
);
Assert.equal(
gleanEvents.length,
0,
"No install_stats Glean event should be recorded."
);
} else {
Assert.equal(
installStatsEvents.length,
1,
"only one install_stats event should be recorded"
);
Assert.equal(
gleanEvents.length,
1,
"Only one install_stats Glean event should be recorded."
);
const installStatsEvent = installStatsEvents[0];
Assert.deepEqual(installStatsEvent, {
method: "install_stats",
object: "extension",
value: expectedHashedAddonId,
extra: {
addon_id: addonId,
...expectedAmoAttribution,
},
});
Assert.deepEqual(gleanEvents[0], {
addon_id: addonId,
addon_type: "extension",
...expectedAmoAttribution,
});
}
await extension.upgrade({
...extDefinition,
manifest: {
...extDefinition.manifest,
version: "2.0",
},
});
Assert.deepEqual(
getTelemetryEvents(["install_stats"]),
[],
"no install_stats event should be recorded on addon updates"
);
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents(["installStats"]),
[],
"No install_stats Glean event should be recorded on addon updates."
);
await extension.unload();
Services.fog.testResetFOG();
}
getTelemetryEvents();
});
add_task(async function test_collect_attribution_data_for_amo_with_long_id() {
assertNoTelemetryEvents();
// We pass the `source` value to `installTelemetryInfo` in this test so the
// host could be anything in this variable below. Whether to collect
// attribution data for AMO is determined by the `source` value, not this
// host.
const addonId = `@${"a".repeat(90)}`;
// This is the SHA256 hash of the `addonId` above.
const expectedHashedAddonId =
"964d902353fc1c127228b66ec8a174c340cb2e02dbb550c6000fb1cd3ca2f489";
const installTelemetryInfo = {
...FAKE_INSTALL_TELEMETRY_INFO,
sourceURL: `${url}?utm_content=utm-content-value`,
source: "amo",
};
// We call `recordInstallStatsEvent()` directly because using an add-on ID
// longer than 64 chars causes signing issues in tests (because of the
// differences between the fake CertDB injected by
// `AddonTestUtils.overrideCertDB()` and the real one).
const fakeAddonInstall = {
addon: { id: addonId },
type: "extension",
installTelemetryInfo,
hashedAddonId: expectedHashedAddonId,
};
AMTelemetry.recordInstallStatsEvent(fakeAddonInstall);
const installStatsEvents = getTelemetryEvents(["install_stats"]);
Assert.equal(
installStatsEvents.length,
1,
"only one install_stats event should be recorded"
);
const installStatsEvent = installStatsEvents[0];
Assert.deepEqual(installStatsEvent, {
method: "install_stats",
object: "extension",
value: expectedHashedAddonId,
extra: {
addon_id: AMTelemetry.getTrimmedString(addonId),
utm_content: "utm-content-value",
},
});
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents(["installStats"]),
[
{
addon_type: "extension",
addon_id: AMTelemetry.getTrimmedString(addonId),
utm_content: "utm-content-value",
},
],
"Got the expected install_stats Glean event."
);
Services.fog.testResetFOG();
});
add_task(async function test_collect_attribution_data_for_rtamo() {
assertNoTelemetryEvents();
const addonId = "{28374a9a-676c-5640-bfa7-865cd4686ead}";
// This is the SHA256 hash of the `addonId` above.
const expectedHashedAddonId =
"cf815c9f45c249473d630705f89e64d359737a106a375bbb83be71e6d52dc234";
// We simulate what is happening in:
const installTelemetryInfo = {
...FAKE_INSTALL_TELEMETRY_INFO,
sourceURL: `${url}?utm_content=utm-content-value`,
source: "rtamo",
};
const fakeAddonInstall = {
addon: { id: addonId },
type: "extension",
installTelemetryInfo,
hashedAddonId: expectedHashedAddonId,
};
AMTelemetry.recordInstallStatsEvent(fakeAddonInstall);
const installStatsEvents = getTelemetryEvents(["install_stats"]);
Assert.equal(
installStatsEvents.length,
1,
"only one install_stats event should be recorded"
);
const installStatsEvent = installStatsEvents[0];
Assert.deepEqual(installStatsEvent, {
method: "install_stats",
object: "extension",
value: expectedHashedAddonId,
extra: {
addon_id: AMTelemetry.getTrimmedString(addonId),
},
});
Assert.deepEqual(
AddonTestUtils.getAMGleanEvents(["installStats"]),
[
{
addon_type: "extension",
addon_id: AMTelemetry.getTrimmedString(addonId),
},
],
"Got the expected install_stats Glean event."
);
Services.fog.testResetFOG();
});
add_task(async function teardown() {
await TelemetryController.testShutdown();
});