Bug 1408456 - Convert test_analyserNodeOutput.html to a web-platform-test: test-analyser-output.html. r?karlt draft
authorPaul Adenot <paul@paul.cx>
Tue, 17 Oct 2017 14:47:44 +0200
changeset 681592 9ae9be58eaee326abf6fa1b20c24b8b522dbe420
parent 681591 cf9731daba00b493705493208eb68807d0ef3595
child 736172 d357c8a295115096c94db19676aa85b088132292
push id84857
push userpaul@paul.cx
push dateTue, 17 Oct 2017 12:49:21 +0000
reviewerskarlt
bugs1408456
milestone58.0a1
Bug 1408456 - Convert test_analyserNodeOutput.html to a web-platform-test: test-analyser-output.html. r?karlt This also brings in and convert some utilities we have in Gecko, that will ease porting more tests. MozReview-Commit-ID: 2yPZQO504kH
dom/media/webaudio/test/mochitest.ini
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webaudio/js/helpers.js
testing/web-platform/tests/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -31,16 +31,17 @@ support-files =
   sine-440-10s.opus
   webaudio.js
 
 [test_analyserNode.html]
 skip-if = !asan || toolkit != android
 [test_analyserScale.html]
 skip-if = !asan || toolkit != android
 [test_analyserNodeOutput.html]
