Bug 1331705 - shield-recipe-client: Do not use an XRay-ed Promise during recipe execution, r=Gijs
authorMythmon <mcooper@mozilla.com>
Tue, 17 Jan 2017 11:27:40 -0800
changeset 377289 a506d21c911efc23fd6f5d7c0d600a90b70b2277
parent 377288 f5138ecf6374787133e3381b071cd7737aa16f7a
child 377290 8f4893e390c3bdbc7c8ae3ba77ebb62c02273020
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(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");
+});