forked from mirrors/gecko-dev
We can't add the provenance data to the `installation.first_seen` extra data because it is already at its maximum number of keys. So instead we will add the `installation.first_seen_prov_ext` event which will be sent at the same time as `installation.first_seen` and will contain provenance attribution data in its extras object. Differential Revision: https://phabricator.services.mozilla.com/D172520
284 lines
7.8 KiB
JavaScript
284 lines
7.8 KiB
JavaScript
/* 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" });
|
|
});
|