fune/browser/base/content/test/static/browser_parsable_script.js
Kris Maglione a78b44be00 Bug 1777886 - Check that denylist/intermittent files actually exist in startup perf tests. r=florian
This will require that entries for renamed files be updated so that they don't
accidentally start being loaded under the new name later. It also prevents
dead code entries from sticking around after their targets are removed.

Using `throttledMapPromises` is probably not strictly necessary given the
small number of entries in most lists, but since it already exists, we may as
well use it here.

Differential Revision: https://phabricator.services.mozilla.com/D150921
2022-07-08 00:59:02 +00:00

168 lines
5.5 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* This list allows pre-existing or 'unfixable' JS issues to remain, while we
* detect newly occurring issues in shipping JS. It is a list of regexes
* matching files which have errors:
*/
requestLongerTimeout(2);
const kWhitelist = new Set([
/browser\/content\/browser\/places\/controller.js$/,
]);
const kESModuleList = new Set([
/browser\/lockwise-card.js$/,
/browser\/monitor-card.js$/,
/browser\/proxy-card.js$/,
/browser\/vpn-card.js$/,
/browser\/content\/browser\/certerror\/aboutNetError\.js$/,
/toolkit\/content\/global\/certviewer\/components\/.*\.js$/,
/toolkit\/content\/global\/certviewer\/.*\.js$/,
/chrome\/pdfjs\/content\/web\/.*\.js$/,
]);
// Normally we would use reflect.jsm to get Reflect.parse. However, if
// we do that, then all the AST data is allocated in reflect.jsm's
// zone. That exposes a bug in our GC. The GC collects reflect.jsm's
// zone but not the zone in which our test code lives (since no new
// data is being allocated in it). The cross-compartment wrappers in
// our zone that point to the AST data never get collected, and so the
// AST data itself is never collected. We need to GC both zones at
// once to fix the problem.
const init = Cc["@mozilla.org/jsreflect;1"].createInstance();
init();
/**
* Check if an error should be ignored due to matching one of the whitelist
* objects defined in kWhitelist
*
* @param uri the uri to check against the whitelist
* @return true if the uri should be skipped, false otherwise.
*/
function uriIsWhiteListed(uri) {
for (let whitelistItem of kWhitelist) {
if (whitelistItem.test(uri.spec)) {
return true;
}
}
return false;
}
/**
* Check if a URI should be parsed as an ES module.
*
* @param uri the uri to check against the ES module list
* @return true if the uri should be parsed as a module, otherwise parse it as a script.
*/
function uriIsESModule(uri) {
if (uri.filePath.endsWith(".mjs")) {
return true;
}
for (let whitelistItem of kESModuleList) {
if (whitelistItem.test(uri.spec)) {
return true;
}
}
return false;
}
function parsePromise(uri, parseTarget) {
let promise = new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", uri, true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
let scriptText = this.responseText;
try {
info(`Checking ${parseTarget} ${uri}`);
let parseOpts = {
source: uri,
target: parseTarget,
};
Reflect.parse(scriptText, parseOpts);
resolve(true);
} catch (ex) {
let errorMsg = "Script error reading " + uri + ": " + ex;
ok(false, errorMsg);
resolve(false);
}
}
};
xhr.onerror = error => {
ok(false, "XHR error reading " + uri + ": " + error);
resolve(false);
};
xhr.overrideMimeType("application/javascript");
xhr.send(null);
});
return promise;
}
add_task(async function checkAllTheJS() {
// In debug builds, even on a fast machine, collecting the file list may take
// more than 30 seconds, and parsing all files may take four more minutes.
// For this reason, this test must be explictly requested in debug builds by
// using the "--setpref parse=<filter>" argument to mach. You can specify:
// - A case-sensitive substring of the file name to test (slow).
// - A single absolute URI printed out by a previous run (fast).
// - An empty string to run the test on all files (slowest).
let parseRequested = Services.prefs.prefHasUserValue("parse");
let parseValue = parseRequested && Services.prefs.getCharPref("parse");
if (SpecialPowers.isDebugBuild) {
if (!parseRequested) {
ok(
true,
"Test disabled on debug build. To run, execute: ./mach" +
" mochitest-browser --setpref parse=<case_sensitive_filter>" +
" browser/base/content/test/general/browser_parsable_script.js"
);
return;
}
// Request a 15 minutes timeout (30 seconds * 30) for debug builds.
requestLongerTimeout(30);
}
let uris;
// If an absolute URI is specified on the command line, use it immediately.
if (parseValue && parseValue.includes(":")) {
uris = [NetUtil.newURI(parseValue)];
} else {
let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
// our zipreader APIs are all sync)
let startTimeMs = Date.now();
info("Collecting URIs");
uris = await generateURIsFromDirTree(appDir, [".js", ".jsm", ".mjs"]);
info("Collected URIs in " + (Date.now() - startTimeMs) + "ms");
// Apply the filter specified on the command line, if any.
if (parseValue) {
uris = uris.filter(uri => {
if (uri.spec.includes(parseValue)) {
return true;
}
info("Not checking filtered out " + uri.spec);
return false;
});
}
}
// We create an array of promises so we can parallelize all our parsing
// and file loading activity:
await PerfTestHelpers.throttledMapPromises(uris, uri => {
if (uriIsWhiteListed(uri)) {
info("Not checking whitelisted " + uri.spec);
return undefined;
}
let target = "script";
if (uriIsESModule(uri)) {
target = "module";
}
return parsePromise(uri.spec, target);
});
ok(true, "All files parsed");
});