Bug 1247149 - mozscreenshots: Support restricting configurations in sets. r?jaws draft
authorRand Mustafa <rndmustafa@gmail.com>
Tue, 09 Feb 2016 19:12:40 -0800
changeset 684076 b98bab52ab5044d409665fd787ad4da058f1bb60
parent 680490 4ab539ae8b5b2927e219b219d5735f2557e82425
child 687766 d7b3997b93b30df142234b5ff6217548da0733af
child 688018 6c16b1b75c6bf3910eafee42ffba0093505ccf04
child 690491 69ca21e2b0035ba3c01749a1160ea4e1e4626d88
push id85547
push userbmo:rndmustafa@gmail.com
push dateFri, 20 Oct 2017 19:26:07 +0000
reviewersjaws
bugs1247149
milestone58.0a1
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
browser/tools/mozscreenshots/browser_screenshots.js
browser/tools/mozscreenshots/moz.build
browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm
browser/tools/mozscreenshots/tests/xpcshell/.eslintrc.js
browser/tools/mozscreenshots/tests/xpcshell/test_testConfigurations.js
browser/tools/mozscreenshots/tests/xpcshell/xpcshell.ini
--- 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]