Bug 822109 - Add a bunch of basic functional tests for getUserMedia and enable a per config setting for those tests for fake vs. non-fake. r=jesup
authorJason Smith <jsmith@mozilla.com>
Thu, 07 Feb 2013 17:35:17 -0800
changeset 131415 9e3a4589e251469070da5e605d08a54f86973e48
parent 131414 a101d8932e912e50b9752cfca4f166f523695644
child 131416 ed3a6ed1dd2018e92216b9774fe3b64dbac6bb3c
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs822109
milestone21.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 822109 - Add a bunch of basic functional tests for getUserMedia and enable a per config setting for those tests for fake vs. non-fake. r=jesup
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
dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -10,16 +10,26 @@ relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES = \
   test_getUserMedia_exceptions.html \
   test_getUserMedia_basicAudio.html \
   test_getUserMedia_basicVideo.html \
   test_getUserMedia_basicVideoAudio.html \
+  test_getUserMedia_gumWithinGum.html \
+  test_getUserMedia_playAudioTwice.html \
+  test_getUserMedia_playVideoTwice.html \
+  test_getUserMedia_playVideoAudioTwice.html \
+  test_getUserMedia_stopAudioStream.html \
+  test_getUserMedia_stopAudioStreamWithFollowupAudio.html \
+  test_getUserMedia_stopVideoStreamWithFollowupVideo.html \
+  test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html \
+  test_getUserMedia_stopVideoStream.html \
+  test_getUserMedia_stopVideoAudioStream.html \
   test_peerConnection_basicAudio.html \
   test_peerConnection_basicAudioVideo.html \
   test_peerConnection_basicAudioVideoCombined.html \
   test_peerConnection_basicVideo.html \
   test_peerConnection_bug827843.html \
   test_peerConnection_bug825703.html \
   test_peerConnection_bug834153.html \
   head.js \
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -1,16 +1,19 @@
 /* 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;
 
+// Specifies whether we are using fake streams to run this automation
+var FAKE_ENABLED = true;
+
 /**
  * Setup any Mochitest for WebRTC by enabling the preference for
  * peer connections. As by bug 797979 it will also enable mozGetUserMedia().
  *
  * @param {Function} aCallback Test method to execute after initialization
  * @param {Boolean} desktopSupportedOnly specifies if the test currently
  *                                       is known to work on desktop only
  */
