Index: third_party/WebKit/LayoutTests/webaudio/audioparam-nominal-range.html |
diff --git a/third_party/WebKit/LayoutTests/webaudio/audioparam-nominal-range.html b/third_party/WebKit/LayoutTests/webaudio/audioparam-nominal-range.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a917b826248d63ab7b6742429a05b29a398e8702 |
--- /dev/null |
+++ b/third_party/WebKit/LayoutTests/webaudio/audioparam-nominal-range.html |
@@ -0,0 +1,396 @@ |
+<!doctype html> |
+<html> |
+ <head> |
+ <script src="../resources/js-test.js"></script> |
+ <script src="resources/compatibility.js"></script> |
+ <script src="resources/audio-testing.js"></script> |
+ <title>Test AudioParam Nominal Range Values</title> |
+ </head> |
+ |
+ <body> |
+ <script> |
+ description("Test Automation of SpatialListener Position."); |
+ window.jsTestIsAsync = true; |
+ |
+ // Some arbitrary sample rate for the offline context. |
+ var sampleRate = 48000; |
+ |
+ // The actual offline context |
+ var context; |
+ |
+ // The set of all methods that we've tested for verifying that we tested all of the necessary |
+ // objects. |
+ var testedMethods = new Set(); |
+ |
+ // The most positive single float value (the value just before infinity). Be careful when |
+ // changing this value! Javascript only uses double floats, so the value here should be the |
+ // max single-float value, converted directly to a double-float value. This also depends on |
+ // Javascript reading this value and producing the desired double-float value correctly. |
+ var mostPositiveFloat = 3.4028234663852886e38; |
+ |
+ var audit = Audit.createTaskRunner(); |
+ |
+ // Array describing the tests that should be run. |
+ var testConfigs = [{ |
+ // The name of the method to create the particular node to be tested. |
+ creator: "createGain", |
+ |
+ // Any args to pass to the creator function. |
+ args: [], |
+ |
+ // The min/max limits for each AudioParam of the node. This is a dictionary whose keys are |
+ // the names of each AudioParam in the node. Don't define this if the node doesn't have any |
+ // AudioParam attributes. |
+ limits: { |
+ gain: { |
+ // The expected min and max values for this AudioParam. |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ } |
+ } |
+ }, { |
+ creator: "createDelay", |
+ // Just specify a non-default value for the maximum delay so we can make sure the limits are |
+ // set correctly. |
+ args: [1.5], |
+ limits: { |
+ delayTime: { |
+ minValue: 0, |
+ maxValue: 1.5 |
+ } |
+ } |
+ }, { |
+ creator: "createBufferSource", |
+ args: [], |
+ limits: { |
+ playbackRate: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ }, |
+ detune: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ } |
+ } |
+ }, { |
+ creator: "createStereoPanner", |
+ args: [], |
+ limits: { |
+ pan: { |
+ minValue: -1, |
+ maxValue: 1 |
+ } |
+ } |
+ }, { |
+ creator: "createDynamicsCompressor", |
+ args: [], |
+ // Do not set limits for reduction; it's currently an AudioParam but should be a float. |
+ // So let the test fail for reduction. When reduction is changed, this test will then |
+ // correctly pass. |
+ limits: { |
+ threshold: { |
+ minValue: -100, |
+ maxValue: 0 |
+ }, |
+ knee: { |
+ minValue: 0, |
+ maxValue: 40 |
+ }, |
+ ratio: { |
+ minValue: 1, |
+ maxValue: 20 |
+ }, |
+ attack: { |
+ minValue: 0, |
+ maxValue: 1 |
+ }, |
+ release: { |
+ minValue: 0, |
+ maxValue: 1 |
+ } |
+ } |
+ }, |
+ { |
+ creator: "createBiquadFilter", |
+ args: [], |
+ limits: { |
+ gain: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ }, |
+ Q: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ }, |
+ frequency: { |
+ minValue: 0, |
+ maxValue: sampleRate / 2 |
+ }, |
+ detune: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ } |
+ } |
+ }, { |
+ creator: "createOscillator", |
+ args: [], |
+ limits: { |
+ frequency: { |
+ minValue: 0, |
+ maxValue: sampleRate / 2 |
+ }, |
+ detune: { |
+ minValue: -mostPositiveFloat, |
+ maxValue: mostPositiveFloat |
+ } |
+ } |
+ }, |
+ // These nodes don't have AudioParams, but we want to test them anyway. Any arguments for the |
+ // constructor are pretty much arbitrary; they just need to be valid. |
+ { |
+ creator: "createBuffer", |
+ args: [1, 1, sampleRate], |
+ }, { |
+ creator: "createIIRFilter", |
+ args: [[1,2],[3,4]] |
+ }, { |
+ creator: "createWaveShaper", |
+ args: [], |
+ }, { |
+ creator: "createConvolver", |
+ args: [], |
+ }, { |
+ creator: "createAnalyser", |
+ args: [], |
+ }, { |
+ creator: "createScriptProcessor", |
+ args: [0], |
+ }, { |
+ creator: "createPeriodicWave", |
+ args: [Float32Array.from([0, 0]), Float32Array.from([1, 0])], |
+ }, { |
+ creator: "createChannelSplitter", |
+ args: [], |
+ }, { |
+ creator: "createChannelMerger", |
+ args: [], |
+ }, { |
+ creator: "createPanner", |
+ args: [], |
+ }, { |
+ creator: "createMediaElementSource", |
+ args: [new Audio()] |
+ },{ |
+ creator: "createMediaStreamDestination", |
+ args: [] |
+ } |
+ // Can't currently test MediaStreamSource |
+ ]; |
+ |
+ // Create the context so we can use it in the following test. |
+ audit.defineTask("initialize", function (done) { |
+ // Just any context so that we can create the nodes. |
+ context = new OfflineAudioContext(1, 1, sampleRate); |
+ done(); |
+ }); |
+ |
+ // Create a task for each entry in testConfigs |
+ for (var test in testConfigs) { |
+ var config = testConfigs[test] |
+ audit.defineTask(config.creator, (function (c) { |
+ return function (done) { |
+ var node = context[c.creator](...c.args); |
+ testLimits(c.creator, node, c.limits); |
+ done(); |
+ }; |
+ })(config)); |
+ } |
+ |
+ // Verify that we have tested all the create methods available on the context. |
+ audit.defineTask("verifyTests", function (done) { |
+ var allNodes = new Set(); |
+ // Create the set of all "create" methods from the context. |
+ for (var method in context) { |
+ if (typeof context[method] === "function" && method.substring(0, 6) === "create") { |
+ allNodes.add(method); |
+ } |
+ } |
+ |
+ // Compute the difference between the set of all create methods on the context and the set |
+ // of tests that we've run. |
+ var diff = new Set([...allNodes].filter(x => !testedMethods.has(x))); |
+ |
+ // Can't currently test a MediaStreamSourceNode, so remove it from the diff set. |
+ diff.delete("createMediaStreamSource"); |
+ |
+ // It's a test failure if we didn't test all of the create methods in the context (except |
+ // createMediaStreamSource, of course). |
+ if (diff.size) { |
+ var output = []; |
+ for (let item of diff) |
+ output.push(" " + item.substring(6)); |
+ testFailed("These nodes were not tested:" + output + "\n"); |
+ } else { |
+ testPassed("All nodes were tested.\n"); |
+ } |
+ |
+ done(); |
+ }); |
+ |
+ // Simple test of a few automation methods to verify we get warnings. |
+ audit.defineTask("automation", function (done) { |
+ // Just use a DelayNode for testing because the audio param has finite limits. |
+ var d = context.createDelay(); |
+ |
+ // The console output should have the warnings that we're interested in. |
+ d.delayTime.setValueAtTime(-1, 0); |
+ d.delayTime.linearRampToValueAtTime(2, 1); |
+ d.delayTime.exponentialRampToValueAtTime(3, 2); |
+ d.delayTime.setTargetAtTime(-1, 3, .1); |
+ d.delayTime.setValueCurveAtTime(Float32Array.from([.1, .2, 1.5, -1]), 4, .1); |
+ done(); |
+ }); |
+ |
+ // All done! |
+ audit.defineTask("finish", function (done) { |
+ finishJSTest(); |
+ done(); |
+ }); |
+ |
+ audit.runTasks(); |
+ |
+ // Is |object| an AudioParam? We determine this by checking the constructor name. |
+ function isAudioParam(object) { |
+ return object && object.constructor.name === "AudioParam"; |
+ } |
+ |
+ // Does |limitOptions| exist and does it have valid values for the expected min and max |
+ // values? |
+ function hasValidLimits(limitOptions) { |
+ return limitOptions && (typeof limitOptions.minValue === "number") && (typeof limitOptions.maxValue === "number"); |
+ } |
+ |
+ // Check the min and max values for the AudioParam attribute named |paramName| for the |node|. |
+ // The expected limits is given by the dictionary |limits|. If some test fails, add the name |
+ // of the failed |
+ function validateAudioParamLimits(node, paramName, limits) { |
+ var nodeName = node.constructor.name; |
+ var parameter = node[paramName]; |
+ var prefix = nodeName + "." + paramName; |
+ |
+ var success = true; |
+ if (hasValidLimits(limits[paramName])) { |
+ // Verify that the min and max values for the parameter are correct. |
+ var isCorrect = Should(prefix + ".minValue", parameter.minValue) |
+ .beEqualTo(limits[paramName].minValue); |
+ isCorrect = Should(prefix + ".maxValue", parameter.maxValue) |
+ .beEqualTo(limits[paramName].maxValue) && isCorrect; |
+ |
+ // Verify that the min and max attributes are read-only |
+ parameter.minValue = Math.PI; |
+ var isReadOnly; |
+ isReadOnly = Should(prefix + ".minValue = Math.PI", parameter.minValue) |
+ .notBeEqualTo(Math.PI); |
+ if (isReadOnly) |
+ testPassed(prefix + ".minValue is read-only."); |
+ else |
+ testFailed(prefix + ".minValue should be read-only but was changed."); |
+ isCorrect = isReadOnly && isCorrect; |
+ |
+ parameter.maxValue = Math.PI; |
+ isReadOnly = Should(prefix + ".maxValue = Math.PI", parameter.maxValue) |
+ .notBeEqualTo(Math.PI); |
+ if (isReadOnly) |
+ testPassed(prefix + ".maxValue is read-only."); |
+ else |
+ testFailed(prefix + ".maxValue should be read-only but was changed."); |
+ isCorrect = isReadOnly && isCorrect; |
+ |
+ // Now try to set the parameter outside the nominal range. |
+ var newValue = 2 * limits[paramName].minValue - 1; |
+ |
+ var isClipped = true; |
+ var clippingTested = false; |
+ // If the new value is beyond float the largest single-precision float, skip the test |
+ // because Chrome throws an error. |
+ if (newValue >= -mostPositiveFloat) { |
+ parameter.value = newValue; |
+ clippingTested = true; |
+ isClipped = Should("Set " + prefix + ".value = " + newValue, parameter.value) |
+ .beEqualTo(parameter.minValue) && isClipped; |
+ } |
+ |
+ newValue = 2 * limits[paramName].maxValue + 1; |
+ |
+ if (newValue <= mostPositiveFloat) { |
+ parameter.value = newValue; |
+ clippingTested = true; |
+ isClipped = Should("Set " + prefix + ".value = " + newValue, parameter.value) |
+ .beEqualTo(parameter.maxValue) && isClipped; |
+ |
+ } |
+ |
+ if (clippingTested) { |
+ if (isClipped) |
+ testPassed(prefix + " was correctly clipped to lie within the nominal range.") |
+ else |
+ testPassed(prefix + " was not correctly clipped to lie within the nominal range.") |
+ } |
+ |
+ isCorrect = isCorrect && isClipped; |
+ |
+ success = isCorrect && success; |
+ } else { |
+ // Test config didn't specify valid limits. Fail this test! |
+ testFailed("Limits for " + nodeName + "." + paramName + " were not correctly defined."); |
+ success = false; |
+ } |
+ |
+ return success; |
+ } |
+ |
+ // Test all of the AudioParams for |node| using the expected values in |limits|. |
+ // |creatorName| is the name of the method to create the node, and is used to keep trakc of |
+ // which tests we've run. |
+ function testLimits(creatorName, node, limits) { |
+ var nodeName = node.constructor.name; |
+ testedMethods.add(creatorName); |
+ |
+ var success = true; |
+ |
+ // List of all of the AudioParams that were tested. |
+ var audioParams = []; |
+ |
+ // List of AudioParams that failed the test. |
+ var incorrectParams = []; |
+ |
+ // Look through all of the keys for the node and extract just the AudioParams |
+ Object.keys(node.__proto__).forEach(function (paramName) { |
+ if (isAudioParam(node[paramName])) { |
+ audioParams.push(paramName); |
+ var isValid = validateAudioParamLimits(node, paramName, limits, incorrectParams); |
+ if (!isValid) |
+ incorrectParams.push(paramName); |
+ |
+ success = isValid && success; |
+ } |
+ }); |
+ |
+ // Print an appropriate message depending on whether there were AudioParams defined or not. |
+ if (audioParams.length) { |
+ var message = "Nominal ranges for AudioParam(s) of " + node.constructor.name; |
+ if (success) |
+ testPassed(message + " are correct.\n"); |
+ else |
+ testFailed(message + " are incorrect for: " + incorrectParams + ".\n"); |
+ return success; |
+ } else { |
+ if (limits) |
+ testFailed(nodeName + " has no AudioParams but test expected " + limits + ".\n"); |
+ else |
+ testPassed(nodeName + " has no AudioParams as expected.\n"); |
+ } |
+ } |
+ </script> |
+ </body> |
+</html> |