Bug 1004418 - create a mochitest to verify all JS we ship parses, r=ttaubert

--HG--
extra : rebase_source : 94af2c3b26c3fca0f3c697b56c5e7b7d6488fce6
This commit is contained in:
Gijs Kruitbosch 2014-08-19 15:43:31 +02:00
parent 7a6a4c6b03
commit bd0172679e
4 changed files with 215 additions and 102 deletions

View File

@ -64,6 +64,7 @@ support-files =
offlineQuotaNotification.cacheManifest
offlineQuotaNotification.html
page_style_sample.html
parsingTestHelpers.jsm
print_postdata.sjs
redirect_bug623155.sjs
searchSuggestionEngine.sjs
@ -342,6 +343,7 @@ skip-if = buildapp == 'mulet' || e10s # Bug 866413 - PageInfo doesn't work in e1
skip-if = e10s # Bug ?????? - test directly manipulates content
[browser_parsable_css.js]
[browser_parsable_script.js]
[browser_pinnedTabs.js]
skip-if = e10s # Bug 921905 - pinTab/unpinTab fail in e10s

View File

@ -17,6 +17,9 @@ const kWhitelist = [
{sourceName: /loop\/.*sdk-content\/.*\.css$/i /* TokBox SDK assets, see bug 1032469 */}
];
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
/**
* Check if an error should be ignored due to matching one of the whitelist
* objects defined in kWhitelist
@ -40,113 +43,12 @@ function ignoredError(aErrorObject) {
return false;
}
/**
* Returns a promise that is resolved with a list of CSS files to check,
* represented by their nsIURI objects.
*
* @param appDir the application directory to scan for CSS files (nsIFile)
*/
function generateURIsFromDirTree(appDir) {
let rv = [];
let dirQueue = [appDir.path];
return Task.spawn(function*() {
while (dirQueue.length) {
let nextDir = dirQueue.shift();
let {subdirs, cssfiles} = yield iterateOverPath(nextDir);
dirQueue = dirQueue.concat(subdirs);
rv = rv.concat(cssfiles);
}
return rv;
});
}
/* Shorthand constructor to construct an nsI(Local)File */
let LocalFile = Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath");
/**
* Uses OS.File.DirectoryIterator to asynchronously iterate over a directory.
* It returns a promise that is resolved with an object with two properties:
* - cssfiles: an array of nsIURIs corresponding to CSS that needs checking
* - subdirs: an array of paths for subdirectories we need to recurse into
* (handled by generateURIsFromDirTree above)
*
* @param path the path to check (string)
*/
function iterateOverPath(path) {
let iterator = new OS.File.DirectoryIterator(path);
let parentDir = new LocalFile(path);
let subdirs = [];
let cssfiles = [];
// Iterate through the directory
let promise = iterator.forEach(
function onEntry(entry) {
if (entry.isDir) {
let subdir = parentDir.clone();
subdir.append(entry.name);
subdirs.push(subdir.path);
} else if (entry.name.endsWith(".css")) {
let file = parentDir.clone();
file.append(entry.name);
let uriSpec = getURLForFile(file);
cssfiles.push(Services.io.newURI(uriSpec, null, null));
} else if (entry.name.endsWith(".ja")) {
let file = parentDir.clone();
file.append(entry.name);
let subentries = [uri for (uri of generateEntriesFromJarFile(file))];
cssfiles = cssfiles.concat(subentries);
}
}
);
let outerPromise = Promise.defer();
promise.then(function() {
outerPromise.resolve({cssfiles: cssfiles, subdirs: subdirs});
iterator.close();
}, function(e) {
outerPromise.reject(e);
iterator.close();
});
return outerPromise.promise;
}
/* Helper function to generate a URI spec (NB: not an nsIURI yet!)
* given an nsIFile object */
function getURLForFile(file) {
let fileHandler = Services.io.getProtocolHandler("file");
fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
return fileHandler.getURLSpecFromFile(file);
}
/**
* A generator that generates nsIURIs for CSS files found in jar files
* like omni.ja.
*
* @param jarFile an nsIFile object for the jar file that needs checking.
*/
function* generateEntriesFromJarFile(jarFile) {
const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open");
let zr = new ZipReader(jarFile);
let entryEnumerator = zr.findEntries("*.css$");
const kURIStart = getURLForFile(jarFile);
while (entryEnumerator.hasMore()) {
let entry = entryEnumerator.getNext();
let entryURISpec = "jar:" + kURIStart + "!/" + entry;
yield Services.io.newURI(entryURISpec, null, null);
}
zr.close();
}
/**
* The actual test.
*/
add_task(function checkAllTheCSS() {
let appDir = Services.dirsvc.get("XCurProcD", 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 uris = yield generateURIsFromDirTree(appDir);
let uris = yield generateURIsFromDirTree(appDir, ".css");
// Create a clean iframe to load all the files into:
let hiddenWin = Services.appShell.hiddenDOMWindow;

View File

@ -0,0 +1,83 @@
/* 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:
*/
const kWhitelist = new Set([
/defaults\/profile\/prefs.js$/,
]);
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
let {Reflect} = Cu.import("resource://gre/modules/reflect.jsm", {});
/**
* 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;
}
function parsePromise(uri) {
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;
let ast;
try {
info("Checking " + uri);
ast = Reflect.parse(scriptText);
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(function* checkAllTheJS() {
let appDir = Services.dirsvc.get("XCurProcD", 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 uris = yield generateURIsFromDirTree(appDir, [".js", ".jsm"]);
// We create an array of promises so we can parallelize all our parsing
// and file loading activity:
let allPromises = [];
for (let uri of uris) {
if (uriIsWhiteListed(uri)) {
info("Not checking " + uri.spec);
continue;
}
allPromises.push(parsePromise(uri.spec));
}
let promiseResults = yield Promise.all(allPromises);
is(promiseResults.filter((x) => !x).length, 0, "There should be 0 parsing errors");
});

View File

@ -0,0 +1,126 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
this.EXPORTED_SYMBOLS = ["generateURIsFromDirTree"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* Shorthand constructors to construct an nsI(Local)File and zip reader: */
const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath");
const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Task.jsm");
/**
* Returns a promise that is resolved with a list of files that have one of the
* extensions passed, represented by their nsIURI objects, which exist inside
* the directory passed.
*
* @param dir the directory which to scan for files (nsIFile)
* @param extensions the extensions of files we're interested in (Array).
*/
function generateURIsFromDirTree(dir, extensions) {
if (!Array.isArray(extensions)) {
extensions = [extensions];
}
let dirQueue = [dir.path];
return Task.spawn(function*() {
let rv = [];
while (dirQueue.length) {
let nextDir = dirQueue.shift();
let {subdirs, files} = yield iterateOverPath(nextDir, extensions);
dirQueue.push(...subdirs);
rv.push(...files);
}
return rv;
});
}
/**
* Uses OS.File.DirectoryIterator to asynchronously iterate over a directory.
* It returns a promise that is resolved with an object with two properties:
* - files: an array of nsIURIs corresponding to files that match the extensions passed
* - subdirs: an array of paths for subdirectories we need to recurse into
* (handled by generateURIsFromDirTree above)
*
* @param path the path to check (string)
* @param extensions the file extensions we're interested in.
*/
function iterateOverPath(path, extensions) {
let iterator = new OS.File.DirectoryIterator(path);
let parentDir = new LocalFile(path);
let subdirs = [];
let files = [];
let pathEntryIterator = (entry) => {
if (entry.isDir) {
subdirs.push(entry.path);
} else if (extensions.some((extension) => entry.name.endsWith(extension))) {
let file = parentDir.clone();
file.append(entry.name);
let uriSpec = getURLForFile(file);
files.push(Services.io.newURI(uriSpec, null, null));
} else if (entry.name.endsWith(".ja") || entry.name.endsWith(".jar")) {
let file = parentDir.clone();
file.append(entry.name);
for (let extension of extensions) {
let jarEntryIterator = generateEntriesFromJarFile(file, extension);
files.push(...jarEntryIterator);
}
}
};
return new Promise((resolve, reject) => {
Task.spawn(function* () {
try {
// Iterate through the directory
yield iterator.forEach(pathEntryIterator);
resolve({files: files, subdirs: subdirs});
} catch (ex) {
reject(ex);
} finally {
iterator.close();
}
});
});
}
/* Helper function to generate a URI spec (NB: not an nsIURI yet!)
* given an nsIFile object */
function getURLForFile(file) {
let fileHandler = Services.io.getProtocolHandler("file");
fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
return fileHandler.getURLSpecFromActualFile(file);
}
/**
* A generator that generates nsIURIs for particular files found in jar files
* like omni.ja.
*
* @param jarFile an nsIFile object for the jar file that needs checking.
* @param extension the extension we're interested in.
*/
function* generateEntriesFromJarFile(jarFile, extension) {
let zr = new ZipReader(jarFile);
let entryEnumerator = zr.findEntries("*" + extension + "$");
const kURIStart = getURLForFile(jarFile);
while (entryEnumerator.hasMore()) {
let entry = entryEnumerator.getNext();
// Ignore the JS cache which is stored in omni.ja
if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) {
continue;
}
let entryURISpec = "jar:" + kURIStart + "!/" + entry;
yield Services.io.newURI(entryURISpec, null, null);
}
zr.close();
}