Bug 1247149 - mozscreenshots: Support restricting configurations in sets. r?jaws
The initial commit was authored by Kit Cambridge. His commit enabled the user to specify a single configuration in a set e.g. Toolbars[onlyNavBar],Tabs.
The next set of commits allowed multiple configurations to be specified and also checked the validity of the configurations. Various bugs were squashed
along the way.
MozReview-Commit-ID: LTT7auJfcHa
--- a/browser/tools/mozscreenshots/browser_screenshots.js
+++ b/browser/tools/mozscreenshots/browser_screenshots.js
@@ -6,11 +6,11 @@
add_task(async function capture() {
let setsEnv = env.get("MOZSCREENSHOTS_SETS");
if (!setsEnv) {
ok(true, "MOZSCREENSHOTS_SETS wasn't specified so there's nothing to capture");
return;
}
- let sets = setsEnv.trim().split(",");
+ let sets = TestRunner.splitEnv(setsEnv.trim());
await TestRunner.start(sets);
});
--- a/browser/tools/mozscreenshots/moz.build
+++ b/browser/tools/mozscreenshots/moz.build
@@ -28,8 +28,10 @@ BROWSER_CHROME_MANIFESTS += [
'permissionPrompts/browser.ini',
'preferences/browser.ini',
'primaryUI/browser.ini',
]
TEST_DIRS += [
'mozscreenshots/extension',
]
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm
@@ -15,17 +15,17 @@ Cu.import("resource://gre/modules/FileUt
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
"resource://testing-common/BrowserTestUtils.jsm");
-Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "chrome://mozscreenshots/content/Screenshot.jsm");
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL = "extensions.mozscreenshots@mozilla.org.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
let consoleOptions = {
maxLogLevel: "info",
@@ -109,38 +109,76 @@ this.TestRunner = {
}
log.info("Done: Completed " + this.completedCombos + " out of " +
this.combos.length + " configurations.");
this.cleanup();
},
/**
+ * Helper function for loadSets. This filters out the restricted configs from setName.
+ * This was made a helper function to facilitate xpcshell unit testing.
+ * @param {String} setName - set name to be filtered e.g. "Toolbars[onlyNavBar,allToolbars]"
+ * @return {Object} Returns an object with two values: the filtered set name and a set of
+ * restricted configs.
+ */
+ filterRestrictions(setName) {
+ let match = /\[([^\]]+)\]$/.exec(setName);
+ if (!match) {
+ throw new Error(`Invalid restrictions in ${setName}`);
+ }
+ // Trim the restrictions from the set name.
+ setName = setName.slice(0, match.index);
+ let restrictions = match[1].split(",").reduce((set, name) => set.add(name.trim())
+ , new Set());
+
+ return { trimmedSetName: setName, restrictions };
+ },
+
+ /**
* Load sets of configurations from JSMs.
* @param {String[]} setNames - array of set names (e.g. ["Tabs", "WindowSize"].
* @return {Object[]} Array of sets containing `name` and `configurations` properties.
*/
loadSets(setNames) {
let sets = [];
for (let setName of setNames) {
+ let restrictions = null;
+ if (setName.includes("[")) {
+ let filteredData = this.filterRestrictions(setName);
+ setName = filteredData.trimmedSetName;
+ restrictions = filteredData.restrictions;
+ }
try {
let imported = {};
Cu.import("chrome://mozscreenshots/content/configurations/" + setName + ".jsm",
imported);
imported[setName].init(this._libDir);
let configurationNames = Object.keys(imported[setName].configurations);
if (!configurationNames.length) {
throw new Error(setName + " has no configurations for this environment");
}
+ // Checks to see if nonexistent configuration have been specified
+ if (restrictions) {
+ let incorrectConfigs = [...restrictions].filter(r => !configurationNames.includes(r));
+ if (incorrectConfigs.length) {
+ throw new Error("non existent configurations: " + incorrectConfigs);
+ }
+ }
+ let configurations = {};
for (let config of configurationNames) {
// Automatically set the name property of the configuration object to
// its name from the configuration object.
imported[setName].configurations[config].name = config;
+ // Filter restricted configurations.
+ if (!restrictions || restrictions.has(config)) {
+ configurations[config] = imported[setName].configurations[config];
+ }
}
- sets.push(imported[setName].configurations);
+ sets.push(configurations);
} catch (ex) {
log.error("Error loading set: " + setName);
log.error(ex);
throw ex;
}
}
return sets;
},
@@ -234,16 +272,55 @@ this.TestRunner = {
return delayedScreenshot();
},
_comboName(combo) {
return combo.reduce(function(a, b) {
return a + "_" + b.name;
}, "");
},
+
+ /**
+ * Finds the index of the first comma that is not enclosed within square brackets.
+ * @param {String} envVar - the string that needs to be searched
+ * @return {Integer} index of valid comma or -1 if not found.
+ */
+ findComma(envVar) {
+ let nestingDepth = 0;
+ for (let i = 0; i < envVar.length; i++) {
+ if (envVar[i] === "[") {
+ nestingDepth += 1;
+ } else if (envVar[i] === "]") {
+ nestingDepth -= 1;
+ } else if (envVar[i] === "," && nestingDepth === 0) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ /**
+ * Splits the environment variable around commas not enclosed in brackets.
+ * @param {String} envVar - The environment variable
+ * @return {String[]} Array of strings containing the configurations
+ * e.g. ["Toolbars[onlyNavBar,allToolbars]","DevTools[jsdebugger,webconsole]","Tabs"]
+ */
+ splitEnv(envVar) {
+ let result = [];
+
+ let commaIndex = this.findComma(envVar);
+ while (commaIndex != -1) {
+ result.push(envVar.slice(0, commaIndex).trim());
+ envVar = envVar.slice(commaIndex + 1);
+ commaIndex = this.findComma(envVar);
+ }
+ result.push(envVar.trim());
+ return result;
+ }
};
/**
* Helper to lazily compute the Cartesian product of all of the sets of configurations.
**/
function LazyProduct(sets) {
/**
* An entry for each set with the value being:
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/tests/xpcshell/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "plugin:mozilla/xpcshell-test"
+ ]
+};
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/tests/xpcshell/test_testConfigurations.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+Cu.import("resource://test/TestRunner.jsm");
+
+add_task(async function capture() {
+ equal(TestRunner.findComma("Toolbars,Devs"), 8);
+ equal(TestRunner.findComma("Toolbars"), -1);
+ equal(TestRunner.findComma("Toolbars[onlyNavBar,allToolbars],DevTools"), 32);
+ equal(TestRunner.findComma("Toolbars[onlyNavBar,allToolbars],DevTools[bottomToolbox,sideToolbox]"), 32);
+ equal(TestRunner.findComma("Toolbars[[onlyNavBar],[]], Tabs[ [fiveTabbed], [[[fourPinned]]] ]"), 25);
+ equal(TestRunner.findComma("[[[[[[[[[[[[[[[[[[[[]]"), -1);
+ equal(TestRunner.findComma("Preferences[[[[[,]]]]]"), -1);
+
+ deepEqual(TestRunner.splitEnv("Toolbars"), ["Toolbars"]);
+ deepEqual(TestRunner.splitEnv("Buttons,Tabs"), ["Buttons", "Tabs"]);
+ deepEqual(TestRunner.splitEnv("Buttons, Tabs"), ["Buttons", "Tabs"]);
+ deepEqual(TestRunner.splitEnv(" Buttons , Tabs "), ["Buttons", "Tabs"]);
+ deepEqual(TestRunner.splitEnv("Toolbars[onlyNavBar,allToolbars],DevTools"), ["Toolbars[onlyNavBar,allToolbars]", "DevTools"]);
+ deepEqual(TestRunner.splitEnv("Toolbars[onlyNavBar,allToolbars],DevTools[bottomToolbox]"), ["Toolbars[onlyNavBar,allToolbars]", "DevTools[bottomToolbox]"]);
+ deepEqual(TestRunner.splitEnv("Toolbars[onlyNavBar,allToolbars],DevTools[bottomToolbox],Tabs"), ["Toolbars[onlyNavBar,allToolbars]", "DevTools[bottomToolbox]", "Tabs"]);
+
+ let filteredData = TestRunner.filterRestrictions("Toolbars[onlyNavBar]");
+ equal(filteredData.trimmedSetName, "Toolbars");
+ ok(filteredData.restrictions.has("onlyNavBar"));
+
+ filteredData = TestRunner.filterRestrictions("DevTools[bottomToolbox,sideToolbox]");
+ equal(filteredData.trimmedSetName, "DevTools");
+ ok(filteredData.restrictions.has("bottomToolbox"));
+ ok(filteredData.restrictions.has("sideToolbox"));
+});
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+support-files = ../../mozscreenshots/extension/TestRunner.jsm
+
+[test_testConfigurations.js]