Bug 781534 - Create basic automated test coverage for the mozGetUserMedia for desktop. r=roc
authorJason Smith <jsmith@mozilla.com>
Mon, 26 Nov 2012 18:27:46 -0800
changeset 123350 6b73c8bdc701d488614b6715681b4ef9bbc9b45c
parent 123349 7d3c31d17d2b0c0a9f7f90b81fac59b60ab73665
child 123351 16dc855a02165608489a9c1ca17b3939df022157
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs781534
milestone20.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 781534 - Create basic automated test coverage for the mozGetUserMedia for desktop. r=roc
dom/media/tests/mochitest/Makefile.in
dom/media/tests/mochitest/head.js
dom/media/tests/mochitest/mediaStreamPlayback.js
dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
dom/media/tests/mochitest/test_getUserMedia_exceptions.html
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -5,14 +5,19 @@
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MOCHITEST_FILES	= \
+# TODO: When bug 814718 and bug 814721 are fixed, add the following files:
+#          test_getUserMedia_basicVideo.html
+#          test_getUserMedia_basicAudio.thml
+#          test_getUserMedia_basicVideoAudio.html
+#          mediaStreamPlayback.js
+MOCHITEST_FILES = \
   test_getUserMedia_exceptions.html \
   head.js \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -1,22 +1,41 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var Cc = SpecialPowers.Cc;
 var Ci = SpecialPowers.Ci;
 var Cr = SpecialPowers.Cr;
 
 /**
  * Setup any Mochitest for WebRTC by enabling the preference for
  * peer connections. As by bug 797979 it will also enable mozGetUserMedia().
- * Additionally, we disable the permissions prompt for these mochitests.
  *
  * @param {Function} aCallback Test method to execute after initialization
+ * @param {Boolean} desktopSupportedOnly specifies if the test currently
+ *                                       is known to work on desktop only
  */
