summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html')
-rw-r--r--dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html116
1 files changed, 116 insertions, 0 deletions
diff --git a/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html
new file mode 100644
index 0000000000..7920af9f7b
--- /dev/null
+++ b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+ <title>Test that MediaStreamAudioSourceNode and its input MediaStream stays alive while there are active tracks</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("gUM and WebAudio data is async to main thread. " +
+ "We need a timeout to see that something does " +
+ "NOT happen to data.");
+
+let context = new AudioContext();
+let analyser = context.createAnalyser();
+
+function wait(millis, resolveWithThis) {
+ return new Promise(resolve => setTimeout(() => resolve(resolveWithThis), millis));
+}
+
+function binIndexForFrequency(frequency) {
+ return 1 + Math.round(frequency * analyser.fftSize / context.sampleRate);
+}
+
+function waitForAudio(analysisFunction, cancelPromise) {
+ let data = new Uint8Array(analyser.frequencyBinCount);
+ let cancelled = false;
+ let cancelledMsg = "";
+ cancelPromise.then(msg => {
+ cancelled = true;
+ cancelledMsg = msg;
+ });
+ return new Promise((resolve, reject) => {
+ let loop = () => {
+ analyser.getByteFrequencyData(data);
+ if (cancelled) {
+ reject(new Error("waitForAudio cancelled: " + cancelledMsg));
+ return;
+ }
+ if (analysisFunction(data)) {
+ resolve();
+ return;
+ }
+ requestAnimationFrame(loop);
+ };
+ loop();
+ });
+}
+
+async function test(sourceNode) {
+ try {
+ await analyser.connect(context.destination);
+
+ ok(true, "Waiting for audio to pass through the analyser")
+ await waitForAudio(arr => arr[binIndexForFrequency(1000)] > 200,
+ wait(60000, "Timeout waiting for audio"));
+
+ ok(true, "Audio was detected by the analyser. Forcing CC.");
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+
+ info("Checking that GC didn't destroy the stream or source node");
+ await waitForAudio(arr => arr[binIndexForFrequency(1000)] < 50,
+ wait(5000, "Timeout waiting for GC (timeout OK)"))
+ .then(() => Promise.reject("Audio stopped unexpectedly"),
+ () => Promise.resolve());
+
+ ok(true, "Audio is still flowing");
+ } catch(e) {
+ ok(false, "Error executing test: " + e + (e.stack ? "\n" + e.stack : ""));
+ SimpleTest.finish();
+ }
+}
+
+(async function() {
+ try {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // This test expects the fake audio device, specifically for the tones
+ // it outputs. Explicitly disable the audio loopback device and enable
+ // fake streams.
+ ['media.audio_loopback_dev', ''],
+ ['media.navigator.streams.fake', true],
+ ['media.navigator.permission.disabled', true]
+ ]
+ });
+
+ // Test stream source GC
+ let stream = await navigator.mediaDevices.getUserMedia({audio: true});
+ let source = context.createMediaStreamSource(stream);
+ stream = null;
+ source.connect(analyser);
+ await test(source);
+
+ // Test track source GC
+ stream = await navigator.mediaDevices.getUserMedia({audio: true});
+ source = context.createMediaStreamTrackSource(stream.getAudioTracks()[0]);
+ stream = null;
+ source.connect(analyser);
+ await test(source);
+ } catch(e) {
+ ok(false, `Error executing test: ${e}${e.stack ? "\n" + e.stack : ""}`);
+ } finally {
+ context.close();
+ SimpleTest.finish();
+ }
+})();
+</script>
+</pre>
+</body>
+</html>