Bug 1438319 [wpt PR 9524] - Fix timeouts in WPT AudioParam tests, a=testonly
authorRaymond Toy <rtoy@chromium.org>
Mon, 26 Mar 2018 12:22:47 +0000
changeset 410953 762b46786b949074921f572e6932dbb35dd3fda6
parent 410952 381ad8c9826edd2f72cbd052d3c8f05b35b0e11d
child 410954 6d7cc54dce31dba17c4c67342233c5082ab27d44
push id101593
push userjames@hoppipolla.co.uk
push dateSat, 31 Mar 2018 22:09:06 +0000
treeherdermozilla-inbound@feb3750f2fac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1438319, 812285, 626703, 919151, 537311
milestone61.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 1438319 [wpt PR 9524] - Fix timeouts in WPT AudioParam tests, a=testonly Automatic update from web-platform-testsFix timeouts in WPT AudioParam tests Replace the tests that are using ScriptProcessor and online contexts with an offline context and verify all the output values instead of just one. This change exposed a couple of issues: - setTargetAtTime wasn't actually testing setTargetAtTime because a linearRampToValueAtTime event was called at the same time, effectively replacing the setTargetAtTime event - linearRampToValue and exponentialRampToValue tests expose bugs in Chrome's implementation of these when the event is scheduled in the past, and there is no preceding event. Bug: 812285, 626703 Change-Id: Iad3f54dd4373411431c019de44d4c3bad07587ff Reviewed-on: https://chromium-review.googlesource.com/919151 Commit-Queue: Raymond Toy <rtoy@chromium.org> Reviewed-by: Hongchan Choi <hongchan@chromium.org> Cr-Commit-Position: refs/heads/master@{#537311} wpt-commits: 81ada4212ece1e2f5c6cc560d6b2f45593679e93 wpt-pr: 9524 wpt-commits: 81ada4212ece1e2f5c6cc560d6b2f45593679e93 wpt-pr: 9524
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-exponentialRampToValueAtTime.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-linearRampToValueAtTime.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setTargetAtTime.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueCurveAtTime.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-test.js
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -293331,16 +293331,21 @@
      {}
     ]
    ],
    "webaudio/the-audio-api/the-audioparam-interface/.gitkeep": [
     [
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-audioparam-interface/retrospective-test.js": [
+    [
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep": [
     [
      {}
     ]
    ],
    "webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep": [
     [
      {}
@@ -596440,34 +596445,38 @@
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/idl-test.html": [
    "bd2b076121c523ecb20586975fc94866f62da980",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/retrospective-exponentialRampToValueAtTime.html": [
-   "53a2c031fd3c51cd401c1d192b09b745dd6b9260",
+   "12e1f35dca3f0b28d421ab39bdb2a98dc18abb3d",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/retrospective-linearRampToValueAtTime.html": [
-   "ebd5ea3d866634ff0958eb2fda96bf58cbbea6ad",
+   "30b41f51caaf643c1acd746be3a7ce19d2498ca1",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/retrospective-setTargetAtTime.html": [
-   "2fc8054f87b7d622b4cb5801e9f3a2083dbc23a4",
+   "2a012bfbd59445aa14789d80b94f514726e819f0",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html": [
-   "30bcf53f72c13690f606b1c27584e9bb937017ef",
+   "7558fec8601a9aa3a6fc8fc93c7e978a6bf50a9e",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueCurveAtTime.html": [
-   "fd40dc7cc8aca8ab829852dbabda3b00bcec5817",
-   "testharness"
+   "db939616369e2ce66b717d47512141e6f28e4c14",
+   "testharness"
+  ],
+  "webaudio/the-audio-api/the-audioparam-interface/retrospective-test.js": [
+   "aeb2213183f2fd26848c507e030de3e5f7f01fee",
+   "support"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/setTargetAtTime-after-event-within-block.html": [
    "3e54f9a98e7488223e6c40929268ddec3c8b5961",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/setValueAtTime-within-block.html": [
    "fc18277b2fbc5979f45c68491150eb715a82850e",
    "testharness"
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-exponentialRampToValueAtTime.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-exponentialRampToValueAtTime.html
@@ -1,51 +1,70 @@
 <!doctype html>
 <meta charset=utf-8>
-<title>Test exponentialRampToValue with end time in the past</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script>
-function do_test(t, context) {
-  var source = context.createConstantSource();
-  source.start();
+<html>
+  <head>
+    <title>Test exponentialRampToValue with end time in the past</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+    <script src="retrospective-test.js"></script>
+  </head>
+  <body>
+    <script>
+      let audit = Audit.createTaskRunner();
 
-  var test = context.createGain();
-  test.gain.exponentialRampToValueAtTime(0.1, 0.5*context.currentTime);
-  test.gain.exponentialRampToValueAtTime(0.9, 2.0);
+      audit.define(
+          {
+            label: 'test',
+            description: 'Test exponentialRampToValue with end time in the past'
+          },
+          (task, should) => {
+            let {context, source, test, reference} = setupRetrospectiveGraph();
 
-  var reference = context.createGain();
-  reference.gain.exponentialRampToValueAtTime(0.1, context.currentTime);
-  reference.gain.exponentialRampToValueAtTime(0.9, 2.0);
+            // Suspend the context at this frame so we can synchronously set up
+            // automations.
+            const suspendFrame = 128;
 
-  source.connect(test);
-  source.connect(reference);
-
-  var merger = context.createChannelMerger();
-  test.connect(merger, 0, 0);
-  reference.connect(merger, 0, 1);
+            context.suspend(suspendFrame / context.sampleRate)
+                .then(() => {
+                  // Call setTargetAtTime with a time in the past
+                  test.gain.exponentialRampToValueAtTime(
+                      0.1, 0.5 * context.currentTime);
+                  test.gain.exponentialRampToValueAtTime(0.9, 1.0);
 
-  var processor = context.createScriptProcessor(0, 2, 0);
-  merger.connect(processor);
-  processor.onaudioprocess =
-    t.step_func_done((e) => {
-      source.stop();
-      processor.onaudioprocess = null;
+                  reference.gain.exponentialRampToValueAtTime(
+                      0.1, context.currentTime);
+                  reference.gain.exponentialRampToValueAtTime(0.9, 1.0);
+                })
+                .then(() => context.resume());
 
-      var testValue = e.inputBuffer.getChannelData(0)[0];
-      var referenceValue = e.inputBuffer.getChannelData(1)[0];
+            source.start();
+
+            context.startRendering()
+                .then(resultBuffer => {
+                  let testValue = resultBuffer.getChannelData(0);
+                  let referenceValue = resultBuffer.getChannelData(1);
 
-      assert_equals(testValue, referenceValue,
-                        "value matches expected");
-    });
-}
+                  // Until the suspendFrame, both should be exactly equal to 1.
+                  should(
+                      testValue.slice(0, suspendFrame),
+                      `Test[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
+                  should(
+                      referenceValue.slice(0, suspendFrame),
+                      `Reference[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
 
-async_test(function(t) {
-  var context = new AudioContext;
-  (function waitForTimeAdvance() {
-    if (context.currentTime == 0) {
-      t.step_timeout(waitForTimeAdvance, 0);
-    } else {
-      do_test(t, context);
-    }
-  })();
-});
-</script>
+                  // After the suspendFrame, both should be equal (and not
+                  // constant)
+                  should(
+                      testValue.slice(suspendFrame), `Test[${suspendFrame}:]`)
+                      .beEqualToArray(referenceValue.slice(suspendFrame));
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>       
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-linearRampToValueAtTime.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-linearRampToValueAtTime.html
@@ -1,51 +1,70 @@
 <!doctype html>
 <meta charset=utf-8>
-<title>Test linearRampToValue with end time in the past</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script>
-function do_test(t, context) {
-  var source = context.createConstantSource();
-  source.start();
+<html>
+  <head>
+    <title>Test linearRampToValue with end time in the past</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+    <script src="retrospective-test.js"></script>
+  </head>
+  <body>
+    <script>
+      let audit = Audit.createTaskRunner();
 
-  var test = context.createGain();
-  test.gain.linearRampToValueAtTime(0.1, 0.5*context.currentTime);
-  test.gain.linearRampToValueAtTime(0.9, 2.0);
+      audit.define(
+          {
+            label: 'test',
+            description: 'Test linearRampToValue with end time in the past'
+          },
+          (task, should) => {
+            let {context, source, test, reference} = setupRetrospectiveGraph();
 
-  var reference = context.createGain();
-  reference.gain.linearRampToValueAtTime(0.1, context.currentTime);
-  reference.gain.linearRampToValueAtTime(0.9, 2.0);
+            // Suspend the context at this frame so we can synchronously set up
+            // automations.
+            const suspendFrame = 128;
 
-  source.connect(test);
-  source.connect(reference);
-
-  var merger = context.createChannelMerger();
-  test.connect(merger, 0, 0);
-  reference.connect(merger, 0, 1);
+            context.suspend(suspendFrame / context.sampleRate)
+                .then(() => {
+                  // Call setTargetAtTime with a time in the past
+                  test.gain.linearRampToValueAtTime(
+                      0.1, 0.5 * context.currentTime);
+                  test.gain.linearRampToValueAtTime(0.9, 1.0);
 
-  var processor = context.createScriptProcessor(0, 2, 0);
-  merger.connect(processor);
-  processor.onaudioprocess =
-    t.step_func_done((e) => {
-      source.stop();
-      processor.onaudioprocess = null;
+                  reference.gain.linearRampToValueAtTime(
+                      0.1, context.currentTime);
+                  reference.gain.linearRampToValueAtTime(0.9, 1.0);
+                })
+                .then(() => context.resume());
 
-      var testValue = e.inputBuffer.getChannelData(0)[0];
-      var referenceValue = e.inputBuffer.getChannelData(1)[0];
+            source.start();
+
+            context.startRendering()
+                .then(resultBuffer => {
+                  let testValue = resultBuffer.getChannelData(0);
+                  let referenceValue = resultBuffer.getChannelData(1);
 
-      assert_equals(testValue, referenceValue,
-                        "value matches expected");
-    });
-}
+                  // Until the suspendFrame, both should be exactly equal to 1.
+                  should(
+                      testValue.slice(0, suspendFrame),
+                      `Test[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
+                  should(
+                      referenceValue.slice(0, suspendFrame),
+                      `Reference[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
 
-async_test(function(t) {
-  var context = new AudioContext;
-  (function waitForTimeAdvance() {
-    if (context.currentTime == 0) {
-      t.step_timeout(waitForTimeAdvance, 0);
-    } else {
-      do_test(t, context);
-    }
-  })();
-});
-</script>
+                  // After the suspendFrame, both should be equal (and not
+                  // constant)
+                  should(
+                      testValue.slice(suspendFrame), `Test[${suspendFrame}:]`)
+                      .beEqualToArray(referenceValue.slice(suspendFrame));
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>       
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setTargetAtTime.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setTargetAtTime.html
@@ -1,51 +1,80 @@
 <!doctype html>
 <meta charset=utf-8>
-<title>Test setTargetAtTime with start time in the past</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script>
-function do_test(t, context) {
-  var source = context.createConstantSource();
-  source.start();
+<html>
+  <head>
+    <title>Test setTargetAtTime with start time in the past</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+  <body>    
+    <script>
+      let audit = Audit.createTaskRunner();
 
-  var test = context.createGain();
-  test.gain.setTargetAtTime(0.1, 0.5*context.currentTime, 0.1);
-  test.gain.linearRampToValueAtTime(0.9, 2.0);
+      audit.define(
+          {
+            label: 'test',
+            description: 'Test setTargetAtTime with start time in the past'
+          },
+          (task, should) => {
+            // Use a sample rate that is a power of two to eliminate round-off
+            // in computing the currentTime.
+            let context = new OfflineAudioContext(2, 16384, 16384);
+            let source = new ConstantSourceNode(context);
 
-  var reference = context.createGain();
-  reference.gain.setTargetAtTime(0.1, context.currentTime, 0.1);
-  reference.gain.linearRampToValueAtTime(0.9, 2.0);
+            // Suspend the context at this frame so we can synchronously set up
+            // automations.
+            const suspendFrame = 128;
+
+            let test = new GainNode(context);
+            let reference = new GainNode(context);
 
-  source.connect(test);
-  source.connect(reference);
+            source.connect(test);
+            source.connect(reference);
 
-  var merger = context.createChannelMerger();
-  test.connect(merger, 0, 0);
-  reference.connect(merger, 0, 1);
+            let merger = new ChannelMergerNode(
+                context, {numberOfInputs: context.destination.channelCount});
+            test.connect(merger, 0, 0);
+            reference.connect(merger, 0, 1);
 
-  var processor = context.createScriptProcessor(0, 2, 0);
-  merger.connect(processor);
-  processor.onaudioprocess =
-    t.step_func_done((e) => {
-      source.stop();
-      processor.onaudioprocess = null;
+            merger.connect(context.destination);
 
-      var testValue = e.inputBuffer.getChannelData(0)[0];
-      var referenceValue = e.inputBuffer.getChannelData(1)[0];
+            context.suspend(suspendFrame / context.sampleRate)
+                .then(() => {
+                  // Call setTargetAtTime with a time in the past
+                  test.gain.setTargetAtTime(0.1, 0.5*context.currentTime, 0.1);
+                  reference.gain.setTargetAtTime(0.1, context.currentTime, 0.1);
+                })
+                .then(() => context.resume());
+
+            source.start();
+
+            context.startRendering()
+                .then(resultBuffer => {
+                  let testValue = resultBuffer.getChannelData(0);
+                  let referenceValue = resultBuffer.getChannelData(1);
 
-      assert_equals(testValue, referenceValue,
-                        "value matches expected");
-    });
-}
+                  // Until the suspendFrame, both should be exactly equal to 1.
+                  should(
+                      testValue.slice(0, suspendFrame),
+                      `Test[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
+                  should(
+                      referenceValue.slice(0, suspendFrame),
+                      `Reference[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
 
-async_test(function(t) {
-  var context = new AudioContext;
-  (function waitForTimeAdvance() {
-    if (context.currentTime == 0) {
-      t.step_timeout(waitForTimeAdvance, 0);
-    } else {
-      do_test(t, context);
-    }
-  })();
-});
-</script>
+                  // After the suspendFrame, both should be equal (and not
+                  // constant)
+                  should(
+                      testValue.slice(suspendFrame), `Test[${suspendFrame}:]`)
+                      .beEqualToArray(referenceValue.slice(suspendFrame));
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html
@@ -1,54 +1,74 @@
 <!DOCTYPE html>
-<title>Test setValueAtTime with startTime in the past</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-function do_test(t, context) {
-  var source = context.createConstantSource();
-  source.start();
+<html>
+  <head>
+    <title>Test setValueAtTime with startTime in the past</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+    <script src="retrospective-test.js"></script>
+  </head>
+  <body>
+    <script>
+      let audit = Audit.createTaskRunner();
 
-  // Use a ramp of slope 1/sample to measure time.
-  // The end value is the extent of exact precision in single precision float.
-  const rampEnd = Math.pow(2, 24);
-  const rampEndSeconds = rampEnd / context.sampleRate;
-  var test = context.createGain();
-  test.gain.setValueAtTime(0.0, 0.5*context.currentTime);
-  test.gain.linearRampToValueAtTime(rampEnd, rampEndSeconds);
+      audit.define(
+          {
+            label: 'test',
+            description: 'Test setValueAtTime with startTime in the past'
+          },
+          (task, should) => {
+            let {context, source, test, reference} = setupRetrospectiveGraph();
 
-  var reference = context.createGain();
-  reference.gain.setValueAtTime(0.0, context.currentTime);
-  reference.gain.linearRampToValueAtTime(rampEnd, rampEndSeconds);
+            // Suspend the context at this frame so we can synchronously set up
+            // automations.
+            const suspendFrame = 128;
 
-  source.connect(test);
-  source.connect(reference);
+            // Use a ramp of slope 1 per frame to measure time.
+            // The end value is the extent of exact precision in single
+            // precision float.
+            const rampEnd = context.length - suspendFrame;
+            const rampEndSeconds = context.length / context.sampleRate;
 
-  var merger = context.createChannelMerger();
-  test.connect(merger, 0, 0);
-  reference.connect(merger, 0, 1);
+            context.suspend(suspendFrame / context.sampleRate)
+                .then(() => {
+                  // Call setValueAtTime with a time in the past
+                  test.gain.setValueAtTime(0.0, 0.5 * context.currentTime);
+                  test.gain.linearRampToValueAtTime(rampEnd, rampEndSeconds);
 
-  var processor = context.createScriptProcessor(0, 2, 0);
-  merger.connect(processor);
-  processor.onaudioprocess =
-    t.step_func_done((e) => {
-      source.stop();
-      processor.onaudioprocess = null;
+                  reference.gain.setValueAtTime(0.0, context.currentTime);
+                  reference.gain.linearRampToValueAtTime(
+                      rampEnd, rampEndSeconds);
+                })
+                .then(() => context.resume());
 
-      var testValue = e.inputBuffer.getChannelData(0)[0];
-      var referenceValue = e.inputBuffer.getChannelData(1)[0];
+            source.start();
+
+            context.startRendering()
+                .then(resultBuffer => {
+                  let testValue = resultBuffer.getChannelData(0);
+                  let referenceValue = resultBuffer.getChannelData(1);
 
-      assert_equals(testValue, referenceValue,
-                        "ramp value matches expected");
-    });
-}
+                  // Until the suspendFrame, both should be exactly equal to 1.
+                  should(
+                      testValue.slice(0, suspendFrame),
+                      `Test[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
+                  should(
+                      referenceValue.slice(0, suspendFrame),
+                      `Reference[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
 
-async_test(function(t) {
-  var context = new AudioContext;
-  (function waitForTimeAdvance() {
-    if (context.currentTime == 0) {
-      t.step_timeout(waitForTimeAdvance, 0);
-    } else {
-      do_test(t, context);
-    }
-  })();
-});
-</script>
+                  // After the suspendFrame, both should be equal (and not
+                  // constant)
+                  should(
+                      testValue.slice(suspendFrame), `Test[${suspendFrame}:]`)
+                      .beEqualToArray(referenceValue.slice(suspendFrame));
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueCurveAtTime.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueCurveAtTime.html
@@ -1,49 +1,67 @@
 <!doctype html>
-<meta charset=utf-8>
-<title>Test SetValueCurve with start time in the past</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script>
-function do_test(t, context) {
-  var source = context.createConstantSource();
-  source.start();
+<html>
+  <head>
+    <title>Test SetValueCurve with start time in the past</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+    <script src="retrospective-test.js"></script>
+  </head>
+  </body>
+    <script>
+      let audit = Audit.createTaskRunner();
 
-  var test = context.createGain();
-  test.gain.setValueCurveAtTime(new Float32Array([1.0, 0.1]), 0.0, 1.0);
-
-  var reference = context.createGain();
-  reference.gain.setValueCurveAtTime(new Float32Array([1.0, 0.1]), 0.5*context.currentTime, 1.0);
+      audit.define(
+          {
+            label: 'test',
+            description: 'Test SetValueCurve with start time in the past'
+          },
+          (task, should) => {
+            let {context, source, test, reference} = setupRetrospectiveGraph();
 
-  source.connect(test);
-  source.connect(reference);
-
-  var merger = context.createChannelMerger();
-  test.connect(merger, 0, 0);
-  reference.connect(merger, 0, 1);
+            // Suspend the context at this frame so we can synchronously set up
+            // automations.
+            const suspendFrame = 128;
 
-  var processor = context.createScriptProcessor(0, 2, 0);
-  merger.connect(processor);
-  processor.onaudioprocess =
-    t.step_func_done((e) => {
-      source.stop();
-      processor.onaudioprocess = null;
+            context.suspend(suspendFrame / context.sampleRate)
+                .then(() => {
+                  // Call setValueAtTime with a time in the past
+                  test.gain.setValueCurveAtTime(
+                      new Float32Array([1.0, 0.1]), 0.5 * context.currentTime,
+                      1.0);
+                  reference.gain.setValueCurveAtTime(
+                      new Float32Array([1.0, 0.1]), context.currentTime, 1.0);
+                })
+                .then(() => context.resume());
 
-      var testValue = e.inputBuffer.getChannelData(0)[0];
-      var referenceValue = e.inputBuffer.getChannelData(1)[0];
+            source.start();
+
+            context.startRendering()
+                .then(resultBuffer => {
+                  let testValue = resultBuffer.getChannelData(0);
+                  let referenceValue = resultBuffer.getChannelData(1);
 
-      assert_equals(testValue, referenceValue,
-                        "value matches expected");
-    });
-}
+                  // Until the suspendFrame, both should be exactly equal to 1.
+                  should(
+                      testValue.slice(0, suspendFrame),
+                      `Test[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
+                  should(
+                      referenceValue.slice(0, suspendFrame),
+                      `Reference[0:${suspendFrame - 1}]`)
+                      .beConstantValueOf(1);
 
-async_test(function(t) {
-  var context = new AudioContext;
-  (function waitForTimeAdvance() {
-    if (context.currentTime == 0) {
-      t.step_timeout(waitForTimeAdvance, 0);
-    } else {
-      do_test(t, context);
-    }
-  })();
-});
-</script>
+                  // After the suspendFrame, both should be equal (and not
+                  // constant)
+                  should(
+                      testValue.slice(suspendFrame), `Test[${suspendFrame}:]`)
+                      .beEqualToArray(referenceValue.slice(suspendFrame));
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/retrospective-test.js
@@ -0,0 +1,29 @@
+// Create an audio graph on an offline context that consists of a
+// constant source and two gain nodes. One of the nodes is the node te
+// be tested and the other is the reference node.  The output from the
+// test node is in channel 0 of the offline context; the reference
+// node is in channel 1.
+//
+// Returns a dictionary with the context, source node, the test node,
+// and the reference node.
+function setupRetrospectiveGraph() {
+  // Use a sample rate that is a power of two to eliminate round-off
+  // in computing the currentTime.
+  let context = new OfflineAudioContext(2, 16384, 16384);
+  let source = new ConstantSourceNode(context);
+
+  let test = new GainNode(context);
+  let reference = new GainNode(context);
+
+  source.connect(test);
+  source.connect(reference);
+
+  let merger = new ChannelMergerNode(
+      context, {numberOfInputs: context.destination.channelCount});
+  test.connect(merger, 0, 0);
+  reference.connect(merger, 0, 1);
+
+  merger.connect(context.destination);
+
+  return {context: context, source: source, test: test, reference: reference};
+}