Backed out changeset ab33904d0edb (bug 1955429) Backed out changeset abdbe82d2cc1 (bug 1955429) Backed out changeset eba2fd65a5cf (bug 1955429) Backed out changeset e0aa301f9301 (bug 1955429)
909 lines
27 KiB
JavaScript
909 lines
27 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
|
|
Assert: "resource://testing-common/Assert.sys.mjs",
|
|
// AttributionCode is only needed for Firefox
|
|
AttributionCode: "resource:///modules/AttributionCode.sys.mjs",
|
|
|
|
MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
|
|
});
|
|
|
|
const gIsWindows = AppConstants.platform == "win";
|
|
const gIsMac = AppConstants.platform == "macosx";
|
|
const gIsAndroid = AppConstants.platform == "android";
|
|
const gIsLinux = AppConstants.platform == "linux";
|
|
|
|
const MILLISECONDS_PER_MINUTE = 60 * 1000;
|
|
const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE;
|
|
const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR;
|
|
|
|
const PLATFORM_VERSION = "1.9.2";
|
|
const APP_VERSION = "1";
|
|
const APP_ID = "xpcshell@tests.mozilla.org";
|
|
const APP_NAME = "XPCShell";
|
|
|
|
const DISTRIBUTION_ID = "distributor-id";
|
|
const DISTRIBUTION_VERSION = "4.5.6b";
|
|
const DISTRIBUTOR_NAME = "Some Distributor";
|
|
const DISTRIBUTOR_CHANNEL = "A Channel";
|
|
const PARTNER_NAME = "test";
|
|
const PARTNER_ID = "NicePartner-ID-3785";
|
|
|
|
// The profile reset date, in milliseconds (Today)
|
|
const PROFILE_RESET_DATE_MS = Date.now();
|
|
// The profile creation date, in milliseconds (Yesterday).
|
|
const PROFILE_FIRST_USE_MS = PROFILE_RESET_DATE_MS - MILLISECONDS_PER_DAY;
|
|
const PROFILE_CREATION_DATE_MS = PROFILE_FIRST_USE_MS - MILLISECONDS_PER_DAY;
|
|
const PROFILE_RECOVERED_FROM_BACKUP =
|
|
PROFILE_RESET_DATE_MS - MILLISECONDS_PER_HOUR;
|
|
|
|
const GFX_VENDOR_ID = "0xabcd";
|
|
const GFX_DEVICE_ID = "0x1234";
|
|
|
|
const EXPECTED_HDD_FIELDS = ["profile", "binary", "system"];
|
|
|
|
// Valid attribution code to write so that settings.attribution can be tested.
|
|
const ATTRIBUTION_CODE = "source%3Dgoogle.com%26dlsource%3Dunittest";
|
|
|
|
function truncateToDays(aMsec) {
|
|
return Math.floor(aMsec / MILLISECONDS_PER_DAY);
|
|
}
|
|
|
|
var SysInfo = {
|
|
overrides: {},
|
|
|
|
getProperty(name) {
|
|
// Assert.ok(false, "Mock SysInfo: " + name + ", " + JSON.stringify(this.overrides));
|
|
if (name in this.overrides) {
|
|
return this.overrides[name];
|
|
}
|
|
|
|
return this._genuine.QueryInterface(Ci.nsIPropertyBag).getProperty(name);
|
|
},
|
|
|
|
getPropertyAsACString(name) {
|
|
return this.get(name);
|
|
},
|
|
|
|
getPropertyAsUint32(name) {
|
|
return this.get(name);
|
|
},
|
|
|
|
get(name) {
|
|
return this._genuine.QueryInterface(Ci.nsIPropertyBag2).get(name);
|
|
},
|
|
|
|
get diskInfo() {
|
|
return this._genuine.QueryInterface(Ci.nsISystemInfo).diskInfo;
|
|
},
|
|
|
|
get osInfo() {
|
|
return this._genuine.QueryInterface(Ci.nsISystemInfo).osInfo;
|
|
},
|
|
|
|
get processInfo() {
|
|
return this._genuine.QueryInterface(Ci.nsISystemInfo).processInfo;
|
|
},
|
|
|
|
QueryInterface: ChromeUtils.generateQI(["nsIPropertyBag2", "nsISystemInfo"]),
|
|
};
|
|
|
|
/**
|
|
* TelemetryEnvironmentTesting - tools for testing the telemetry environment
|
|
* reporting.
|
|
*/
|
|
export var TelemetryEnvironmentTesting = {
|
|
EXPECTED_HDD_FIELDS,
|
|
|
|
init(appInfo) {
|
|
this.appInfo = appInfo;
|
|
},
|
|
|
|
setSysInfoOverrides(overrides) {
|
|
SysInfo.overrides = overrides;
|
|
},
|
|
|
|
spoofGfxAdapter() {
|
|
try {
|
|
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
|
|
Ci.nsIGfxInfoDebug
|
|
);
|
|
gfxInfo.spoofVendorID(GFX_VENDOR_ID);
|
|
gfxInfo.spoofDeviceID(GFX_DEVICE_ID);
|
|
} catch (x) {
|
|
// If we can't test gfxInfo, that's fine, we'll note it later.
|
|
}
|
|
},
|
|
|
|
spoofProfileReset() {
|
|
return IOUtils.writeJSON(
|
|
PathUtils.join(PathUtils.profileDir, "times.json"),
|
|
{
|
|
created: PROFILE_CREATION_DATE_MS,
|
|
reset: PROFILE_RESET_DATE_MS,
|
|
firstUse: PROFILE_FIRST_USE_MS,
|
|
recoveredFromBackup: PROFILE_RECOVERED_FROM_BACKUP,
|
|
}
|
|
);
|
|
},
|
|
|
|
spoofPartnerInfo() {
|
|
let prefsToSpoof = {};
|
|
prefsToSpoof["distribution.id"] = DISTRIBUTION_ID;
|
|
prefsToSpoof["distribution.version"] = DISTRIBUTION_VERSION;
|
|
prefsToSpoof["app.distributor"] = DISTRIBUTOR_NAME;
|
|
prefsToSpoof["app.distributor.channel"] = DISTRIBUTOR_CHANNEL;
|
|
prefsToSpoof["app.partner.test"] = PARTNER_NAME;
|
|
prefsToSpoof["mozilla.partner.id"] = PARTNER_ID;
|
|
|
|
// Spoof the preferences.
|
|
for (let pref in prefsToSpoof) {
|
|
Services.prefs
|
|
.getDefaultBranch(null)
|
|
.setStringPref(pref, prefsToSpoof[pref]);
|
|
}
|
|
},
|
|
|
|
async spoofAttributionData() {
|
|
if (gIsWindows) {
|
|
lazy.AttributionCode._clearCache();
|
|
await lazy.AttributionCode.writeAttributionFile(ATTRIBUTION_CODE);
|
|
} else if (gIsMac) {
|
|
lazy.AttributionCode._clearCache();
|
|
const { MacAttribution } = ChromeUtils.importESModule(
|
|
"resource:///modules/MacAttribution.sys.mjs"
|
|
);
|
|
await MacAttribution.setAttributionString(ATTRIBUTION_CODE);
|
|
}
|
|
},
|
|
|
|
async cleanupAttributionData() {
|
|
if (gIsWindows) {
|
|
lazy.AttributionCode.attributionFile.remove(false);
|
|
lazy.AttributionCode._clearCache();
|
|
} else if (gIsMac) {
|
|
const { MacAttribution } = ChromeUtils.importESModule(
|
|
"resource:///modules/MacAttribution.sys.mjs"
|
|
);
|
|
await MacAttribution.delAttributionString();
|
|
}
|
|
},
|
|
|
|
registerFakeSysInfo() {
|
|
lazy.MockRegistrar.register("@mozilla.org/system-info;1", SysInfo);
|
|
},
|
|
|
|
/**
|
|
* Check that a value is a string and not empty.
|
|
*
|
|
* @param aValue The variable to check.
|
|
* @return True if |aValue| has type "string" and is not empty, False otherwise.
|
|
*/
|
|
checkString(aValue) {
|
|
return typeof aValue == "string" && aValue != "";
|
|
},
|
|
|
|
/**
|
|
* If value is non-null, check if it's a valid string.
|
|
*
|
|
* @param aValue The variable to check.
|
|
* @return True if it's null or a valid string, false if it's non-null and an invalid
|
|
* string.
|
|
*/
|
|
checkNullOrString(aValue) {
|
|
if (aValue) {
|
|
return this.checkString(aValue);
|
|
} else if (aValue === null) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* If value is non-null, check if it's a boolean.
|
|
*
|
|
* @param aValue The variable to check.
|
|
* @return True if it's null or a valid boolean, false if it's non-null and an invalid
|
|
* boolean.
|
|
*/
|
|
checkNullOrBool(aValue) {
|
|
return aValue === null || typeof aValue == "boolean";
|
|
},
|
|
|
|
checkBuildSection(data) {
|
|
const expectedInfo = {
|
|
applicationId: APP_ID,
|
|
applicationName: APP_NAME,
|
|
buildId: this.appInfo.appBuildID,
|
|
version: APP_VERSION,
|
|
vendor: "Mozilla",
|
|
platformVersion: PLATFORM_VERSION,
|
|
xpcomAbi: "noarch-spidermonkey",
|
|
};
|
|
|
|
lazy.Assert.ok(
|
|
"build" in data,
|
|
"There must be a build section in Environment."
|
|
);
|
|
|
|
for (let f in expectedInfo) {
|
|
lazy.Assert.ok(
|
|
this.checkString(data.build[f]),
|
|
f + " must be a valid string."
|
|
);
|
|
lazy.Assert.equal(
|
|
data.build[f],
|
|
expectedInfo[f],
|
|
f + " must have the correct value."
|
|
);
|
|
}
|
|
|
|
// Make sure architecture is in the environment.
|
|
lazy.Assert.ok(this.checkString(data.build.architecture));
|
|
|
|
lazy.Assert.equal(
|
|
data.build.updaterAvailable,
|
|
AppConstants.MOZ_UPDATER,
|
|
"build.updaterAvailable must equal AppConstants.MOZ_UPDATER"
|
|
);
|
|
|
|
// Check Glean's values
|
|
lazy.Assert.equal(Glean.xpcom.abi.testGetValue(), expectedInfo.xpcomAbi);
|
|
lazy.Assert.equal(
|
|
Glean.updater.available.testGetValue(),
|
|
AppConstants.MOZ_UPDATER
|
|
);
|
|
},
|
|
|
|
checkSettingsSection(data) {
|
|
const EXPECTED_FIELDS_TYPES = {
|
|
blocklistEnabled: "boolean",
|
|
e10sEnabled: "boolean",
|
|
e10sMultiProcesses: "number",
|
|
fissionEnabled: "boolean",
|
|
intl: "object",
|
|
locale: "string",
|
|
update: "object",
|
|
userPrefs: "object",
|
|
};
|
|
|
|
lazy.Assert.ok(
|
|
"settings" in data,
|
|
"There must be a settings section in Environment."
|
|
);
|
|
|
|
for (let f in EXPECTED_FIELDS_TYPES) {
|
|
lazy.Assert.equal(
|
|
typeof data.settings[f],
|
|
EXPECTED_FIELDS_TYPES[f],
|
|
f + " must have the correct type."
|
|
);
|
|
}
|
|
lazy.Assert.equal(typeof Glean.blocklist.enabled.testGetValue(), "boolean");
|
|
lazy.Assert.equal(typeof Glean.e10s.enabled.testGetValue(), "boolean");
|
|
lazy.Assert.equal(
|
|
typeof Glean.e10s.multiProcesses.testGetValue(),
|
|
"number"
|
|
);
|
|
lazy.Assert.equal(typeof Glean.fission.enabled.testGetValue(), "boolean");
|
|
lazy.Assert.equal(
|
|
typeof Glean.preferences.userPrefs.testGetValue(),
|
|
"object"
|
|
);
|
|
|
|
// This property is not always present, but when it is, it must be a number.
|
|
if ("launcherProcessState" in data.settings) {
|
|
lazy.Assert.equal(typeof data.settings.launcherProcessState, "number");
|
|
lazy.Assert.equal(
|
|
typeof Glean.launcherProcess.state.testGetValue(),
|
|
"number"
|
|
);
|
|
}
|
|
|
|
// Check "addonCompatibilityCheckEnabled" separately.
|
|
lazy.Assert.equal(
|
|
data.settings.addonCompatibilityCheckEnabled,
|
|
lazy.AddonManager.checkCompatibility
|
|
);
|
|
lazy.Assert.equal(
|
|
Glean.addonsManager.compatibilityCheckEnabled.testGetValue(),
|
|
lazy.AddonManager.checkCompatibility
|
|
);
|
|
|
|
// Check "isDefaultBrowser" separately, as it is not available on Android an can either be
|
|
// null or boolean on other platforms.
|
|
if (gIsAndroid) {
|
|
lazy.Assert.ok(
|
|
!("isDefaultBrowser" in data.settings),
|
|
"Must not be available on Android."
|
|
);
|
|
lazy.Assert.equal(null, Glean.browser.defaultAtLaunch.testGetValue());
|
|
} else if ("isDefaultBrowser" in data.settings) {
|
|
// isDefaultBrowser might not be available in the payload, since it's
|
|
// gathered after the session was restored.
|
|
lazy.Assert.ok(this.checkNullOrBool(data.settings.isDefaultBrowser));
|
|
lazy.Assert.ok(
|
|
this.checkNullOrBool(Glean.browser.defaultAtLaunch.testGetValue())
|
|
);
|
|
}
|
|
|
|
// Check "channel" separately, as it can either be null or string.
|
|
let update = data.settings.update;
|
|
lazy.Assert.ok(this.checkNullOrString(update.channel));
|
|
lazy.Assert.equal(typeof update.enabled, "boolean");
|
|
lazy.Assert.equal(typeof update.autoDownload, "boolean");
|
|
lazy.Assert.equal(typeof update.background, "boolean");
|
|
|
|
// Check sandbox settings exist and make sense
|
|
if (data.settings.sandbox.effectiveContentProcessLevel !== null) {
|
|
lazy.Assert.equal(
|
|
typeof data.settings.sandbox.effectiveContentProcessLevel,
|
|
"number",
|
|
"sandbox.effectiveContentProcessLevel must have the correct type"
|
|
);
|
|
}
|
|
|
|
if (data.settings.sandbox.contentWin32kLockdownState !== null) {
|
|
lazy.Assert.equal(
|
|
typeof data.settings.sandbox.contentWin32kLockdownState,
|
|
"number",
|
|
"sandbox.contentWin32kLockdownState must have the correct type"
|
|
);
|
|
|
|
let win32kLockdownState =
|
|
data.settings.sandbox.contentWin32kLockdownState;
|
|
lazy.Assert.ok(win32kLockdownState >= 1 && win32kLockdownState <= 17);
|
|
}
|
|
|
|
// Check "defaultSearchEngine" separately, as it can either be undefined or string.
|
|
if ("defaultSearchEngine" in data.settings) {
|
|
this.checkString(data.settings.defaultSearchEngine);
|
|
lazy.Assert.equal(typeof data.settings.defaultSearchEngineData, "object");
|
|
}
|
|
|
|
if ("defaultPrivateSearchEngineData" in data.settings) {
|
|
lazy.Assert.equal(
|
|
typeof data.settings.defaultPrivateSearchEngineData,
|
|
"object"
|
|
);
|
|
}
|
|
|
|
if ((gIsWindows || gIsMac) && AppConstants.MOZ_BUILD_APP == "browser") {
|
|
lazy.Assert.equal(typeof data.settings.attribution, "object");
|
|
lazy.Assert.equal(data.settings.attribution.source, "google.com");
|
|
lazy.Assert.equal(data.settings.attribution.dlsource, "unittest");
|
|
}
|
|
|
|
this.checkIntlSettings(data.settings);
|
|
},
|
|
|
|
checkIntlSettings({ intl }) {
|
|
let fields = [
|
|
"requestedLocales",
|
|
"availableLocales",
|
|
"appLocales",
|
|
"acceptLanguages",
|
|
];
|
|
|
|
for (let field of fields) {
|
|
lazy.Assert.ok(Array.isArray(intl[field]), `${field} is an array`);
|
|
}
|
|
|
|
// These fields may be null if they aren't ready yet. This is mostly to deal
|
|
// with test failures on Android, but they aren't guaranteed to exist.
|
|
let optionalFields = ["systemLocales", "regionalPrefsLocales"];
|
|
|
|
for (let field of optionalFields) {
|
|
let isArray = Array.isArray(intl[field]);
|
|
let isNull = intl[field] === null;
|
|
lazy.Assert.ok(isArray || isNull, `${field} is an array or null`);
|
|
}
|
|
},
|
|
|
|
checkProfileSection(data) {
|
|
lazy.Assert.ok(
|
|
"profile" in data,
|
|
"There must be a profile section in Environment."
|
|
);
|
|
lazy.Assert.equal(
|
|
data.profile.creationDate,
|
|
truncateToDays(PROFILE_CREATION_DATE_MS)
|
|
);
|
|
lazy.Assert.equal(
|
|
data.profile.resetDate,
|
|
truncateToDays(PROFILE_RESET_DATE_MS)
|
|
);
|
|
lazy.Assert.equal(
|
|
data.profile.firstUseDate,
|
|
truncateToDays(PROFILE_FIRST_USE_MS)
|
|
);
|
|
lazy.Assert.equal(
|
|
data.profile.recoveredFromBackup,
|
|
truncateToDays(PROFILE_RECOVERED_FROM_BACKUP)
|
|
);
|
|
},
|
|
|
|
checkPartnerSection(data, isInitial) {
|
|
const EXPECTED_FIELDS = {
|
|
distributionId: DISTRIBUTION_ID,
|
|
distributionVersion: DISTRIBUTION_VERSION,
|
|
partnerId: PARTNER_ID,
|
|
distributor: DISTRIBUTOR_NAME,
|
|
distributorChannel: DISTRIBUTOR_CHANNEL,
|
|
};
|
|
|
|
lazy.Assert.ok(
|
|
"partner" in data,
|
|
"There must be a partner section in Environment."
|
|
);
|
|
|
|
for (let f in EXPECTED_FIELDS) {
|
|
let expected = isInitial ? null : EXPECTED_FIELDS[f];
|
|
lazy.Assert.strictEqual(
|
|
data.partner[f],
|
|
expected,
|
|
f + " must have the correct value."
|
|
);
|
|
}
|
|
|
|
// Check that "partnerNames" exists and contains the correct element.
|
|
lazy.Assert.ok(Array.isArray(data.partner.partnerNames));
|
|
if (isInitial) {
|
|
lazy.Assert.equal(data.partner.partnerNames.length, 0);
|
|
} else {
|
|
lazy.Assert.ok(data.partner.partnerNames.includes(PARTNER_NAME));
|
|
}
|
|
},
|
|
|
|
checkGfxAdapter(data) {
|
|
const EXPECTED_ADAPTER_FIELDS_TYPES = {
|
|
description: "string",
|
|
vendorID: "string",
|
|
deviceID: "string",
|
|
subsysID: "string",
|
|
RAM: "number",
|
|
driver: "string",
|
|
driverVendor: "string",
|
|
driverVersion: "string",
|
|
driverDate: "string",
|
|
GPUActive: "boolean",
|
|
};
|
|
|
|
for (let f in EXPECTED_ADAPTER_FIELDS_TYPES) {
|
|
lazy.Assert.ok(f in data, f + " must be available.");
|
|
|
|
if (data[f]) {
|
|
// Since we have a non-null value, check if it has the correct type.
|
|
lazy.Assert.equal(
|
|
typeof data[f],
|
|
EXPECTED_ADAPTER_FIELDS_TYPES[f],
|
|
f + " must have the correct type."
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
checkSystemSection(data, assertProcessData) {
|
|
const EXPECTED_FIELDS = [
|
|
"memoryMB",
|
|
"cpu",
|
|
"os",
|
|
"hdd",
|
|
"gfx",
|
|
"appleModelId",
|
|
];
|
|
|
|
lazy.Assert.ok(
|
|
"system" in data,
|
|
"There must be a system section in Environment."
|
|
);
|
|
|
|
// Make sure we have all the top level sections and fields.
|
|
for (let f of EXPECTED_FIELDS) {
|
|
lazy.Assert.ok(f in data.system, f + " must be available.");
|
|
}
|
|
|
|
lazy.Assert.ok(
|
|
Number.isFinite(data.system.memoryMB),
|
|
"MemoryMB must be a number."
|
|
);
|
|
|
|
if (assertProcessData) {
|
|
if (gIsWindows || gIsMac || gIsLinux) {
|
|
let EXTRA_CPU_FIELDS = [
|
|
"cores",
|
|
"model",
|
|
"family",
|
|
"stepping",
|
|
"l2cacheKB",
|
|
"l3cacheKB",
|
|
"speedMHz",
|
|
"vendor",
|
|
"name",
|
|
];
|
|
|
|
for (let f of EXTRA_CPU_FIELDS) {
|
|
// Note this is testing TelemetryEnvironment.js only, not that the
|
|
// values are valid - null is the fallback.
|
|
lazy.Assert.ok(
|
|
f in data.system.cpu,
|
|
f + " must be available under cpu."
|
|
);
|
|
}
|
|
|
|
if (gIsWindows) {
|
|
lazy.Assert.equal(
|
|
typeof data.system.isWow64,
|
|
"boolean",
|
|
"isWow64 must be available on Windows and have the correct type."
|
|
);
|
|
lazy.Assert.equal(
|
|
typeof data.system.isWowARM64,
|
|
"boolean",
|
|
"isWowARM64 must be available on Windows and have the correct type."
|
|
);
|
|
lazy.Assert.equal(
|
|
typeof data.system.hasWinPackageId,
|
|
"boolean",
|
|
"hasWinPackageId must be available on Windows and have the correct type."
|
|
);
|
|
// This is only sent for Mozilla produced MSIX packages
|
|
lazy.Assert.ok(
|
|
!("winPackageFamilyName" in data.system) ||
|
|
data.system.winPackageFamilyName === null ||
|
|
typeof data.system.winPackageFamilyName === "string",
|
|
"winPackageFamilyName must be a string if non null"
|
|
);
|
|
lazy.Assert.ok(
|
|
"virtualMaxMB" in data.system,
|
|
"virtualMaxMB must be available."
|
|
);
|
|
lazy.Assert.ok(
|
|
Number.isFinite(data.system.virtualMaxMB),
|
|
"virtualMaxMB must be a number."
|
|
);
|
|
|
|
for (let f of [
|
|
"count",
|
|
"model",
|
|
"family",
|
|
"stepping",
|
|
"l2cacheKB",
|
|
"l3cacheKB",
|
|
"speedMHz",
|
|
]) {
|
|
lazy.Assert.ok(
|
|
Number.isFinite(data.system.cpu[f]),
|
|
f + " must be a number if non null."
|
|
);
|
|
}
|
|
}
|
|
|
|
// These should be numbers if they are not null
|
|
for (let f of [
|
|
"count",
|
|
"model",
|
|
"family",
|
|
"stepping",
|
|
"l2cacheKB",
|
|
"l3cacheKB",
|
|
"speedMHz",
|
|
]) {
|
|
lazy.Assert.ok(
|
|
!(f in data.system.cpu) ||
|
|
data.system.cpu[f] === null ||
|
|
Number.isFinite(data.system.cpu[f]),
|
|
f + " must be a number if non null."
|
|
);
|
|
}
|
|
|
|
// We insist these are available
|
|
for (let f of ["cores"]) {
|
|
lazy.Assert.ok(
|
|
!(f in data.system.cpu) || Number.isFinite(data.system.cpu[f]),
|
|
f + " must be a number if non null."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
let cpuData = data.system.cpu;
|
|
|
|
lazy.Assert.ok(
|
|
Array.isArray(cpuData.extensions),
|
|
"CPU extensions must be available."
|
|
);
|
|
|
|
let osData = data.system.os;
|
|
lazy.Assert.ok(this.checkNullOrString(osData.name));
|
|
lazy.Assert.ok(this.checkNullOrString(osData.version));
|
|
lazy.Assert.ok(this.checkNullOrString(osData.locale));
|
|
|
|
// Service pack is only available on Windows.
|
|
if (gIsWindows) {
|
|
lazy.Assert.ok(
|
|
Number.isFinite(osData.servicePackMajor),
|
|
"ServicePackMajor must be a number."
|
|
);
|
|
lazy.Assert.ok(
|
|
Number.isFinite(osData.servicePackMinor),
|
|
"ServicePackMinor must be a number."
|
|
);
|
|
if ("windowsBuildNumber" in osData) {
|
|
// This might not be available on all Windows platforms.
|
|
lazy.Assert.ok(
|
|
Number.isFinite(osData.windowsBuildNumber),
|
|
"windowsBuildNumber must be a number."
|
|
);
|
|
}
|
|
if ("windowsUBR" in osData) {
|
|
// This might not be available on all Windows platforms.
|
|
lazy.Assert.ok(
|
|
osData.windowsUBR === null || Number.isFinite(osData.windowsUBR),
|
|
"windowsUBR must be null or a number."
|
|
);
|
|
}
|
|
} else if (gIsLinux) {
|
|
lazy.Assert.ok(this.checkNullOrString(osData.distro));
|
|
lazy.Assert.ok(this.checkNullOrString(osData.distroVersion));
|
|
}
|
|
|
|
for (let disk of EXPECTED_HDD_FIELDS) {
|
|
lazy.Assert.ok(this.checkNullOrString(data.system.hdd[disk].model));
|
|
lazy.Assert.ok(this.checkNullOrString(data.system.hdd[disk].revision));
|
|
lazy.Assert.ok(this.checkNullOrString(data.system.hdd[disk].type));
|
|
}
|
|
|
|
let gfxData = data.system.gfx;
|
|
lazy.Assert.ok("D2DEnabled" in gfxData);
|
|
lazy.Assert.ok("DWriteEnabled" in gfxData);
|
|
lazy.Assert.ok("Headless" in gfxData);
|
|
lazy.Assert.ok("TargetFrameRate" in gfxData);
|
|
lazy.Assert.equal(typeof gfxData.TargetFrameRate, "number");
|
|
lazy.Assert.ok("textScaleFactor" in gfxData);
|
|
if (gIsWindows) {
|
|
lazy.Assert.equal(typeof gfxData.D2DEnabled, "boolean");
|
|
lazy.Assert.equal(typeof gfxData.DWriteEnabled, "boolean");
|
|
}
|
|
|
|
lazy.Assert.ok("adapters" in gfxData);
|
|
lazy.Assert.ok(
|
|
!!gfxData.adapters.length,
|
|
"There must be at least one GFX adapter."
|
|
);
|
|
for (let adapter of gfxData.adapters) {
|
|
this.checkGfxAdapter(adapter);
|
|
}
|
|
lazy.Assert.equal(typeof gfxData.adapters[0].GPUActive, "boolean");
|
|
lazy.Assert.ok(
|
|
gfxData.adapters[0].GPUActive,
|
|
"The first GFX adapter must be active."
|
|
);
|
|
|
|
lazy.Assert.ok(Array.isArray(gfxData.monitors));
|
|
if (gIsWindows || gIsMac || gIsLinux) {
|
|
lazy.Assert.ok(
|
|
gfxData.monitors.length >= 1,
|
|
"There is at least one monitor."
|
|
);
|
|
lazy.Assert.equal(typeof gfxData.monitors[0].screenWidth, "number");
|
|
lazy.Assert.equal(typeof gfxData.monitors[0].screenHeight, "number");
|
|
lazy.Assert.equal(
|
|
typeof gfxData.monitors[0].defaultCSSScaleFactor,
|
|
"number"
|
|
);
|
|
lazy.Assert.equal(
|
|
typeof gfxData.monitors[0].contentsScaleFactor,
|
|
"number"
|
|
);
|
|
if (gIsWindows) {
|
|
lazy.Assert.equal(typeof gfxData.monitors[0].refreshRate, "number");
|
|
lazy.Assert.equal(typeof gfxData.monitors[0].pseudoDisplay, "boolean");
|
|
}
|
|
}
|
|
|
|
lazy.Assert.equal(typeof gfxData.features, "object");
|
|
lazy.Assert.equal(typeof gfxData.features.compositor, "string");
|
|
|
|
lazy.Assert.equal(typeof gfxData.features.gpuProcess, "object");
|
|
lazy.Assert.equal(typeof gfxData.features.gpuProcess.status, "string");
|
|
|
|
try {
|
|
// If we've not got nsIGfxInfoDebug, then this will throw and stop us doing
|
|
// this test.
|
|
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
|
|
Ci.nsIGfxInfoDebug
|
|
);
|
|
|
|
if (gIsWindows || gIsMac) {
|
|
lazy.Assert.equal(GFX_VENDOR_ID, gfxData.adapters[0].vendorID);
|
|
lazy.Assert.equal(GFX_DEVICE_ID, gfxData.adapters[0].deviceID);
|
|
}
|
|
|
|
let features = gfxInfo.getFeatures();
|
|
lazy.Assert.equal(features.compositor, gfxData.features.compositor);
|
|
lazy.Assert.equal(
|
|
features.gpuProcess.status,
|
|
gfxData.features.gpuProcess.status
|
|
);
|
|
lazy.Assert.equal(features.opengl, gfxData.features.opengl);
|
|
lazy.Assert.equal(features.webgl, gfxData.features.webgl);
|
|
} catch (e) {}
|
|
|
|
if (gIsMac) {
|
|
lazy.Assert.ok(this.checkString(data.system.appleModelId));
|
|
} else {
|
|
lazy.Assert.ok(this.checkNullOrString(data.system.appleModelId));
|
|
}
|
|
|
|
// This feature is only available on Windows
|
|
if (AppConstants.platform == "win") {
|
|
lazy.Assert.ok(
|
|
"sec" in data.system,
|
|
"sec must be available under data.system"
|
|
);
|
|
|
|
let SEC_FIELDS = ["antivirus", "antispyware", "firewall"];
|
|
for (let f of SEC_FIELDS) {
|
|
lazy.Assert.ok(
|
|
f in data.system.sec,
|
|
f + " must be available under data.system.sec"
|
|
);
|
|
|
|
let value = data.system.sec[f];
|
|
// value is null on Windows Server
|
|
lazy.Assert.ok(
|
|
value === null || Array.isArray(value),
|
|
f + " must be either null or an array"
|
|
);
|
|
if (Array.isArray(value)) {
|
|
for (let product of value) {
|
|
lazy.Assert.equal(
|
|
typeof product,
|
|
"string",
|
|
"Each element of " + f + " must be a string"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
checkActiveAddon(data, partialRecord) {
|
|
let signedState = "number";
|
|
// system add-ons have an undefined signState
|
|
if (data.isSystem) {
|
|
signedState = "undefined";
|
|
}
|
|
|
|
const EXPECTED_ADDON_FIELDS_TYPES = {
|
|
version: "string",
|
|
scope: "number",
|
|
type: "string",
|
|
updateDay: "number",
|
|
isSystem: "boolean",
|
|
isWebExtension: "boolean",
|
|
multiprocessCompatible: "boolean",
|
|
};
|
|
|
|
const FULL_ADDON_FIELD_TYPES = {
|
|
blocklisted: "boolean",
|
|
name: "string",
|
|
userDisabled: "boolean",
|
|
appDisabled: "boolean",
|
|
foreignInstall: "boolean",
|
|
hasBinaryComponents: "boolean",
|
|
installDay: "number",
|
|
signedState,
|
|
};
|
|
|
|
let fields = EXPECTED_ADDON_FIELDS_TYPES;
|
|
if (!partialRecord) {
|
|
fields = Object.assign({}, fields, FULL_ADDON_FIELD_TYPES);
|
|
}
|
|
|
|
for (let [name, type] of Object.entries(fields)) {
|
|
lazy.Assert.ok(name in data, name + " must be available.");
|
|
lazy.Assert.equal(
|
|
typeof data[name],
|
|
type,
|
|
name + " must have the correct type."
|
|
);
|
|
}
|
|
|
|
if (!partialRecord) {
|
|
// We check "description" separately, as it can be null.
|
|
lazy.Assert.ok(this.checkNullOrString(data.description));
|
|
}
|
|
},
|
|
|
|
checkTheme(data) {
|
|
const EXPECTED_THEME_FIELDS_TYPES = {
|
|
id: "string",
|
|
blocklisted: "boolean",
|
|
name: "string",
|
|
userDisabled: "boolean",
|
|
appDisabled: "boolean",
|
|
version: "string",
|
|
scope: "number",
|
|
foreignInstall: "boolean",
|
|
installDay: "number",
|
|
updateDay: "number",
|
|
};
|
|
|
|
for (let f in EXPECTED_THEME_FIELDS_TYPES) {
|
|
lazy.Assert.ok(f in data, f + " must be available.");
|
|
lazy.Assert.equal(
|
|
typeof data[f],
|
|
EXPECTED_THEME_FIELDS_TYPES[f],
|
|
f + " must have the correct type."
|
|
);
|
|
}
|
|
|
|
// We check "description" separately, as it can be null.
|
|
lazy.Assert.ok(this.checkNullOrString(data.description));
|
|
},
|
|
|
|
checkActiveGMPlugin(data) {
|
|
// GMP plugin version defaults to null until GMPDownloader runs to update it.
|
|
if (data.version) {
|
|
lazy.Assert.equal(typeof data.version, "string");
|
|
}
|
|
lazy.Assert.equal(typeof data.userDisabled, "boolean");
|
|
lazy.Assert.equal(typeof data.applyBackgroundUpdates, "number");
|
|
},
|
|
|
|
checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
|
|
const EXPECTED_FIELDS = ["activeAddons", "theme", "activeGMPlugins"];
|
|
|
|
lazy.Assert.ok(
|
|
"addons" in data,
|
|
"There must be an addons section in Environment."
|
|
);
|
|
for (let f of EXPECTED_FIELDS) {
|
|
lazy.Assert.ok(f in data.addons, f + " must be available.");
|
|
}
|
|
|
|
// Check the active addons, if available.
|
|
if (!expectBrokenAddons) {
|
|
let activeAddons = data.addons.activeAddons;
|
|
for (let addon in activeAddons) {
|
|
this.checkActiveAddon(activeAddons[addon], partialAddonsRecords);
|
|
}
|
|
}
|
|
|
|
// Check "theme" structure.
|
|
if (Object.keys(data.addons.theme).length !== 0) {
|
|
this.checkTheme(data.addons.theme);
|
|
}
|
|
|
|
// Check active GMPlugins
|
|
let activeGMPlugins = data.addons.activeGMPlugins;
|
|
for (let gmPlugin in activeGMPlugins) {
|
|
this.checkActiveGMPlugin(activeGMPlugins[gmPlugin]);
|
|
}
|
|
},
|
|
|
|
checkEnvironmentData(data, options = {}) {
|
|
const {
|
|
isInitial = false,
|
|
expectBrokenAddons = false,
|
|
assertProcessData = false,
|
|
} = options;
|
|
|
|
this.checkBuildSection(data);
|
|
this.checkSettingsSection(data);
|
|
this.checkProfileSection(data);
|
|
this.checkPartnerSection(data, isInitial);
|
|
this.checkSystemSection(data, assertProcessData);
|
|
this.checkAddonsSection(data, expectBrokenAddons);
|
|
},
|
|
};
|