Bug 1259788 - Create haveEvents() and friends in head.js. r?jib draft
authorAndreas Pehrson <pehrsons@gmail.com>
Mon, 22 Aug 2016 12:01:39 +0200
changeset 404495 70e47f6fbebdb0a0476e27b7e9e2ab1f73c82caa
parent 403581 f97a056ae6235de7855fd8aaa04fb1c8d183bd06
child 404496 20c3efb1f9e809b2684c3f9e604270c540ecccd1
push id27223
push userpehrsons@gmail.com
push dateTue, 23 Aug 2016 16:22:06 +0000
reviewersjib
bugs1259788
milestone51.0a1
Bug 1259788 - Create haveEvents() and friends in head.js. r?jib When we expect multiple events of the same type (in this case "addtrack" on MediaStream) we can use this convenience function to attach a listener and wait for the expected number of events before removing the listener and resolving. This also adds a function to check that there were no extra events of the same kind after the expected N events, and a function that checks that we don't see a certain event within a given timeout period. MozReview-Commit-ID: 1P8MkEI0Yzm
dom/media/tests/mochitest/head.js
dom/media/tests/mochitest/test_peerConnection_addtrack_removetrack_events.html
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -588,34 +588,97 @@ function createOneShotEventWrapper(wrapp
     e.wrapper = wrapper;
     wrapper[onx](e);
     wrapper[onx] = unexpected;
   };
 }
 
 /**
  * Returns a promise that resolves when `target` has raised an event with the
+ * given name the given number of times. Cancel the returned promise by passing
+ * in a `cancelPromise` and resolve it.
+ *
+ * @param {object} target
+ *        The target on which the event should occur.
+ * @param {string} name
+ *        The name of the event that should occur.
+ * @param {integer} count
+ *        Optional number of times the event should be raised before resolving.
+ * @param {promise} cancelPromise
+ *        Optional promise that on resolving rejects the returned promise,
+ *        so we can avoid logging results after a test has finished.
+ * @returns {promise} A promise that resolves to the last of the seen events.
+ */
+function haveEvents(target, name, count, cancelPromise) {
+  var listener;
+  var counter = count || 1;
+  return Promise.race([
+    (cancelPromise || new Promise(() => {})).then(e => Promise.reject(e)),
+    new Promise(resolve =>
+        target.addEventListener(name, listener = e => (--counter < 1 && resolve(e))))
+  ])
+  .then(e => (target.removeEventListener(name, listener), e));
+};
+
+/**
+ * Returns a promise that resolves when `target` has raised an event with the
  * given name. Cancel the returned promise by passing in a `cancelPromise` and
  * resolve it.
  *
  * @param {object} target
  *        The target on which the event should occur.
  * @param {string} name
  *        The name of the event that should occur.
  * @param {promise} cancelPromise
- *        A promise that on resolving rejects the returned promise,
+ *        Optional promise that on resolving rejects the returned promise,
  *        so we can avoid logging results after a test has finished.
+ * @returns {promise} A promise that resolves to the seen event.
  */
 function haveEvent(target, name, cancelPromise) {
-  var listener;
-  var p = Promise.race([
-    (cancelPromise || new Promise()).then(e => Promise.reject(e)),
-    new Promise(resolve => target.addEventListener(name, listener = resolve))
-  ]);
-  return p.then(event => (target.removeEventListener(name, listener), event));
+  return haveEvents(target, name, 1, cancelPromise);
+};
+
+/**
+ * Returns a promise that resolves if the target has not seen the given event
+ * after one crank (or until the given timeoutPromise resolves) of the event
+ * loop.
+ *
+ * @param {object} target
+ *        The target on which the event should not occur.
+ * @param {string} name
+ *        The name of the event that should not occur.
+ * @param {promise} timeoutPromise
+ *        Optional promise defining how long we should wait before resolving.
+ * @returns {promise} A promise that is rejected if we see the given event, or
+ *                    resolves after a timeout otherwise.
+ */
+function haveNoEvent(target, name, timeoutPromise) {
+  return haveEvent(target, name, timeoutPromise || wait(0))
+    .then(() => Promise.reject(new Error("Too many " + name + " events")),
+          () => {});
+};
+
+/**
+ * Returns a promise that resolves after the target has seen the given number
+ * of events but no such event in a following crank of the event loop.
+ *
+ * @param {object} target
+ *        The target on which the events should occur.
+ * @param {string} name
+ *        The name of the event that should occur.
+ * @param {integer} count
+ *        Optional number of times the event should be raised before resolving.
+ * @param {promise} cancelPromise
+ *        Optional promise that on resolving rejects the returned promise,
+ *        so we can avoid logging results after a test has finished.
+ * @returns {promise} A promise that resolves to the last of the seen events.
+ */
+function haveEventsButNoMore(target, name, count, cancelPromise) {
+  return haveEvents(target, name, count, cancelPromise)
+    .then(e => haveNoEvent(target, name).then(() => e));
 };
 
 /**
  * This class executes a series of functions in a continuous sequence.
  * Promise-bearing functions are executed after the previous promise completes.
  *
  * @constructor
  * @param {object} framework
--- a/dom/media/tests/mochitest/test_peerConnection_addtrack_removetrack_events.html
+++ b/dom/media/tests/mochitest/test_peerConnection_addtrack_removetrack_events.html
@@ -27,31 +27,27 @@ runNetworkTest(function (options) {
 
           let videoSenderIndex =
             test.pcLocal._pc.getSenders().findIndex(s => s.track.kind == "video");
           isnot(videoSenderIndex, -1, "Should have video sender");
 
           test.pcLocal.removeSender(videoSenderIndex);
           test.pcLocal.attachLocalTrack(stream.getTracks()[0], localStream);
 
-          let onNextLoop = wait(0);
           eventsPromise = haveEvent(remoteStream, "addtrack", wait(50000, "No addtrack event"))
             .then(trackEvent => {
               ok(trackEvent instanceof MediaStreamTrackEvent,
                  "Expected event to be instance of MediaStreamTrackEvent");
               is(trackEvent.type, "addtrack",
                  "Expected addtrack event type");
               is(trackEvent.track.id, newTrack.id, "Expected track in event");
               is(trackEvent.track.readyState, "live",
                  "added track should be live");
             })
-            .then(() => haveEvent(remoteStream, "addtrack", onNextLoop)
-              .then(() => Promise.reject("Unexpected addtrack event for remote stream " + remoteStream.id),
-                    () => Promise.resolve())
-            );
+            .then(() => haveNoEvent(remoteStream, "addtrack"));
           remoteStream.addEventListener("removetrack",
                                         function onRemovetrack(trackEvent) {
             ok(false, "UA shouldn't raise 'removetrack' when receiving peer connection");
           })
         });
       },
     ],
     [