Bug 1331705 - shield-recipe-client: Do not use an XRay-ed Promise during recipe execution, r=Gijs
☠☠ backed out by d5479d988aff ☠ ☠
authorMythmon <mcooper@mozilla.com>
Tue, 17 Jan 2017 11:27:40 -0800
changeset 377119 ce90a9d52e86c1edc7e194a23eabc1548e4dc104
parent 377118 9ca73fb60c36714d16b9a74cb36e5d80f4162f65
child 377120 9a193223d4aa501014257551ffe659e6568aef27
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1331705
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1331705 - shield-recipe-client: Do not use an XRay-ed Promise during recipe execution, r=Gijs MozReview-Commit-ID: DEM6lUiCHnj
browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
browser/extensions/shield-recipe-client/test/browser.ini
browser/extensions/shield-recipe-client/test/browser_RecipeRunner.js
--- a/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
+++ b/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
@@ -123,40 +123,57 @@ this.RecipeRunner = {
   },
 
   /**
    * Execute a recipe by fetching it action and executing it.
    * @param  {Object} recipe A recipe to execute
    * @promise Resolves when the action has executed
    */
   executeRecipe: Task.async(function* (recipe, extraContext) {
-    const sandboxManager = new SandboxManager();
-    const {sandbox} = sandboxManager;
-
     const action = yield NormandyApi.fetchAction(recipe.action);
     const response = yield fetch(action.implementation_url);
 
     const actionScript = yield response.text();
-    const prepScript = `
-      var pendingAction = null;
+    yield this.executeAction(recipe, extraContext, actionScript);
+  }),
 
-      function registerAction(name, Action) {
-        let a = new Action(sandboxedDriver, sandboxedRecipe);
-        pendingAction = a.execute()
-          .catch(err => sandboxedDriver.log(err, 'error'));
+  /**
+   * Execute an action in a sandbox for a specific recipe.
+   * @param  {Object} recipe A recipe to execute
+   * @param  {Object} extraContext Extra data about the user, see NormandyDriver
+   * @param  {String} actionScript The JavaScript for the action to execute.
+   * @promise Resolves or rejects when the action has executed or failed.
+   */
+  executeAction: function(recipe, extraContext, actionScript) {
+    return new Promise((resolve, reject) => {
+      const sandboxManager = new SandboxManager();
+      const {sandbox} = sandboxManager;
+      const prepScript = `
+        function registerAction(name, Action) {
+          let a = new Action(sandboxedDriver, sandboxedRecipe);
+          a.execute()
+            .then(actionFinished)
+            .catch(err => sandboxedDriver.log(err, 'error'));
+        };
+
+        window.registerAction = registerAction;
+        window.setTimeout = sandboxedDriver.setTimeout;
+        window.clearTimeout = sandboxedDriver.clearTimeout;
+      `;
+
+      const driver = new NormandyDriver(sandboxManager, extraContext);
+      sandbox.sandboxedDriver = Cu.cloneInto(driver, sandbox, {cloneFunctions: true});
+      sandbox.sandboxedRecipe = Cu.cloneInto(recipe, sandbox);
+      sandbox.actionFinished = result => {
+        sandboxManager.removeHold("recipeExecution");
+        resolve(result);
+      };
+      sandbox.actionFailed = err => {
+        sandboxManager.removeHold("recipeExecution");
+        reject(err);
       };
 
-      window.registerAction = registerAction;
-      window.setTimeout = sandboxedDriver.setTimeout;
-      window.clearTimeout = sandboxedDriver.clearTimeout;
-    `;
-
-    const driver = new NormandyDriver(sandboxManager, extraContext);
-    sandbox.sandboxedDriver = Cu.cloneInto(driver, sandbox, {cloneFunctions: true});
-    sandbox.sandboxedRecipe = Cu.cloneInto(recipe, sandbox);
-
-    Cu.evalInSandbox(prepScript, sandbox);
-    Cu.evalInSandbox(actionScript, sandbox);
-
-    sandboxManager.addHold("recipeExecution");
-    sandbox.pendingAction.then(() => sandboxManager.removeHold("recipeExecution"));
-  }),
+      sandboxManager.addHold("recipeExecution");
+      Cu.evalInSandbox(prepScript, sandbox);
+      Cu.evalInSandbox(actionScript, sandbox);
+    });
+  },
 };
--- a/browser/extensions/shield-recipe-client/test/browser.ini
+++ b/browser/extensions/shield-recipe-client/test/browser.ini
@@ -1,8 +1,9 @@
 [browser_driver_uuids.js]
 [browser_env_expressions.js]
 [browser_EventEmitter.js]
 [browser_Storage.js]
 [browser_Heartbeat.js]
 [browser_NormandyApi.js]
   support-files =
     test_server.sjs
+[browser_RecipeRunner.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/shield-recipe-client/test/browser_RecipeRunner.js
@@ -0,0 +1,29 @@
+"use strict";
+
+const {utils: Cu} = Components;
+Cu.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this);
+
+add_task(function*() {
+  // Test that RecipeRunner can execute a basic recipe/action.
+  const recipe = {
+    foo: "bar",
+  };
+  const actionScript = `
+    class TestAction {
+      constructor(driver, recipe) {
+        this.recipe = recipe;
+      }
+
+      execute() {
+        return new Promise(resolve => {
+          resolve(this.recipe.foo);
+        });
+      }
+    }
+
+    registerAction('test-action', TestAction);
+  `;
+
+  const result = yield RecipeRunner.executeAction(recipe, {}, actionScript);
+  is(result, "bar", "Recipe executed correctly");
+});