Bug 1142108 - Make TestUtils.topicObserved cover more use cases. r=smacleod
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Fri, 13 Mar 2015 15:54:43 +0000
changeset 251867 d1ffaeb0dbfdea161d220d88742f49e4b3ce655c
parent 251866 6c16de2f0bbf6881dec74b7153d80797b57cc0c9
child 251868 8516ba1309cb6d334f4ea6a2ae60ece1adcc7ab1
push id7860
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:46:02 +0000
treeherdermozilla-aurora@8ac636cd51f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmacleod
bugs1142108
milestone39.0a1
Bug 1142108 - Make TestUtils.topicObserved cover more use cases. r=smacleod
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
testing/modules/TestUtils.jsm
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -64,36 +64,34 @@ this.BrowserTestUtils = {
    *        {
    *          private: A boolean indicating if the window should be
    *                   private
    *        }
    * @return {Promise}
    *         Resolves with the new window once it is loaded.
    */
   openNewBrowserWindow(options) {
-    return new Promise(resolve => {
-      let argString = Cc["@mozilla.org/supports-string;1"].
-                      createInstance(Ci.nsISupportsString);
-      argString.data = "";
-      let features = "chrome,dialog=no,all";
+    let argString = Cc["@mozilla.org/supports-string;1"].
+                    createInstance(Ci.nsISupportsString);
+    argString.data = "";
+    let features = "chrome,dialog=no,all";
 
-      if (options && options.private || false) {
-        features += ",private";
-      }
+    if (options && options.private || false) {
+      features += ",private";
+    }
 
-      let win = Services.ww.openWindow(
-        null, Services.prefs.getCharPref("browser.chromeURL"), "_blank",
-        features, argString);
+    let win = Services.ww.openWindow(
+      null, Services.prefs.getCharPref("browser.chromeURL"), "_blank",
+      features, argString);
 
-      // Wait for browser-delayed-startup-finished notification, it indicates
-      // that the window has loaded completely and is ready to be used for
-      // testing.
-      TestUtils.topicObserved("browser-delayed-startup-finished", win).then(
-        () => resolve(win));
-    });
+    // Wait for browser-delayed-startup-finished notification, it indicates
+    // that the window has loaded completely and is ready to be used for
+    // testing.
+    return TestUtils.topicObserved("browser-delayed-startup-finished",
+                                   subject => subject == win).then(() => win);
   },
 
   /**
    * Waits a specified number of miliseconds for a specified event to be
    * fired on a specified element.
    *
    * Usage:
    *    let receivedEvent = BrowserTestUtil.waitForEvent(element, "eventName");
--- a/testing/modules/TestUtils.jsm
+++ b/testing/modules/TestUtils.jsm
@@ -27,26 +27,40 @@ this.TestUtils = {
     Services.tm.mainThread.dispatch(callbackFn, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
   /**
    * Waits for the specified topic to be observed.
    *
    * @param {string} topic
    *        The topic to observe.
-   * @param {*} subject
-   *        A value to check the notification subject against. Only a
-   *        notification with a matching subject will cause the promise to
-   *        resolve.
+   * @param {function} checkFn [optional]
+   *        Called with (subject, data) as arguments, should return true if the
+   *        notification is the expected one, or false if it should be ignored
+   *        and listening should continue. If not specified, the first
+   *        notification for the specified topic resolves the returned promise.
+   *
+   * @note Because this function is intended for testing, any error in checkFn
+   *       will cause the returned promise to be rejected instead of waiting for
+   *       the next notification, since this is probably a bug in the test.
+   *
    * @return {Promise}
-   *         Resolves with the data provided when the topic has been observed.
+   * @resolves The array [subject, data] from the observed notification.
    */
-  topicObserved(topic, subject=null) {
-    return new Promise(resolve => {
-      Services.obs.addObserver(function observer(observedSubject, topic, data) {
-        if (subject !== null && subject !== observedSubject) { return; }
-
-        Services.obs.removeObserver(observer, topic);
-        resolve(data);
+  topicObserved(topic, checkFn) {
+    return new Promise((resolve, reject) => {
+      Services.obs.addObserver(function observer(subject, topic, data) {
+        try {
+          try {
+            if (checkFn && !checkFn(subject, data)) {
+              return;
+            }
+          } finally {
+            Services.obs.removeObserver(observer, topic);
+          }
+          resolve([subject, data]);
+        } catch (ex) {
+          reject(ex);
+        }
       }, topic, false);
     });
   },
 };