Bug 904549 - Make browser_dbg_cmd.js wait for helpers.audit to complete; r=past,anton,nfitzgerald
authorJoe Walker <jwalker@mozilla.com>
Thu, 29 Aug 2013 14:00:52 +0100
changeset 157841 8081dc2924bc037c3b02c7d829b320aae440ba9d
parent 157839 996762943f301a71b2e70c1d4bfee618d9d59b3d
child 157842 3483a63fd3ca923b5c4ab389f8d9c95ec53e5703
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast, anton, nfitzgerald
bugs904549
milestone26.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 904549 - Make browser_dbg_cmd.js wait for helpers.audit to complete; r=past,anton,nfitzgerald
browser/devtools/commandline/test/helpers.js
browser/devtools/debugger/test/browser_dbg_cmd.js
browser/devtools/debugger/test/browser_dbg_cmd_blackbox.js
browser/devtools/profiler/commands.js
browser/devtools/profiler/test/browser_profiler_cmd.js
browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
--- a/browser/devtools/commandline/test/helpers.js
+++ b/browser/devtools/commandline/test/helpers.js
@@ -104,16 +104,87 @@ helpers.addTab = function(url, callback,
   };
 
   options.browser.contentWindow.location = url;
   options.browser.addEventListener("load", onPageLoad, true);
 
   return deferred.promise;
 };
 
