Bug 1404977 - Tests P2: Add test to ensure multiple, concurrent gUM calls in a single window succeed. r?pehrsons draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Fri, 03 Aug 2018 10:35:59 -0400
changeset 826330 fa8851738c196056c09ce5f5992ca6ade79e5f85
parent 826329 6e1f95afdf4134f45236f75f2fc6454fda751895
push id118293
push userbvandyk@mozilla.com
push dateFri, 03 Aug 2018 15:18:25 +0000
reviewerspehrsons
bugs1404977
milestone63.0a1
Bug 1404977 - Tests P2: Add test to ensure multiple, concurrent gUM calls in a single window succeed. r?pehrsons MozReview-Commit-ID: HGwcu6Z2mDn
dom/media/tests/mochitest/mochitest.ini
dom/media/tests/mochitest/test_getUserMedia_audioConstraints_concurrentStreams.html
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -51,16 +51,18 @@ skip-if = toolkit == 'android' # android
 [test_getUserMedia_addTrackRemoveTrack.html]
 skip-if = android_version == '18' || os == 'linux' # android(Bug 1189784, timeouts on 4.3 emulator), linux bug 1377450
 [test_getUserMedia_addtrack_removetrack_events.html]
 skip-if = os == 'linux' && debug # Bug 1389983
 [test_getUserMedia_audioConstraints.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
 [test_getUserMedia_audioConstraints_concurrentIframes.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
+[test_getUserMedia_audioConstraints_concurrentStreams.html]
+skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
 [test_getUserMedia_basicAudio_loopback.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
 [test_getUserMedia_basicAudio.html]
 [test_getUserMedia_basicVideo.html]
 [test_getUserMedia_basicVideo_playAfterLoadedmetadata.html]
 [test_getUserMedia_basicScreenshare.html]
 skip-if = toolkit == 'android' # no screenshare on android
 [test_getUserMedia_basicTabshare.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_audioConstraints_concurrentStreams.html
@@ -0,0 +1,132 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+createHTML({
+  title: "getUserMedia multiple times, concurrently, and with different constraints",
+  bug: "1404977"
+});
+/**
+  * Verify that we can successfully call getUserMedia multiple times for the
+  * same device, concurrently. This is checked by calling getUserMedia a number
+  * of times with different constraints. We verify that the stream returned by
+  * that call has the same constraints as requested both immediately after the
+  * call and after all gUM calls have been made. The test then verifies the
+  * streams can be played.
+  */
+runTest(async function() {
+  // Compare constraints and return a string with the differences in
+  // echoCancellation, autoGainControl, and noiseSuppression. The string
+  // will be empty if there are no differences.
+  function getConstraintDifferenceString(constraints, otherConstraints) {
+    let diffString = "";
+    if (constraints.echoCancellation != otherConstraints.echoCancellation) {
+      diffString += "echoCancellation different: " +
+                    `${constraints.echoCancellation} != ${otherConstraints.echoCancellation}, `;
+    }
+    if (constraints.autoGainControl != otherConstraints.autoGainControl) {
+      diffString += "autoGainControl different: " +
+                    `${constraints.autoGainControl} != ${otherConstraints.autoGainControl}, `;
+    }
+    if (constraints.noiseSuppression != otherConstraints.noiseSuppression) {
+      diffString += "noiseSuppression different: " +
+                    `${constraints.noiseSuppression} != ${otherConstraints.noiseSuppression}, `;
+    }
+    // Replace trailing comma and space if any
+    return diffString.replace(/, $/, "");
+  }
+
+  // We need a real device to get a MediaEngine supporting constraints
+  let audioDevice = SpecialPowers.getCharPref("media.audio_loopback_dev", "");
+  if (!audioDevice) {
+    todo(false, "No device set by framework. Try --use-test-media-devices");
+    return;
+  }
+
+  let egn = (e, g, n) => ({
+    echoCancellation: e,
+    autoGainControl: g,
+    noiseSuppression: n
+  });
+
+  let allConstraintCombinations = [
+    egn(false, false, false),
+    egn(true,  false, false),
+    egn(false, true,  false),
+    egn(false, false, true),
+    egn(true,  true,  false),
+    egn(true,  false, true),
+    egn(false, true,  true),
+    egn(true,  true,  true),
+  ];
+
+  // TODO: We would like to be able to perform an arbitrary number of gUM calls
+  // at once, but issues with pulse and audio IPC mean on some systems we're
+  // limited to as few as 2 concurrent calls. To avoid issues we chunk test runs
+  // to only two calls at a time. The while and the splice lines can be removed,
+  // and allConstraintCombinations can be renamed to constraintCombinations once
+  // this issue is resolved. See bug 1480489
+  while (allConstraintCombinations.length) {
+    let constraintCombinations = allConstraintCombinations.splice(0, 2);
+    // Array to store objects that associate information used in our test such as
+    // constraints, gum streams, and various promises.
+    let testCases = [];
+
+    for (let constraints of constraintCombinations) {
+      let testCase = {requestedConstraints: constraints};
+      // Provide an id for logging, labeling related elements.
+      testCase.id = `testCase.` +
+                    `e=${constraints.echoCancellation}.` +
+                    `g=${constraints.noiseSuppression}.` +
+                    `n=${constraints.noiseSuppression}`;
+      testCases.push(testCase);
+      testCase.gumStream =
+        await getUserMedia({audio: testCase.requestedConstraints})
+        .catch(e => Promise.reject(`getUserMedia calls should not fail! Failed at ${testCase.id} with: ${e}!`));
+      let differenceString = getConstraintDifferenceString(
+        testCase.requestedConstraints,
+        testCase.gumStream.getAudioTracks()[0].getSettings());
+        ok(!differenceString,
+          `gUM stream for ${testCase.id} should have the same constraints as were ` +
+          `requested from gUM. Differences: ${differenceString}`);
+    }
+    is(testCases.length,
+      constraintCombinations.length,
+      "Should have a stream for each constraint");
+
+    // Once all streams are collected, make sure the constraints haven't been
+    // mutated by another gUM call.
+    for (let testCase of testCases) {
+      let differenceString = getConstraintDifferenceString(
+        testCase.requestedConstraints,
+        testCase.gumStream.getAudioTracks()[0].getSettings());
+      ok(!differenceString,
+        `gUM stream for ${testCase.id} should not have had constraints altered after ` +
+        `all gUM calls are done. Differences: ${differenceString}`);
+    }
+
+    // We do not currently have tests to verify the behaviour of the different
+    // constraints. Once we do we should do further verificaiton here. See
+    // bug 1406372, bug 1406376, and bug 1406377.
+
+    for (let testCase of testCases) {
+      let testAudio = createMediaElement("audio", `testAudio.${testCase.id}`);
+      let playback = new LocalMediaStreamPlayback(testAudio, testCase.gumStream);
+      await playback.playMediaWithoutStoppingTracks(false);
+    }
+
+    // Stop the tracks for each stream, we left them running above via
+    // playMediaWithoutStoppingTracks to make sure they can play concurrently.
+    for (let testCase of testCases) {
+      testCase.gumStream.getTracks().map(t => t.stop());
+    }
+  }
+});
+</script>
+</pre>
+</body>
+</html>