@@ -19,28 +22,46 @@ function runTest(aCallback, desktopSuppo
 
   // 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.userAgent.indexOf('Android') > -1 ||
      navigator.platform === '')) {
     ok(true, navigator.userAgent + ' currently not supported');
     SimpleTest.finish();
   } else {
-    SpecialPowers.pushPrefEnv({'set': [['media.peerconnection.enabled', true]]}, function () {
+    SpecialPowers.pushPrefEnv({'set': [
+      ['media.peerconnection.enabled', true],
+      ['media.navigator.permission.denied', true]]}, function () {
       try {
         aCallback();
       }
       catch (err) {
         unexpectedCallbackAndFinish(err);
       }
     });
   }
 }
 
 /**
+ * Wrapper function for mozGetUserMedia to allow a singular area of control
+ * for determining whether we run this with fake devices or not.
+ *
+ * @param {Dictionary} constraints the constraints for this mozGetUserMedia
+ *                                 callback
+ * @param {Function} onSuccess the success callback if the stream is
+ *                             successfully retrieved
+ * @param {Function} onError the error callback if the stream fails to be
+ *                           retrieved
+ */
+function getUserMedia(constraints, onSuccess, onError) {
+  constraints["fake"] = FAKE_ENABLED;
+  navigator.mozGetUserMedia(constraints, onSuccess, onError);
+}
+
+/**
  * A callback function fired only under unexpected circumstances while
  * running the tests. Kills off the test as well gracefully.
  *
  * @param {object} aObj The object fired back from the callback
  */
 function unexpectedCallbackAndFinish(aObj) {
   ok(false, "Unexpected error callback with " + aObj);
   SimpleTest.finish();
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -1,48 +1,69 @@
 /* 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 TIMEOUT_LENGTH = 10000;
+
 /**
  * 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
+ * @param {MediaStream} mediaStream the media stream used in
+ *                                  the mediaElement for playback
  */
 function MediaStreamPlayback(mediaElement, mediaStream) {
+  this.mediaElement = mediaElement;
+  this.mediaStream = mediaStream;
+}
 
-  /** The HTMLMediaElement used for media playback */
-  this.mediaElement = mediaElement;
+MediaStreamPlayback.prototype = {
 
-  /** The LocalMediaStream used with the HTMLMediaElement */
-  this.mediaStream = mediaStream;
+  /**
+   * Starts media with a media stream, runs it until a canplaythrough and
+   * timeupdate event fires, and stops the media.
+   *
+   * @param {Boolean} isResume specifies if this media element is being resumed
+   *                           from a previous run
+   * @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
+   */
+  playMedia : function MSP_playMedia(isResume, onSuccess, onError) {
+    var self = this;
+
+    this.startMedia(isResume, function() {
+      self.stopMediaElement();
+      onSuccess();
+    }, onError);
+  },
 
   /**
    * Starts the media with the associated stream.
    *
-   * @param {Integer} timeoutLength the timeout length to wait for
-   *                                canplaythrough to fire in milliseconds
+   * @param {Boolean} isResume specifies if the media element playback
+   *                           is being resumed from a previous run
    * @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) {
+  startMedia : function MSP_startMedia(isResume, 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");
+    // If we're initially running this media, check that the time is zero
+    if (!isResume) {
+      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() {
@@ -76,84 +97,149 @@ function MediaStreamPlayback(mediaElemen
       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() {
-        if(self.mediaStream.currentTime > 0 &&
-           self.mediaElement.currentTime > 0) {
+        if (self.mediaStream.currentTime > 0 &&
+            self.mediaElement.currentTime > 0) {
           timeUpdateFired = true;
-          self.mediaElement.removeEventListener('timeupdate', timeUpdateCallback,
-            false);
+          self.mediaElement.removeEventListener('timeupdate',
+            timeUpdateCallback, false);
           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) {
+        if (!timeUpdateFired) {
           self.mediaElement.removeEventListener('timeupdate',
             timeUpdateCallback, false);
           ok(false, "timeUpdate event never fired");
           onError();
         }
-      }, timeoutLength);
+      }, TIMEOUT_LENGTH);
     };
 
     // 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.mozSrcObject = this.mediaStream;
     this.mediaElement.play();
 
     // If canplaythrough doesn't fire in enough time, we fail the test
     setTimeout(function() {
-      if(!canPlayThroughFired) {
+      if (!canPlayThroughFired) {
         self.mediaElement.removeEventListener('canplaythrough',
           canPlayThroughCallback, false);
         ok(false, "canplaythrough event never fired");
         onError();
       }
-    }, timeoutLength);
-  };
+    }, TIMEOUT_LENGTH);
+  },
 
   /**
    * Stops the media with the associated stream.
    *
    * Precondition: The media stream and element should both be actively
    *               being played.
    */
-  this.stopMedia = function() {
+  stopMediaElement : function MSP_stopMediaElement() {
     this.mediaElement.pause();
     this.mediaElement.mozSrcObject = null;
-  };
+  }
+}
+
+
+/**
+ * This class is basically the same as MediaStreamPlayback except
+ * ensures that the instance provided startMedia is a MediaStream.
+ *
+ * @param {HTMLMediaElement} mediaElement the media element for playback
+ * @param {LocalMediaStream} mediaStream the media stream used in
+ *                                       the mediaElement for playback
+ */
+function LocalMediaStreamPlayback(mediaElement, mediaStream) {
+  ok(mediaStream instanceof LocalMediaStream,
+     "Stream should be a LocalMediaStream");
+  MediaStreamPlayback.call(this, mediaElement, mediaStream);
+}
+
+// Sets up the inheritance chain from LMSP --> MSP
+LocalMediaStreamPlayback.prototype = new MediaStreamPlayback();
+LocalMediaStreamPlayback.prototype.constructor = LocalMediaStreamPlayback;
+
+/**
+ * Starts media with a media stream, runs it until a canplaythrough and
+ * timeupdate event fires, and calls stop() on the stream.
+ *
+ * @param {Boolean} isResume specifies if this media element is being resumed
+ *                           from a previous run
+ * @param {Function} onSuccess the success callback if the media element
+ *                             successfully fires ended on a stop() call
+ *                             on the stream
+ * @param {Function} onError the error callback if the media element fails
+ *                           to fire an ended callback on a stop() call
+ *                           on the stream
+ */
+LocalMediaStreamPlayback.prototype.playMediaWithStreamStop = function(
+  isResume, onSuccess, onError) {
+  var self = this;
+
+  this.startMedia(isResume, function() {
+    self.stopStreamInMediaPlayback(function() {
+      self.stopMediaElement();
+      onSuccess();
+    }, onError);
+  }, onError);
+}
+
+/**
+ * Stops the local media stream while it's currently in playback in
+ * a media element.
+ *
+ * Precondition: The media stream and element should both be actively
+ *               being played.
+ *
+ * @param {Function} onSuccess the success callback if the media element
+ *                             fires an ended event from stop() being called
+ * @param {Function} onError the error callback if the media element
+ *                           fails to fire an ended event from stop() being
+ *                           called
+ */
+LocalMediaStreamPlayback.prototype.stopStreamInMediaPlayback = function(
+  onSuccess, onError) {
+  var endedFired = false;
+  var self = this;
 
   /**
-   * 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
+   * Callback fired when the ended event fires when stop() is called on the
+   * stream.
    */
-  this.playMedia = function(timeoutLength, onSuccess, onError) {
-    var self = this;
+  var endedCallback = function() {
+    endedFired = true;
+    self.mediaElement.removeEventListener('ended', endedCallback, false);
+    ok(true, "ended event successfully fired");
+    onSuccess();
+  };
 
-    this.startMedia(timeoutLength, function() {
-      self.stopMedia();
-      onSuccess();
-    }, onError);
-  };
+  this.mediaElement.addEventListener('ended', endedCallback, false);
+  this.mediaStream.stop();
+
+  // If ended doesn't fire in enough time, then we fail the test
+  setTimeout(function() {
+    if (!endedFired) {
+      ok(false, "ended event never fired");
+      onError();
+    }
+  }, TIMEOUT_LENGTH);
 }
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -22,21 +22,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   /**
    * 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 () {
     var testAudio = document.getElementById('testAudio');
 
-    navigator.mozGetUserMedia({audio: true, fake: true}, function (aStream) {
-      var playback = new MediaStreamPlayback(testAudio, aStream);
-      playback.playMedia(10000, function () {
+    getUserMedia({audio: true}, function (aStream) {
+      var playback = new LocalMediaStreamPlayback(testAudio, aStream);
+
+      playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
       }, unexpectedCallbackAndFinish);
+
     }, unexpectedCallbackAndFinish);
+
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -22,21 +22,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   /**
    * 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 () {
     var testVideo = document.getElementById('testVideo');
 
-    navigator.mozGetUserMedia({video: true, fake: true}, function (aStream) {
-      var playback = new MediaStreamPlayback(testVideo, aStream);
-      playback.playMedia(10000, function () {
+    getUserMedia({video: true}, function (aStream) {
+      var playback = new LocalMediaStreamPlayback(testVideo, aStream);
+
+      playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
       }, unexpectedCallbackAndFinish);
+
     }, unexpectedCallbackAndFinish);
+
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -22,22 +22,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   /**
    * 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 () {
     var testVideoAudio = document.getElementById('testVideoAudio');
 
-    navigator.mozGetUserMedia({video: true, audio: true, fake: true},
-      function (aStream) {
-        var playback = new MediaStreamPlayback(testVideoAudio, aStream);
-        playback.playMedia(10000, function () {
-          aStream.stop();
-          SimpleTest.finish();
-        }, unexpectedCallbackAndFinish);
+    getUserMedia({video: true, audio: true}, function (aStream) {
+      var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream);
+
+      playback.playMedia(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, unexpectedCallbackAndFinish);
+
     }, unexpectedCallbackAndFinish);
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -1,9 +1,9 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=795367
 -->
 <head>
   <meta charset="utf-8">
   <title>Test mozGetUserMedia Exceptions</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia gum within gum</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia gum within gum</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+  <audio id="testAudio"></audio>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test that we can complete a playback cycle for a video,
+   * then upon completion, do a playback cycle with audio, such that
+   * the audio gum call happens within the video gum call.
+   */
+  runTest(function () {
+    getUserMedia({video: true}, function(videoStream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
+        videoStream);
+
+      videoStreamPlayback.playMedia(false, function() {
+        getUserMedia({audio: true}, function(audioStream) {
+          var testAudio = document.getElementById('testAudio');
+          var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
+            audioStream);
+
+          audioStreamPlayback.playMedia(false, function() {
+            audioStream.stop();
+            videoStream.stop();
+            SimpleTest.finish();
+          }, unexpectedCallbackAndFinish);
+
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Audio Twice</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Audio Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></audio>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test that we can complete an audio playback cycle twice in a row.
+   */
+  runTest(function () {
+    getUserMedia({audio: true}, function(audioStream) {
+      var testAudio = document.getElementById('testAudio');
+      var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
+        audioStream);
+
+      audioStreamPlayback.playMedia(false, function() {
+
+        audioStreamPlayback.playMedia(true, function() {
+          audioStream.stop();
+          SimpleTest.finish();
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Video and Audio Twice</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video and Audio Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test that we can complete a video playback cycle twice in a row.
+   */
+  runTest(function () {
+    getUserMedia({video: true, audio: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+
+      streamPlayback.playMedia(false, function() {
+
+        streamPlayback.playMedia(true, function() {
+          stream.stop();
+          SimpleTest.finish();
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Video Twice</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test that we can complete a video playback cycle twice in a row.
+   */
+  runTest(function () {
+    getUserMedia({video: true}, function(videoStream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
+        videoStream);
+
+      videoStreamPlayback.playMedia(false, function() {
+
+        videoStreamPlayback.playMedia(true, function() {
+          videoStream.stop();
+          SimpleTest.finish();
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Audio Stream</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that we can start an audio stream in a media element,
+   * call stop() on the stream, and successfully get an ended event fired.
+   */
+  runTest(function () {
+    getUserMedia({audio: true}, function(stream) {
+      var testAudio = document.getElementById('testAudio');
+      var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
+
+      audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Audio Stream With Followup Audio</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream With Followup Audio</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></audio>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that I can complete an audio gum playback in a media
+   * element, stop the stream, and then complete another audio gum playback
+   * in a media element.
+   */
+  runTest(function () {
+    getUserMedia({audio: true}, function(firstStream) {
+      var testAudio = document.getElementById('testAudio');
+      var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({audio: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
+
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, unexpectedCallbackAndFinish);
+
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Audio Stream</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Audio Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that we can start a video+audio stream in a
+   * media element, call stop() on the stream, and successfully get an
+   * ended event fired.
+   */
+  runTest(function () {
+    getUserMedia({video: true, audio: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+
+      streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that I can complete an video+audio gum playback in a
+   * media element, stop the stream, and then complete another video+audio gum
+   * playback in a media element.
+   */
+  runTest(function () {
+    getUserMedia({video: true, audio: true}, function(firstStream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({video: true, audio: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
+
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, unexpectedCallbackAndFinish);
+
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Stream</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that we can start a video stream in a media element,
+   * call stop() on the stream, and successfully get an ended event fired.
+   */
+  runTest(function () {
+    getUserMedia({video: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+
+      videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Stream With Followup Video</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream With Followup Video</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  /**
+   * Run a test to verify that I can complete an audio gum playback in a media
+   * element, stop the stream, and then complete another audio gum playback
+   * in a media element.
+   */
+  runTest(function () {
+    getUserMedia({video: true}, function(firstStream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo,
+        firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({video: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
+
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, unexpectedCallbackAndFinish);
+
+        }, unexpectedCallbackAndFinish);
+
+      }, unexpectedCallbackAndFinish);
+
+    }, unexpectedCallbackAndFinish);
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>