browser_parsable_script.js (4810B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /* This list allows pre-existing or 'unfixable' JS issues to remain, while we 5 * detect newly occurring issues in shipping JS. It is a list of regexes 6 * matching files which have errors: 7 */ 8 9 requestLongerTimeout(2); 10 11 const kAllowlist = new Set([ 12 /browser\/content\/browser\/places\/controller.js$/, 13 ]); 14 15 // Normally we would use reflect.sys.mjs to get Reflect.parse. However, if 16 // we do that, then all the AST data is allocated in reflect.sys.mjs's 17 // zone. That exposes a bug in our GC. The GC collects reflect.sys.mjs's 18 // zone but not the zone in which our test code lives (since no new 19 // data is being allocated in it). The cross-compartment wrappers in 20 // our zone that point to the AST data never get collected, and so the 21 // AST data itself is never collected. We need to GC both zones at 22 // once to fix the problem. 23 const init = Cc["@mozilla.org/jsreflect;1"].createInstance(); 24 init(); 25 26 /** 27 * Check if an error should be ignored due to matching one of the allowlist 28 * objects. 29 * 30 * @param uri the uri to check against the allowlist 31 * @return true if the uri should be skipped, false otherwise. 32 */ 33 function uriIsAllowed(uri) { 34 for (let allowlistItem of kAllowlist) { 35 if (allowlistItem.test(uri.spec)) { 36 return true; 37 } 38 } 39 return false; 40 } 41 42 function parsePromise(uri, parseTarget) { 43 let promise = new Promise(resolve => { 44 let xhr = new XMLHttpRequest(); 45 xhr.open("GET", uri, true); 46 xhr.onreadystatechange = function () { 47 if (this.readyState == this.DONE) { 48 let scriptText = this.responseText; 49 try { 50 info(`Checking ${parseTarget} ${uri}`); 51 let parseOpts = { 52 source: uri, 53 target: parseTarget, 54 }; 55 Reflect.parse(scriptText, parseOpts); 56 resolve(true); 57 } catch (ex) { 58 let errorMsg = "Script error reading " + uri + ": " + ex; 59 ok(false, errorMsg); 60 resolve(false); 61 } 62 } 63 }; 64 xhr.onerror = error => { 65 ok(false, "XHR error reading " + uri + ": " + error); 66 resolve(false); 67 }; 68 xhr.overrideMimeType("application/javascript"); 69 xhr.send(null); 70 }); 71 return promise; 72 } 73 74 add_task(async function checkAllTheJS() { 75 // In debug builds, even on a fast machine, collecting the file list may take 76 // more than 30 seconds, and parsing all files may take four more minutes. 77 // For this reason, this test must be explictly requested in debug builds by 78 // using the "--setpref parse=<filter>" argument to mach. You can specify: 79 // - A case-sensitive substring of the file name to test (slow). 80 // - A single absolute URI printed out by a previous run (fast). 81 // - An empty string to run the test on all files (slowest). 82 let parseRequested = Services.prefs.prefHasUserValue("parse"); 83 let parseValue = parseRequested && Services.prefs.getCharPref("parse"); 84 if (SpecialPowers.isDebugBuild) { 85 if (!parseRequested) { 86 ok( 87 true, 88 "Test disabled on debug build. To run, execute: ./mach" + 89 " mochitest-browser --setpref parse=<case_sensitive_filter>" + 90 " browser/base/content/test/general/browser_parsable_script.js" 91 ); 92 return; 93 } 94 // Request a 15 minutes timeout (30 seconds * 30) for debug builds. 95 requestLongerTimeout(30); 96 } 97 98 let uris; 99 // If an absolute URI is specified on the command line, use it immediately. 100 if (parseValue && parseValue.includes(":")) { 101 uris = [NetUtil.newURI(parseValue)]; 102 } else { 103 let appDir = Services.dirsvc.get("GreD", Ci.nsIFile); 104 // This asynchronously produces a list of URLs (sadly, mostly sync on our 105 // test infrastructure because it runs against jarfiles there, and 106 // our zipreader APIs are all sync) 107 let startTimeMs = Date.now(); 108 info("Collecting URIs"); 109 uris = await generateURIsFromDirTree(appDir, [".js", ".jsm", ".mjs"]); 110 info("Collected URIs in " + (Date.now() - startTimeMs) + "ms"); 111 112 // Apply the filter specified on the command line, if any. 113 if (parseValue) { 114 uris = uris.filter(uri => { 115 if (uri.spec.includes(parseValue)) { 116 return true; 117 } 118 info("Not checking filtered out " + uri.spec); 119 return false; 120 }); 121 } 122 } 123 124 // We create an array of promises so we can parallelize all our parsing 125 // and file loading activity: 126 await PerfTestHelpers.throttledMapPromises(uris, uri => { 127 if (uriIsAllowed(uri)) { 128 info("Not checking allowlisted " + uri.spec); 129 return undefined; 130 } 131 let target = "script"; 132 if (uriIsESModule(uri)) { 133 target = "module"; 134 } 135 return parsePromise(uri.spec, target); 136 }); 137 ok(true, "All files parsed"); 138 });