mirror of
https://github.com/servo/servo.git
synced 2025-08-18 20:05:34 +01:00
Update web-platform-tests to revision 9659a35d816ff1b32835a2eccf3e0117dea4e5ce
This commit is contained in:
parent
6df0dc5b27
commit
4f0af2af66
54 changed files with 1873 additions and 137 deletions
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Floated table that becomes wider than its specified width, due to wide contents</title>
|
||||
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
|
||||
<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#float-width" title="10.3.5 Floating, non-replaced elements">
|
||||
<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#auto-table-layout" title="17.5.2.2 Automatic table layout">
|
||||
<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
|
||||
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||
<div style="width:200px; height:200px; background:red;">
|
||||
<div style="width:300px;">
|
||||
<div style="float:left; width:200px; height:100px; margin-right:10px; background:green;"></div>
|
||||
<div style="float:left; display:table; width:50px; height:10px; background:green;">
|
||||
<div style="width:200px; height:100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="assert" content="Style attribute values are the content of a
|
||||
declaration block: the braces must not be included and are therefore invalid."/>
|
||||
<style type="text/css">
|
||||
p { background: lime; color: green; }
|
||||
p { background: lime; color: green; margin-top: 1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'border-image-source' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('border-image-source', [
|
||||
{ syntax: 'none' },
|
||||
{ syntax: '<image>' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'box-sizing' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('box-sizing', [
|
||||
{ syntax: 'content-box'},
|
||||
{ syntax: 'border-box' }
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'clear' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('clear', [
|
||||
{ syntax: 'none' },
|
||||
{ syntax: 'left' },
|
||||
{ syntax: 'right' },
|
||||
{ syntax: 'both' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,22 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'float' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('float', [
|
||||
{ syntax: 'left' },
|
||||
{ syntax: 'right' },
|
||||
{ syntax: 'none' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,22 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'font-style' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('font-style', [
|
||||
{ syntax: 'normal' },
|
||||
{ syntax: 'italic' },
|
||||
{ syntax: 'oblique' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'font-weight' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function assert_is_font_weight(weight, result) {
|
||||
assert_style_value_equals(result, new CSSUnitValue(weight, 'number'));
|
||||
}
|
||||
|
||||
runPropertyTests('font-weight', [
|
||||
{
|
||||
syntax: 'normal',
|
||||
computed: (_, result) => assert_is_font_weight(400, result)
|
||||
},
|
||||
{
|
||||
syntax: 'bold',
|
||||
computed: (_, result) => assert_is_font_weight(700, result)
|
||||
},
|
||||
{
|
||||
syntax: 'bolder',
|
||||
computed: (_, result) => assert_is_unit('number', result)
|
||||
},
|
||||
{
|
||||
syntax: 'lighter',
|
||||
computed: (_, result) => assert_is_unit('number', result)
|
||||
},
|
||||
{
|
||||
syntax: '<number>',
|
||||
specified: (input, result) => {
|
||||
if (input instanceof CSSUnitValue &&
|
||||
(input.value < 0 || input.value > 1000))
|
||||
assert_style_value_equals(result, new CSSMathSum(input));
|
||||
else
|
||||
assert_style_value_equals(result, input);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'line-height' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('line-height', [
|
||||
{ syntax: 'normal' },
|
||||
{
|
||||
syntax: '<length>',
|
||||
specified: assert_is_equal_with_range_handling,
|
||||
computed: (_, result) => assert_is_unit('px', result)
|
||||
},
|
||||
{
|
||||
syntax: '<number>',
|
||||
specified: assert_is_equal_with_range_handling,
|
||||
computed: (_, result) => assert_is_unit('px', result)
|
||||
},
|
||||
{
|
||||
syntax: '<percentage>',
|
||||
specified: assert_is_equal_with_range_handling,
|
||||
computed: (_, result) => assert_is_unit('px', result)
|
||||
},
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'list-style-position' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('list-style-position', [
|
||||
{ syntax: 'inside' },
|
||||
{ syntax: 'outside' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,22 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'mask-image' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// FIXME: mask-image is list-valued. Run list-valued tests here too.
|
||||
runPropertyTests('mask-image', [
|
||||
{ syntax: 'none' },
|
||||
{ syntax: '<image>' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'outline-style' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('outline-style', [
|
||||
{ syntax: 'auto' },
|
||||
{ syntax: 'none' },
|
||||
{ syntax: 'dotted' },
|
||||
{ syntax: 'dashed' },
|
||||
{ syntax: 'solid' },
|
||||
{ syntax: 'double' },
|
||||
{ syntax: 'groove' },
|
||||
{ syntax: 'ridge' },
|
||||
{ syntax: 'inset' },
|
||||
{ syntax: 'outset' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,26 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'overflow' properties</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
for (const suffix of ['x', 'y']) {
|
||||
runPropertyTests(`overflow-${suffix}`, [
|
||||
{ syntax: 'visible' },
|
||||
{ syntax: 'hidden' },
|
||||
{ syntax: 'clip' },
|
||||
{ syntax: 'scroll' },
|
||||
{ syntax: 'auto' },
|
||||
]);
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'shape-outside' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('shape-outside', [
|
||||
{ syntax: 'none' },
|
||||
{ syntax: 'margin-box' },
|
||||
{ syntax: 'border-box' },
|
||||
{ syntax: 'padding-box' },
|
||||
{ syntax: 'content-box' },
|
||||
{ syntax: '<image>' },
|
||||
]);
|
||||
|
||||
// <basic-shape>s are not supported in level 1
|
||||
runUnsupportedPropertyTests('shape-outside', [
|
||||
'inset(22% 12% 15px 35px)',
|
||||
'circle(6rem at 12rem 6rem)',
|
||||
'ellipse(115px 55px at 50% 40%)',
|
||||
'polygon(50% 20%, 90% 80%, 10% 80%)',
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -16,7 +16,9 @@
|
|||
runPropertyTests('text-decoration-style', [
|
||||
{ syntax: 'solid' },
|
||||
{ syntax: 'double' },
|
||||
// and other keywords
|
||||
{ syntax: 'dotted' },
|
||||
{ syntax: 'dashed' },
|
||||
{ syntax: 'wavy' },
|
||||
]);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'text-transform' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('text-transform', [
|
||||
{ syntax: 'none' },
|
||||
{ syntax: 'captialize' },
|
||||
{ syntax: 'uppercase' },
|
||||
{ syntax: 'lowercase' },
|
||||
{ syntax: 'full-width' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
runPropertyTests('visibility', [
|
||||
{ syntax: 'visible'},
|
||||
{ syntax: 'hidden' }
|
||||
// and other keywords
|
||||
{ syntax: 'hidden' },
|
||||
{ syntax: 'collapse' },
|
||||
]);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>'white-space' property</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../resources/testhelper.js"></script>
|
||||
<script src="resources/testsuite.js"></script>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
runPropertyTests('white-space', [
|
||||
{ syntax: 'normal'},
|
||||
{ syntax: 'pre' },
|
||||
{ syntax: 'nowrap' },
|
||||
{ syntax: 'pre-wrap' },
|
||||
{ syntax: 'pre-line' },
|
||||
]);
|
||||
|
||||
</script>
|
|
@ -50,6 +50,21 @@ test(function () {
|
|||
assert_true(instance2 instanceof DefinedLater);
|
||||
}, 'document.createElement must create an instance of autonomous custom elements when it has is attribute');
|
||||
|
||||
test(() => {
|
||||
class SuperP extends HTMLParagraphElement {}
|
||||
customElements.define("super-p", SuperP, { extends: "p" });
|
||||
|
||||
const superP = document.createElement("p", { is: "super-p" });
|
||||
assert_true(superP instanceof HTMLParagraphElement);
|
||||
assert_true(superP instanceof SuperP);
|
||||
assert_equals(superP.localName, "p");
|
||||
|
||||
const notSuperP = document.createElement("p", "super-p");
|
||||
assert_true(notSuperP instanceof HTMLParagraphElement);
|
||||
assert_false(notSuperP instanceof SuperP);
|
||||
assert_equals(notSuperP.localName, "p");
|
||||
}, "document.createElement()'s second argument is to be ignored when it's a string");
|
||||
|
||||
function assert_reports(expected, testFunction, message) {
|
||||
var uncaughtError = null;
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
|
|
|
@ -49,5 +49,20 @@ test(() => {
|
|||
assert_false(element instanceof MyBuiltinElement2);
|
||||
assert_false(element.hasAttribute('is'));
|
||||
}, 'builtin: document.createElementNS should check namespaces.');
|
||||
|
||||
test(() => {
|
||||
class SuperP extends HTMLParagraphElement {}
|
||||
customElements.define("super-p", SuperP, { extends: "p" });
|
||||
|
||||
const superP = document.createElementNS("http://www.w3.org/1999/xhtml", "p", { is: "super-p" });
|
||||
assert_true(superP instanceof HTMLParagraphElement);
|
||||
assert_true(superP instanceof SuperP);
|
||||
assert_equals(superP.localName, "p");
|
||||
|
||||
const notSuperP = document.createElementNS("http://www.w3.org/1999/xhtml", "p", "super-p");
|
||||
assert_true(notSuperP instanceof HTMLParagraphElement);
|
||||
assert_false(notSuperP instanceof SuperP);
|
||||
assert_equals(notSuperP.localName, "p");
|
||||
}, "document.createElementNS()'s third argument is to be ignored when it's a string");
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
To run WPT on Chrome on an android device, some additional set up is required.
|
||||
To run WPT on Chrome on an Android device, some additional set up is required.
|
||||
|
||||
First of all, as usual Android development, we need to have `adb` and be able to
|
||||
connect to the device.
|
||||
|
||||
Furthermore, until we find a better way, we need to root the android device and
|
||||
Furthermore, until we find a better way, we need to root the Android device and
|
||||
update the /etc/hosts file to include
|
||||
|
||||
```
|
||||
|
|
|
@ -40,6 +40,9 @@ first two, and it is at times advisable to use testharness.js tests
|
|||
for things which would typically be tested using reftests but for
|
||||
which it would be overly cumbersome.
|
||||
|
||||
See [file names][] for test types and features determined by the file names,
|
||||
and [server features][] for advanced testing features.
|
||||
|
||||
In addition to the four main test types, there are also WebDriver
|
||||
tests, which are used exclusively for testing the WebDriver protocol
|
||||
itself. There is currently no documentation about these tests,
|
||||
|
@ -52,8 +55,10 @@ the [typical GitHub Pull Request workflow][submission-process]; please
|
|||
make sure you run the [`lint` script][lint-tool] before opening a pull request!
|
||||
|
||||
[introduction]: {{ site.baseurl }}{% link introduction.md %}
|
||||
[file names]: {{ site.baseurl }}{% link _writing-tests/file-names.md %}
|
||||
[general guidelines]: {{ site.baseurl }}{% link _writing-tests/general-guidelines.md %}
|
||||
[reftests]: {{ site.baseurl }}{% link _writing-tests/reftests.md %}
|
||||
[server features]: {{ site.baseurl }}{% link _writing-tests/server-features.md %}
|
||||
[testharness.js]: {{ site.baseurl }}{% link _writing-tests/testharness.md %}
|
||||
[visual]: {{ site.baseurl }}{% link _writing-tests/visual.md %}
|
||||
[manual]: {{ site.baseurl }}{% link _writing-tests/manual.md %}
|
||||
|
|
|
@ -8,8 +8,8 @@ order: 12
|
|||
|
||||
Certain test scenarios require more than just static HTML
|
||||
generation. This is supported through the
|
||||
[wptserve](http://wptserve.readthedocs.io) server. Several scenarios
|
||||
in particular are common:
|
||||
[wptserve](http://wptserve.readthedocs.io) server, and controlled by
|
||||
[file name flags][file names]. Several scenarios in particular are common:
|
||||
|
||||
|
||||
### Tests Involving Multiple Origins
|
||||
|
@ -85,3 +85,6 @@ responses. It also provides the ability to write Python scripts that
|
|||
have access to request data and can manipulate the content and timing
|
||||
of the response. For details see the
|
||||
[wptserve documentation](https://wptserve.readthedocs.org).
|
||||
|
||||
|
||||
[file names]: {{ site.baseurl }}{% link _writing-tests/file-names.md %}
|
||||
|
|
|
@ -16,8 +16,8 @@ documented in two sections:
|
|||
* [idlharness.js Documentation][idlharness] — A library for testing
|
||||
IDL interfaces using `testharness.js`.
|
||||
|
||||
As always, we recommend reading over the [general guidelines][] for
|
||||
all test types.
|
||||
See [server features][] for advanced testing features that are commonly used
|
||||
with testharness.js. See also the [general guidelines][] for all test types.
|
||||
|
||||
## Auto-generated test boilerplate
|
||||
|
||||
|
@ -103,3 +103,4 @@ Use `// META: timeout=long` at the beginning of the resource.
|
|||
[general guidelines]: {{ site.baseurl }}{% link _writing-tests/general-guidelines.md %}
|
||||
[testharness-api]: {{ site.baseurl }}{% link _writing-tests/testharness-api.md %}
|
||||
[idlharness]: {{ site.baseurl }}{% link _writing-tests/idlharness.md %}
|
||||
[server features]: {{ site.baseurl }}{% link _writing-tests/server-features.md %}
|
||||
|
|
|
@ -104,10 +104,18 @@ test(() => {
|
|||
}
|
||||
}, "sendBeacon URL: unpaired surrogate codepoint should not make any exceptions.")
|
||||
|
||||
test(() => {
|
||||
// This shouldn't throw an exception.
|
||||
window.navigator.registerProtocolHandler('web+myprotocol', "custom-scheme\uD800/url=%s", "title");
|
||||
}, "RegisterPtotocolHandler URL: unpaired surrogate codepoint should not make any exceptions.")
|
||||
|
||||
test(() => {
|
||||
var w = window.open("about:blank#\uD800");
|
||||
assert_equals(w.document.URL, 'about:blank#%EF%BF%BD');
|
||||
assert_equals(w.document.documentURI, 'about:blank#%EF%BF%BD');
|
||||
// TODO(gyuyoung): How to test document.origin? When opening a URL with an
|
||||
// unpaired surrogate codepoint, invalid URL exception happens.
|
||||
// e.g) var w = window.open("http://test.com\uDB89");
|
||||
}, "Document URLs: unpaired surrogate codepoint should be replaced with U+FFFD")
|
||||
|
||||
promise_test(t => {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
|
||||
<title>2.7 Safe passing of structured data</title>
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data" />
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Note, this test is designed to be in a similar style to
|
||||
// html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html
|
||||
// It is in a separate file to avoid causing a syntax error on UAs which
|
||||
// do not yet support BigInt, so the rest of the test can continue running.
|
||||
|
||||
var worker;
|
||||
var testCollection;
|
||||
setup(function()
|
||||
{
|
||||
//the worker is used for each test in sequence
|
||||
//worker's callback will be set for each test
|
||||
//worker's internal onmessage echoes the data back to this thread through postMessage
|
||||
worker = new Worker("./echo.js");
|
||||
testCollection = [
|
||||
function() {
|
||||
var t = async_test("Primitive BigInt is cloned");
|
||||
t.id = 0;
|
||||
worker.onmessage = t.step_func(function(e) {assert_equals(1n, e.data, "1n === event.data"); t.done(); });
|
||||
t.step(function() { worker.postMessage(1n);});
|
||||
},
|
||||
function() {
|
||||
var t = async_test("Instance of BigInt is cloned");
|
||||
t.id = 1;
|
||||
var obj;
|
||||
t.step(function() {obj = Object(1n);});
|
||||
worker.onmessage = t.step_func(function(e) {
|
||||
assert_equals(obj.constructor, e.data.constructor, "BigInt === event.data.constructor");
|
||||
assert_equals(obj.valueOf(), e.data.valueOf(), "(BigInt(1n)).valueOf() === event.data.valueOf()");
|
||||
t.done();
|
||||
});
|
||||
t.step(function() { worker.postMessage(obj);});
|
||||
},
|
||||
];
|
||||
}, {explicit_done:true});
|
||||
|
||||
//Callback for result_callback
|
||||
//queues the next test in the array testCollection
|
||||
//serves to make test execution sequential from the async worker callbacks
|
||||
//alternatively, we would have to create a worker for each test
|
||||
function testFinished(test) {
|
||||
if(test.id < testCollection.length - 1) {
|
||||
//queue the function so that stack remains shallow
|
||||
queue(testCollection[test.id+1]);
|
||||
} else {
|
||||
//when the last test has run, explicitly end test suite
|
||||
done();
|
||||
}
|
||||
}
|
||||
function queue(func) {
|
||||
step_timeout(func, 10);
|
||||
}
|
||||
|
||||
add_result_callback(testFinished);
|
||||
//start the first test manually
|
||||
queue(testCollection[0]);
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -148,5 +148,22 @@ async_test(function(t) {
|
|||
assert_true(m.hasAttribute('muted'));
|
||||
assert_true(m.muted);
|
||||
}, 'getting ' + tagName + '.muted with muted="" (document.write-created)');
|
||||
|
||||
test(function() {
|
||||
var m = document.createElement(tagName);
|
||||
m.setAttribute('muted', '');
|
||||
|
||||
var c = m.cloneNode(true);
|
||||
assert_true(c.muted);
|
||||
}, 'cloning ' + tagName + ' propagates muted (script-created)');
|
||||
|
||||
test(function() {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<' + tagName + ' muted>';
|
||||
m = div.firstChild;
|
||||
|
||||
var c = m.cloneNode(true);
|
||||
assert_true(c.muted);
|
||||
}, 'cloning ' + tagName + ' propagates muted (innerHTML-created)');
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
div.b {
|
||||
all: initial;
|
||||
direction: initial;
|
||||
unicode-bidi: initial;
|
||||
unicode-bidi: isolate;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
test(() => {
|
||||
assert_false(self.isSecureContext);
|
||||
}, "Lack of .https file name flag implies non-secure context");
|
||||
|
||||
test(() => {
|
||||
assert_equals(location.protocol, "http:");
|
||||
}, "Lack of .https file name flag implies HTTP scheme");
|
||||
|
||||
done();
|
|
@ -0,0 +1,9 @@
|
|||
test(() => {
|
||||
assert_true(self.isSecureContext);
|
||||
}, "Use of .https file name flag implies secure context");
|
||||
|
||||
test(() => {
|
||||
assert_equals(location.protocol, "https:");
|
||||
}, "Use of .https file name flag implies HTTPS scheme");
|
||||
|
||||
done();
|
|
@ -266,8 +266,8 @@ interface Document : Node {
|
|||
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
|
||||
HTMLCollection getElementsByClassName(DOMString classNames);
|
||||
|
||||
[NewObject] Element createElement(DOMString localName, optional ElementCreationOptions options);
|
||||
[NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options);
|
||||
[NewObject] Element createElement(DOMString localName, optional (DOMString or ElementCreationOptions) options);
|
||||
[NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional (DOMString or ElementCreationOptions) options);
|
||||
[NewObject] DocumentFragment createDocumentFragment();
|
||||
[NewObject] Text createTextNode(DOMString data);
|
||||
[NewObject] CDATASection createCDATASection(DOMString data);
|
||||
|
|
|
@ -4,69 +4,24 @@
|
|||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
promise_test(t => {
|
||||
const invalid_lock_types = [
|
||||
"invalid-orientation",
|
||||
null,
|
||||
undefined,
|
||||
123,
|
||||
window,
|
||||
["portrait-primary", "landscape-primary"],
|
||||
];
|
||||
const promisesToReject = invalid_lock_types.map(type =>
|
||||
promise_rejects(t, new TypeError(), screen.orientation.lock(type))
|
||||
);
|
||||
return Promise.all(promisesToReject);
|
||||
}, "screen.orientation.lock() must throw given invalid input.");
|
||||
|
||||
var test = async_test("Test that screen.orientation.lock() throws when the input isn't valid.");
|
||||
|
||||
function onOrientationChangeEvent(ev) {
|
||||
assert_unreached('Unexpected orientation change');
|
||||
}
|
||||
|
||||
window.screen.orientation.addEventListener('change', test.step_func(onOrientationChangeEvent));
|
||||
|
||||
test.step(function() {
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock('invalid-orientation');
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(null);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(undefined);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(undefined);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(123);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(window);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(['portrait-primary']);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock(['portrait-primary', 'landscape-primary']);
|
||||
});
|
||||
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
assert_throws(new TypeError(), function() {
|
||||
screen.orientation.lock();
|
||||
});
|
||||
});
|
||||
|
||||
// Finish asynchronously to give events a chance to fire.
|
||||
setTimeout(test.step_func(function() {
|
||||
assert_equals(screen.orientation.type, 'portrait-primary');
|
||||
screen.orientation.unlock();
|
||||
test.done();
|
||||
}));
|
||||
|
||||
promise_test(t => {
|
||||
return promise_rejects(t, new TypeError(), screen.orientation.lock());
|
||||
}, "screen.orientation.lock() must throw when the input is missing.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -45,12 +45,6 @@ class Browser(object):
|
|||
"""Name of the browser-specific wptrunner requirements file"""
|
||||
return NotImplemented
|
||||
|
||||
def prepare_environment(self):
|
||||
"""Do any additional setup of the environment required to start the
|
||||
browser successfully
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Firefox(Browser):
|
||||
"""Firefox-specific interface.
|
||||
|
@ -262,26 +256,6 @@ class Chrome(Browser):
|
|||
output = call(self.binary, "--version")
|
||||
return re.search(r"[0-9\.]+( [a-z]+)?$", output.strip()).group(0)
|
||||
|
||||
def prepare_environment(self):
|
||||
# https://bugs.chromium.org/p/chromium/issues/detail?id=713947
|
||||
logger.debug("DBUS_SESSION_BUS_ADDRESS %s" % os.environ.get("DBUS_SESSION_BUS_ADDRESS"))
|
||||
if "DBUS_SESSION_BUS_ADDRESS" not in os.environ:
|
||||
if find_executable("dbus-launch"):
|
||||
logger.debug("Attempting to start dbus")
|
||||
dbus_conf = subprocess.check_output(["dbus-launch"])
|
||||
logger.debug(dbus_conf)
|
||||
|
||||
# From dbus-launch(1):
|
||||
#
|
||||
# > When dbus-launch prints bus information to standard output,
|
||||
# > by default it is in a simple key-value pairs format.
|
||||
for line in dbus_conf.strip().split("\n"):
|
||||
key, _, value = line.partition("=")
|
||||
os.environ[key] = value
|
||||
else:
|
||||
logger.critical("dbus not running and can't be started")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class ChromeAndroid(Browser):
|
||||
"""Chrome-specific interface for Android.
|
||||
|
@ -364,26 +338,6 @@ class Opera(Browser):
|
|||
output = call(self.binary, "--version")
|
||||
return re.search(r"[0-9\.]+( [a-z]+)?$", output.strip()).group(0)
|
||||
|
||||
def prepare_environment(self):
|
||||
# https://bugs.chromium.org/p/chromium/issues/detail?id=713947
|
||||
logger.debug("DBUS_SESSION_BUS_ADDRESS %s" % os.environ.get("DBUS_SESSION_BUS_ADDRESS"))
|
||||
if "DBUS_SESSION_BUS_ADDRESS" not in os.environ:
|
||||
if find_executable("dbus-launch"):
|
||||
logger.debug("Attempting to start dbus")
|
||||
dbus_conf = subprocess.check_output(["dbus-launch"])
|
||||
logger.debug(dbus_conf)
|
||||
|
||||
# From dbus-launch(1):
|
||||
#
|
||||
# > When dbus-launch prints bus information to standard output,
|
||||
# > by default it is in a simple key-value pairs format.
|
||||
for line in dbus_conf.strip().split("\n"):
|
||||
key, _, value = line.partition("=")
|
||||
os.environ[key] = value
|
||||
else:
|
||||
logger.critical("dbus not running and can't be started")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Edge(Browser):
|
||||
"""Edge-specific interface."""
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
let sampleRate = 44100.0;
|
||||
|
||||
let renderLengthSeconds = 4;
|
||||
let delayTimeSeconds = 0.5;
|
||||
let toneLengthSeconds = 2;
|
||||
|
||||
function createToneBuffer(context, frequency, numberOfCycles, sampleRate) {
|
||||
let duration = numberOfCycles / frequency;
|
||||
let sampleFrameLength = duration * sampleRate;
|
||||
|
||||
let audioBuffer = context.createBuffer(1, sampleFrameLength, sampleRate);
|
||||
|
||||
let n = audioBuffer.length;
|
||||
let data = audioBuffer.getChannelData(0);
|
||||
|
||||
for (let i = 0; i < n; ++i)
|
||||
data[i] = Math.sin(frequency * 2.0 * Math.PI * i / sampleRate);
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
function checkDelayedResult(renderedBuffer, toneBuffer, should) {
|
||||
let sourceData = toneBuffer.getChannelData(0);
|
||||
let renderedData = renderedBuffer.getChannelData(0);
|
||||
|
||||
let delayTimeFrames = delayTimeSeconds * sampleRate;
|
||||
let toneLengthFrames = toneLengthSeconds * sampleRate;
|
||||
|
||||
let success = true;
|
||||
|
||||
let n = renderedBuffer.length;
|
||||
|
||||
for (let i = 0; i < n; ++i) {
|
||||
if (i < delayTimeFrames) {
|
||||
// Check that initial portion is 0 (since signal is delayed).
|
||||
if (renderedData[i] != 0) {
|
||||
should(
|
||||
renderedData[i], 'Initial portion expected to be 0 at frame ' + i)
|
||||
.beEqualTo(0);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
} else if (i >= delayTimeFrames && i < delayTimeFrames + toneLengthFrames) {
|
||||
// Make sure that the tone data is delayed by exactly the expected number
|
||||
// of frames.
|
||||
let j = i - delayTimeFrames;
|
||||
if (renderedData[i] != sourceData[j]) {
|
||||
should(renderedData[i], 'Actual data at frame ' + i)
|
||||
.beEqualTo(sourceData[j]);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Make sure we have silence after the delayed tone.
|
||||
if (renderedData[i] != 0) {
|
||||
should(renderedData[j], 'Final portion at frame ' + i).beEqualTo(0);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
should(
|
||||
success, 'Delaying test signal by ' + delayTimeSeconds + ' sec was done')
|
||||
.message('correctly', 'incorrectly')
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode-max-default-delay.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description: 'DelayNode with delay set to default maximum delay'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
let delay = context.createDelay();
|
||||
delayTimeSeconds = 1;
|
||||
delay.delayTime.value = delayTimeSeconds;
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode-max-nondefault-delay.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description: 'DelayNode with delay set to non-default maximum delay'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
let maxDelay = 1.5;
|
||||
let delay = context.createDelay(maxDelay);
|
||||
delayTimeSeconds = maxDelay;
|
||||
delay.delayTime.value = delayTimeSeconds;
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(() => task.done());
|
||||
;
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode-maxdelay.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description:
|
||||
'Basic functionality of DelayNode with a non-default max delay time'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
// Create a delay node with an explicit max delay time (greater than
|
||||
// the default of 1 second).
|
||||
let delay = context.createDelay(2);
|
||||
// Set the delay time to a value greater than the default max delay
|
||||
// so we can verify the delay is working for this case.
|
||||
delayTimeSeconds = 1.5;
|
||||
delay.delayTime.value = delayTimeSeconds;
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode-maxdelaylimit.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description:
|
||||
'Tests attribute and maximum allowed delay of DelayNode'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
window.context = context;
|
||||
should(() => context.createDelay(180)).throw();
|
||||
should(() => context.createDelay(0)).throw();
|
||||
should(() => context.createDelay(-1)).throw();
|
||||
should(() => context.createDelay(NaN)).throw();
|
||||
;
|
||||
let delay = context.createDelay(179);
|
||||
delay.delayTime.value = delayTimeSeconds;
|
||||
window.delay = delay;
|
||||
should(
|
||||
delay.delayTime.value,
|
||||
'delay.delayTime.value = ' + delayTimeSeconds)
|
||||
.beEqualTo(delayTimeSeconds);
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode-scheduling.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description:
|
||||
'DelayNode delayTime parameter can be scheduled at a given time'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
let delay = context.createDelay();
|
||||
|
||||
// Schedule delay time at time zero.
|
||||
delay.delayTime.setValueAtTime(delayTimeSeconds, 0);
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
delaynode.html
|
||||
</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="/webaudio/resources/delay-testing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description: 'Tests attribute and basic functionality of DelayNode'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
// Create offline audio context.
|
||||
let context = new OfflineAudioContext(
|
||||
1, sampleRate * renderLengthSeconds, sampleRate);
|
||||
let toneBuffer = createToneBuffer(
|
||||
context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
|
||||
|
||||
let bufferSource = context.createBufferSource();
|
||||
bufferSource.buffer = toneBuffer;
|
||||
|
||||
let delay = context.createDelay();
|
||||
|
||||
window.delay = delay;
|
||||
should(delay.numberOfInputs, 'delay.numberOfInputs').beEqualTo(1);
|
||||
should(delay.numberOfOutputs, 'delay.numberOfOutputs').beEqualTo(1);
|
||||
should(delay.delayTime.defaultValue, 'delay.delayTime.defaultValue')
|
||||
.beEqualTo(0.0);
|
||||
should(delay.delayTime.value, 'delay.delayTime.value')
|
||||
.beEqualTo(0.0);
|
||||
|
||||
delay.delayTime.value = delayTimeSeconds;
|
||||
should(
|
||||
delay.delayTime.value,
|
||||
'delay.delayTime.value = ' + delayTimeSeconds)
|
||||
.beEqualTo(delayTimeSeconds);
|
||||
|
||||
bufferSource.connect(delay);
|
||||
delay.connect(context.destination);
|
||||
bufferSource.start(0);
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => checkDelayedResult(buffer, toneBuffer, should))
|
||||
.then(task.done.bind(task));
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Test DelayNode Has No Dezippering
|
||||
</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 id="layout-test-code">
|
||||
// The sample rate must be a power of two to avoid any round-off errors in
|
||||
// computing when to suspend a context on a rendering quantum boundary.
|
||||
// Otherwise this is pretty arbitrary.
|
||||
let sampleRate = 16384;
|
||||
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{label: 'test0', description: 'Test DelayNode has no dezippering'},
|
||||
(task, should) => {
|
||||
let context = new OfflineAudioContext(1, sampleRate, sampleRate);
|
||||
|
||||
// Simple integer ramp for testing delay node
|
||||
let buffer = new AudioBuffer(
|
||||
{length: context.length, sampleRate: context.sampleRate});
|
||||
let rampData = buffer.getChannelData(0);
|
||||
for (let k = 0; k < rampData.length; ++k) {
|
||||
rampData[k] = k + 1;
|
||||
}
|
||||
|
||||
// |delay0Frame| is the initial delay in frames. |delay1Frame| is
|
||||
// the new delay in frames. These must be integers.
|
||||
let delay0Frame = 64;
|
||||
let delay1Frame = 16;
|
||||
|
||||
let src = new AudioBufferSourceNode(context, {buffer: buffer});
|
||||
let delay = new DelayNode(
|
||||
context, {delayTime: delay0Frame / context.sampleRate});
|
||||
|
||||
src.connect(delay).connect(context.destination);
|
||||
|
||||
// After a render quantum, change the delay to |delay1Frame|.
|
||||
context.suspend(RENDER_QUANTUM_FRAMES / context.sampleRate)
|
||||
.then(() => {
|
||||
delay.delayTime.value = delay1Frame / context.sampleRate;
|
||||
})
|
||||
.then(() => context.resume());
|
||||
|
||||
src.start();
|
||||
context.startRendering()
|
||||
.then(renderedBuffer => {
|
||||
let renderedData = renderedBuffer.getChannelData(0);
|
||||
|
||||
// The first |delay0Frame| frames should be zero.
|
||||
should(
|
||||
renderedData.slice(0, delay0Frame),
|
||||
'output[0:' + (delay0Frame - 1) + ']')
|
||||
.beConstantValueOf(0);
|
||||
|
||||
// Now we have the ramp should show up from the delay.
|
||||
let ramp0 =
|
||||
new Float32Array(RENDER_QUANTUM_FRAMES - delay0Frame);
|
||||
for (let k = 0; k < ramp0.length; ++k) {
|
||||
ramp0[k] = rampData[k];
|
||||
}
|
||||
|
||||
should(
|
||||
renderedData.slice(delay0Frame, RENDER_QUANTUM_FRAMES),
|
||||
'output[' + delay0Frame + ':' +
|
||||
(RENDER_QUANTUM_FRAMES - 1) + ']')
|
||||
.beEqualToArray(ramp0);
|
||||
|
||||
// After one rendering quantum, the delay is changed to
|
||||
// |delay1Frame|.
|
||||
let ramp1 =
|
||||
new Float32Array(context.length - RENDER_QUANTUM_FRAMES);
|
||||
for (let k = 0; k < ramp1.length; ++k) {
|
||||
// ramp1[k] = 1 + k + RENDER_QUANTUM_FRAMES - delay1Frame;
|
||||
ramp1[k] =
|
||||
rampData[k + RENDER_QUANTUM_FRAMES - delay1Frame];
|
||||
}
|
||||
should(
|
||||
renderedData.slice(RENDER_QUANTUM_FRAMES),
|
||||
'output[' + RENDER_QUANTUM_FRAMES + ':]')
|
||||
.beEqualToArray(ramp1);
|
||||
})
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.define(
|
||||
{label: 'test1', description: 'Test value setter and setValueAtTime'},
|
||||
(task, should) => {
|
||||
testWithAutomation(should, {prefix: ''}).then(() => task.done());
|
||||
});
|
||||
|
||||
audit.define(
|
||||
{label: 'test2', description: 'Test value setter and modulation'},
|
||||
(task, should) => {
|
||||
testWithAutomation(should, {
|
||||
prefix: 'With modulation: ',
|
||||
modulator: true
|
||||
}).then(() => task.done());
|
||||
});
|
||||
|
||||
// Compare .value setter with setValueAtTime, Optionally allow modulation
|
||||
// of |delayTime|.
|
||||
function testWithAutomation(should, options) {
|
||||
let prefix = options.prefix;
|
||||
// Channel 0 is the output of delay node using the setter and channel 1
|
||||
// is the output using setValueAtTime.
|
||||
let context = new OfflineAudioContext(2, sampleRate, sampleRate);
|
||||
|
||||
let merger = new ChannelMergerNode(
|
||||
context, {numberOfInputs: context.destination.channelCount});
|
||||
merger.connect(context.destination);
|
||||
|
||||
let src = new OscillatorNode(context);
|
||||
|
||||
// |delay0Frame| is the initial delay value in frames. |delay1Frame| is
|
||||
// the new delay in frames. The values here are constrained only by the
|
||||
// constraints for a DelayNode. These are pretty arbitrary except we
|
||||
// wanted them to be fractional so as not be on a frame boundary to
|
||||
// test interpolation compared with |setValueAtTime()|..
|
||||
let delay0Frame = 3.1;
|
||||
let delay1Frame = 47.2;
|
||||
|
||||
let delayTest = new DelayNode(
|
||||
context, {delayTime: delay0Frame / context.sampleRate});
|
||||
let delayRef = new DelayNode(
|
||||
context, {delayTime: delay0Frame / context.sampleRate});
|
||||
|
||||
src.connect(delayTest).connect(merger, 0, 0);
|
||||
src.connect(delayRef).connect(merger, 0, 1);
|
||||
|
||||
if (options.modulator) {
|
||||
// Fairly arbitrary modulation of the delay time, with a peak
|
||||
// variation of 10 ms.
|
||||
let mod = new OscillatorNode(context, {frequency: 1000});
|
||||
let modGain = new GainNode(context, {gain: .01});
|
||||
mod.connect(modGain);
|
||||
modGain.connect(delayTest.delayTime);
|
||||
modGain.connect(delayRef.delayTime);
|
||||
mod.start();
|
||||
}
|
||||
|
||||
// The time at which the delay time of |delayTest| node will be
|
||||
// changed. This MUST be on a render quantum boundary, but is
|
||||
// otherwise arbitrary.
|
||||
let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate;
|
||||
|
||||
// Schedule the delay change on |delayRef| and also apply the value
|
||||
// setter for |delayTest| at |changeTime|.
|
||||
delayRef.delayTime.setValueAtTime(
|
||||
delay1Frame / context.sampleRate, changeTime);
|
||||
context.suspend(changeTime)
|
||||
.then(() => {
|
||||
delayTest.delayTime.value = delay1Frame / context.sampleRate;
|
||||
})
|
||||
.then(() => context.resume());
|
||||
|
||||
src.start();
|
||||
|
||||
return context.startRendering().then(renderedBuffer => {
|
||||
let actual = renderedBuffer.getChannelData(0);
|
||||
let expected = renderedBuffer.getChannelData(1);
|
||||
|
||||
let match = should(actual, prefix + '.value setter output')
|
||||
.beEqualToArray(expected);
|
||||
should(
|
||||
match,
|
||||
prefix + '.value setter output matches setValueAtTime output')
|
||||
.beTrue();
|
||||
});
|
||||
}
|
||||
|
||||
audit.run();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
dynamicscompressor-basic.html
|
||||
</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 id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
let context;
|
||||
let compressor;
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test',
|
||||
description: 'Basic tests for DynamicsCompressorNode API'
|
||||
},
|
||||
function(task, should) {
|
||||
|
||||
context = new AudioContext();
|
||||
compressor = context.createDynamicsCompressor();
|
||||
|
||||
should(compressor.threshold.value, 'compressor.threshold.value')
|
||||
.beEqualTo(-24);
|
||||
should(compressor.knee.value, 'compressor.knee.value')
|
||||
.beEqualTo(30);
|
||||
should(compressor.ratio.value, 'compressor.ratio.value')
|
||||
.beEqualTo(12);
|
||||
should(compressor.attack.value, 'compressor.attack.value')
|
||||
.beEqualTo(Math.fround(0.003));
|
||||
should(compressor.release.value, 'compressor.release.value')
|
||||
.beEqualTo(0.25);
|
||||
should(typeof compressor.reduction, 'typeof compressor.reduction')
|
||||
.beEqualTo('number');
|
||||
should(compressor.reduction, 'compressor.reduction').beEqualTo(0);
|
||||
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Verifies GainNode attributes and their type.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
gain-basic.html
|
||||
</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 id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define('test', function(task, should) {
|
||||
// Create audio context.
|
||||
let context = new AudioContext();
|
||||
|
||||
// Create gain node.
|
||||
let gainNode = context.createGain();
|
||||
|
||||
should(
|
||||
gainNode.gain instanceof AudioParam,
|
||||
'gainNode.gain instanceof AudioParam')
|
||||
.beTrue();
|
||||
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,154 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Basic GainNode Functionality
|
||||
</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 id="layout-test-code">
|
||||
// Tests that GainNode is properly scaling the gain. We'll render 11
|
||||
// notes, starting at a gain of 1.0, decreasing in gain by 0.1. The 11th
|
||||
// note will be of gain 0.0, so it should be silent (at the end in the
|
||||
// rendered output).
|
||||
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
let sampleRate = 44100.0;
|
||||
let bufferDurationSeconds = 0.125;
|
||||
let numberOfNotes = 11;
|
||||
let noteSpacing = bufferDurationSeconds +
|
||||
0.020; // leave 20ms of silence between each "note"
|
||||
let lengthInSeconds = numberOfNotes * noteSpacing;
|
||||
|
||||
let context = 0;
|
||||
let sinWaveBuffer = 0;
|
||||
|
||||
// Create a stereo AudioBuffer of duration |lengthInSeconds| consisting of
|
||||
// a pure sine wave with the given |frequency|. Both channels contain the
|
||||
// same data.
|
||||
function createSinWaveBuffer(lengthInSeconds, frequency) {
|
||||
let audioBuffer =
|
||||
context.createBuffer(2, lengthInSeconds * sampleRate, sampleRate);
|
||||
|
||||
let n = audioBuffer.length;
|
||||
let channelL = audioBuffer.getChannelData(0);
|
||||
let channelR = audioBuffer.getChannelData(1);
|
||||
|
||||
for (let i = 0; i < n; ++i) {
|
||||
channelL[i] = Math.sin(frequency * 2.0 * Math.PI * i / sampleRate);
|
||||
channelR[i] = channelL[i];
|
||||
}
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
function playNote(time, gain, merger) {
|
||||
let source = context.createBufferSource();
|
||||
source.buffer = sinWaveBuffer;
|
||||
|
||||
let gainNode = context.createGain();
|
||||
gainNode.gain.value = gain;
|
||||
|
||||
let sourceSplitter = context.createChannelSplitter(2);
|
||||
let gainSplitter = context.createChannelSplitter(2);
|
||||
|
||||
// Split the stereo channels from the source output and the gain output
|
||||
// and merge them into the desired channels of the merger.
|
||||
source.connect(gainNode).connect(gainSplitter);
|
||||
source.connect(sourceSplitter);
|
||||
|
||||
gainSplitter.connect(merger, 0, 0);
|
||||
gainSplitter.connect(merger, 1, 1);
|
||||
sourceSplitter.connect(merger, 0, 2);
|
||||
sourceSplitter.connect(merger, 1, 3);
|
||||
|
||||
source.start(time);
|
||||
}
|
||||
|
||||
audit.define(
|
||||
{label: 'create context', description: 'Create context for test'},
|
||||
function(task, should) {
|
||||
// Create offline audio context.
|
||||
context = new OfflineAudioContext(
|
||||
4, sampleRate * lengthInSeconds, sampleRate);
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.define(
|
||||
{label: 'test', description: 'GainNode functionality'},
|
||||
function(task, should) {
|
||||
let merger = new ChannelMergerNode(
|
||||
context, {numberOfInputs: context.destination.channelCount});
|
||||
merger.connect(context.destination);
|
||||
|
||||
// Create a buffer for a short "note".
|
||||
sinWaveBuffer = createSinWaveBuffer(bufferDurationSeconds, 880.0);
|
||||
|
||||
let startTimes = [];
|
||||
let gainValues = [];
|
||||
|
||||
// Render 11 notes, starting at a gain of 1.0, decreasing in gain by
|
||||
// 0.1. The last note will be of gain 0.0, so shouldn't be
|
||||
// perceptible in the rendered output.
|
||||
for (let i = 0; i < numberOfNotes; ++i) {
|
||||
let time = i * noteSpacing;
|
||||
let gain = 1.0 - i / (numberOfNotes - 1);
|
||||
startTimes.push(time);
|
||||
gainValues.push(gain);
|
||||
playNote(time, gain, merger);
|
||||
}
|
||||
|
||||
context.startRendering()
|
||||
.then(buffer => {
|
||||
let actual0 = buffer.getChannelData(0);
|
||||
let actual1 = buffer.getChannelData(1);
|
||||
let reference0 = buffer.getChannelData(2);
|
||||
let reference1 = buffer.getChannelData(3);
|
||||
|
||||
// It's ok to a frame too long since the sine pulses are
|
||||
// followed by silence.
|
||||
let bufferDurationFrames =
|
||||
Math.ceil(bufferDurationSeconds * context.sampleRate);
|
||||
|
||||
// Apply the gains to the reference signal.
|
||||
for (let k = 0; k < startTimes.length; ++k) {
|
||||
// It's ok to be a frame early because the sine pulses are
|
||||
// preceded by silence.
|
||||
let startFrame =
|
||||
Math.floor(startTimes[k] * context.sampleRate);
|
||||
let gain = gainValues[k];
|
||||
for (let n = 0; n < bufferDurationFrames; ++n) {
|
||||
reference0[startFrame + n] *= gain;
|
||||
reference1[startFrame + n] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the channels are clsoe to the reference.
|
||||
should(actual0, 'Left output from gain node')
|
||||
.beCloseToArray(
|
||||
reference0, {relativeThreshold: 1.1908e-7});
|
||||
should(actual1, 'Right output from gain node')
|
||||
.beCloseToArray(
|
||||
reference1, {relativeThreshold: 1.1908e-7});
|
||||
|
||||
// Test the SNR too for both channels.
|
||||
let snr0 = 10 * Math.log10(computeSNR(actual0, reference0));
|
||||
let snr1 = 10 * Math.log10(computeSNR(actual1, reference1));
|
||||
should(snr0, 'Left SNR (in dB)')
|
||||
.beGreaterThanOrEqualTo(148.69);
|
||||
should(snr1, 'Right SNR (in dB)')
|
||||
.beGreaterThanOrEqualTo(148.69);
|
||||
})
|
||||
.then(() => task.done());
|
||||
;
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Gain Dezippering Test: Dezippering Removed
|
||||
</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 id="layout-test-code">
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define(
|
||||
{label: 'test0', description: 'Dezippering of GainNode removed'},
|
||||
(task, should) => {
|
||||
// Only need a few frames to verify that dezippering has been
|
||||
// removed from the GainNode. Sample rate is pretty arbitrary.
|
||||
let context = new OfflineAudioContext(1, 1024, 16000);
|
||||
|
||||
// Send a unit source to the gain node so we can measure the effect
|
||||
// of the gain node.
|
||||
let src = new ConstantSourceNode(context, {offset: 1});
|
||||
let g = new GainNode(context, {gain: 1});
|
||||
src.connect(g).connect(context.destination);
|
||||
|
||||
context.suspend(RENDER_QUANTUM_FRAMES / context.sampleRate)
|
||||
.then(() => {
|
||||
g.gain.value = .5;
|
||||
})
|
||||
.then(() => context.resume());
|
||||
|
||||
src.start();
|
||||
|
||||
context.startRendering()
|
||||
.then(audio => {
|
||||
let c = audio.getChannelData(0);
|
||||
|
||||
// If dezippering has been removed, the gain output should
|
||||
// instantly jump at frame 128 to 0.5.
|
||||
should(c.slice(0, 128), 'output[0:127]').beConstantValueOf(1);
|
||||
should(c.slice(128), 'output[128:]').beConstantValueOf(0.5);
|
||||
})
|
||||
.then(() => task.done());
|
||||
});
|
||||
|
||||
audit.define(
|
||||
{
|
||||
label: 'test2',
|
||||
description: 'Compare value setter and setValueAtTime'
|
||||
},
|
||||
(task, should) => {
|
||||
testWithAutomation(should, {prefix: ''}).then(() => task.done());
|
||||
});
|
||||
|
||||
audit.define(
|
||||
{label: 'test3', description: 'Automation effects'},
|
||||
(task, should) => {
|
||||
testWithAutomation(should, {
|
||||
prefix: 'With modulation: ',
|
||||
modulator: true
|
||||
}).then(() => task.done());
|
||||
});
|
||||
|
||||
audit.run();
|
||||
|
||||
function testWithAutomation(should, options) {
|
||||
// Sample rate must be a power of two to eliminate round-off in
|
||||
// computing the time at render quantum boundaries.
|
||||
let context = new OfflineAudioContext(2, 1024, 16384);
|
||||
let merger = new ChannelMergerNode(context, {numberOfChannels: 2});
|
||||
merger.connect(context.destination);
|
||||
|
||||
let src = new OscillatorNode(context);
|
||||
let gainTest = new GainNode(context);
|
||||
let gainRef = new GainNode(context);
|
||||
|
||||
src.connect(gainTest).connect(merger, 0, 0);
|
||||
src.connect(gainRef).connect(merger, 0, 1);
|
||||
|
||||
if (options.modulator) {
|
||||
let mod = new OscillatorNode(context, {frequency: 1000});
|
||||
let modGain = new GainNode(context);
|
||||
mod.connect(modGain);
|
||||
modGain.connect(gainTest.gain);
|
||||
modGain.connect(gainRef.gain);
|
||||
mod.start();
|
||||
}
|
||||
|
||||
// Change the gains. Must do the change on a render boundary!
|
||||
let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate;
|
||||
let newGain = .3;
|
||||
|
||||
gainRef.gain.setValueAtTime(newGain, changeTime);
|
||||
context.suspend(changeTime)
|
||||
.then(() => gainTest.gain.value = newGain)
|
||||
.then(() => context.resume());
|
||||
|
||||
src.start();
|
||||
|
||||
return context.startRendering().then(audio => {
|
||||
let actual = audio.getChannelData(0);
|
||||
let expected = audio.getChannelData(1);
|
||||
|
||||
// The values using the .value setter must be identical to the
|
||||
// values using setValueAtTime.
|
||||
let match = should(actual, options.prefix + '.value setter output')
|
||||
.beEqualToArray(expected);
|
||||
|
||||
should(
|
||||
match,
|
||||
options.prefix +
|
||||
'.value setter output matches setValueAtTime output')
|
||||
.beTrue();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue