/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { BrowserUsageTelemetry } = ChromeUtils.importESModule(
"resource:///modules/BrowserUsageTelemetry.sys.mjs"
);
const { TelemetryTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/TelemetryTestUtils.sys.mjs"
);
ChromeUtils.defineESModuleGetters(
this, {
FileUtils:
"resource://gre/modules/FileUtils.sys.mjs",
});
const TIMESTAMP_PREF =
"app.installation.timestamp";
function encodeUtf16(str) {
const buf =
new ArrayBuffer(str.length * 2);
const utf16 =
new Uint16Array(buf);
for (let i = 0; i < str.length; i++) {
utf16[i] = str.charCodeAt(i);
}
return new Uint8Array(buf);
}
// Returns Promise
function writeJsonUtf16(fileName, obj) {
const str = JSON.stringify(obj);
return IOUtils.write(fileName, encodeUtf16(str));
}
async
function runReport(
dataFile,
installType,
{ clearTS, setTS, assertRejects, expectExtra, expectTS, msixPrefixes }
) {
// Setup timestamp
if (clearTS) {
Services.prefs.clearUserPref(TIMESTAMP_PREF);
}
if (
typeof setTS ==
"string") {
Services.prefs.setStringPref(TIMESTAMP_PREF, setTS);
}
// Init events
Services.telemetry.clearEvents();
// Exercise reportInstallationTelemetry
if (
typeof assertRejects !=
"undefined") {
await
Assert.rejects(
BrowserUsageTelemetry.reportInstallationTelemetry(dataFile),
assertRejects
);
}
else if (!msixPrefixes) {
await BrowserUsageTelemetry.reportInstallationTelemetry(dataFile);
}
else {
await BrowserUsageTelemetry.reportInstallationTelemetry(
dataFile,
msixPrefixes
);
}
// Check events
TelemetryTestUtils.assertEvents(
expectExtra
? [{ object: installType, value:
null, extra: expectExtra }]
: [],
{ category:
"installation", method:
"first_seen" }
);
// Check timestamp
if (
typeof expectTS ==
"string") {
Assert.equal(expectTS, Services.prefs.getStringPref(TIMESTAMP_PREF));
}
}
let condition = {
skip_if: () =>
AppConstants.platform !==
"win" ||
!Services.sysinfo.getProperty(
"hasWinPackageId"),
};
add_task(condition, async
function testInstallationTelemetryMSIX() {
// Unfortunately, we have no way to inject different installation ping data
// into the system in a way that doesn't just completely override the code
// under test - so other than a basic test of the happy path, there's
// nothing we can do here.
let msixExtra = {
version: AppConstants.MOZ_APP_VERSION,
build_id: AppConstants.MOZ_BULIDID,
admin_user:
"false",
from_msi:
"false",
silent:
"false",
default_path:
"true",
install_existed:
"false",
other_inst:
"false",
other_msix_inst:
"false",
profdir_existed:
"false",
};
await runReport(
"fake",
"msix", {
expectExtra: msixExtra,
});
});
condition = {
skip_if: () =>
AppConstants.platform ===
"win" &&
Services.sysinfo.getProperty(
"hasWinPackageId"),
};
add_task(condition, async
function testInstallationTelemetry() {
let dataFilePath = await IOUtils.createUniqueFile(
Services.dirsvc.get(
"TmpD", Ci.nsIFile).path,
"installation-telemetry-test-data" + Math.random() +
".json"
);
let dataFile =
new FileUtils.File(dataFilePath);
registerCleanupFunction(async () => {
try {
await IOUtils.remove(dataFilePath);
}
catch (ex) {
// Ignore remove failure, file may not exist by now
}
Services.prefs.clearUserPref(TIMESTAMP_PREF);
});
// Test with normal stub data
let stubData = {
version:
"99.0abc",
build_id:
"123",
installer_type:
"stub",
admin_user:
true,
install_existed:
false,
profdir_existed:
false,
install_timestamp:
"0",
};
let stubExtra = {
version:
"99.0abc",
build_id:
"123",
admin_user:
"true",
install_existed:
"false",
other_inst:
"false",
other_msix_inst:
"false",
profdir_existed:
"false",
};
await writeJsonUtf16(dataFilePath, stubData);
await runReport(dataFile,
"stub", {
clearTS:
true,
expectExtra: stubExtra,
expectTS:
"0",
});
// Check that it doesn't generate another event when the timestamp is unchanged
await runReport(dataFile,
"stub", { expectTS:
"0" });
// New timestamp
stubData.install_timestamp =
"1";
await writeJsonUtf16(dataFilePath, stubData);
await runReport(dataFile,
"stub", {
expectExtra: stubExtra,
expectTS:
"1",
});
// Test with normal full data
let fullData = {
version:
"99.0abc",
build_id:
"123",
installer_type:
"full",
admin_user:
false,
install_existed:
true,
profdir_existed:
true,
silent:
false,
from_msi:
false,
default_path:
true,
install_timestamp:
"1",
};
let fullExtra = {
version:
"99.0abc",
build_id:
"123",
admin_user:
"false",
install_existed:
"true",
other_inst:
"false",
other_msix_inst:
"false",
profdir_existed:
"true",
silent:
"false",
from_msi:
"false",
default_path:
"true",
};
await writeJsonUtf16(dataFilePath, fullData);
await runReport(dataFile,
"full", {
clearTS:
true,
expectExtra: fullExtra,
expectTS:
"1",
});
// Check that it doesn't generate another event when the timestamp is unchanged
await runReport(dataFile,
"full", { expectTS:
"1" });
// New timestamp and a check to make sure we can find installed MSIX packages
// by overriding the prefixes a bit further down.
fullData.install_timestamp =
"2";
// This check only works on Windows
if (AppConstants.platform ==
"win") {
fullExtra.other_msix_inst =
"true";
}
await writeJsonUtf16(dataFilePath, fullData);
await runReport(dataFile,
"full", {
expectExtra: fullExtra,
expectTS:
"2",
msixPrefixes: [
"Microsoft"],
});
// Missing field
delete fullData.install_existed;
fullData.install_timestamp =
"3";
await writeJsonUtf16(dataFilePath, fullData);
await runReport(dataFile,
"full", { assertRejects: /install_existed/ });
// Malformed JSON
await IOUtils.write(dataFilePath, encodeUtf16(
"hello"));
await runReport(dataFile,
"stub", {
assertRejects: /unexpected character/,
});
// Missing file, should return with no exception
await IOUtils.remove(dataFilePath);
await runReport(dataFile,
"stub", { setTS:
"3", expectTS:
"3" });
});