-function runTest(aCallback) {
+function runTest(aCallback, desktopSupportedOnly) {
   SimpleTest.waitForExplicitFinish();
 
-  SpecialPowers.pushPrefEnv({'set': [['media.peerconnection.enabled', true],
-                            ['media.navigator.permission.disabled', true]]},
-                            aCallback);
+  // If this is a desktop supported test and we're on android or b2g,
+  // indicate that the test is not supported and skip the test
+  if(desktopSupportedOnly && (navigator.platform === 'Android' ||
+     navigator.platform === '')) {
+    ok(true, navigator.platform + ' currently not supported');
+    SimpleTest.finish();
+  } else {
+    SpecialPowers.pushPrefEnv({'set': [['media.peerconnection.enabled', true]]},
+      aCallback);
+  }
 }
+
+/**
+ * A callback function fired only under unexpected circumstances while
+ * running the tests. Kills off the test as well gracefully.
+ *
+ * @param {String} obj the object fired back from the callback
+ */
+function unexpectedCallbackAndFinish(obj) {
+  ok(false, "Unexpected error callback with " + obj);
+  SimpleTest.finish();
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This class manages playback of a HTMLMediaElement with a MediaStream.
+ * When constructed by a caller, an object instance is created with
+ * a media element and a media stream object.
+ *
+ * @param {HTMLMediaElement} mediaElement the media element for playback
+ * @param {LocalMediaStream} mediaStream the media stream used in
+ *                                       the mediaElement for playback
+ */
+function MediaStreamPlayback(mediaElement, mediaStream) {
+
+  /** The HTMLMediaElement used for media playback */
+  this.mediaElement = mediaElement;
+
+  /** The LocalMediaStream used with the HTMLMediaElement */
+  this.mediaStream = mediaStream;
+
+  /**
+   * Starts the media with the associated stream.
+   *
+   * @param {Integer} timeoutLength the timeout length to wait for
+   *                                canplaythrough to fire in milliseconds
+   * @param {Function} onSuccess the success function call back
+   *                             if media starts correctly
+   * @param {Function} onError the error function call back
+   *                           if media fails to start
+   */
+  this.startMedia = function(timeoutLength, onSuccess, onError) {
+    var self = this;
+    var canPlayThroughFired = false;
+
+    // Verifies we've received a correctly initialized LocalMediaStream
+    ok(this.mediaStream instanceof LocalMediaStream,
+      "Stream should be a LocalMediaStream");
+    is(this.mediaStream.currentTime, 0,
+      "Before starting the media element, currentTime = 0");
+
+    /**
+     * Callback fired when the canplaythrough event is fired. We only
+     * run the logic of this function once, as this event can fire
+     * multiple times while a HTMLMediaStream is playing content from
+     * a real-time MediaStream.
+     */
+    var canPlayThroughCallback = function() {
+      // Disable the canplaythrough event listener to prevent multiple calls
+      canPlayThroughFired = true;
+      self.mediaElement.removeEventListener('canplaythrough',
+        canPlayThroughCallback, false);
+
+      is(self.mediaElement.paused, false,
+        "Media element should be playing");
+      is(self.mediaElement.ended, false,
+        "Media element should not have ended");
+      is(self.mediaElement.duration, Number.POSITIVE_INFINITY,
+        "Duration should be infinity");
+
+      // When the media element is playing with a real-time stream, we
+      // constantly switch between having data to play vs. queuing up data,
+      // so we can only check that the ready state is one of those two values
+      ok(self.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA ||
+         self.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA,
+         "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA");
+
+      is(self.mediaElement.seekable.length, 0,
+         "Seekable length shall be zero");
+      is(self.mediaElement.buffered.length, 0,
+         "Buffered length shall be zero");
+      is(self.mediaElement.played.length, 1, "Played length shall be one");
+
+      if(self.mediaElement.played.length > 0) {
+        is(self.mediaElement.played.start(0), 0,
+          "Played start shall be zero");
+        is(self.mediaElement.played.end(0), self.mediaElement.currentTime,
+          "End shall be current time");
+      }
+
+      is(self.mediaElement.seeking, false,
+         "MediaElement is not seekable with MediaStream");
+      ok(isNaN(self.mediaElement.startOffsetTime),
+         "Start offset time shall not be a number");
+      is(self.mediaElement.loop, false, "Loop shall be false");
+      is(self.mediaElement.preload, "", "Preload should not exist");
+      is(self.mediaElement.src, "", "No src should be defined");
+      is(self.mediaElement.currentSrc, "",
+         "Current src should still be an empty string");
+
+      var timeUpdateFired = false;
+
+      var timeUpdateCallback = function() {
+        timeUpdateFired = true;
+        self.mediaElement.removeEventListener('timeupdate', timeUpdateCallback,
+          false);
+
+        ok(self.mediaStream.currentTime > 0,
+           "Stream's time should be greater than zero");
+        ok(self.mediaElement.currentTime > 0,
+           "MediaElement time shall be greater than zero");
+        onSuccess();
+      };
+
+      // When timeupdate fires, we validate time has passed and move
+      // onto the success condition
+      self.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
+        false);
+
+      // If timeupdate doesn't fire in enough time, we fail the test
+      setTimeout(function() {
+        if(!timeUpdateFired) {
+          ok(false, "timeUpdate event never fired");
+          onError();
+        }
+      }, timeoutLength);
+    };
+
+    // Adds a listener intended to be fired when playback is available
+    // without further buffering.
+    this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
+      false);
+
+    // Hooks up the media stream to the media element and starts playing it
+    this.mediaElement.mozSrcObject = mediaStream;
+    this.mediaElement.play();
+
+    // If canplaythrough doesn't fire in enough time, we fail the test
+    setTimeout(function() {
+      if(!canPlayThroughFired) {
+        ok(false, "canplaythrough event never fired");
+        onError();
+      }
+    }, timeoutLength);
+  };
+
+  /**
+   * Stops the media with the associated stream.
+   *
+   * Precondition: The media stream and element should both be actively
+   *               being played.
+   */
+  this.stopMedia = function() {
+    this.mediaElement.pause();
+    this.mediaElement.mozSrcObject = null;
+  };
+
+  /**
+   * Starts media with a media stream, runs it until a canplaythrough and
+   * timeupdate event fires, and stops the media.
+   *
+   * @param {Integer} timeoutLength the length of time to wait for certain
+   *                                media events to fire
+   * @param {Function} onSuccess the success callback if the media playback
+   *                             start and stop cycle completes successfully
+   * @param {Function} onError the error callback if the media playback
+   *                           start and stop cycle fails
+   */
+  this.playMedia = function(timeoutLength, onSuccess, onError) {
+    var self = this;
+
+    this.startMedia(timeoutLength, function() {
+      self.stopMedia();
+      onSuccess();
+    }, onError);
+  };
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Audio Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<audio id="testAudio"></audio>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Audio Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Run a test to verify that we can complete a start and stop media playback
+ * cycle for an audio LocalMediaStream on an audio HTMLMediaElement.
+ */
+runTest(function () {
+  try {
+    navigator.mozGetUserMedia({audio: true, fake: true}, function(stream) {
+      var testAudio = document.getElementById('testAudio');
+      var audioStreamPlayback = new MediaStreamPlayback(testAudio, stream);
+      audioStreamPlayback.playMedia(5000, function() {
+				stream.stop();
+				SimpleTest.finish();
+			}, unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  } catch (err) {
+    unexpectedCallbackAndFinish(err);
+  }
+}, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Video Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<video id="testVideo"></video>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Run a test to verify that we can complete a start and stop media playback
+ * cycle for an video LocalMediaStream on a video HTMLMediaElement.
+ */
+runTest(function () {
+  try {
+    navigator.mozGetUserMedia({video: true, fake: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new MediaStreamPlayback(testVideo, stream);
+      videoStreamPlayback.playMedia(5000, function() {
+				stream.stop();
+				SimpleTest.finish();
+			}, unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  } catch (err) {
+    unexpectedCallbackAndFinish(err);
+  }
+}, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Video & Audio Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<video id="testVideoAudio"></video>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video & Audio Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Run a test to verify that we can complete a start and stop media playback
+ * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement.
+ */
+runTest(function () {
+  try {
+    navigator.mozGetUserMedia({video: true, audio: true, fake: true},
+      function(stream) {
+      var testVideoAudio = document.getElementById('testVideoAudio');
+      var videoAudioStreamPlayback = new MediaStreamPlayback(testVideoAudio,
+        stream);
+      videoAudioStreamPlayback.playMedia(5000, function() {
+				stream.stop();
+				SimpleTest.finish();
+			}, unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  } catch (err) {
+    unexpectedCallbackAndFinish(err);
+  }
+}, true);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -73,14 +73,14 @@ runTest(function () {
       navigator.mozGetUserMedia.apply(navigator, test.params);
     } catch (e) {
       exception = (e.result === test.error);
     }
     ok(exception, "Exception for " + test.message);
   });
 
   SimpleTest.finish();
-});
+}, false);
 
 </script>
 </pre>
 </body>
 </html>