+skip-if = !asan || toolkit != android
 [test_analyserNodePassThrough.html]
 [test_analyserNodeWithGain.html]
 skip-if = !asan || toolkit != android
 [test_analyserNodeMinimum.html]
 skip-if = !asan || toolkit != android
 [test_AudioBuffer.html]
 [test_audioBufferSourceNode.html]
 [test_audioBufferSourceNodeEnded.html]
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -380079,16 +380079,22 @@
     ]
    ],
    "webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html": [
     [
      "/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html",
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html": [
+    [
+     "/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html": [
     [
      "/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html",
      {}
     ]
    ],
    "webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html": [
     [
@@ -631555,17 +631561,17 @@
    "230684ec60fd2e408f9b6014417f3eddfe2dc95a",
    "support"
   ],
   "webaudio/js/buffer-loader.js": [
    "4d564eae0b3d7d1045626d1f144cd2638dba64e5",
    "support"
   ],
   "webaudio/js/helpers.js": [
-   "c5d44cf8101c50b59c366ed1971205193f32e1bf",
+   "dff18a7e57adb3847b70fa7f1f3752b591b38d6e",
    "support"
   ],
   "webaudio/js/lodash.js": [
    "58c3eae918fedad54c56d488a14f99ce87943500",
    "support"
   ],
   "webaudio/js/vendor-prefixes.js": [
    "3b7d06164273077415b7fc99652355bcf0e10e17",
@@ -631590,16 +631596,20 @@
   "webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html": [
    "c3f5f5969ed0ab58a9df332196e138aef8e693f3",
    "testharness"
   ],
   "webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html": [
    "cfbeb7283e7375974943ccf689cca73942e6259f",
    "testharness"
   ],
+  "webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html": [
+   "f27d081b9b3df8af7449f130a455b90c6e93ca7b",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html": [
    "1909a2970f0529ad0433c8e6e75733695d44d3e0",
    "testharness"
   ],
   "webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html": [
    "8478aa405a4641a9c47554529762e85a37d7593a",
    "testharness"
   ],
--- a/testing/web-platform/tests/webaudio/js/helpers.js
+++ b/testing/web-platform/tests/webaudio/js/helpers.js
@@ -16,8 +16,203 @@ function trimEmptyElements(array) {
   while (end > 0) {
     end--;
     if (array[end] !== 0) {
       break;
     }
   }
   return array.subarray(start, end);
 }
+
+
+function fuzzyCompare(a, b) {
+  return Math.abs(a - b) < 9e-3;
+}
+
+function compareChannels(buf1, buf2,
+                        /*optional*/ length,
+                        /*optional*/ sourceOffset,
+                        /*optional*/ destOffset,
+                        /*optional*/ skipLengthCheck) {
+  if (!skipLengthCheck) {
+    assert_equals(buf1.length, buf2.length, "Channels must have the same length");
+  }
+  sourceOffset = sourceOffset || 0;
+  destOffset = destOffset || 0;
+  if (length == undefined) {
+    length = buf1.length - sourceOffset;
+  }
+  var difference = 0;
+  var maxDifference = 0;
+  var firstBadIndex = -1;
+  for (var i = 0; i < length; ++i) {
+    if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) {
+      difference++;
+      maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset]));
+      if (firstBadIndex == -1) {
+        firstBadIndex = i;
+      }
+    }
+  };
+
+  assert_equals(difference, 0, "maxDifference: " + maxDifference +
+     ", first bad index: " + firstBadIndex +
+     " with test-data offset " + sourceOffset + " and expected-data offset " +
+     destOffset + "; corresponding values " + buf1[firstBadIndex + sourceOffset] +
+     " and " + buf2[firstBadIndex + destOffset] + " --- differences");
+}
+
+function compareBuffers(got, expected) {
+  if (got.numberOfChannels != expected.numberOfChannels) {
+    assert_equals(got.numberOfChannels, expected.numberOfChannels,
+       "Correct number of buffer channels");
+    return;
+  }
+  if (got.length != expected.length) {
+    assert_equals(got.length, expected.length,
+       "Correct buffer length");
+    return;
+  }
+  if (got.sampleRate != expected.sampleRate) {
+    assert_equals(got.sampleRate, expected.sampleRate,
+                   "Correct sample rate");
+    return;
+  }
+
+  for (var i = 0; i < got.numberOfChannels; ++i) {
+    compareChannels(got.getChannelData(i), expected.getChannelData(i),
+                    got.length, 0, 0, true);
+  }
+}
+
+/**
+ * This function assumes that the test is a "single page test" [0], and defines a
+ * single gTest variable with the following properties and methods:
+ *
+ * + numberOfChannels: optional property which specifies the number of channels
+ *                     in the output.  The default value is 2.
+ * + createGraph: mandatory method which takes a context object and does
+ *                everything needed in order to set up the Web Audio graph.
+ *                This function returns the node to be inspected.
+ * + createGraphAsync: async version of createGraph.  This function takes
+ *                     a callback which should be called with an argument
+ *                     set to the node to be inspected when the callee is
+ *                     ready to proceed with the test.  Either this function
+ *                     or createGraph must be provided.
+ * + createExpectedBuffers: optional method which takes a context object and
+ *                          returns either one expected buffer or an array of
+ *                          them, designating what is expected to be observed
+ *                          in the output.  If omitted, the output is expected
+ *                          to be silence.  All buffers must have the same
+ *                          length, which must be a bufferSize supported by
+ *                          ScriptProcessorNode.  This function is guaranteed
+ *                          to be called before createGraph.
+ * + length: property equal to the total number of frames which we are waiting
+ *           to see in the output, mandatory if createExpectedBuffers is not
+ *           provided, in which case it must be a bufferSize supported by
+ *           ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384).
+ *           If createExpectedBuffers is provided then this must be equal to
+ *           the number of expected buffers * the expected buffer length.
+ *
+ * + skipOfflineContextTests: optional. when true, skips running tests on an offline
+ *                            context by circumventing testOnOfflineContext.
+ *
+ * [0]: http://web-platform-tests.org/writing-tests/testharness-api.html#single-page-tests
+ */
+function runTest(name)
+{
+  function runTestFunction () {
+    if (!gTest.numberOfChannels) {
+      gTest.numberOfChannels = 2; // default
+    }
+
+    var testLength;
+
+    function runTestOnContext(context, callback, testOutput) {
+      if (!gTest.createExpectedBuffers) {
+        // Assume that the output is silence
+        var expectedBuffers = getEmptyBuffer(context, gTest.length);
+      } else {
+        var expectedBuffers = gTest.createExpectedBuffers(context);
+      }
+      if (!(expectedBuffers instanceof Array)) {
+        expectedBuffers = [expectedBuffers];
+      }
+      var expectedFrames = 0;
+      for (var i = 0; i < expectedBuffers.length; ++i) {
+        assert_equals(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels,
+           "Correct number of channels for expected buffer " + i);
+        expectedFrames += expectedBuffers[i].length;
+      }
+      if (gTest.length && gTest.createExpectedBuffers) {
+        assert_equals(expectedFrames,
+                     gTest.length, "Correct number of expected frames");
+      }
+
+      if (gTest.createGraphAsync) {
+        gTest.createGraphAsync(context, function(nodeToInspect) {
+          testOutput(nodeToInspect, expectedBuffers, callback);
+        });
+      } else {
+        testOutput(gTest.createGraph(context), expectedBuffers, callback);
+      }
+    }
+
+    function testOnNormalContext(callback) {
+      function testOutput(nodeToInspect, expectedBuffers, callback) {
+        testLength = 0;
+        var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 0);
+        nodeToInspect.connect(sp);
+        sp.onaudioprocess = function(e) {
+          var expectedBuffer = expectedBuffers.shift();
+          testLength += expectedBuffer.length;
+          compareBuffers(e.inputBuffer, expectedBuffer);
+          if (expectedBuffers.length == 0) {
+            sp.onaudioprocess = null;
+            callback();
+          }
+        };
+      }
+      var context = new AudioContext();
+      runTestOnContext(context, callback, testOutput);
+    }
+
+    function testOnOfflineContext(callback, sampleRate) {
+      function testOutput(nodeToInspect, expectedBuffers, callback) {
+        nodeToInspect.connect(context.destination);
+        context.oncomplete = function(e) {
+          var samplesSeen = 0;
+          while (expectedBuffers.length) {
+            var expectedBuffer = expectedBuffers.shift();
+            assert_equals(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
+               "Correct number of input buffer channels");
+            for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) {
+              compareChannels(e.renderedBuffer.getChannelData(i),
+                             expectedBuffer.getChannelData(i),
+                             expectedBuffer.length,
+                             samplesSeen,
+                             undefined,
+                             true);
+            }
+            samplesSeen += expectedBuffer.length;
+          }
+          callback();
+        };
+        context.startRendering();
+      }
+
+      var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate);
+      runTestOnContext(context, callback, testOutput);
+    }
+
+    testOnNormalContext(function() {
+      if (!gTest.skipOfflineContextTests) {
+        testOnOfflineContext(function() {
+          testOnOfflineContext(done, 44100);
+        }, 48000);
+      } else {
+        done();
+      }
+    });
+  };
+
+  runTestFunction();
+}
copy from dom/media/webaudio/test/test_analyserNodeOutput.html
copy to testing/web-platform/tests/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html
--- a/dom/media/webaudio/test/test_analyserNodeOutput.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html
@@ -1,20 +1,17 @@
-<!DOCTYPE HTML>
+<!DOCTYPE html>
 <html>
 <head>
-  <title>Test AnalyserNode</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="webaudio.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
+  <meta charset="utf-8">
+  <title>AnalyserNode output</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/webaudio/js/helpers.js"></script>
+  <script>
 var gTest = {
   length: 2048,
   numberOfChannels: 1,
   createGraph: function(context) {
     var source = context.createBufferSource();
 
     var analyser = context.createAnalyser();
 
@@ -23,21 +20,22 @@ var gTest = {
     source.connect(analyser);
 
     source.start(0);
     return analyser;
   },
   createExpectedBuffers: function(context) {
     this.buffer = context.createBuffer(1, 2048, context.sampleRate);
     for (var i = 0; i < 2048; ++i) {
-      this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+      this.buffer.getChannelData(0)[i] = Math.sin(
+        440 * 2 * Math.PI * i / context.sampleRate
+      );
     }
 
     return [this.buffer];
-  },
+  }
 };
 
-runTest();
-
-</script>
-</pre>
+runTest("AnalyserNode output");
+  </script>
+</head>
 </body>
 </html>