Bug 1436523 - Update dom/media/test/ tests to better handle loopback + gUM device config. r?achronop draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Wed, 21 Feb 2018 09:11:29 -0500
changeset 773433 a55255734449384822f6cfaa4036f67e0881f791
parent 773432 7389ca2dddb11f8ec99f0f8e6b0942f75d57ec79
child 773434 c06d5d6cf097f5da035f747ce681786c60a22f71
push id104235
push userbvandyk@mozilla.com
push dateTue, 27 Mar 2018 22:55:40 +0000
reviewersachronop
bugs1436523
milestone61.0a1
Bug 1436523 - Update dom/media/test/ tests to better handle loopback + gUM device config. r?achronop This changeset adds a gUM_support.js to dom/media/test/. This file provides functions to setup prefs for loopback or fake device selection before gUM calls are made. This is useful for configuring tests and providing an explicit point of reference for settings, rather than the implicit ones provided by the harness. Updates tests so that the new helper functions are called before gUM. This will result in loopback prefs being set if loopback device names are detected, if not then fake devices will be used. This also removes the use of the fake constraint in gUM calls. Update touched tests to use some more modern JS. No behavioural changes were made (except in minor cases, but functionality should be the same). These changes are largely as follows: - var -> let - async is used in places where I felt it improved readability - semicolons added to various event handler assignments MozReview-Commit-ID: 1HuE8thBA6w
dom/media/test/gUM_support.js
dom/media/test/mochitest.ini
dom/media/test/test_imagecapture.html
dom/media/test/test_mediarecorder_avoid_recursion.html
dom/media/test/test_mediarecorder_record_gum_video_timeslice.html
dom/media/test/test_mediarecorder_unsupported_src.html
dom/media/test/test_mediatrack_consuming_mediastream.html
dom/media/test/test_mediatrack_events.html
dom/media/test/test_multiple_mediastreamtracks.html
dom/media/test/test_streams_individual_pause.html
new file mode 100644
--- /dev/null
+++ b/dom/media/test/gUM_support.js
@@ -0,0 +1,87 @@
+// Support script for test that use getUserMedia. This allows explicit
+// configuration of prefs which affect gUM. See also
+// `testing/mochitest/runtests.py` for how the harness configures values.
+
+// Setup preconditions for tests using getUserMedia. This functions helps
+// manage different prefs that affect gUM calls in tests and makes explicit
+// the expected state before test runs.
+async function pushGetUserMediaTestPrefs({
+  fakeAudio = false,
+  fakeVideo = false,
+  loopbackAudio = false,
+  loopbackVideo = false}) {
+  // Make sure we have sensical arguments
+  if (!fakeAudio && !loopbackAudio) {
+    throw new Error("pushGetUserMediaTestPrefs: Should have fake or loopback audio!");
+  } else if (fakeAudio && loopbackAudio) {
+    throw new Error("pushGetUserMediaTestPrefs: Should not have both fake and loopback audio!");
+  }
+  if (!fakeVideo && !loopbackVideo) {
+    throw new Error("pushGetUserMediaTestPrefs: Should have fake or loopback video!");
+  } else if (fakeVideo && loopbackVideo) {
+    throw new Error("pushGetUserMediaTestPrefs: Should not have both fake and loopback video!");
+  }
+
+  let testPrefs = [];
+  if (fakeAudio) {
+    // Unset the loopback device so it doesn't take precedence
+    testPrefs.push(["media.audio_loopback_dev", ""]);
+    // Setup fake streams pref
+    testPrefs.push(["media.navigator.streams.fake", true]);
+  }
+  if (loopbackAudio) {
+    // If audio loopback is requested we expect the test harness to have set
+    // the loopback device pref, make sure it's set
+    let audioLoopDev = SpecialPowers.getCharPref("media.audio_loopback_dev", "");
+    if (!audioLoopDev) {
+      throw new Error("pushGetUserMediaTestPrefs: Loopback audio requested but " +
+        "media.audio_loopback_dev does not appear to be set!");
+    }
+  }
+  if (fakeVideo) {
+    // Unset the loopback device so it doesn't take precedence
+    testPrefs.push(["media.video_loopback_dev", ""]);
+    // Setup fake streams pref
+    testPrefs.push(["media.navigator.streams.fake", true]);
+  }
+  if (loopbackVideo) {
+    // If video loopback is requested we expect the test harness to have set
+    // the loopback device pref, make sure it's set
+    let videoLoopDev = SpecialPowers.getCharPref("media.video_loopback_dev", "");
+    if (!videoLoopDev) {
+      throw new Error("pushGetUserMediaTestPrefs: Loopback video requested but " +
+        "media.video_loopback_dev does not appear to be set!");
+    }
+  }
+  if (loopbackAudio || loopbackVideo) {
+    // Prevent gUM permission prompt. Since loopback devices are considered
+    // real devices we need to set prefs so the gUM prompt isn't presented.
+    testPrefs.push(['media.navigator.permission.disabled', true]);
+  }
+  return SpecialPowers.pushPrefEnv({set: testPrefs});
+}
+
+// Setup preconditions for tests using getUserMedia. This function will
+// configure prefs to select loopback device(s) if it can find loopback device
+// names already set in the prefs. If no loopback device name can be found then
+// prefs are setup such that a fake device is used.
+async function setupGetUserMediaTestPrefs() {
+  prefRequests = {};
+  let audioLoopDev = SpecialPowers.getCharPref("media.audio_loopback_dev", "");
+  if (audioLoopDev) {
+    prefRequests.fakeAudio = false;
+    prefRequests.loopbackAudio = true;
+  } else {
+    prefRequests.fakeAudio = true;
+    prefRequests.loopbackAudio = false;
+  }
+  let videoLoopDev = SpecialPowers.getCharPref("media.video_loopback_dev", "");
+  if (videoLoopDev) {
+    prefRequests.fakeVideo = false;
+    prefRequests.loopbackVideo = true;
+  } else {
+    prefRequests.fakeVideo = true;
+    prefRequests.loopbackVideo = false;
+  }
+  return pushGetUserMediaTestPrefs(prefRequests);
+}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -452,16 +452,17 @@ support-files =
   gizmo-noaudio.mp4
   gizmo-noaudio.mp4^headers^
   gizmo-short.mp4
   gizmo-short.mp4^headers^
   gizmo.webm
   gizmo.webm^headers^
   gizmo-noaudio.webm
   gizmo-noaudio.webm^headers^