+helpers.openTab = function(url, options) {
+  var deferred = promise.defer();
+
+  waitForExplicitFinish();
+
+  options = options || {};
+  options.chromeWindow = options.chromeWindow || window;
+  options.isFirefox = true;
+
+  var tabbrowser = options.chromeWindow.gBrowser;
+  options.tab = tabbrowser.addTab();
+  tabbrowser.selectedTab = options.tab;
+  options.browser = tabbrowser.getBrowserForTab(options.tab);
+  options.target = TargetFactory.forTab(options.tab);
+
+  options.browser.contentWindow.location = url;
+
+  var onPageLoad = function() {
+    options.browser.removeEventListener("load", onPageLoad, true);
+    options.document = options.browser.contentDocument;
+    options.window = options.document.defaultView;
+
+    deferred.resolve(options);
+  };
+
+  options.browser.addEventListener("load", onPageLoad, true);
+
+  return deferred.promise;
+};
+
+helpers.closeTab = function(options) {
+  options.chromeWindow.gBrowser.removeTab(options.tab);
+
+  delete options.window;
+  delete options.document;
+
+  delete options.target;
+  delete options.browser;
+  delete options.tab;
+
+  delete options.chromeWindow;
+  delete options.isFirefox;
+
+  return promise.resolve(undefined);
+};
+
+helpers.openToolbar = function(options) {
+  var deferred = promise.defer();
+
+  options.chromeWindow.DeveloperToolbar.show(true, function() {
+    options.display = options.chromeWindow.DeveloperToolbar.display;
+
+    deferred.resolve(options);
+  });
+
+  return deferred.promise;
+};
+
+helpers.closeToolbar = function(options) {
+  options.chromeWindow.DeveloperToolbar.hide();
+  delete options.display;
+
+  return promise.resolve(undefined);
+};
+
+helpers.handleError = function(ex) {
+  console.error(ex);
+  ok(false, ex);
+  finish();
+};
+
 /**
  * Warning: For use with Firefox Mochitests only.
  *
  * As addTab, but that also opens the developer toolbar. In addition a new
  * 'display' property is added to the options object with the display from GCLI
  * in the developer toolbar
  */
 helpers.addTabWithToolbar = function(url, callback, options) {
@@ -128,16 +199,17 @@ helpers.addTabWithToolbar = function(url
         win.DeveloperToolbar.hide();
         delete innerOptions.display;
         deferred.resolve();
       };
 
       var reply = callback(innerOptions);
       promise.resolve(reply).then(cleanUp, function(error) {
         ok(false, error);
+        console.error(error);
         cleanUp();
       });
     });
     return deferred.promise;
   }, options);
 };
 
 /**
@@ -739,16 +811,23 @@ helpers._exec = function(options, name, 
   }
 
   if ('completed' in expected) {
     assert.is(output.completed,
               expected.completed,
               'output.completed false for: ' + name);
   }
 
+  if (!options.window) {
+    assert.ok(false, 'Missing options.window in \'' + name + '\'. ' +
+                     'Are you assming that helpers.audit is synchronous? ' +
+                     'It returns a promise');
+    return { output: output };
+  }
+
   if (!options.window.document.createElement) {
     assert.log('skipping output tests (missing doc.createElement) for ' + name);
 
     if (expected.error) {
       cli.logErrors = origLogErrors;
     }
     return promise.resolve({ output: output });
   }
--- a/browser/devtools/debugger/test/browser_dbg_cmd.js
+++ b/browser/devtools/debugger/test/browser_dbg_cmd.js
@@ -1,106 +1,106 @@
-/* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
-  const TEST_URI = "http://example.com/browser/browser/devtools/debugger/" +
-                   "test/browser_dbg_cmd.html";
+  let Task = Cu.import("resource://gre/modules/Task.jsm", {}).Task;
 
-  helpers.addTabWithToolbar(TEST_URI, function(options) {
-    let deferred = promise.defer();
+  return Task.spawn(function() {
+    const TEST_URI = "http://example.com/browser/browser/devtools/debugger/" +
+                     "test/browser_dbg_cmd.html";
 
-    let openDone = helpers.audit(options, [{
+    let options = yield helpers.openTab(TEST_URI);
+    yield helpers.openToolbar(options);
+
+    yield helpers.audit(options, [{
       setup: "dbg open",
       exec: { output: "", completed: false }
     }]);
 
-    openDone.then(function() {
-      gDevTools.showToolbox(options.target, "jsdebugger").then(function(toolbox) {
-        let dbg = toolbox.getCurrentPanel();
-        ok(dbg, "DebuggerPanel exists");
+    let toolbox = yield gDevTools.showToolbox(options.target, "jsdebugger");
+    let dbg = toolbox.getCurrentPanel();
+    ok(dbg, "DebuggerPanel exists");
 
-        function cmd(typed, callback) {
-          dbg._controller.activeThread.addOneTimeListener("paused", callback);
-          helpers.audit(options, [{
-            setup: typed,
-            exec: { output: "" }
-          }]);
-        }
-
-        // Wait for the initial resume...
-        dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
-          info("Starting tests");
-
-          let contentDoc = content.window.document;
-          let output = contentDoc.querySelector("input[type=text]");
-          let btnDoit = contentDoc.querySelector("input[type=button]");
+    // Wait for the initial resume...
+    let resumeDeferred = promise.defer();
+    dbg.panelWin.gClient.addOneTimeListener("resumed", () => {
+      resumeDeferred.resolve();
+    });
+    yield resumeDeferred.promise;
 
-          helpers.audit(options, [{
-            setup: "dbg list",
-            exec: { output: /browser_dbg_cmd.html/ }
-          }]);
+    yield helpers.audit(options, [{
+      setup: "dbg list",
+      exec: { output: /browser_dbg_cmd.html/ }
+    }]);
 
-          cmd("dbg interrupt", function() {
-            ok(true, "debugger is paused");
-            dbg._controller.activeThread.addOneTimeListener("resumed", function() {
-              ok(true, "debugger continued");
-              dbg._controller.activeThread.addOneTimeListener("paused", function() {
-                cmd("dbg step in", function() {
-                  cmd("dbg step in", function() {
-                    cmd("dbg step in", function() {
-                      is(output.value, "step in", "debugger stepped in");
-                      cmd("dbg step over", function() {
-                        is(output.value, "step over", "debugger stepped over");
-                        cmd("dbg step out", function() {
-                          is(output.value, "step out", "debugger stepped out");
-                          cmd("dbg continue", function() {
-                            is(output.value, "dbg continue", "debugger continued");
+    // exec a command with GCLI to resume the debugger and wait until it stops
+    let cmd = function(typed) {
+      let cmdDeferred = promise.defer();
+      dbg._controller.activeThread.addOneTimeListener("paused", ev => {
+        cmdDeferred.resolve();
+      });
+      helpers.audit(options, [{
+        setup: typed,
+        exec: { output: "" }
+      }]).then(null, cmdDeferred.reject);
+      return cmdDeferred.promise;
+    };
+
+    yield cmd("dbg interrupt");
 
-                            function closeDebugger(cb) {
-                              helpers.audit(options, [{
-                                setup: "dbg close",
-                                completed: false,
-                                exec: { output: "" }
-                              }]);
+    let interruptDeferred = promise.defer();
+    ok(true, "debugger is paused");
+    dbg._controller.activeThread.addOneTimeListener("resumed", () => {
+      ok(true, "debugger continued");
+      dbg._controller.activeThread.addOneTimeListener("paused", () => {
+        interruptDeferred.resolve();
+      });
+      let btnDoit = options.window.document.querySelector("input[type=button]");
+      EventUtils.sendMouseEvent({ type:"click" }, btnDoit);
+    });
+    helpers.audit(options, [{
+      setup: "dbg continue",
+      exec: { output: "" }
+    }]);
+    yield interruptDeferred.promise;
 
-                              let toolbox = gDevTools.getToolbox(options.target);
-                              if (!toolbox) {
-                                ok(true, "Debugger was closed.");
-                                cb();
-                              } else {
-                                toolbox.on("destroyed", function () {
-                                  ok(true, "Debugger was closed.");
-                                  cb();
-                                });
-                              }
-                            }
-
-                            // We're closing the debugger twice to make sure
-                            // 'dbg close' doesn't error when toolbox is already
-                            // closed. See bug 884638 for more info.
+    yield cmd("dbg step in");
+    yield cmd("dbg step in");
+    yield cmd("dbg step in");
+    let output = options.window.document.querySelector("input[type=text]");
+    is(output.value, "step in", "debugger stepped in");
+    yield cmd("dbg step over");
+    is(output.value, "step over", "debugger stepped over");
+    yield cmd("dbg step out");
+    is(output.value, "step out", "debugger stepped out");
+    yield cmd("dbg continue");
+    is(output.value, "dbg continue", "debugger continued");
 
-                            closeDebugger(() => {
-                              closeDebugger(() => deferred.resolve());
-                            });
-                          });
-                        });
-                      });
-                    });
-                  });
-                });
-              });
-              EventUtils.sendMouseEvent({type:"click"}, btnDoit);
-            });
+    let closeDebugger = function() {
+      let closeDeferred = promise.defer();
+      helpers.audit(options, [{
+        setup: "dbg close",
+        completed: false,
+        exec: { output: "" }
+      }]).then(function() {
+        let closeToolbox = gDevTools.getToolbox(options.target);
+        if (!closeToolbox) {
+          ok(true, "Debugger is closed.");
+          closeDeferred.resolve();
+        } else {
+          closeToolbox.on("destroyed", () => {
+            ok(true, "Debugger just closed.");
+            closeDeferred.resolve();
+          });
+        }
+      });
+      return closeDeferred.promise;
+    };
 
-            helpers.audit(options, [{
-              setup: "dbg continue",
-              exec: { output: "" }
-            }]);
-          });
-        });
-      });
-    });
-
-    return deferred.promise;
-  }).then(finish);
+    // We close the debugger twice to ensure 'dbg close' doesn't error when
+    // toolbox is already closed. See bug 884638 for more info.
+    yield closeDebugger();
+    yield closeDebugger();
+    yield helpers.closeToolbar(options);
+    yield helpers.closeTab(options);
+  }).then(finish, helpers.handleError);
 }
--- a/browser/devtools/debugger/test/browser_dbg_cmd_blackbox.js
+++ b/browser/devtools/debugger/test/browser_dbg_cmd_blackbox.js
@@ -35,19 +35,19 @@ function cmd(typed, expectedNumEvents=1,
     setup: typed,
     exec: {}
   };
 
   if (output) {
     audit.output = output;
   }
 
-  helpers.audit(gOptions, [audit]);
-
-  return deferred.promise;
+  return helpers.audit(gOptions, [audit]).then(function() {
+    return deferred.promise;
+  });
 }
 
 function test() {
   helpers.addTabWithToolbar(TEST_URL, function(options) {
     gOptions = options;
     gTarget = options.target;
     return gDevTools.showToolbox(options.target, "jsdebugger")
       .then(setupGlobals)
--- a/browser/devtools/profiler/commands.js
+++ b/browser/devtools/profiler/commands.js
@@ -89,16 +89,17 @@ gcli.addCommand({
   exec: function (args, context) {
     function stop() {
       let panel = getPanel(context, "jsprofiler");
 
       if (!panel.recordingProfile)
         throw gcli.lookup("profilerNotStarted3");
 
       panel.toggleRecording();
+      return gcli.lookup("profilerStopped");
     }
 
     return gDevTools.showToolbox(context.environment.target, "jsprofiler")
       .then(stop);
   }
 });
 
 /*
--- a/browser/devtools/profiler/test/browser_profiler_cmd.js
+++ b/browser/devtools/profiler/test/browser_profiler_cmd.js
@@ -1,141 +1,112 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
 
 let gcli = Cu.import("resource://gre/modules/devtools/gcli.jsm", {}).gcli;
 let gTarget, gPanel, gOptions;
 
-function cmd(typed, expected="") {
-  helpers.audit(gOptions, [{
+function cmd(typed, expected="", waitforEvent=null) {
+  let eventPromise;
+  if (waitforEvent == null) {
+    eventPromise = promise.resolve();
+  }
+  else {
+    let deferred = promise.defer();
+    gPanel.once(waitforEvent, () => { deferred.resolve(); });
+    eventPromise = deferred.promise;
+  }
+
+  let commandPromise = helpers.audit(gOptions, [{
     setup: typed,
     exec: { output: expected }
   }]);
+
+  return promise.all([ commandPromise, eventPromise ]);
 }
 
 function test() {
   waitForExplicitFinish();
 
   helpers.addTabWithToolbar(URL, function (options) {
     gOptions = options;
     gTarget = options.target;
 
     return gDevTools.showToolbox(options.target, "jsprofiler")
       .then(setupGlobals)
       .then(testProfilerStart)
       .then(testProfilerList)
       .then(testProfilerStop)
+      // We need to call this test twice to make sure there are no
+      // errors when executing 'profiler close' on a closed
+      // toolbox. See bug 863636 for more info.
       .then(testProfilerClose)
-      .then(testProfilerCloseWhenClosed)
-  }).then(finishUp);
+      .then(testProfilerClose);
+  }).then(finishUp, helpers.handleError);
 }
 
 function setupGlobals() {
   let deferred = promise.defer();
   gPanel = gDevTools.getToolbox(gTarget).getPanel("jsprofiler");
   deferred.resolve();
   return deferred.promise;
 }
 
 function testProfilerStart() {
-  let deferred = promise.defer();
-
-  gPanel.once("started", function () {
+  let expected = gcli.lookup("profilerStarted2");
+  return cmd("profiler start", expected, "started").then(() => {
     is(gPanel.profiles.size, 1, "There is a new profile");
     is(gPanel.getProfileByName("Profile 1"), gPanel.recordingProfile, "Recording profile is OK");
     ok(!gPanel.activeProfile, "There's no active profile yet");
-    cmd("profiler start", gcli.lookup("profilerAlreadyStarted2"));
-    deferred.resolve();
+    return cmd("profiler start", gcli.lookup("profilerAlreadyStarted2"));
   });
-
-  cmd("profiler start", gcli.lookup("profilerStarted2"));
-  return deferred.promise;
 }
 
 function testProfilerList() {
-  cmd("profiler list", /^.*Profile\s1\s\*.*$/);
+  return cmd("profiler list", /^.*Profile\s1\s\*.*$/);
 }
 
 function testProfilerStop() {
-  let deferred = promise.defer();
-
-  gPanel.once("stopped", function () {
+  return cmd("profiler stop", gcli.lookup("profilerStopped"), "stopped").then(() => {
     is(gPanel.activeProfile, gPanel.getProfileByName("Profile 1"), "Active profile is OK");
     ok(!gPanel.recordingProfile, "There's no recording profile");
-    cmd("profiler stop", gcli.lookup("profilerNotStarted3"));
-    deferred.resolve();
+    return cmd("profiler stop", gcli.lookup("profilerNotStarted3"));
   });
-
-  cmd("profiler stop");
-  return deferred.promise;
 }
 
 function testProfilerShow() {
-  let deferred = promise.defer();
-
-  gPanel.once("profileSwitched", function () {
+  return cmd('profile show "Profile 1"', "", "profileSwitched").then(() => {
     is(gPanel.getProfileByName("Profile 1"), gPanel.activeProfile, "Profile 1 is active");
-    cmd('profile show "invalid"', gcli.lookup("profilerNotFound"));
-    deferred.resolve();
+    return cmd('profile show "invalid"', gcli.lookup("profilerNotFound"));
   });
-
-  cmd('profile show "Profile 1"');
-  return deferred.promise;
 }
 
 function testProfilerClose() {
   let deferred = promise.defer();
 
   helpers.audit(gOptions, [{
     setup: "profiler close",
     completed: false,
     exec: { output: "" }
-  }]);
-
-  let toolbox = gDevTools.getToolbox(gOptions.target);
-  if (!toolbox) {
-    ok(true, "Profiler was closed.");
-    deferred.resolve();
-  } else {
-    toolbox.on("destroyed", function () {
+  }]).then(function() {
+    let toolbox = gDevTools.getToolbox(gOptions.target);
+    if (!toolbox) {
       ok(true, "Profiler was closed.");
       deferred.resolve();
-    });
-  }
+    } else {
+      toolbox.on("destroyed", () => {
+        ok(true, "Profiler was closed.");
+        deferred.resolve();
+      });
+    }
+  });
 
   return deferred.promise;
-}
-
-function testProfilerCloseWhenClosed() {
-  // We need to call this test to make sure there are no
-  // errors when executing 'profiler close' on a closed
-  // toolbox. See bug 863636 for more info.
-
-  let deferred = promise.defer();
-
-  helpers.audit(gOptions, [{
-    setup: "profiler close",
-    completed: false,
-    exec: { output: "" }
-  }]);
-
-  let toolbox = gDevTools.getToolbox(gOptions.target);
-  if (!toolbox) {
-    ok(true, "Profiler was closed.");
-    deferred.resolve();
-  } else {
-    toolbox.on("destroyed", function () {
-      ok(true, "Profiler was closed.");
-      deferred.resolve();
-    });
-  }
-
-  return deferred.promise;
-}
+};
 
 function finishUp() {
   gTarget = null;
   gPanel = null;
   gOptions = null;
   finish();
 }
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -1323,16 +1323,20 @@ profilerNotFound=Profile not found
 # started yet. It also contains a hint to use the 'profile start' command to
 # start the profiler.
 profilerNotStarted3=Profiler has not been started yet. Use 'profile start' to start profiling
 
 # LOCALIZATION NOTE (profilerStarted2) A very short string that indicates that
 # we have started recording.
 profilerStarted2=Recording…
 
+# LOCALIZATION NOTE (profilerStopped) A very short string that indicates that
+# we have stopped recording.
+profilerStopped=Stopped…
+
 # LOCALIZATION NOTE (profilerNotReady) A message that is displayed whenever
 # an operation cannot be completed because the profiler has not been opened yet.
 profilerNotReady=For this command to work you need to open the profiler first
 
 # LOCALIZATION NOTE (listenDesc) A very short string used to describe the
 # function of the 'listen' command.
 listenDesc=Open a remote debug port