summaryrefslogtreecommitdiffstats
path: root/browser/modules/test/unit/test_InstallationTelemetry.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/modules/test/unit/test_InstallationTelemetry.js')
-rw-r--r--browser/modules/test/unit/test_InstallationTelemetry.js284
1 files changed, 284 insertions, 0 deletions
diff --git a/browser/modules/test/unit/test_InstallationTelemetry.js b/browser/modules/test/unit/test_InstallationTelemetry.js
new file mode 100644
index 0000000000..64d9964cbe
--- /dev/null
+++ b/browser/modules/test/unit/test_InstallationTelemetry.js
@@ -0,0 +1,284 @@
+/* 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 { AttributionIOUtils } = ChromeUtils.importESModule(
+ "resource:///modules/AttributionCode.sys.mjs"
+);
+const { BrowserUsageTelemetry } = ChromeUtils.import(
+ "resource:///modules/BrowserUsageTelemetry.jsm"
+);
+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" },
+ { clear: false }
+ );
+ // Provenance Data is currently only supported on Windows.
+ if (AppConstants.platform == "win") {
+ let provenanceExtra = {
+ data_exists: "true",
+ file_system: "NTFS",
+ ads_exists: "true",
+ security_zone: "3",
+ refer_url_exist: "true",
+ refer_url_moz: "true",
+ host_url_exist: "true",
+ host_url_moz: "true",
+ };
+ TelemetryTestUtils.assertEvents(
+ expectExtra
+ ? [{ object: installType, value: null, extra: provenanceExtra }]
+ : [],
+ { category: "installation", method: "first_seen_prov_ext" }
+ );
+ } else {
+ TelemetryTestUtils.assertEvents(
+ expectExtra ? [{ object: installType, value: null, extra: {} }] : [],
+ { category: "installation", method: "first_seen_prov_ext" }
+ );
+ }
+
+ // Check timestamp
+ if (typeof expectTS == "string") {
+ Assert.equal(expectTS, Services.prefs.getStringPref(TIMESTAMP_PREF));
+ }
+}
+
+add_setup(function setup() {
+ let origReadUTF8 = AttributionIOUtils.readUTF8;
+ registerCleanupFunction(() => {
+ AttributionIOUtils.readUTF8 = origReadUTF8;
+ });
+ AttributionIOUtils.readUTF8 = async path => {
+ return `
+[Mozilla]
+fileSystem=NTFS
+zoneIdFileSize=194
+zoneIdBufferLargeEnough=true
+zoneIdTruncated=false
+
+[MozillaZoneIdentifierStartSentinel]
+[ZoneTransfer]
+ZoneId=3
+ReferrerUrl=https://mozilla.org/
+HostUrl=https://download-installer.cdn.mozilla.net/pub/firefox/nightly/latest-mozilla-central-l10n/Firefox%20Installer.en-US.exe
+`;
+ };
+});
+
+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 10 and above
+ if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+ 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" });
+});