+  gUM_support.js
   gzipped_mp4.sjs
   huge-id3.mp3
   huge-id3.mp3^headers^
   id3tags.mp3
   id3tags.mp3^headers^
   invalid-cmap-s0c0.opus
   invalid-cmap-s0c0.opus^headers^
   invalid-cmap-s0c2.opus
--- a/dom/media/test/test_imagecapture.html
+++ b/dom/media/test/test_imagecapture.html
@@ -3,30 +3,31 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1041393
 -->
 <head>
   <meta charset="utf-8">
   <title>ImageCapture tests</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1041393">ImageCapture tests</a>
 <script type="application/javascript">
 
-var repeat = 100;
-var count;
+let repeat = 100;
+let count;
 
 // Check if the callback returns even no JS reference on it.
 function gcTest(track) {
   return new Promise(function(resolve, reject) {
     count = 0;
-    var i;
-    var imageCapture;
+    let i;
+    let imageCapture;
     for(i = 0; i < repeat; i++) {
       imageCapture = new ImageCapture(track);
       imageCapture.onphoto = function(blob) {
         count++;
         if (count == repeat) {
           ok(true, "pass gc testing");
           resolve(track);
         }
@@ -41,112 +42,103 @@ function gcTest(track) {
     info("Call gc ");
     SpecialPowers.gc();
   });
 }
 
 // Continue calling takePhoto() in rapid succession.
 function rapidTest(track) {
   return new Promise(function(resolve, reject) {
-    var imageCapture = new ImageCapture(track);
+    let imageCapture = new ImageCapture(track);
     imageCapture.onphoto = function(blob) {
       count++;
       if (count == repeat) {
         ok(true, "pass raipd takePhoto() testing");
         resolve(track);
       }
     };
     imageCapture.onerror = function(error) {
       ok(false, "takePhoto() failure in rapid testing");
       reject();
     };
 
     count = 0;
-    var i;
+    let i;
     for(i = 0; i < repeat; i++) {
       imageCapture.takePhoto();
     }
   });
 }
 
 // Check if the blob is decodable.
 function blobTest(track) {
   return new Promise(function(resolve, reject) {
-    var imageCapture = new ImageCapture(track);
+    let imageCapture = new ImageCapture(track);
     imageCapture.onphoto = function(blob) {
-      var img = new Image();
+      let img = new Image();
       img.onerror = function() {
         ok(false, "fail to decode blob");
         reject();
-      }
+      };
       img.onload = function() {
         ok(true, "decode blob success");
         resolve(track);
-      }
+      };
       img.src = URL.createObjectURL(blob.data);
     };
     imageCapture.onerror = function(error) {
       ok(false, "fail to capture image");
     };
 
     imageCapture.takePhoto();
   });
 }
 
 // It should return an error event after disabling video track.
 function trackTest(track) {
   return new Promise(function(resolve, reject) {
-    var imageCapture = new ImageCapture(track);
+    let imageCapture = new ImageCapture(track);
     imageCapture.onphoto = function(blob) {
       ok(false, "expect error when video track is disable");
       reject();
     };
     imageCapture.onerror = function(error) {
       ok(error.imageCaptureError.code == error.imageCaptureError.PHOTO_ERROR, "error code is PHOTO_ERROR")
       track.enabled = true;
       resolve(track);
     };
 
     track.enabled = false;
-    imageCapture.takePhoto()
+    imageCapture.takePhoto();
   });
 }
 
-function init() {
-  return new Promise(function(resolve, reject) {
-    // use fake camera, MediaStreamGraph will be the backend of ImageCapture.
-    var constraints = {video: true, fake: true}
-
-    window.navigator.mozGetUserMedia(
-      constraints,
-      function(stream) {
-        var track = stream.getVideoTracks()[0];
-        resolve(track);
-      },
-      function(err) {
-        reject(err);
-      }
-    );
-  });
+async function init() {
+  // use loopback camera if available, otherwise fake, MediaStreamGraph will be the backend of ImageCapture.
+  await setupGetUserMediaTestPrefs();
+  let stream = await navigator.mediaDevices.getUserMedia({video: true});
+  return stream.getVideoTracks()[0];
 }
 
-function start() {
-  init().then(function(track) {
+async function start() {
+  try {
+    let track = await init();
     info("ImageCapture track disable test.");
-    return trackTest(track);
-  }).then(function(track) {
+    track = await trackTest(track);
     info("ImageCapture blob test.");
-    return blobTest(track);
-  }).then(function(track) {
+    track = await blobTest(track);
     info("ImageCapture rapid takePhoto() test.");
-    return rapidTest(track);
-  }).then(function(track) {
+    track = await rapidTest(track);
     info("ImageCapture multiple instances test.");
-    return gcTest(track);
-  }).then(SimpleTest.finish);
+    await gcTest(track);
+  } catch (e) {
+    ok(false, "Unexpected error during test: " + e);
+  } finally {
+    SimpleTest.finish();
+  }
 }
 
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv({"set": [["dom.imagecapture.enabled", true],
                                   ["media.navigator.permission.disabled", true]
                                   ]}, start);
--- a/dom/media/test/test_mediarecorder_avoid_recursion.html
+++ b/dom/media/test/test_mediarecorder_avoid_recursion.html
@@ -1,26 +1,27 @@
 <html>
 <head>
   <title>MediaRecorder infinite recursion with requestData() calls in "dataavailable" event</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=897776">Mozill
 a Bug 897776</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-function startTest() {
-  navigator.mozGetUserMedia({audio: true, fake: true}, function(stream) {
-    var mediaRecorder = new MediaRecorder(stream);
-    var count = 0;
-    mediaRecorder.start();
-    info("mediaRecorder start");
+async function startTest() {
+  try {
+    await setupGetUserMediaTestPrefs();
+    let stream = await navigator.mediaDevices.getUserMedia({audio: true});
+    let mediaRecorder = new MediaRecorder(stream);
+    let count = 0;
     mediaRecorder.onerror = function () {
       ok(false, 'Unexpected onerror callback fired');
       SimpleTest.finish();
     };
     mediaRecorder.onwarning = function () {
       ok(false, 'Unexpected onwarning callback fired');
     };
     mediaRecorder.ondataavailable = function (e) {
@@ -30,27 +31,29 @@ function startTest() {
       // the encoding thread
       if (count == 30) {
         info("stream.stop");
         stream.stop();
       } else if (count < 30 && mediaRecorder.state == 'recording') {
         info("requestData again");
         mediaRecorder.requestData();
       }
-    }
-    mediaRecorder.requestData();
-    info("mediaRecorder requestData");
+    };
     mediaRecorder.onstop = function () {
       ok(true, "requestData within ondataavailable successfully avoided infinite recursion");
       SimpleTest.finish();
-    }
-  }, function(err) {
-    ok(false, 'Unexpected error fired with: ' + err);
+    };
+    mediaRecorder.start();
+    info("mediaRecorder start");
+    mediaRecorder.requestData();
+    info("mediaRecorder requestData");
+  } catch (e) {
+    ok(false, 'Unexpected error fired with: ' + e);
     SimpleTest.finish();
-  });
+  }
 }
 
 SimpleTest.waitForExplicitFinish();
 startTest();
 
 </script>
 </pre>
 </body>
--- a/dom/media/test/test_mediarecorder_record_gum_video_timeslice.html
+++ b/dom/media/test/test_mediarecorder_record_gum_video_timeslice.html
@@ -1,25 +1,28 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test MediaRecorder Record gUM video with Timeslice</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <pre id="test">
 <div id="content" style="display: none">
 </div>
 <script class="testbody" type="text/javascript">
 
-function startTest() {
-  navigator.mozGetUserMedia({audio: true, video: true, fake: true}, function(stream) {
-    var dataAvailableCount = 0;
-    var onDataAvailableFirst = false;
+async function startTest() {
+  try {
+    await setupGetUserMediaTestPrefs();
+    let stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+    let dataAvailableCount = 0;
+    let onDataAvailableFirst = false;
 
     mediaRecorder = new MediaRecorder(stream);
     is(mediaRecorder.stream, stream,
        'Media recorder stream = element stream at the start of recording');
     mediaRecorder.onwarning = function() {
       ok(false, 'onwarning unexpectedly fired');
     };
 
@@ -68,20 +71,20 @@ function startTest() {
         // Ensure we've received at least two ondataavailable events before
         // onstop
         onDataAvailableFirst = true;
       }
     };
 
     mediaRecorder.start(250);
     is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
-  }, function(err) {
+  } catch (err) {
     ok(false, 'Unexpected error fired with: ' + err);
     SimpleTest.finish();
-  });
+  }
 }
 
 SimpleTest.waitForExplicitFinish();
 startTest();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_mediarecorder_unsupported_src.html
+++ b/dom/media/test/test_mediarecorder_unsupported_src.html
@@ -1,96 +1,96 @@
 <html>
 <head>
   <title>Bug 957439 - Media Recording - Assertion fail at Pause if unsupported input stream.</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=957439">Mozilla Bug 957439</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 
-function startTest() {
+async function startTest() {
   // also do general checks on mimetype support for audio-only
   ok(MediaRecorder.isTypeSupported("audio/ogg"), 'Should support audio/ogg');
   ok(MediaRecorder.isTypeSupported('audio/ogg; codecs="opus"'), 'Should support audio/ogg+opus');
   ok(!MediaRecorder.isTypeSupported('audio/ogg; codecs="foobar"'), 'Should not support audio/ogg + unknown_codec');
   ok(!MediaRecorder.isTypeSupported("video/webm"), 'Should not support video/webm');
   ok(!MediaRecorder.isTypeSupported("video/mp4"), 'Should not support video/mp4');
 
-  navigator.mozGetUserMedia({audio: false, video: true, fake: true},
-    function(stream) {
-
-      // Expected callback sequence should be:
-      // 1. onerror (from start)
-      // 2. ondataavailable
-      // 3. onstop
-      var callbackStep = 0;
-      var mediaRecorder = new MediaRecorder(stream);
-
-      is(mediaRecorder.stream, stream, 'Stream should be provided on creation');
+  try {
+    await setupGetUserMediaTestPrefs();
+    let stream = await navigator.mediaDevices.getUserMedia({audio: false, video: true});
 
-      mediaRecorder.onerror = function (e) {
-        callbackStep++;
-        info('onerror callback fired');
-        if (callbackStep == 1) {
-          try {
-            mediaRecorder.pause();
-            ok(false, 'pause should fire an exception if called on an inactive recorder');
-          } catch(e) {
-            ok(e instanceof DOMException, 'pause should fire an exception ' +
-              'if called on an inactive recorder');
-            is(e.name, 'InvalidStateError', 'Exception name should be InvalidStateError');
-          }
-        }
-        ok(callbackStep == 1, 'onerror callback should handle be the 1st event fired');
-        is(e.error.name, 'UnknownError', 'Error name should be UnknownError.');
-        ok(e.error.stack.includes('test_mediarecorder_unsupported_src.html'),
-          'Events fired from onerror should include an error with a stack trace indicating ' +
-          'an error in this test');
-        is(mediaRecorder.mimeType, '', 'mimetype should be empty');
-        is(mediaRecorder.state, 'inactive', 'state is inactive');
-      };
+    // Expected callback sequence should be:
+    // 1. onerror (from start)
+    // 2. ondataavailable
+    // 3. onstop
+    let callbackStep = 0;
+    let mediaRecorder = new MediaRecorder(stream);
+
+    is(mediaRecorder.stream, stream, 'Stream should be provided on creation');
 
-      mediaRecorder.onwarning = function () {
-        ok(false, 'Unexpected onwarning callback fired.');
-      };
+    mediaRecorder.onerror = function (e) {
+      callbackStep++;
+      info('onerror callback fired');
+      if (callbackStep == 1) {
+        try {
+          mediaRecorder.pause();
+          ok(false, 'pause should fire an exception if called on an inactive recorder');
+        } catch(e) {
+          ok(e instanceof DOMException, 'pause should fire an exception ' +
+            'if called on an inactive recorder');
+          is(e.name, 'InvalidStateError', 'Exception name should be InvalidStateError');
+        }
+      }
+      ok(callbackStep == 1, 'onerror callback should handle be the 1st event fired');
+      is(e.error.name, 'UnknownError', 'Error name should be UnknownError.');
+      ok(e.error.stack.includes('test_mediarecorder_unsupported_src.html'),
+        'Events fired from onerror should include an error with a stack trace indicating ' +
+        'an error in this test');
+      is(mediaRecorder.mimeType, '', 'mimetype should be empty');
+      is(mediaRecorder.state, 'inactive', 'state is inactive');
+    };
 
-      mediaRecorder.ondataavailable = function (evt) {
-        callbackStep++;
-        info('ondataavailable callback fired');
-        is(callbackStep, 2, 'ondataavailable callback should handle the 2nd event fired');
-        is(evt.data.size, 0, 'data size should be zero');
-        ok(evt instanceof BlobEvent,
-           'Events fired from ondataavailable should be BlobEvent');
-        is(evt.data.type, '', 'encoder start fail, blob miemType should be empty');
-      };
+    mediaRecorder.onwarning = function () {
+      ok(false, 'Unexpected onwarning callback fired.');
+    };
 
-      mediaRecorder.onstop = function() {
-        callbackStep++;
-        info('onstop callback fired');
-        is(mediaRecorder.state, 'inactive', 'state should be inactive');
-        is(callbackStep, 3, 'onstop callback should handle the 3rd event fired');
-        SimpleTest.finish();
-      };
+    mediaRecorder.ondataavailable = function (evt) {
+      callbackStep++;
+      info('ondataavailable callback fired');
+      is(callbackStep, 2, 'ondataavailable callback should handle the 2nd event fired');
+      is(evt.data.size, 0, 'data size should be zero');
+      ok(evt instanceof BlobEvent,
+          'Events fired from ondataavailable should be BlobEvent');
+      is(evt.data.type, '', 'encoder start fail, blob miemType should be empty');
+    };
 
-      try {
-        mediaRecorder.start();
-      } catch(e) {
-        ok(false, 'Should not get exception in start call.');
-      }
-    },
-    function(err) {
-      ok(false, 'Unexpected error fired with: ' + err);
+    mediaRecorder.onstop = function() {
+      callbackStep++;
+      info('onstop callback fired');
+      is(mediaRecorder.state, 'inactive', 'state should be inactive');
+      is(callbackStep, 3, 'onstop callback should handle the 3rd event fired');
       SimpleTest.finish();
+    };
+
+    try {
+      mediaRecorder.start();
+    } catch(e) {
+      ok(false, 'Should not get exception in start call.');
     }
-  );
+  } catch (err) {
+    ok(false, 'Unexpected error fired with: ' + err);
+    SimpleTest.finish();
+  }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 // In order to generate an "unsupported stream", pref off video encoding to
 // make the platform support audio encoding only.
 SpecialPowers.pushPrefEnv(
   {
--- a/dom/media/test/test_mediatrack_consuming_mediastream.html
+++ b/dom/media/test/test_mediatrack_consuming_mediastream.html
@@ -1,149 +1,150 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test track interfaces when consuming a MediaStream from gUM</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-function startTest() {
-  navigator.mozGetUserMedia({audio:true, video:true, fake:true},
-    function(stream) {
-      var element = document.createElement("video");
-
-      var audioOnchange = 0;
-      var audioOnaddtrack = 0;
-      var audioOnremovetrack = 0;
-      var videoOnchange = 0;
-      var videoOnaddtrack = 0;
-      var videoOnremovetrack = 0;
-      var isPlaying = false;
-
-      element.audioTracks.onaddtrack = function(e) {
-        audioOnaddtrack++;
-      }
-
-      element.audioTracks.onremovetrack = function(e) {
-        audioOnremovetrack++;
-      }
-
-      element.audioTracks.onchange = function(e) {
-        audioOnchange++;
-      }
+async function startTest() {
+  let steps = 0;
+  let audioOnchange = 0;
+  let audioOnaddtrack = 0;
+  let audioOnremovetrack = 0;
+  let videoOnchange = 0;
+  let videoOnaddtrack = 0;
+  let videoOnremovetrack = 0;
+  let isPlaying = false;
+  let element = document.createElement("video");
+  let stream;
+  try {
+    await setupGetUserMediaTestPrefs();
+    stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+  } catch (err) {
+    ok(false, 'Unexpected error fired with: ' + err);
+    SimpleTest.finish();
+    return;
+  }
 
-      element.videoTracks.onaddtrack = function(e) {
-        videoOnaddtrack++;
-      }
+  element.audioTracks.onaddtrack = function(e) {
+    audioOnaddtrack++;
+  };
 
-      element.videoTracks.onremovetrack = function(e) {
-        videoOnremovetrack++;
-      }
+  element.audioTracks.onremovetrack = function(e) {
+    audioOnremovetrack++;
+  };
 
-      element.videoTracks.onchange = function(e) {
-        videoOnchange++;
-      }
+  element.audioTracks.onchange = function(e) {
+    audioOnchange++;
+  };
 
-      function checkTrackRemoved() {
-        if (isPlaying) {
-          is(element.audioTracks.length, 0, 'The length of audioTracks should be 0.');
-          is(element.videoTracks.length, 0, 'The length of videoTracks should be 0.');
-        }
-      }
+  element.videoTracks.onaddtrack = function(e) {
+    videoOnaddtrack++;
+  };
 
-      function onended() {
-        ok(true, 'Event ended is expected to be fired on element.');
-        checkTrackRemoved();
-        element.onended = null;
-        element.onplaying = null;
-        element.onpause = null;
-        SimpleTest.finish();
-      }
+  element.videoTracks.onremovetrack = function(e) {
+    videoOnremovetrack++;
+  };
+
+  element.videoTracks.onchange = function(e) {
+    videoOnchange++;
+  };
 
-      function checkTrackAdded() {
-        isPlaying = true;
-        is(audioOnaddtrack, 1, 'Calls of onaddtrack on audioTracks should be 1.');
-        is(element.audioTracks.length, 1, 'The length of audioTracks should be 1.');
-        ok(element.audioTracks[0].enabled, 'Audio track should be enabled as default.');
-        is(videoOnaddtrack, 1, 'Calls of onaddtrack on videoTracks should be 1.');
-        is(element.videoTracks.length, 1, 'The length of videoTracks should be 1.');
-        is(element.videoTracks.selectedIndex, 0,
-           'The first video track is set selected as default.');
-      }
+  function checkTrackRemoved() {
+    if (isPlaying) {
+      is(element.audioTracks.length, 0, 'The length of audioTracks should be 0.');
+      is(element.videoTracks.length, 0, 'The length of videoTracks should be 0.');
+    }
+  }
 
-      function setTrackEnabled(enabled) {
-        element.audioTracks[0].enabled = enabled;
-        element.videoTracks[0].selected = enabled;
-      }
-
-      function checkTrackChanged(calls, enabled) {
-        is(audioOnchange, calls, 'Calls of onchange on audioTracks should be '+calls);
-        is(element.audioTracks[0].enabled, enabled,
-           'Enabled value of the audio track should be ' +enabled);
-        is(videoOnchange, calls, 'Calls of onchange on videoTracks should be '+calls);
-        is(element.videoTracks[0].selected, enabled,
-           'Selected value of the video track should be ' +enabled);
-        var index = enabled ? 0 : -1;
-        is(element.videoTracks.selectedIndex, index,
-           'SelectedIndex of video tracks should be ' +index);
-      }
+  element.onended = function() {
+    ok(true, 'Event ended is expected to be fired on element.');
+    checkTrackRemoved();
+    element.onended = null;
+    element.onplaying = null;
+    element.onpause = null;
+    SimpleTest.finish();
+  }
 
-      function onpause() {
-        element.onpause = null;
-        if (element.ended) {
-          return;
-        }
-        if (steps == 1) {
-          setTrackEnabled(false);
-          element.onplaying = onplaying;
-          element.play();
-          steps++;
-        } else if (steps == 2) {
-          setTrackEnabled(true);
-          element.onplaying = onplaying;
-          element.play();
-          steps++;
-        }
-      }
+  function checkTrackAdded() {
+    isPlaying = true;
+    is(audioOnaddtrack, 1, 'Calls of onaddtrack on audioTracks should be 1.');
+    is(element.audioTracks.length, 1, 'The length of audioTracks should be 1.');
+    ok(element.audioTracks[0].enabled, 'Audio track should be enabled as default.');
+    is(videoOnaddtrack, 1, 'Calls of onaddtrack on videoTracks should be 1.');
+    is(element.videoTracks.length, 1, 'The length of videoTracks should be 1.');
+    is(element.videoTracks.selectedIndex, 0,
+        'The first video track is set selected as default.');
+  }
+
+  function setTrackEnabled(enabled) {
+    element.audioTracks[0].enabled = enabled;
+    element.videoTracks[0].selected = enabled;
+  }
 
-      function onplaying() {
-        element.onplaying = null;
-        if (element.ended) {
-          return;
-        }
-        if (steps == 1) {
-          element.onpause = onpause;
-          element.pause();
-          checkTrackAdded();
-        } else if (steps == 2) {
-          element.onpause = onpause;
-          element.pause();
-          checkTrackChanged(1, false);
-        } else if (steps == 3) {
-          checkTrackChanged(2, true);
-          stream.stop();
-        }
-      }
+  function checkTrackChanged(calls, enabled) {
+    is(audioOnchange, calls, 'Calls of onchange on audioTracks should be '+calls);
+    is(element.audioTracks[0].enabled, enabled,
+        'Enabled value of the audio track should be ' +enabled);
+    is(videoOnchange, calls, 'Calls of onchange on videoTracks should be '+calls);
+    is(element.videoTracks[0].selected, enabled,
+        'Selected value of the video track should be ' +enabled);
+    var index = enabled ? 0 : -1;
+    is(element.videoTracks.selectedIndex, index,
+        'SelectedIndex of video tracks should be ' +index);
+  }
 
-      var steps = 0;
-      element.srcObject = stream;
+  function onpause() {
+    element.onpause = null;
+    if (element.ended) {
+      return;
+    }
+    if (steps == 1) {
+      setTrackEnabled(false);
       element.onplaying = onplaying;
-      element.onended = onended;
       element.play();
       steps++;
-    },
-    function(err) {
-      ok(false, 'Unexpected error fired with: ' + err);
-      SimpleTest.finish();
+    } else if (steps == 2) {
+      setTrackEnabled(true);
+      element.onplaying = onplaying;
+      element.play();
+      steps++;
+    }
+  }
+
+  function onplaying() {
+    element.onplaying = null;
+    if (element.ended) {
+      return;
     }
-  );
+    if (steps == 1) {
+      element.onpause = onpause;
+      element.pause();
+      checkTrackAdded();
+    } else if (steps == 2) {
+      element.onpause = onpause;
+      element.pause();
+      checkTrackChanged(1, false);
+    } else if (steps == 3) {
+      checkTrackChanged(2, true);
+      stream.stop();
+    }
+  }
+
+  element.onplaying = onplaying;
+  element.srcObject = stream;
+
+  steps++;
+  await element.play();
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv(
   {
     "set": [
       ["media.track.enabled", true]
     ]
--- a/dom/media/test/test_mediatrack_events.html
+++ b/dom/media/test/test_mediatrack_events.html
@@ -1,127 +1,129 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test events of media track interfaces</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-function startTest() {
-  navigator.mozGetUserMedia({audio:true, video:true, fake:true},
-    function(stream) {
-      var element = document.createElement("video");
-
-      isnot(element.audioTracks, undefined,
-            'HTMLMediaElement::AudioTracks() property should be available.');
-      isnot(element.videoTracks, undefined,
-            'HTMLMediaElement::VideoTracks() property should be available.');
-
-      function verifyEvent(e, type) {
-        is(e.type, type, "Event type should be " + type);
-        ok(e.isTrusted, "Event should be trusted.");
-        ok(!e.bubbles, "Event shouldn't bubble.");
-        ok(!e.cancelable, "Event shouldn't be cancelable.");
-      }
-
-      element.audioTracks.onaddtrack = function(e) {
-        ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
-        ok(true, 'onaddtrack is expected to be called from audioTracks.');
-        verifyEvent(e, "addtrack");
-      }
+async function startTest() {
+  let steps = 0;
+  let element = document.createElement("video");
+  let stream;
+  try {
+    await setupGetUserMediaTestPrefs();
+    stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+  } catch (err) {
+    ok(false, 'Unexpected error fired with: ' + err);
+    SimpleTest.finish();
+    return;
+  }
 
-      element.audioTracks.onremovetrack = function(e) {
-        ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
-        ok(true, 'onremovetrack is expected to be called from audioTracks.');
-        verifyEvent(e, "removetrack");
-      }
-
-      element.audioTracks.onchange = function(e) {
-        ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
-        ok(true, 'onchange is expected to be called from audioTracks.');
-        verifyEvent(e, "change");
-      }
+  function verifyEvent(e, type) {
+    is(e.type, type, "Event type should be " + type);
+    ok(e.isTrusted, "Event should be trusted.");
+    ok(!e.bubbles, "Event shouldn't bubble.");
+    ok(!e.cancelable, "Event shouldn't be cancelable.");
+  }
 
-      element.videoTracks.onaddtrack = function(e) {
-        ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
-        ok(true, 'onaddtrack is expected to be called from videoTracks.');
-        verifyEvent(e, "addtrack");
-      }
+  element.audioTracks.onaddtrack = function(e) {
+    ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
+    ok(true, 'onaddtrack is expected to be called from audioTracks.');
+    verifyEvent(e, "addtrack");
+  };
 
-      element.videoTracks.onremovetrack = function(e) {
-        ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
-        ok(true, 'onremovetrack is expected to be called from videoTracks.');
-        verifyEvent(e, "removetrack");
-      }
+  element.audioTracks.onremovetrack = function(e) {
+    ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
+    ok(true, 'onremovetrack is expected to be called from audioTracks.');
+    verifyEvent(e, "removetrack");
+  };
+
+  element.audioTracks.onchange = function(e) {
+    ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
+    ok(true, 'onchange is expected to be called from audioTracks.');
+    verifyEvent(e, "change");
+  };
 
-      element.videoTracks.onchange = function(e) {
-        ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
-        ok(true, 'onchange is expected to be called from videoTracks.');
-        verifyEvent(e, "change");
-      }
+  element.videoTracks.onaddtrack = function(e) {
+    ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
+    ok(true, 'onaddtrack is expected to be called from videoTracks.');
+    verifyEvent(e, "addtrack");
+  };
 
-     function onended() {
-        ok(true, 'Event ended is expected to be fired on element.');
-        element.onended = null;
-        element.onplaying = null;
-        element.onpause = null;
-        //This helps to prevent these events from firing after SimpleTest.finish()
-        //on B2G ICS Emulator, but not sure they have been run at all, then
-        element.audioTracks.onremovetrack = null;
-        element.audioTracks.onaddtrack = null;
-        element.audioTracks.onchange = null;
-        element.videoTracks.onremovetrack = null;
-        element.videoTracks.onaddtrack = null;
-        element.videoTracks.onchange = null;
-        SimpleTest.finish();
-      }
+  element.videoTracks.onremovetrack = function(e) {
+    ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
+    ok(true, 'onremovetrack is expected to be called from videoTracks.');
+    verifyEvent(e, "removetrack");
+  };
+
+  element.videoTracks.onchange = function(e) {
+    ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
+    ok(true, 'onchange is expected to be called from videoTracks.');
+    verifyEvent(e, "change");
+  };
 
-      function onpause() {
-        element.onpause = null;
-        if (element.ended) {
-          return;
-        }
-        if (steps == 1) {
-          element.audioTracks[0].enabled = false;
-          element.videoTracks[0].selected = false;
-          element.onplaying = onplaying;
-          element.play();
-          steps++;
-        }
-      }
+  element.onended = function() {
+    ok(true, 'Event ended is expected to be fired on element.');
+    element.onended = null;
+    element.onplaying = null;
+    element.onpause = null;
+    //This helps to prevent these events from firing after SimpleTest.finish()
+    //on B2G ICS Emulator, but not sure they have been run at all, then
+    element.audioTracks.onremovetrack = null;
+    element.audioTracks.onaddtrack = null;
+    element.audioTracks.onchange = null;
+    element.videoTracks.onremovetrack = null;
+    element.videoTracks.onaddtrack = null;
+    element.videoTracks.onchange = null;
+    SimpleTest.finish();
+  }
 
-      function onplaying() {
-        element.onplaying = null;
-        if (element.ended) {
-          return;
-        }
-        if (steps == 1) {
-          element.onpause = onpause;
-          element.pause();
-        } else if (steps == 2) {
-          stream.stop();
-        }
-      }
-
-      var steps = 0;
-      element.srcObject = stream;
+  function onpause() {
+    element.onpause = null;
+    if (element.ended) {
+      return;
+    }
+    if (steps == 1) {
+      element.audioTracks[0].enabled = false;
+      element.videoTracks[0].selected = false;
       element.onplaying = onplaying;
-      element.onended = onended;
       element.play();
       steps++;
-    },
-    function(err) {
-      ok(false, 'Unexpected error fired with: ' + err);
-      SimpleTest.finish();
+    }
+  }
+
+  function onplaying() {
+    element.onplaying = null;
+    if (element.ended) {
+      return;
     }
-  );
+    if (steps == 1) {
+      element.onpause = onpause;
+      element.pause();
+    } else if (steps == 2) {
+      stream.stop();
+    }
+  }
+
+  element.onplaying = onplaying;
+  element.srcObject = stream;
+
+  isnot(element.audioTracks, undefined,
+        'HTMLMediaElement::AudioTracks() property should be available.');
+  isnot(element.videoTracks, undefined,
+        'HTMLMediaElement::VideoTracks() property should be available.');
+
+  steps++;
+  await element.play();
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv(
   {
     "set": [
       ["media.track.enabled", true]
     ]
--- a/dom/media/test/test_multiple_mediastreamtracks.html
+++ b/dom/media/test/test_multiple_mediastreamtracks.html
@@ -1,40 +1,41 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test the ability of MediaStream with multiple MediaStreamTracks</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-function startTest() {
-  navigator.mediaDevices.getUserMedia({audio:true, video:true, fake:true})
-  .then(function(orgStream) {
-    var a = orgStream.getAudioTracks()[0];
-    var v = orgStream.getVideoTracks()[0];
-    var stream = new MediaStream([a, a, a, a, v, v, v].map(track => track.clone()));
-    var element = document.createElement("video");
+async function startTest() {
+  try {
+    await setupGetUserMediaTestPrefs();
+    let orgStream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+    let a = orgStream.getAudioTracks()[0];
+    let v = orgStream.getVideoTracks()[0];
+    let stream = new MediaStream([a, a, a, a, v, v, v].map(track => track.clone()));
+    let element = document.createElement("video");
 
     element.onloadedmetadata = function() {
       is(stream.getAudioTracks().length, 4, 'Length of audio tracks should be 4.');
       is(stream.getVideoTracks().length, 3, 'Length of vudio tracks should be 3.');
       SimpleTest.finish();
     };
 
     element.srcObject = stream;
     element.play();
-  })
-  .catch(function(reason) {
-    ok(false, "unexpected error = " + reason.message);
+  } catch (err) {
+    ok(false, 'Unexpected error fired with: ' + err);
     SimpleTest.finish();
-  });
+  }
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv(
   {
     "set": [
       ["media.track.enabled", true]
     ]
--- a/dom/media/test/test_streams_individual_pause.html
+++ b/dom/media/test/test_streams_individual_pause.html
@@ -1,79 +1,81 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for bug 1073406. Pausing a video element should not pause another playing the same stream.</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <video id="video1" autoplay></video>
 <video id="video2" autoplay></video>
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
+async function startTest() {
+  function getVideoImagePixelData(v) {
+    let canvas = document.createElement("canvas");
+    let ctx = canvas.getContext("2d");
+    ctx.drawImage(v, 0, 0);
+    let imgData = ctx.getImageData(canvas.width/2, canvas.height/2, 1, 1).data;
+    return "r" + imgData[0] +
+           "g" + imgData[1] +
+           "b" + imgData[2] +
+           "a" + imgData[3];
+  }
+
+  try {
+    // This test expects fake devices so that the video color will change
+    // over time, explicitly request fakes.
+    await pushGetUserMediaTestPrefs({fakeAudio: true, fakeVideo: true});
+    let stream = await navigator.mediaDevices.getUserMedia({video: true});
+    let video1 = document.getElementById('video1');
+    let video2 = document.getElementById('video2');
+
+    let src = URL.createObjectURL(stream);
+    video1.src = src;
+    video2.src = src;
+
+    video1.onplaying = () => video1.pause();
+
+    let v1PausedImageData;
+    let v2PausedImageData;
 
-var getVideoImagePixelData = function(v) {
-  var canvas = document.createElement("canvas");
-  var ctx = canvas.getContext("2d");
-  ctx.drawImage(v, 0, 0);
-  var imgData = ctx.getImageData(canvas.width/2, canvas.height/2, 1, 1).data;
-  return "r" + imgData[0] +
-         "g" + imgData[1] +
-         "b" + imgData[2] +
-         "a" + imgData[3];
+    video1.onpause = function() {
+      v1PausedImageData = getVideoImagePixelData(video1);
+      v2PausedImageData = getVideoImagePixelData(video2);
+      v2TimesToTest = 3;
+      video2.ontimeupdate = function() {
+        if (getVideoImagePixelData(video2) === v2PausedImageData) {
+          // Wait until video2 has progressed it's video.
+          // If it doesn't, we'll time out and fail.
+          info("video2 has not progressed. Waiting.");
+          return;
+        }
+
+        if (--v2TimesToTest > 0) {
+          // Wait for a while to be sure video1 would have gotten a frame
+          // if it is playing.
+          info("video2 progressed OK");
+          return;
+        }
+
+        video2.ontimeupdate = null;
+        ok(true, "video2 is playing");
+        isnot(video1.currentTime, video2.currentTime,
+              "v1 and v2 should not be at the same currentTime");
+        is(v1PausedImageData, getVideoImagePixelData(video1),
+           "video1 video frame should not have updated since video1 paused");
+        SimpleTest.finish();
+      };
+    };
+  } catch (error) {
+    ok(false, "getUserMedia should not fail, got " + error.name);
+    SimpleTest.finish();
+  }
 }
 
-// This test does not appear to work with the "Dummy video source" provided on
-// linux through the "media.video_loopback_dev" pref in the tree test environment.
-// We force a stream always by requesting `fake: true` here.
-
-navigator.mozGetUserMedia({video: true, fake: true },
-                          function(stream) {
-  var stream = stream;
-  var video1 = document.getElementById('video1');
-  var video2 = document.getElementById('video2');
-
-  var src = URL.createObjectURL(stream);
-  video1.src = src;
-  video2.src = src;
-
-  video1.onplaying = () => video1.pause();
-
-  var v1PausedImageData;
-  var v2PausedImageData;
-
-  video1.onpause = function() {
-    v1PausedImageData = getVideoImagePixelData(video1);
-    v2PausedImageData = getVideoImagePixelData(video2);
-    v2TimesToTest = 3;
-    video2.ontimeupdate = function() {
-      if (getVideoImagePixelData(video2) === v2PausedImageData) {
-        // Wait until video2 has progressed it's video.
-        // If it doesn't, we'll time out and fail.
-        info("video2 has not progressed. Waiting.");
-        return;
-      }
-
-      if (--v2TimesToTest > 0) {
-        // Wait for a while to be sure video1 would have gotten a frame
-        // if it is playing.
-        info("video2 progressed OK");
-        return;
-      }
-
-      video2.ontimeupdate = null;
-      ok(true, "video2 is playing");
-      isnot(video1.currentTime, video2.currentTime,
-            "v1 and v2 should not be at the same currentTime");
-      is(v1PausedImageData, getVideoImagePixelData(video1),
-         "video1 video frame should not have updated since video1 paused");
-      SimpleTest.finish();
-    };
-  };
-}, function(error) {
-  ok(false, "getUserMedia should not fail, got " + error.name);
-  SimpleTest.finish();
-});
+SimpleTest.waitForExplicitFinish();
+startTest();
 </script>
 </body>
 </html>