mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Update web-platform-tests to revision 0d9238c8062f05a55898a0cb60dc0c353794d87a
This commit is contained in:
parent
c80fa33864
commit
7e8624d921
57 changed files with 5290 additions and 266 deletions
|
@ -3225,6 +3225,14 @@
|
|||
"path": "touch-events/single-touch-manual.html",
|
||||
"url": "/touch-events/single-touch-manual.html"
|
||||
},
|
||||
{
|
||||
"path": "uievents/order-of-events/mouse-events/click-on-body-manual.html",
|
||||
"url": "/uievents/order-of-events/mouse-events/click-on-body-manual.html"
|
||||
},
|
||||
{
|
||||
"path": "uievents/order-of-events/mouse-events/click-on-html-manual.html",
|
||||
"url": "/uievents/order-of-events/mouse-events/click-on-html-manual.html"
|
||||
},
|
||||
{
|
||||
"path": "uievents/order-of-events/mouse-events/mouseevents-mousemove-manual.htm",
|
||||
"url": "/uievents/order-of-events/mouse-events/mouseevents-mousemove-manual.htm"
|
||||
|
@ -4669,6 +4677,16 @@
|
|||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html"
|
||||
},
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html",
|
||||
"references": [
|
||||
[
|
||||
"/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html"
|
||||
},
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html",
|
||||
"references": [
|
||||
|
@ -4769,6 +4787,16 @@
|
|||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html"
|
||||
},
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html",
|
||||
"references": [
|
||||
[
|
||||
"/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html"
|
||||
},
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html",
|
||||
"references": [
|
||||
|
@ -19055,6 +19083,10 @@
|
|||
"path": "html/syntax/serializing-html-fragments/outerHTML.html",
|
||||
"url": "/html/syntax/serializing-html-fragments/outerHTML.html"
|
||||
},
|
||||
{
|
||||
"path": "html/syntax/serializing-xml-fragments/outerHTML.html",
|
||||
"url": "/html/syntax/serializing-xml-fragments/outerHTML.html"
|
||||
},
|
||||
{
|
||||
"path": "html/webappapis/animation-frames/callback-exception.html",
|
||||
"url": "/html/webappapis/animation-frames/callback-exception.html"
|
||||
|
@ -21328,12 +21360,20 @@
|
|||
"url": "/old-tests/submission/Opera/script_scheduling/105.html"
|
||||
},
|
||||
{
|
||||
"path": "old-tests/submission/Opera/script_scheduling/106.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/106.html"
|
||||
"path": "old-tests/submission/Opera/script_scheduling/106-import.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/106-import.html"
|
||||
},
|
||||
{
|
||||
"path": "old-tests/submission/Opera/script_scheduling/107.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/107.html"
|
||||
"path": "old-tests/submission/Opera/script_scheduling/106-noimport.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/106-noimport.html"
|
||||
},
|
||||
{
|
||||
"path": "old-tests/submission/Opera/script_scheduling/107-import.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/107-import.html"
|
||||
},
|
||||
{
|
||||
"path": "old-tests/submission/Opera/script_scheduling/107-noimport.html",
|
||||
"url": "/old-tests/submission/Opera/script_scheduling/107-noimport.html"
|
||||
},
|
||||
{
|
||||
"path": "old-tests/submission/Opera/script_scheduling/108.html",
|
||||
|
@ -26755,6 +26795,58 @@
|
|||
"path": "shadow-dom/untriaged/user-interaction/ranges-and-selections/test-002.html",
|
||||
"url": "/shadow-dom/untriaged/user-interaction/ranges-and-selections/test-002.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/byte-length-queuing-strategy.https.html",
|
||||
"url": "/streams/byte-length-queuing-strategy.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/count-queuing-strategy.https.html",
|
||||
"url": "/streams/count-queuing-strategy.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/bad-strategies.https.html",
|
||||
"url": "/streams/readable-streams/bad-strategies.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/bad-underlying-sources.https.html",
|
||||
"url": "/streams/readable-streams/bad-underlying-sources.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/brand-checks.https.html",
|
||||
"url": "/streams/readable-streams/brand-checks.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/cancel.https.html",
|
||||
"url": "/streams/readable-streams/cancel.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/count-queuing-strategy-integration.https.html",
|
||||
"url": "/streams/readable-streams/count-queuing-strategy-integration.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/garbage-collection.https.html",
|
||||
"url": "/streams/readable-streams/garbage-collection.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/general.https.html",
|
||||
"url": "/streams/readable-streams/general.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/pipe-through.https.html",
|
||||
"url": "/streams/readable-streams/pipe-through.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/readable-stream-reader.https.html",
|
||||
"url": "/streams/readable-streams/readable-stream-reader.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/tee.https.html",
|
||||
"url": "/streams/readable-streams/tee.https.html"
|
||||
},
|
||||
{
|
||||
"path": "streams/readable-streams/templated.https.html",
|
||||
"url": "/streams/readable-streams/templated.https.html"
|
||||
},
|
||||
{
|
||||
"path": "svg/interfaces.html",
|
||||
"url": "/svg/interfaces.html"
|
||||
|
@ -26951,10 +27043,18 @@
|
|||
"path": "web-animations/animation-timeline/idlharness.html",
|
||||
"url": "/web-animations/animation-timeline/idlharness.html"
|
||||
},
|
||||
{
|
||||
"path": "web-animations/animation/constructor.html",
|
||||
"url": "/web-animations/animation/constructor.html"
|
||||
},
|
||||
{
|
||||
"path": "web-animations/keyframe-effect/constructor.html",
|
||||
"url": "/web-animations/keyframe-effect/constructor.html"
|
||||
},
|
||||
{
|
||||
"path": "web-animations/keyframe-effect/getComputedTiming.html",
|
||||
"url": "/web-animations/keyframe-effect/getComputedTiming.html"
|
||||
},
|
||||
{
|
||||
"path": "webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html",
|
||||
"url": "/webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html"
|
||||
|
@ -35436,6 +35536,18 @@
|
|||
"url": "/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html"
|
||||
}
|
||||
],
|
||||
"html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html": [
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html",
|
||||
"references": [
|
||||
[
|
||||
"/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html"
|
||||
}
|
||||
],
|
||||
"html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html": [
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html",
|
||||
|
@ -35556,6 +35668,18 @@
|
|||
"url": "/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html"
|
||||
}
|
||||
],
|
||||
"html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html": [
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html",
|
||||
"references": [
|
||||
[
|
||||
"/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html"
|
||||
}
|
||||
],
|
||||
"html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html": [
|
||||
{
|
||||
"path": "html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html",
|
||||
|
@ -39301,7 +39425,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"rev": "78862c14a70cabb48c685144666912f08e726390",
|
||||
"rev": "0d9238c8062f05a55898a0cb60dc0c353794d87a",
|
||||
"url_base": "/",
|
||||
"version": 2
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
ce0b89d310212aaaa66b759c7c2548fb2f9a2738
|
||||
8a1d0a8f3e4206c98f35a4bba98fc26237c1f906
|
|
@ -465,6 +465,60 @@ test(function() {
|
|||
assert_throws("INUSE_ATTRIBUTE_ERR", function(){el2.setAttributeNode(attrNode)});
|
||||
}, "setAttributeNode on bound attribute should throw InUseAttributeError")
|
||||
|
||||
// Have to use an async_test to see what a DOMAttrModified listener sees,
|
||||
// because otherwise the event dispatch code will swallow our exceptions. And
|
||||
// we want to make sure this test always happens, even when no mutation events
|
||||
// run.
|
||||
var setAttributeNode_mutation_test = async_test("setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute");
|
||||
|
||||
test(function(){
|
||||
var el = document.createElement("div")
|
||||
var attrNode1 = document.createAttribute("foo");
|
||||
attrNode1.value = "bar";
|
||||
el.setAttributeNode(attrNode1);
|
||||
var attrNode2 = document.createAttribute("foo");
|
||||
attrNode2.value = "baz";
|
||||
|
||||
el.addEventListener("DOMAttrModified", function(e) {
|
||||
// If this never gets called, that's OK, I guess. But if it gets called, it
|
||||
// better represent a single modification with attrNode2 as the relatedNode.
|
||||
// We have to do an inner test() call here, because otherwise the exceptions
|
||||
// our asserts trigger will get swallowed by the event dispatch code.
|
||||
setAttributeNode_mutation_test.step(function() {
|
||||
assert_equals(e.attrName, "foo");
|
||||
assert_equals(e.attrChange, MutationEvent.MODIFICATION);
|
||||
assert_equals(e.prevValue, "bar");
|
||||
assert_equals(e.newValue, "baz");
|
||||
assert_equals(e.relatedNode, attrNode2);
|
||||
});
|
||||
});
|
||||
|
||||
var oldNode = el.setAttributeNode(attrNode2);
|
||||
assert_equals(oldNode, attrNode1,
|
||||
"Must return the old attr node from a setAttributeNode call");
|
||||
}, "setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute (outer shell)");
|
||||
setAttributeNode_mutation_test.done();
|
||||
|
||||
test(function(){
|
||||
var el = document.createElement("div")
|
||||
el.setAttribute("a", "b");
|
||||
el.setAttribute("c", "d");
|
||||
|
||||
assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.name }),
|
||||
["a", "c"]);
|
||||
assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.value }),
|
||||
["b", "d"]);
|
||||
|
||||
var attrNode = document.createAttribute("a");
|
||||
attrNode.value = "e";
|
||||
el.setAttributeNode(attrNode);
|
||||
|
||||
assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.name }),
|
||||
["a", "c"]);
|
||||
assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.value }),
|
||||
["e", "d"]);
|
||||
}, "setAttributeNode called with an Attr that has the same name as an existing one should not change attribute order");
|
||||
|
||||
test(function() {
|
||||
var el = document.createElement("div");
|
||||
el.setAttribute("foo", "bar");
|
||||
|
@ -537,6 +591,10 @@ test(function() {
|
|||
["0", "1", "2"])
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "a", "b"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property correctness with non-namespaced attribute before same-name namespaced one");
|
||||
|
||||
test(function() {
|
||||
|
@ -550,6 +608,10 @@ test(function() {
|
|||
["0", "1", "2"])
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "a", "b"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property correctness with namespaced attribute before same-name non-namespaced one");
|
||||
|
||||
test(function() {
|
||||
|
@ -563,5 +625,59 @@ test(function() {
|
|||
["0", "1", "2"])
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "a:b", "c:d"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property correctness with two namespaced attributes with the same name-with-prefix");
|
||||
|
||||
test(function() {
|
||||
var el = document.createElement("div");
|
||||
el.setAttributeNS("foo", "A:B", "");
|
||||
el.setAttributeNS("bar", "c:D", "");
|
||||
el.setAttributeNS("baz", "e:F", "");
|
||||
el.setAttributeNS("qux", "g:h", "");
|
||||
el.setAttributeNS("", "I", "");
|
||||
el.setAttributeNS("", "j", "");
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "3", "4", "5", "g:h", "j"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property names should only include all-lowercase qualified names for an HTML element in an HTML document");
|
||||
|
||||
test(function() {
|
||||
var el = document.createElementNS("", "div");
|
||||
el.setAttributeNS("foo", "A:B", "");
|
||||
el.setAttributeNS("bar", "c:D", "");
|
||||
el.setAttributeNS("baz", "e:F", "");
|
||||
el.setAttributeNS("qux", "g:h", "");
|
||||
el.setAttributeNS("", "I", "");
|
||||
el.setAttributeNS("", "j", "");
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "3", "4", "5", "A:B", "c:D", "e:F", "g:h", "I", "j"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property names should include all qualified names for a non-HTML element in an HTML document");
|
||||
|
||||
test(function() {
|
||||
var doc = document.implementation.createDocument(null, "");
|
||||
assert_equals(doc.contentType, "application/xml");
|
||||
var el = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
||||
el.setAttributeNS("foo", "A:B", "");
|
||||
el.setAttributeNS("bar", "c:D", "");
|
||||
el.setAttributeNS("baz", "e:F", "");
|
||||
el.setAttributeNS("qux", "g:h", "");
|
||||
el.setAttributeNS("", "I", "");
|
||||
el.setAttributeNS("", "j", "");
|
||||
assert_array_equals(Object.getOwnPropertyNames(el.attributes),
|
||||
["0", "1", "2", "3", "4", "5", "A:B", "c:D", "e:F", "g:h", "I", "j"])
|
||||
for (var propName of Object.getOwnPropertyNames(el.attributes)) {
|
||||
assert_true(el.attributes[propName] instanceof Attr,
|
||||
"el.attributes has an Attr for property name " + propName);
|
||||
}
|
||||
}, "Own property names should include all qualified names for an HTML element in a non-HTML document");
|
||||
</script>
|
||||
|
|
|
@ -77,5 +77,17 @@
|
|||
iframe.setAttribute("srcdoc", "<p>foobar</p>");
|
||||
document.body.appendChild(iframe);
|
||||
}, "The fallback base URL of an iframe srcdoc document is the document base URL of the document's browsing context's browsing context container's document.");
|
||||
|
||||
async_test(function () {
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.onload = this.step_func_done(function () {
|
||||
var doc = iframe.contentDocument;
|
||||
assert_resolve_url(doc, location.href.replace("/document-base-url.html", "/sub"));
|
||||
assert_equals(doc.baseURI, document.baseURI.replace("/document-base-url.html", "/sub/"),
|
||||
"The srcdoc document's base URL should be set by the <base> tag.");
|
||||
});
|
||||
iframe.setAttribute("srcdoc", "<base href='sub/'><p>foobar</p>");
|
||||
document.body.appendChild(iframe);
|
||||
}, "The base URL of an iframe srcdoc document with a <base> tag should be set by that tag.");
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>ol@type: unsupported type: invalid</title>
|
||||
<link rel=match href=ol-type-unsupported-ref.html>
|
||||
<ol type=disk><li>1<li>2</ol>
|
||||
<ol type=DISK><li>1<li>2</ol>
|
|
@ -0,0 +1,7 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>ul@type: unsupported type: invalid</title>
|
||||
<link rel=match href=ul-type-unsupported-ref.html>
|
||||
<ul type=disk><li>first item</li><li>second item</li></ul>
|
||||
<ul type=DISK><li>first item</li><li>second item</li></ul>
|
||||
<ul type=x><li>first item</li><li>second item</li></ul>
|
140
tests/wpt/web-platform-tests/html/syntax/html-element-list.js
Normal file
140
tests/wpt/web-platform-tests/html/syntax/html-element-list.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
var elements_with_end_tag = [
|
||||
"a",
|
||||
"abbr",
|
||||
"address",
|
||||
"article",
|
||||
"aside",
|
||||
"audio",
|
||||
"b",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"blockquote",
|
||||
"body",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"cite",
|
||||
"code",
|
||||
"colgroup",
|
||||
"command",
|
||||
"datalist",
|
||||
"dd",
|
||||
"del",
|
||||
"details",
|
||||
"dfn",
|
||||
"dialog",
|
||||
"div",
|
||||
"dl",
|
||||
"dt",
|
||||
"em",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"footer",
|
||||
"form",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"hgroup",
|
||||
"html",
|
||||
"i",
|
||||
"iframe",
|
||||
"ins",
|
||||
"kbd",
|
||||
"label",
|
||||
"legend",
|
||||
"li",
|
||||
"map",
|
||||
"mark",
|
||||
"menu",
|
||||
"meter",
|
||||
"nav",
|
||||
"noscript",
|
||||
"object",
|
||||
"ol",
|
||||
"optgroup",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"pre",
|
||||
"progress",
|
||||
"q",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"script",
|
||||
"section",
|
||||
"select",
|
||||
"small",
|
||||
"span",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
"thead",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"u",
|
||||
"ul",
|
||||
"var",
|
||||
"video",
|
||||
"data",
|
||||
//"acronym",
|
||||
//"applet",
|
||||
//"basefont",
|
||||
//"bgsound",
|
||||
//"big",
|
||||
//"blink",
|
||||
//"center",
|
||||
//"dir",
|
||||
//"font",
|
||||
//"frame",
|
||||
//"frameset",
|
||||
//"isindex",
|
||||
//"listing",
|
||||
//"marquee",
|
||||
//"multicol",
|
||||
//"nextid",
|
||||
//"nobr",
|
||||
//"noembed",
|
||||
//"noframes",
|
||||
//"plaintext",
|
||||
//"rb",
|
||||
//"spacer",
|
||||
//"strike",
|
||||
//"tt",
|
||||
//"xmp",
|
||||
];
|
||||
|
||||
var elements_without_end_tag = [
|
||||
"area",
|
||||
"base",
|
||||
"br",
|
||||
"col",
|
||||
"embed",
|
||||
"hr",
|
||||
"img",
|
||||
"input",
|
||||
"keygen",
|
||||
"link",
|
||||
"meta",
|
||||
"param",
|
||||
"source",
|
||||
"track",
|
||||
"wbr",
|
||||
];
|
|
@ -7,159 +7,19 @@
|
|||
<link rel="help" href="https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../html-element-list.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
var element = [
|
||||
"a",
|
||||
"abbr",
|
||||
"address",
|
||||
"article",
|
||||
"aside",
|
||||
"audio",
|
||||
"b",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"blockquote",
|
||||
"body",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"cite",
|
||||
"code",
|
||||
"colgroup",
|
||||
"command",
|
||||
"datalist",
|
||||
"dd",
|
||||
"del",
|
||||
"details",
|
||||
"dfn",
|
||||
"dialog",
|
||||
"div",
|
||||
"dl",
|
||||
"dt",
|
||||
"em",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"footer",
|
||||
"form",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"hgroup",
|
||||
"html",
|
||||
"i",
|
||||
"iframe",
|
||||
"ins",
|
||||
"kbd",
|
||||
"label",
|
||||
"legend",
|
||||
"li",
|
||||
"map",
|
||||
"mark",
|
||||
"menu",
|
||||
"meter",
|
||||
"nav",
|
||||
"noscript",
|
||||
"object",
|
||||
"ol",
|
||||
"optgroup",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"pre",
|
||||
"progress",
|
||||
"q",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"script",
|
||||
"section",
|
||||
"select",
|
||||
"small",
|
||||
"span",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
"thead",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"u",
|
||||
"ul",
|
||||
"var",
|
||||
"video",
|
||||
"data",
|
||||
//"acronym",
|
||||
//"applet",
|
||||
//"basefont",
|
||||
//"bgsound",
|
||||
//"big",
|
||||
//"blink",
|
||||
//"center",
|
||||
//"dir",
|
||||
//"font",
|
||||
//"frame",
|
||||
//"frameset",
|
||||
//"isindex",
|
||||
//"listing",
|
||||
//"marquee",
|
||||
//"multicol",
|
||||
//"nextid",
|
||||
//"nobr",
|
||||
//"noembed",
|
||||
//"noframes",
|
||||
//"plaintext",
|
||||
//"rb",
|
||||
//"spacer",
|
||||
//"strike",
|
||||
//"tt",
|
||||
//"xmp",
|
||||
];
|
||||
|
||||
var noEndTag = [
|
||||
"area",
|
||||
"base",
|
||||
"br",
|
||||
"col",
|
||||
"embed",
|
||||
"hr",
|
||||
"img",
|
||||
"input",
|
||||
"keygen",
|
||||
"link",
|
||||
"meta",
|
||||
"param",
|
||||
"source",
|
||||
"track",
|
||||
"wbr",
|
||||
];
|
||||
|
||||
test(function() {
|
||||
element.forEach(function(ele) {
|
||||
elements_with_end_tag.forEach(function(ele) {
|
||||
test(function() {
|
||||
var e = document.createElement(ele);
|
||||
assert_equals(e.outerHTML, "<" + ele + "></" + ele + ">", ele + " node created." );
|
||||
}, "Node for " + ele);
|
||||
});
|
||||
noEndTag.forEach(function(ele) {
|
||||
elements_without_end_tag.forEach(function(ele) {
|
||||
test(function() {
|
||||
var e = document.createElement(ele);
|
||||
assert_equals(e.outerHTML, "<" + ele + ">", ele + " node created." );
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>HTML Test: element.outerHTML to verify XML fragment serialization algorithm</title>
|
||||
<link rel="author" title="Intel" href="http://www.intel.com/">
|
||||
<link rel="help" href="https://w3c.github.io/DOM-Parsing/#dfn-concept-serialize-xml">
|
||||
<link rel="help" href="https://w3c.github.io/DOM-Parsing/#widl-Element-outerHTML">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../html-element-list.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
test(function() {
|
||||
var doc = document.implementation.createDocument(null, "");
|
||||
assert_equals(doc.contentType, "application/xml");
|
||||
var html_ns = "http://www.w3.org/1999/xhtml";
|
||||
elements_with_end_tag.forEach(function(ele) {
|
||||
test(function() {
|
||||
var e = doc.createElementNS(html_ns, ele);
|
||||
assert_equals(e.outerHTML,
|
||||
`<${ele} xmlns="${html_ns}"></${ele}>`,
|
||||
ele + " node created." );
|
||||
}, "Node for " + ele);
|
||||
});
|
||||
elements_without_end_tag.forEach(function(ele) {
|
||||
test(function() {
|
||||
var e = doc.createElementNS(html_ns, ele);
|
||||
assert_equals(e.outerHTML,
|
||||
`<${ele} xmlns="${html_ns}" />`,
|
||||
ele + " node created." );
|
||||
}, "Node for " + ele);
|
||||
});
|
||||
}, document.title);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -50,31 +50,13 @@ test(function() {
|
|||
assert_equals(Object.getPrototypeOf(WeakMap.prototype), Object.prototype);
|
||||
}, "The value of the [[Prototype]] internal property of the WeakMap prototype object is the standard built-in Object prototype object (15.2.4).")
|
||||
|
||||
// 15.15.5.1 WeakMap.prototype.constructor
|
||||
// 23.3.3.1 WeakMap.prototype.constructor
|
||||
test(function() {
|
||||
assert_equals(WeakMap.prototype.constructor, WeakMap);
|
||||
assert_propdesc(WeakMap.prototype, "constructor", true, false, true);
|
||||
}, "The initial value of WeakMap.prototype.constructor is the built-in WeakMap constructor.")
|
||||
|
||||
// 15.15.5.2 WeakMap.prototype.clear ()
|
||||
test(function() {
|
||||
assert_propdesc(WeakMap.prototype, "clear", true, false, true);
|
||||
test_length("clear", 0);
|
||||
// Step 1-3
|
||||
test_thisval("clear", null);
|
||||
// Step 4-5
|
||||
test(function() {
|
||||
var wm = new WeakMap();
|
||||
var key = {};
|
||||
wm.set(key, "fail");
|
||||
assert_true(wm.has(key));
|
||||
var res = wm.clear();
|
||||
assert_equals(res, undefined);
|
||||
assert_false(wm.has(key));
|
||||
}, "WeakMap.prototype.clear: basic functionality");
|
||||
}, "WeakMap.prototype.clear")
|
||||
|
||||
// 15.15.5.3 WeakMap.prototype.delete ( key )
|
||||
// 23.3.3.2 WeakMap.prototype.delete ( key )
|
||||
test(function() {
|
||||
assert_propdesc(WeakMap.prototype, "delete", true, false, true);
|
||||
test_length("delete", 1);
|
||||
|
@ -82,7 +64,7 @@ test(function() {
|
|||
test_thisval("delete", [{}]);
|
||||
}, "WeakMap.prototype.delete")
|
||||
|
||||
// 15.15.5.4 WeakMap.prototype.get ( key )
|
||||
// 23.3.3.3 WeakMap.prototype.get ( key )
|
||||
test(function() {
|
||||
assert_propdesc(WeakMap.prototype, "get", true, false, true);
|
||||
test_length("get", 1);
|
||||
|
@ -98,7 +80,7 @@ test(function() {
|
|||
}, "WeakMap.prototype.get: return undefined");
|
||||
}, "WeakMap.prototype.get")
|
||||
|
||||
// 15.14.5.5 Map.prototype.has ( key )
|
||||
// 23.3.3.4 Map.prototype.has ( key )
|
||||
test(function() {
|
||||
assert_propdesc(WeakMap.prototype, "has", true, false, true);
|
||||
test_length("has", 1);
|
||||
|
@ -106,7 +88,7 @@ test(function() {
|
|||
test_thisval("has", [{}]);
|
||||
}, "WeakMap.prototype.has")
|
||||
|
||||
// 15.14.5.6 Map.prototype.set ( key , value )
|
||||
// 23.3.3.5 Map.prototype.set ( key , value )
|
||||
test(function() {
|
||||
assert_propdesc(WeakMap.prototype, "set", true, false, true);
|
||||
test_length("set", 2);
|
||||
|
@ -114,7 +96,7 @@ test(function() {
|
|||
test_thisval("set", [{}, {}]);
|
||||
}, "WeakMap.prototype.set")
|
||||
|
||||
// 15.15.5.7 Map.prototype.@@toStringTag
|
||||
// 23.3.3.6 Map.prototype.@@toStringTag
|
||||
test(function() {
|
||||
assert_class_string(new WeakMap(), "WeakMap");
|
||||
assert_class_string(WeakMap.prototype, "WeakMap");
|
||||
|
|
|
@ -103,6 +103,9 @@ PRINT STATEMENT:dom/nodes/Document-createElement-namespace-tests/generate.py
|
|||
# Should probably be fixed.
|
||||
W3C-TEST.ORG:subresource-integrity/refresh-header.js.headers
|
||||
|
||||
# semi-legitimate use of console.*
|
||||
CONSOLE:streams/resources/test-utils.js
|
||||
|
||||
# Lint doesn't know about sub.svg I guess
|
||||
PARSE-FAILED:content-security-policy/svg/including.sub.svg
|
||||
|
||||
|
@ -110,11 +113,12 @@ PARSE-FAILED:content-security-policy/svg/including.sub.svg
|
|||
PARSE-FAILED:dom/nodes/Document-createElement-namespace-tests/empty.xml
|
||||
PARSE-FAILED:dom/nodes/Document-createElement-namespace-tests/minimal_html.xml
|
||||
|
||||
# The patches may have trailin whitespace
|
||||
# The patches may have trailing whitespace
|
||||
TRAILING WHITESPACE:webgl/tools/*.patch
|
||||
# The tests under this directory are not done by us
|
||||
*:webgl/conformance-1.0.3/*
|
||||
|
||||
|
||||
# Git submodules are not currently scanned
|
||||
*:tools/*
|
||||
*:resources/*
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title> scheduler: stylesheets blocking scripts</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="testlib/testlib.js"></script>
|
||||
<!-- this stylesheet blocks scripts -->
|
||||
<link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
|
||||
</head>
|
||||
<body>
|
||||
<div id="log">FAILED (This TC requires JavaScript enabled)</div>
|
||||
<div id="test">Test</div>
|
||||
|
||||
<script>
|
||||
test(function() {
|
||||
assert_equals(getComputedStyle(document.getElementById("test")).position,
|
||||
"fixed");
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
|
@ -13,7 +13,7 @@
|
|||
<script>
|
||||
test(function() {
|
||||
document.write("<link rel='stylesheet' href='css/import.css?pipe=trickle(d2)'>");
|
||||
<!-- note that the pass condition here is not per spec (but does match implementations) as of 2012-06-26 -->
|
||||
// note that the pass condition here is not per spec (but does match implementations) as of 2012-06-26
|
||||
assert_equals(getComputedStyle(document.getElementById("test")).position,
|
||||
"static");
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title> scheduler: stylesheets blocking scripts document.write</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="testlib/testlib.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log">FAILED (This TC requires JavaScript enabled)</div>
|
||||
<div id="test">Test</div>
|
||||
|
||||
<!-- this stylesheet blocks scripts -->
|
||||
<script>
|
||||
test(function() {
|
||||
document.write("<link rel='stylesheet' href='css/background.css?pipe=trickle(d2)'>");
|
||||
// note that the pass condition here is not per spec (but does match implementations) as of 2012-06-26
|
||||
assert_equals(getComputedStyle(document.getElementById("test")).position,
|
||||
"static");
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="resources/test-initializer.js"></script>
|
||||
|
||||
<script src="byte-length-queuing-strategy.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('byte-length-queuing-strategy.js');
|
||||
</script>
|
|
@ -0,0 +1,107 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
}, 'Can construct a ByteLengthQueuingStrategy with a valid high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
for (let highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark });
|
||||
assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
|
||||
}
|
||||
|
||||
}, 'Can construct a ByteLengthQueuingStrategy with any value as its high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
const highWaterMark = 1;
|
||||
const highWaterMarkObjectGetter = {
|
||||
get highWaterMark() { return highWaterMark; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const highWaterMarkObjectGetterThrowing = {
|
||||
get highWaterMark() { throw error; }
|
||||
};
|
||||
|
||||
assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(), 'construction fails with undefined');
|
||||
assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(null), 'construction fails with null');
|
||||
assert_throws({ name: 'Error' }, () => new ByteLengthQueuingStrategy(highWaterMarkObjectGetterThrowing),
|
||||
'construction fails with an object with a throwing highWaterMark getter');
|
||||
|
||||
// Should not fail:
|
||||
new ByteLengthQueuingStrategy('potato');
|
||||
new ByteLengthQueuingStrategy({});
|
||||
new ByteLengthQueuingStrategy(highWaterMarkObjectGetter);
|
||||
|
||||
}, 'ByteLengthQueuingStrategy constructor behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(), 'size fails with undefined');
|
||||
assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(null), 'size fails with null');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size('potato'), undefined,
|
||||
'size succeeds with undefined with a random non-object type');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size({}), undefined,
|
||||
'size succeeds with undefined with an object without hwm property');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size(chunk), size,
|
||||
'size succeeds with the right amount with an object with a hwm');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size(chunkGetter), size,
|
||||
'size succeeds with the right amount with an object with a hwm getter');
|
||||
assert_throws({ name: 'Error' }, () => ByteLengthQueuingStrategy.prototype.size(chunkGetterThrowing),
|
||||
'size fails with the error thrown by the getter');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy size behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const thisValue = null;
|
||||
const returnValue = { 'returned from': 'byteLength getter' };
|
||||
const chunk = {
|
||||
get byteLength() { return returnValue; }
|
||||
};
|
||||
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size.call(thisValue, chunk), returnValue);
|
||||
|
||||
}, 'ByteLengthQueuingStrategy.prototype.size should work generically on its this and its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
|
||||
{ value: 4, writable: true, enumerable: true, configurable: true },
|
||||
'highWaterMark property should be a data property with the value passed the constructor');
|
||||
assert_equals(typeof strategy.size, 'function');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy instances have the correct properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
assert_equals(strategy.highWaterMark, 4);
|
||||
|
||||
strategy.highWaterMark = 10;
|
||||
assert_equals(strategy.highWaterMark, 10);
|
||||
|
||||
strategy.highWaterMark = 'banana';
|
||||
assert_equals(strategy.highWaterMark, 'banana');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy\'s highWaterMark property can be set to anything');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="resources/test-initializer.js"></script>
|
||||
|
||||
<script src="count-queuing-strategy.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('count-queuing-strategy.js');
|
||||
</script>
|
106
tests/wpt/web-platform-tests/streams/count-queuing-strategy.js
Normal file
106
tests/wpt/web-platform-tests/streams/count-queuing-strategy.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
}, 'Can construct a CountQueuingStrategy with a valid high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
for (let highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark });
|
||||
assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
|
||||
}
|
||||
|
||||
}, 'Can construct a CountQueuingStrategy with any value as its high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
const highWaterMark = 1;
|
||||
const highWaterMarkObjectGetter = {
|
||||
get highWaterMark() { return highWaterMark; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const highWaterMarkObjectGetterThrowing = {
|
||||
get highWaterMark() { throw error; }
|
||||
};
|
||||
|
||||
assert_throws({ name: 'TypeError' }, () => new CountQueuingStrategy(), 'construction fails with undefined');
|
||||
assert_throws({ name: 'TypeError' }, () => new CountQueuingStrategy(null), 'construction fails with null');
|
||||
assert_throws({ name: 'Error' }, () => new CountQueuingStrategy(highWaterMarkObjectGetterThrowing),
|
||||
'construction fails with an object with a throwing highWaterMark getter');
|
||||
|
||||
// Should not fail:
|
||||
new CountQueuingStrategy('potato');
|
||||
new CountQueuingStrategy({});
|
||||
new CountQueuingStrategy(highWaterMarkObjectGetter);
|
||||
|
||||
}, 'CountQueuingStrategy constructor behaves as expected with strange arguments');
|
||||
|
||||
|
||||
test(() => {
|
||||
|
||||
const thisValue = null;
|
||||
const chunk = {
|
||||
get byteLength() {
|
||||
throw new TypeError('shouldn\'t be called');
|
||||
}
|
||||
};
|
||||
|
||||
assert_equals(CountQueuingStrategy.prototype.size.call(thisValue, chunk), 1);
|
||||
|
||||
}, 'CountQueuingStrategy.prototype.size should work generically on its this and its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
|
||||
assert_equals(CountQueuingStrategy.prototype.size(), 1, 'size returns 1 with undefined');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(null), 1, 'size returns 1 with null');
|
||||
assert_equals(CountQueuingStrategy.prototype.size('potato'), 1, 'size returns 1 with non-object type');
|
||||
assert_equals(CountQueuingStrategy.prototype.size({}), 1, 'size returns 1 with empty object');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunk), 1, 'size returns 1 with a chunk');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunkGetter), 1, 'size returns 1 with chunk getter');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunkGetterThrowing), 1,
|
||||
'size returns 1 with chunk getter that throws');
|
||||
|
||||
}, 'CountQueuingStrategy size behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
|
||||
{ value: 4, writable: true, enumerable: true, configurable: true },
|
||||
'highWaterMark property should be a data property with the value passed the constructor');
|
||||
assert_equals(typeof strategy.size, 'function');
|
||||
|
||||
}, 'CountQueuingStrategy instances have the correct properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
assert_equals(strategy.highWaterMark, 4);
|
||||
|
||||
strategy.highWaterMark = 10;
|
||||
assert_equals(strategy.highWaterMark, 10);
|
||||
|
||||
strategy.highWaterMark = 'banana';
|
||||
assert_equals(strategy.highWaterMark, 'banana');
|
||||
|
||||
}, 'CountQueuingStrategy\'s highWaterMark property can be set to anything');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="bad-strategies.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('bad-strategies.js');
|
||||
</script>
|
|
@ -0,0 +1,176 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
assert_throws(theError, () => {
|
||||
new ReadableStream({}, {
|
||||
get size() {
|
||||
throw theError;
|
||||
},
|
||||
highWaterMark: 5
|
||||
});
|
||||
}, 'construction should re-throw the error');
|
||||
|
||||
}, 'Readable stream: throwing strategy.size getter');
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
controller.error(theError);
|
||||
throw theError;
|
||||
},
|
||||
highWaterMark: 5
|
||||
}
|
||||
);
|
||||
|
||||
assert_throws(theError, () => {
|
||||
controller.enqueue('a');
|
||||
}, 'enqueue should re-throw the error');
|
||||
|
||||
}, 'Readable stream: strategy.size errors the stream and then throws');
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
controller.error(theError);
|
||||
return Infinity;
|
||||
},
|
||||
highWaterMark: 5
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
controller.enqueue('a');
|
||||
} catch (error) {
|
||||
assert_equals(error.name, 'RangeError', 'enqueue should throw a RangeError');
|
||||
}
|
||||
|
||||
}, 'Readable stream: strategy.size errors the stream and then returns Infinity');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
assert_throws(theError, () => c.enqueue('a'), 'enqueue should throw the error');
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
throw theError;
|
||||
},
|
||||
highWaterMark: 5
|
||||
}
|
||||
);
|
||||
|
||||
return rs.getReader().closed.catch(e => {
|
||||
assert_equals(e, theError, 'closed should reject with the error');
|
||||
});
|
||||
|
||||
}, 'Readable stream: throwing strategy.size method');
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
assert_throws(theError, () => {
|
||||
new ReadableStream({}, {
|
||||
size() {
|
||||
return 1;
|
||||
},
|
||||
get highWaterMark() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
}, 'construction should re-throw the error');
|
||||
|
||||
}, 'Readable stream: throwing strategy.highWaterMark getter');
|
||||
|
||||
test(() => {
|
||||
|
||||
for (let highWaterMark of [-1, -Infinity]) {
|
||||
assert_throws(new RangeError(), () => {
|
||||
new ReadableStream({}, {
|
||||
size() {
|
||||
return 1;
|
||||
},
|
||||
highWaterMark
|
||||
});
|
||||
}, 'construction should throw a RangeError for ' + highWaterMark);
|
||||
}
|
||||
|
||||
for (let highWaterMark of [NaN, 'foo', {}]) {
|
||||
assert_throws(new TypeError(), () => {
|
||||
new ReadableStream({}, {
|
||||
size() {
|
||||
return 1;
|
||||
},
|
||||
highWaterMark
|
||||
});
|
||||
}, 'construction should throw a TypeError for ' + highWaterMark);
|
||||
}
|
||||
|
||||
}, 'Readable stream: invalid strategy.highWaterMark');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const promises = [];
|
||||
for (let size of [NaN, -Infinity, Infinity, -1]) {
|
||||
let theError;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
try {
|
||||
c.enqueue('hi');
|
||||
assert_unreached('enqueue didn\'t throw');
|
||||
} catch (error) {
|
||||
assert_equals(error.name, 'RangeError', 'enqueue should throw a RangeError for ' + size);
|
||||
theError = error;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
return size;
|
||||
},
|
||||
highWaterMark: 5
|
||||
}
|
||||
);
|
||||
|
||||
promises.push(rs.getReader().closed.catch(e => {
|
||||
assert_equals(e, theError, 'closed should reject with the error for ' + size);
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
|
||||
}, 'Readable stream: invalid strategy.size return value');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="bad-underlying-sources.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('bad-underlying-sources.js');
|
||||
</script>
|
|
@ -0,0 +1,383 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
assert_throws(theError, () => {
|
||||
new ReadableStream({
|
||||
get start() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
}, 'constructing the stream should re-throw the error');
|
||||
|
||||
}, 'Underlying source start: throwing getter');
|
||||
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
assert_throws(theError, () => {
|
||||
new ReadableStream({
|
||||
start() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
}, 'constructing the stream should re-throw the error');
|
||||
|
||||
}, 'Underlying source start: throwing method');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
const rs = new ReadableStream({
|
||||
get pull() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
return promise_rejects(t, theError, rs.getReader().closed);
|
||||
|
||||
}, 'Underlying source: throwing pull getter (initial pull)');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
const rs = new ReadableStream({
|
||||
pull() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
return promise_rejects(t, theError, rs.getReader().closed);
|
||||
|
||||
}, 'Underlying source: throwing pull method (initial pull)');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
let counter = 0;
|
||||
const rs = new ReadableStream({
|
||||
get pull() {
|
||||
++counter;
|
||||
if (counter === 1) {
|
||||
return c => c.enqueue('a');
|
||||
}
|
||||
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'the chunk read should be correct');
|
||||
}),
|
||||
promise_rejects(t, theError, reader.closed)
|
||||
]);
|
||||
|
||||
}, 'Underlying source pull: throwing getter (second pull)');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
|
||||
let counter = 0;
|
||||
const rs = new ReadableStream({
|
||||
pull(c) {
|
||||
++counter;
|
||||
if (counter === 1) {
|
||||
c.enqueue('a');
|
||||
return;
|
||||
}
|
||||
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'the chunk read should be correct');
|
||||
}),
|
||||
promise_rejects(t, theError, reader.closed)
|
||||
]);
|
||||
|
||||
}, 'Underlying source pull: throwing method (second pull)');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
const rs = new ReadableStream({
|
||||
get cancel() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
return promise_rejects(t, theError, rs.cancel());
|
||||
|
||||
}, 'Underlying source cancel: throwing getter');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('a unique string');
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
return promise_rejects(t, theError, rs.cancel());
|
||||
|
||||
}, 'Underlying source cancel: throwing method');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
rs.cancel();
|
||||
controller.enqueue('a'); // Calling enqueue after canceling should not throw anything.
|
||||
|
||||
return rs.getReader().closed;
|
||||
|
||||
}, 'Underlying source: calling enqueue on an empty canceled stream should not throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
rs.cancel();
|
||||
controller.enqueue('c'); // Calling enqueue after canceling should not throw anything.
|
||||
|
||||
return rs.getReader().closed;
|
||||
|
||||
}, 'Underlying source: calling enqueue on a non-empty canceled stream should not throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
return new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
assert_throws(new TypeError(), () => c.enqueue('a'), 'call to enqueue should throw a TypeError');
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
}, 'Underlying source: calling enqueue on a closed stream should throw');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('boo');
|
||||
const closed = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
assert_throws(theError, () => c.enqueue('a'), 'call to enqueue should throw the error');
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return promise_rejects(t, theError, closed);
|
||||
|
||||
}, 'Underlying source: calling enqueue on an errored stream should throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
return new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
assert_throws(new TypeError(), () => c.close(), 'second call to close should throw a TypeError');
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
}, 'Underlying source: calling close twice on an empty stream should throw the second time');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
let readCalled = false;
|
||||
const reader = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.close();
|
||||
assert_throws(new TypeError(), () => c.close(), 'second call to close should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
}).getReader();
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'read() should read the enqueued chunk');
|
||||
readCalled = true;
|
||||
}),
|
||||
reader.closed.then(() => {
|
||||
assert_true(startCalled);
|
||||
assert_true(readCalled);
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'Underlying source: calling close twice on a non-empty stream should throw the second time');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
let startCalled = false;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
rs.cancel();
|
||||
controller.close(); // Calling close after canceling should not throw anything.
|
||||
|
||||
return rs.getReader().closed.then(() => {
|
||||
assert_true(startCalled);
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling close on an empty canceled stream should not throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
let startCalled = false;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
c.enqueue('a');
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
rs.cancel();
|
||||
controller.close(); // Calling close after canceling should not throw anything.
|
||||
|
||||
return rs.getReader().closed.then(() => {
|
||||
assert_true(startCalled);
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling close on a non-empty canceled stream should not throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const theError = new Error('boo');
|
||||
let startCalled = false;
|
||||
|
||||
const closed = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
assert_throws(new TypeError(), () => c.close(), 'call to close should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return closed.catch(e => {
|
||||
assert_true(startCalled);
|
||||
assert_equals(e, theError, 'closed should reject with the error');
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling close after error should throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const theError = new Error('boo');
|
||||
let startCalled = false;
|
||||
|
||||
const closed = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
assert_throws(new TypeError(), () => c.error(), 'second call to error should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return closed.catch(e => {
|
||||
assert_true(startCalled);
|
||||
assert_equals(e, theError, 'closed should reject with the error');
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling error twice should throw the second time');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
|
||||
const closed = new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
assert_throws(new TypeError(), () => c.error(), 'second call to error should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return closed.then(() => assert_true(startCalled));
|
||||
|
||||
}, 'Underlying source: calling error after close should throw');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
const firstError = new Error('1');
|
||||
const secondError = new Error('2');
|
||||
|
||||
const closed = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(firstError);
|
||||
startCalled = true;
|
||||
return Promise.reject(secondError);
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return closed.catch(e => {
|
||||
assert_true(startCalled);
|
||||
assert_equals(e, firstError, 'closed should reject with the first error');
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling error and returning a rejected promise from start should cause the stream to error ' +
|
||||
'with the first error');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
const firstError = new Error('1');
|
||||
const secondError = new Error('2');
|
||||
|
||||
const closed = new ReadableStream({
|
||||
pull(c) {
|
||||
c.error(firstError);
|
||||
startCalled = true;
|
||||
return Promise.reject(secondError);
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
return closed.catch(e => {
|
||||
assert_true(startCalled);
|
||||
assert_equals(e, firstError, 'closed should reject with the first error');
|
||||
});
|
||||
|
||||
}, 'Underlying source: calling error and returning a rejected promise from pull should cause the stream to error ' +
|
||||
'with the first error');
|
||||
|
||||
done();
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="brand-checks.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('brand-checks.js');
|
||||
</script>
|
|
@ -0,0 +1,151 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
let ReadableStreamReader;
|
||||
let ReadableStreamController;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
ReadableStreamController = c.constructor;
|
||||
}
|
||||
});
|
||||
|
||||
}, 'Can get the ReadableStreamController constructor indirectly');
|
||||
|
||||
function fakeReadableStream() {
|
||||
return {
|
||||
cancel() { return Promise.resolve(); },
|
||||
getReader() { return new ReadableStreamReader(new ReadableStream()); },
|
||||
pipeThrough(obj) { return obj.readable; },
|
||||
pipeTo() { return Promise.resolve(); },
|
||||
tee() { return [realReadableStream(), realReadableStream()]; }
|
||||
};
|
||||
}
|
||||
|
||||
function realReadableStream() {
|
||||
return new ReadableStream();
|
||||
}
|
||||
|
||||
function fakeReadableStreamReader() {
|
||||
return {
|
||||
get closed() { return Promise.resolve(); },
|
||||
cancel() { return Promise.resolve(); },
|
||||
read() { return Promise.resolve({ value: undefined, done: true }); },
|
||||
releaseLock() { return; }
|
||||
};
|
||||
}
|
||||
|
||||
function fakeReadableStreamController() {
|
||||
return {
|
||||
close() { },
|
||||
enqueue() { },
|
||||
error() { }
|
||||
};
|
||||
}
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejects(t, ReadableStream.prototype, 'cancel', fakeReadableStream());
|
||||
|
||||
}, 'ReadableStream.prototype.cancel enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStream.prototype, 'getReader', fakeReadableStream());
|
||||
|
||||
}, 'ReadableStream.prototype.getReader enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStream.prototype, 'tee', fakeReadableStream());
|
||||
|
||||
}, 'ReadableStream.prototype.tee enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader(fakeReadableStream()),
|
||||
'Constructing a ReadableStreamReader should throw');
|
||||
|
||||
}, 'ReadableStreamReader enforces a brand check on its argument');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return Promise.all([
|
||||
getterRejects(t, ReadableStreamReader.prototype, 'closed', fakeReadableStreamReader()),
|
||||
getterRejects(t, ReadableStreamReader.prototype, 'closed', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.closed enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return Promise.all([
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'cancel', fakeReadableStreamReader()),
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'cancel', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.cancel enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return Promise.all([
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'read', fakeReadableStreamReader()),
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'read', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.read enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStreamReader.prototype, 'releaseLock', fakeReadableStreamReader());
|
||||
methodThrows(ReadableStreamReader.prototype, 'releaseLock', realReadableStream());
|
||||
|
||||
}, 'ReadableStreamReader.prototype.releaseLock enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamController(fakeReadableStream()),
|
||||
'Constructing a ReadableStreamController should throw');
|
||||
|
||||
}, 'ReadableStreamController enforces a brand check on its argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamController(realReadableStream()),
|
||||
'Constructing a ReadableStreamController should throw');
|
||||
|
||||
}, 'ReadableStreamController can\'t be given a fully-constructed ReadableStream');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStreamController.prototype, 'close', fakeReadableStreamController());
|
||||
|
||||
}, 'ReadableStreamController.prototype.close enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStreamController.prototype, 'enqueue', fakeReadableStreamController());
|
||||
|
||||
}, 'ReadableStreamController.prototype.enqueue enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStreamController.prototype, 'error', fakeReadableStreamController());
|
||||
|
||||
}, 'ReadableStreamController.prototype.error enforces a brand check');
|
||||
|
||||
done();
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="../resources/rs-utils.js"></script>
|
||||
<script src="cancel.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('cancel.js');
|
||||
</script>
|
242
tests/wpt/web-platform-tests/streams/readable-streams/cancel.js
Normal file
242
tests/wpt/web-platform-tests/streams/readable-streams/cancel.js
Normal file
|
@ -0,0 +1,242 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('../resources/rs-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const randomSource = new RandomPushSource();
|
||||
|
||||
let cancellationFinished = false;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
randomSource.ondata = c.enqueue.bind(c);
|
||||
randomSource.onend = c.close.bind(c);
|
||||
randomSource.onerror = c.error.bind(c);
|
||||
},
|
||||
|
||||
pull() {
|
||||
randomSource.readStart();
|
||||
},
|
||||
|
||||
cancel() {
|
||||
randomSource.readStop();
|
||||
randomSource.onend();
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
cancellationFinished = true;
|
||||
resolve();
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
// We call delay multiple times to avoid cancelling too early for the
|
||||
// source to enqueue at least one chunk.
|
||||
const cancel = delay(5).then(() => delay(5)).then(() => delay(5)).then(() => {
|
||||
let cancelPromise = reader.cancel();
|
||||
assert_false(cancellationFinished, 'cancellation in source should happen later');
|
||||
return cancelPromise;
|
||||
})
|
||||
|
||||
return readableStreamToArray(rs, reader).then(chunks => {
|
||||
assert_greater_than(chunks.length, 0, 'at least one chunk should be read');
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
assert_equals(chunks[i].length, 128, 'chunk ' + i + ' should have 128 bytes');
|
||||
}
|
||||
return cancel;
|
||||
}).then(() => {
|
||||
assert_true(cancellationFinished, 'it returns a promise that is fulfilled when the cancellation finishes');
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: integration test on an infinite stream derived from a random push source');
|
||||
|
||||
test(() => {
|
||||
|
||||
let recordedReason;
|
||||
const rs = new ReadableStream({
|
||||
cancel(reason) {
|
||||
recordedReason = reason;
|
||||
}
|
||||
});
|
||||
|
||||
const passedReason = new Error('Sorry, it just wasn\'t meant to be.');
|
||||
rs.cancel(passedReason);
|
||||
|
||||
assert_equals(recordedReason, passedReason,
|
||||
'the error passed to the underlying source\'s cancel method should equal the one passed to the stream\'s cancel');
|
||||
|
||||
}, 'ReadableStream cancellation: cancel(reason) should pass through the given reason to the underlying source');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.close();
|
||||
},
|
||||
cancel() {
|
||||
assert_unreached('underlying source cancel() should not have been called');
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
return rs.cancel().then(() => {
|
||||
assert_unreached('cancel() should be rejected');
|
||||
}, e => {
|
||||
assert_equals(e.name, 'TypeError', 'cancel() should be rejected with a TypeError');
|
||||
}).then(() => {
|
||||
return reader.read();
|
||||
}).then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false }, 'read() should still work after the attempted cancel');
|
||||
return reader.closed;
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: cancel() on a locked stream should fail and not call the underlying source cancel');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let cancelReceived = false;
|
||||
const cancelReason = new Error('I am tired of this stream, I prefer to cancel it');
|
||||
const rs = new ReadableStream({
|
||||
cancel(reason) {
|
||||
cancelReceived = true;
|
||||
assert_equals(reason, cancelReason, 'cancellation reason given to the underlying source should be equal to the one passed');
|
||||
}
|
||||
});
|
||||
|
||||
return rs.cancel(cancelReason).then(() => {
|
||||
assert_true(cancelReceived);
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: should fulfill promise when cancel callback went fine');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
return 'Hello';
|
||||
}
|
||||
});
|
||||
|
||||
return rs.cancel().then(v => {
|
||||
assert_equals(v, undefined, 'cancel() return value should be fulfilled with undefined');
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: returning a value from the underlying source\'s cancel should not affect the fulfillment value of the promise returned by the stream\'s cancel');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const thrownError = new Error('test');
|
||||
let cancelCalled = false;
|
||||
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
cancelCalled = true;
|
||||
throw thrownError;
|
||||
}
|
||||
});
|
||||
|
||||
return rs.cancel('test').then(() => {
|
||||
assert_unreached('cancel should reject');
|
||||
}, e => {
|
||||
assert_true(cancelCalled);
|
||||
assert_equals(e, thrownError);
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: should reject promise when cancel callback raises an exception');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const cancelReason = new Error('test');
|
||||
|
||||
const rs = new ReadableStream({
|
||||
cancel(error) {
|
||||
assert_equals(error, cancelReason);
|
||||
return delay(1);
|
||||
}
|
||||
});
|
||||
|
||||
return rs.cancel(cancelReason);
|
||||
|
||||
}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should fulfill when that one does (1)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let resolveSourceCancelPromise;
|
||||
let sourceCancelPromiseHasFulfilled = false;
|
||||
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
const sourceCancelPromise = new Promise(resolve => resolveSourceCancelPromise = resolve);
|
||||
|
||||
sourceCancelPromise.then(() => {
|
||||
sourceCancelPromiseHasFulfilled = true;
|
||||
});
|
||||
|
||||
return sourceCancelPromise;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => resolveSourceCancelPromise('Hello'), 1);
|
||||
|
||||
return rs.cancel().then(value => {
|
||||
assert_true(sourceCancelPromiseHasFulfilled, 'cancel() return value should be fulfilled only after the promise returned by the underlying source\'s cancel');
|
||||
assert_equals(value, undefined, 'cancel() return value should be fulfilled with undefined');
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should fulfill when that one does (2)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let rejectSourceCancelPromise;
|
||||
let sourceCancelPromiseHasRejected = false;
|
||||
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
const sourceCancelPromise = new Promise((resolve, reject) => rejectSourceCancelPromise = reject);
|
||||
|
||||
sourceCancelPromise.catch(() => {
|
||||
sourceCancelPromiseHasRejected = true;
|
||||
});
|
||||
|
||||
return sourceCancelPromise;
|
||||
}
|
||||
});
|
||||
|
||||
const errorInCancel = new Error('Sorry, it just wasn\'t meant to be.');
|
||||
|
||||
setTimeout(() => rejectSourceCancelPromise(errorInCancel), 1);
|
||||
|
||||
return rs.cancel().then(() => {
|
||||
assert_unreached('cancel() return value should be rejected');
|
||||
}, r => {
|
||||
assert_true(sourceCancelPromiseHasRejected, 'cancel() return value should be rejected only after the promise returned by the underlying source\'s cancel');
|
||||
assert_equals(r, errorInCancel, 'cancel() return value should be rejected with the underlying source\'s rejection reason');
|
||||
});
|
||||
|
||||
}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should reject when that one does');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return new Promise(() => {});
|
||||
},
|
||||
pull() {
|
||||
assert_unreached('pull should not have been called');
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([rs.cancel(), rs.getReader().closed]);
|
||||
|
||||
}, 'ReadableStream cancellation: cancelling before start finishes should prevent pull() from being called');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="count-queuing-strategy-integration.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('count-queuing-strategy-integration.js');
|
||||
</script>
|
|
@ -0,0 +1,213 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
new ReadableStream({}, new CountQueuingStrategy({ highWaterMark: 4 }));
|
||||
|
||||
}, 'Can construct a readable stream with a valid CountQueuingStrategy');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
},
|
||||
new CountQueuingStrategy({ highWaterMark: 0 })
|
||||
);
|
||||
const reader = rs.getReader();
|
||||
|
||||
assert_equals(controller.desiredSize, 0, '0 reads, 0 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('a');
|
||||
assert_equals(controller.desiredSize, -1, '0 reads, 1 enqueue: desiredSize should be -1');
|
||||
controller.enqueue('b');
|
||||
assert_equals(controller.desiredSize, -2, '0 reads, 2 enqueues: desiredSize should be -2');
|
||||
controller.enqueue('c');
|
||||
assert_equals(controller.desiredSize, -3, '0 reads, 3 enqueues: desiredSize should be -3');
|
||||
controller.enqueue('d');
|
||||
assert_equals(controller.desiredSize, -4, '0 reads, 4 enqueues: desiredSize should be -4');
|
||||
|
||||
return reader.read()
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false },
|
||||
'1st read gives back the 1st chunk enqueued (queue now contains 3 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'b', done: false },
|
||||
'2nd read gives back the 2nd chunk enqueued (queue now contains 2 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'c', done: false },
|
||||
'3rd read gives back the 3rd chunk enqueued (queue now contains 1 chunk)');
|
||||
|
||||
assert_equals(controller.desiredSize, -1, '3 reads, 4 enqueues: desiredSize should be -1');
|
||||
controller.enqueue('e');
|
||||
assert_equals(controller.desiredSize, -2, '3 reads, 5 enqueues: desiredSize should be -2');
|
||||
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'd', done: false },
|
||||
'4th read gives back the 4th chunk enqueued (queue now contains 1 chunks)');
|
||||
return reader.read();
|
||||
|
||||
}).then(result => {
|
||||
assert_object_equals(result, { value: 'e', done: false },
|
||||
'5th read gives back the 5th chunk enqueued (queue now contains 0 chunks)');
|
||||
|
||||
assert_equals(controller.desiredSize, 0, '5 reads, 5 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('f');
|
||||
assert_equals(controller.desiredSize, -1, '5 reads, 6 enqueues: desiredSize should be -1');
|
||||
controller.enqueue('g');
|
||||
assert_equals(controller.desiredSize, -2, '5 reads, 7 enqueues: desiredSize should be -2');
|
||||
});
|
||||
|
||||
}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 0)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
},
|
||||
new CountQueuingStrategy({ highWaterMark: 1 })
|
||||
);
|
||||
const reader = rs.getReader();
|
||||
|
||||
assert_equals(controller.desiredSize, 1, '0 reads, 0 enqueues: desiredSize should be 1');
|
||||
controller.enqueue('a');
|
||||
assert_equals(controller.desiredSize, 0, '0 reads, 1 enqueue: desiredSize should be 0');
|
||||
controller.enqueue('b');
|
||||
assert_equals(controller.desiredSize, -1, '0 reads, 2 enqueues: desiredSize should be -1');
|
||||
controller.enqueue('c');
|
||||
assert_equals(controller.desiredSize, -2, '0 reads, 3 enqueues: desiredSize should be -2');
|
||||
controller.enqueue('d');
|
||||
assert_equals(controller.desiredSize, -3, '0 reads, 4 enqueues: desiredSize should be -3');
|
||||
|
||||
return reader.read()
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false },
|
||||
'1st read gives back the 1st chunk enqueued (queue now contains 3 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'b', done: false },
|
||||
'2nd read gives back the 2nd chunk enqueued (queue now contains 2 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'c', done: false },
|
||||
'3rd read gives back the 3rd chunk enqueued (queue now contains 1 chunk)');
|
||||
|
||||
assert_equals(controller.desiredSize, 0, '3 reads, 4 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('e');
|
||||
assert_equals(controller.desiredSize, -1, '3 reads, 5 enqueues: desiredSize should be -1');
|
||||
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'd', done: false },
|
||||
'4th read gives back the 4th chunk enqueued (queue now contains 1 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'e', done: false },
|
||||
'5th read gives back the 5th chunk enqueued (queue now contains 0 chunks)');
|
||||
|
||||
assert_equals(controller.desiredSize, 1, '5 reads, 5 enqueues: desiredSize should be 1');
|
||||
controller.enqueue('f');
|
||||
assert_equals(controller.desiredSize, 0, '5 reads, 6 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('g');
|
||||
assert_equals(controller.desiredSize, -1, '5 reads, 7 enqueues: desiredSize should be -1');
|
||||
});
|
||||
|
||||
}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 1)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
},
|
||||
new CountQueuingStrategy({ highWaterMark: 4 })
|
||||
);
|
||||
const reader = rs.getReader();
|
||||
|
||||
assert_equals(controller.desiredSize, 4, '0 reads, 0 enqueues: desiredSize should be 4');
|
||||
controller.enqueue('a');
|
||||
assert_equals(controller.desiredSize, 3, '0 reads, 1 enqueue: desiredSize should be 3');
|
||||
controller.enqueue('b');
|
||||
assert_equals(controller.desiredSize, 2, '0 reads, 2 enqueues: desiredSize should be 2');
|
||||
controller.enqueue('c');
|
||||
assert_equals(controller.desiredSize, 1, '0 reads, 3 enqueues: desiredSize should be 1');
|
||||
controller.enqueue('d');
|
||||
assert_equals(controller.desiredSize, 0, '0 reads, 4 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('e');
|
||||
assert_equals(controller.desiredSize, -1, '0 reads, 5 enqueues: desiredSize should be -1');
|
||||
controller.enqueue('f');
|
||||
assert_equals(controller.desiredSize, -2, '0 reads, 6 enqueues: desiredSize should be -2');
|
||||
|
||||
|
||||
reader.read()
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false },
|
||||
'1st read gives back the 1st chunk enqueued (queue now contains 5 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'b', done: false },
|
||||
'2nd read gives back the 2nd chunk enqueued (queue now contains 4 chunks)');
|
||||
|
||||
assert_equals(controller.desiredSize, 0, '2 reads, 6 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('g');
|
||||
assert_equals(controller.desiredSize, -1, '2 reads, 7 enqueues: desiredSize should be -1');
|
||||
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'c', done: false },
|
||||
'3rd read gives back the 3rd chunk enqueued (queue now contains 4 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'd', done: false },
|
||||
'4th read gives back the 4th chunk enqueued (queue now contains 3 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'e', done: false },
|
||||
'5th read gives back the 5th chunk enqueued (queue now contains 2 chunks)');
|
||||
return reader.read();
|
||||
})
|
||||
.then(result => {
|
||||
assert_object_equals(result, { value: 'f', done: false },
|
||||
'6th read gives back the 6th chunk enqueued (queue now contains 0 chunks)');
|
||||
|
||||
assert_equals(controller.desiredSize, 3, '6 reads, 7 enqueues: desiredSize should be 3');
|
||||
controller.enqueue('h');
|
||||
assert_equals(controller.desiredSize, 2, '6 reads, 8 enqueues: desiredSize should be 2');
|
||||
controller.enqueue('i');
|
||||
assert_equals(controller.desiredSize, 1, '6 reads, 9 enqueues: desiredSize should be 1');
|
||||
controller.enqueue('j');
|
||||
assert_equals(controller.desiredSize, 0, '6 reads, 10 enqueues: desiredSize should be 0');
|
||||
controller.enqueue('k');
|
||||
assert_equals(controller.desiredSize, -1, '6 reads, 11 enqueues: desiredSize should be -1');
|
||||
});
|
||||
|
||||
}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 4)');
|
||||
|
||||
done();
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="garbage-collection.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('garbage-collection.js');
|
||||
</script>
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
garbageCollect();
|
||||
|
||||
return delay(50).then(() => {
|
||||
controller.close();
|
||||
assert_throws(new TypeError(), () => controller.close(), 'close should throw a TypeError the second time');
|
||||
assert_throws(new TypeError(), () => controller.error(), 'error should throw a TypeError on a closed stream');
|
||||
});
|
||||
|
||||
}, 'ReadableStreamController methods should continue working properly when scripts lose their reference to the ' +
|
||||
'readable stream');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
|
||||
const closedPromise = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
garbageCollect();
|
||||
|
||||
return delay(50).then(() => controller.close()).then(() => closedPromise);
|
||||
|
||||
}, 'ReadableStream closed promise should fulfill even if the stream and reader JS references are lost');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = new Error('boo');
|
||||
let controller;
|
||||
|
||||
const closedPromise = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
}).getReader().closed;
|
||||
|
||||
garbageCollect();
|
||||
|
||||
return delay(50).then(() => controller.error(theError))
|
||||
.then(() => promise_rejects(t, theError, closedPromise));
|
||||
|
||||
}, 'ReadableStream closed promise should reject even if stream and reader JS references are lost');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({});
|
||||
|
||||
rs.getReader();
|
||||
|
||||
garbageCollect();
|
||||
|
||||
return delay(50).then(() => assert_throws(new TypeError(), () => rs.getReader(),
|
||||
'old reader should still be locking the stream even after garbage collection'));
|
||||
|
||||
}, 'Garbage-collecting a ReadableStreamReader should not unlock its stream');
|
||||
|
||||
done();
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="../resources/rs-utils.js"></script>
|
||||
<script src="general.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('general.js');
|
||||
</script>
|
831
tests/wpt/web-platform-tests/streams/readable-streams/general.js
Normal file
831
tests/wpt/web-platform-tests/streams/readable-streams/general.js
Normal file
|
@ -0,0 +1,831 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('../resources/rs-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
new ReadableStream(); // ReadableStream constructed with no parameters
|
||||
new ReadableStream({ }); // ReadableStream constructed with an empty object as parameter
|
||||
new ReadableStream(undefined); // ReadableStream constructed with undefined as parameter
|
||||
|
||||
let x;
|
||||
new ReadableStream(x); // ReadableStream constructed with an undefined variable as parameter
|
||||
|
||||
}, 'ReadableStream can be constructed with no errors');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStream(null), 'constructor should throw when the source is null');
|
||||
|
||||
}, 'ReadableStream can\'t be constructed with garbage');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee'];
|
||||
const properties = methods.concat(['locked']).sort();
|
||||
|
||||
const rs = new ReadableStream();
|
||||
const proto = Object.getPrototypeOf(rs);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, 'should have all the correct methods');
|
||||
|
||||
for (let m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_false(propDesc.enumerable, 'method should be non-enumerable');
|
||||
assert_true(propDesc.configurable, 'method should be configurable');
|
||||
assert_true(propDesc.writable, 'method should be writable');
|
||||
assert_equals(typeof rs[m], 'function', 'method should be a function');
|
||||
}
|
||||
|
||||
const lockedPropDesc = Object.getOwnPropertyDescriptor(proto, 'locked');
|
||||
assert_false(lockedPropDesc.enumerable, 'locked should be non-enumerable');
|
||||
assert_equals(lockedPropDesc.writable, undefined, 'locked should not be a data property');
|
||||
assert_equals(typeof lockedPropDesc.get, 'function', 'locked should have a getter');
|
||||
assert_equals(lockedPropDesc.set, undefined, 'locked should not have a setter');
|
||||
assert_true(lockedPropDesc.configurable, 'locked should be configurable');
|
||||
|
||||
assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter');
|
||||
assert_equals(rs.constructor.length, 0, 'constructor should have no parameters');
|
||||
assert_equals(rs.getReader.length, 0, 'getReader should have no parameters');
|
||||
assert_equals(rs.pipeThrough.length, 2, 'pipeThrough should have 2 parameters');
|
||||
assert_equals(rs.pipeTo.length, 1, 'pipeTo should have 1 parameter');
|
||||
assert_equals(rs.tee.length, 0, 'tee should have no parameters');
|
||||
|
||||
}, 'ReadableStream instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => {
|
||||
new ReadableStream({ start: 'potato' });
|
||||
}, 'constructor should throw when start is not a function');
|
||||
|
||||
}, 'ReadableStream constructor should throw for non-function start arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
new ReadableStream({ cancel: '2' });
|
||||
|
||||
}, 'ReadableStream constructor can get initial garbage as cancel argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
new ReadableStream({ pull: { } });
|
||||
|
||||
}, 'ReadableStream constructor can get initial garbage as pull argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
|
||||
const source = {
|
||||
start(controller) {
|
||||
assert_equals(this, source, 'source is this during start');
|
||||
|
||||
const methods = ['close', 'enqueue', 'error', 'constructor'];
|
||||
const properties = ['desiredSize'].concat(methods).sort();
|
||||
const proto = Object.getPrototypeOf(controller);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties,
|
||||
'the controller should have the right properties');
|
||||
|
||||
for (let m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(typeof controller[m], 'function', `should have a ${m} method`);
|
||||
assert_false(propDesc.enumerable, m + ' should be non-enumerable');
|
||||
assert_true(propDesc.configurable, m + ' should be configurable');
|
||||
assert_true(propDesc.writable, m + ' should be writable');
|
||||
}
|
||||
|
||||
const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desiredSize');
|
||||
assert_false(desiredSizePropDesc.enumerable, 'desiredSize should be non-enumerable');
|
||||
assert_equals(desiredSizePropDesc.writable, undefined, 'desiredSize should not be a data property');
|
||||
assert_equals(typeof desiredSizePropDesc.get, 'function', 'desiredSize should have a getter');
|
||||
assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not have a setter');
|
||||
assert_true(desiredSizePropDesc.configurable, 'desiredSize should be configurable');
|
||||
|
||||
assert_equals(controller.close.length, 0, 'close should have no parameters');
|
||||
assert_equals(controller.constructor.length, 1, 'constructor should have 1 parameter');
|
||||
assert_equals(controller.enqueue.length, 1, 'enqueue should have 1 parameter');
|
||||
assert_equals(controller.error.length, 1, 'error should have 1 parameter');
|
||||
|
||||
startCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
new ReadableStream(source);
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream start should be called with the proper parameters');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
const source = {
|
||||
start(controller) {
|
||||
const properties = ['close', 'constructor', 'desiredSize', 'enqueue', 'error'];
|
||||
assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
|
||||
'prototype should have the right properties');
|
||||
|
||||
controller.test = '';
|
||||
assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
|
||||
'prototype should still have the right properties');
|
||||
assert_not_equals(Object.getOwnPropertyNames(controller).indexOf('test'), -1,
|
||||
'"test" should be a property of the controller');
|
||||
|
||||
startCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
new ReadableStream(source);
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream start controller parameter should be extensible');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
function SimpleStreamSource() {}
|
||||
let resolve;
|
||||
const promise = new Promise(r => resolve = r);
|
||||
SimpleStreamSource.prototype = {
|
||||
start: resolve
|
||||
};
|
||||
|
||||
new ReadableStream(new SimpleStreamSource());
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStream should be able to call start method within prototype chain of its source');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
return delay(5).then(() => {
|
||||
c.enqueue('a');
|
||||
c.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return reader.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'value read should be the one enqueued');
|
||||
return reader.closed;
|
||||
});
|
||||
|
||||
}, 'ReadableStream start should be able to return a promise');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const theError = new Error('rejected!');
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return delay(1).then(() => { throw theError; });
|
||||
}
|
||||
});
|
||||
|
||||
return rs.getReader().closed.then(() => {
|
||||
assert_unreached('closed promise should be rejected');
|
||||
}, e => {
|
||||
assert_equals(e, theError, 'promise should be rejected with the same error');
|
||||
});
|
||||
|
||||
}, 'ReadableStream start should be able to return a promise and reject it');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const objects = [
|
||||
{ potato: 'Give me more!' },
|
||||
'test',
|
||||
1
|
||||
];
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
for (let o of objects) {
|
||||
c.enqueue(o);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([reader.read(), reader.read(), reader.read(), reader.closed]).then(r => {
|
||||
assert_object_equals(r[0], { value: objects[0], done: false }, 'value read should be the one enqueued');
|
||||
assert_object_equals(r[1], { value: objects[1], done: false }, 'value read should be the one enqueued');
|
||||
assert_object_equals(r[2], { value: objects[2], done: false }, 'value read should be the one enqueued');
|
||||
});
|
||||
|
||||
}, 'ReadableStream should be able to enqueue different objects.');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const error = new Error('pull failure');
|
||||
const rs = new ReadableStream({
|
||||
pull() {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
let closed = false;
|
||||
let read = false;
|
||||
|
||||
return Promise.all([
|
||||
reader.closed.then(() => {
|
||||
assert_unreached('closed should be rejected');
|
||||
}, e => {
|
||||
closed = true;
|
||||
assert_true(read);
|
||||
assert_equals(e, error, 'closed should be rejected with the thrown error');
|
||||
}),
|
||||
reader.read().then(() => {
|
||||
assert_unreached('read() should be rejected');
|
||||
}, e => {
|
||||
read = true;
|
||||
assert_false(closed);
|
||||
assert_equals(e, error, 'read() should be rejected with the thrown error');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'ReadableStream: if pull rejects, it should error the stream');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
new ReadableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called exactly once');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should only call pull once upon starting the stream');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
|
||||
const rs = new ReadableStream({
|
||||
pull(c) {
|
||||
// Don't enqueue immediately after start. We want the stream to be empty when we call .read() on it.
|
||||
if (pullCount > 0) {
|
||||
c.enqueue(pullCount);
|
||||
}
|
||||
++pullCount;
|
||||
}
|
||||
});
|
||||
|
||||
return delay(1).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
|
||||
const reader = rs.getReader();
|
||||
const read = reader.read();
|
||||
assert_equals(pullCount, 2, 'pull should be called when read is called');
|
||||
return read;
|
||||
}).then(result => {
|
||||
assert_equals(pullCount, 3, 'pull should be called again in reaction to calling read');
|
||||
assert_object_equals(result, { value: 1, done: false }, 'the result read should be the one enqueued');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should call pull when trying to read from a started, empty stream');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const read = rs.getReader().read();
|
||||
assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
|
||||
|
||||
return startPromise.then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
return read;
|
||||
}).then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
|
||||
assert_equals(pullCount, 1, 'pull should not have been called again');
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called exactly once');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should only call pull once on a non-empty stream read from before start fulfills');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
assert_equals(pullCount, 0, 'pull should not be called once start finishes, since the queue is full');
|
||||
|
||||
const read = rs.getReader().read();
|
||||
assert_equals(pullCount, 1, 'calling read() should cause pull to be called immediately');
|
||||
return read;
|
||||
}).then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called exactly once');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should only call pull once on a non-empty stream read from after start fulfills');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
let controller;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++pullCount;
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return startPromise.then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
|
||||
|
||||
controller.enqueue('a');
|
||||
assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
|
||||
|
||||
return reader.read();
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 2, 'pull should have been called again after read');
|
||||
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 2, 'pull should be called exactly twice');
|
||||
});
|
||||
}, 'ReadableStream: should call pull in reaction to read()ing the last chunk, if not draining');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
let controller;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++pullCount;
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
|
||||
|
||||
controller.enqueue('a');
|
||||
assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
|
||||
|
||||
controller.close();
|
||||
|
||||
return reader.read();
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should not have been called a second time after read');
|
||||
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called exactly once');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should not call pull() in reaction to read()ing the last chunk, if draining');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let resolve;
|
||||
let returnedPromise;
|
||||
let timesCalled = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
pull(c) {
|
||||
c.enqueue(++timesCalled);
|
||||
returnedPromise = new Promise(r => resolve = r);
|
||||
return returnedPromise;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
return reader.read();
|
||||
}).then(result1 => {
|
||||
assert_equals(timesCalled, 1,
|
||||
'pull should have been called once after start, but not yet have been called a second time');
|
||||
assert_object_equals(result1, { value: 1, done: false }, 'read() should fulfill with the enqueued value');
|
||||
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
assert_equals(timesCalled, 1, 'after 10 ms, pull should still only have been called once');
|
||||
|
||||
resolve();
|
||||
return returnedPromise;
|
||||
}).then(() => {
|
||||
assert_equals(timesCalled, 2,
|
||||
'after the promise returned by pull is fulfilled, pull should be called a second time');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let timesCalled = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.enqueue('c');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++timesCalled;
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
return 1;
|
||||
},
|
||||
highWaterMark: Infinity
|
||||
}
|
||||
);
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
return reader.read();
|
||||
}).then(result1 => {
|
||||
assert_object_equals(result1, { value: 'a', done: false }, 'first chunk should be as expected');
|
||||
|
||||
return reader.read();
|
||||
}).then(result2 => {
|
||||
assert_object_equals(result2, { value: 'b', done: false }, 'second chunk should be as expected');
|
||||
|
||||
return reader.read();
|
||||
}).then(result3 => {
|
||||
assert_object_equals(result3, { value: 'c', done: false }, 'third chunk should be as expected');
|
||||
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
// Once for after start, and once for every read.
|
||||
assert_equals(timesCalled, 4, 'pull() should be called exactly four times');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should pull after start, and after every read');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let timesCalled = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.close();
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++timesCalled;
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return startPromise.then(() => {
|
||||
assert_equals(timesCalled, 0, 'after start finishes, pull should not have been called');
|
||||
|
||||
return reader.read();
|
||||
}).then(() => {
|
||||
assert_equals(timesCalled, 0, 'reading should not have triggered a pull call');
|
||||
|
||||
return reader.closed;
|
||||
}).then(() => {
|
||||
assert_equals(timesCalled, 0, 'stream should have closed with still no calls to pull');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should not call pull after start if the stream is now closed');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let timesCalled = 0;
|
||||
let resolve;
|
||||
const ready = new Promise(r => resolve = r);
|
||||
|
||||
new ReadableStream(
|
||||
{
|
||||
start() {},
|
||||
pull(c) {
|
||||
c.enqueue(++timesCalled);
|
||||
|
||||
if (timesCalled === 4) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
size() {
|
||||
return 1;
|
||||
},
|
||||
highWaterMark: 4
|
||||
}
|
||||
);
|
||||
|
||||
return ready.then(() => {
|
||||
// after start: size = 0, pull()
|
||||
// after enqueue(1): size = 1, pull()
|
||||
// after enqueue(2): size = 2, pull()
|
||||
// after enqueue(3): size = 3, pull()
|
||||
// after enqueue(4): size = 4, do not pull
|
||||
assert_equals(timesCalled, 4, 'pull() should have been called four times');
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should call pull after enqueueing from inside pull (with no read requests), if strategy allows');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let pullCalled = false;
|
||||
|
||||
const rs = new ReadableStream({
|
||||
pull(c) {
|
||||
pullCalled = true;
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return reader.closed.then(() => {
|
||||
assert_true(pullCalled);
|
||||
});
|
||||
|
||||
}, 'ReadableStream pull should be able to close a stream.');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.enqueue('a'), undefined, 'the first enqueue should return undefined');
|
||||
c.close();
|
||||
|
||||
assert_throws(new TypeError(), () => c.enqueue('b'), 'enqueue after close should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream: enqueue should throw when the stream is readable but draining');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
|
||||
assert_throws(new TypeError(), () => c.enqueue('a'), 'enqueue after close should throw a TypeError');
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream: enqueue should throw when the stream is closed');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
const expectedError = new Error('i am sad');
|
||||
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
c.error(expectedError);
|
||||
|
||||
assert_throws(expectedError, () => c.enqueue('a'), 'enqueue after error should throw that error');
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream: enqueue should throw the stored error when the stream is errored');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let startCalled = 0;
|
||||
let pullCalled = 0;
|
||||
let cancelCalled = 0;
|
||||
|
||||
/* eslint-disable no-use-before-define */
|
||||
class Source {
|
||||
start(c) {
|
||||
startCalled++;
|
||||
assert_equals(this, theSource, 'start() should be called with the correct this');
|
||||
c.enqueue('a');
|
||||
}
|
||||
|
||||
pull() {
|
||||
pullCalled++;
|
||||
assert_equals(this, theSource, 'pull() should be called with the correct this');
|
||||
}
|
||||
|
||||
cancel() {
|
||||
cancelCalled++;
|
||||
assert_equals(this, theSource, 'cancel() should be called with the correct this');
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-use-before-define */
|
||||
|
||||
const theSource = new Source();
|
||||
theSource.debugName = 'the source object passed to the constructor'; // makes test failures easier to diagnose
|
||||
|
||||
const rs = new ReadableStream(theSource);
|
||||
const reader = rs.getReader();
|
||||
|
||||
return reader.read().then(() => {
|
||||
reader.releaseLock();
|
||||
rs.cancel();
|
||||
assert_equals(startCalled, 1);
|
||||
assert_equals(pullCalled, 1);
|
||||
assert_equals(cancelCalled, 1);
|
||||
return rs.getReader().closed;
|
||||
});
|
||||
|
||||
}, 'ReadableStream: should call underlying source methods as methods');
|
||||
|
||||
test(() => {
|
||||
|
||||
let startCalled = false;
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.desiredSize, 1);
|
||||
c.enqueue('a');
|
||||
assert_equals(c.desiredSize, 0);
|
||||
c.enqueue('b');
|
||||
assert_equals(c.desiredSize, -1);
|
||||
c.enqueue('c');
|
||||
assert_equals(c.desiredSize, -2);
|
||||
c.enqueue('d');
|
||||
assert_equals(c.desiredSize, -3);
|
||||
c.enqueue('e');
|
||||
startCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream strategies: the default strategy should give desiredSize of 1 to start, decreasing by 1 per enqueue');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
assert_equals(controller.desiredSize, 1, 'desiredSize should start at 1');
|
||||
controller.enqueue('a');
|
||||
assert_equals(controller.desiredSize, 0, 'desiredSize should decrease to 0 after first enqueue');
|
||||
|
||||
return reader.read().then(result1 => {
|
||||
assert_object_equals(result1, { value: 'a', done: false }, 'first chunk read should be correct');
|
||||
|
||||
assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the first read');
|
||||
controller.enqueue('b');
|
||||
assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the second enqueue');
|
||||
|
||||
return reader.read();
|
||||
}).then(result2 => {
|
||||
assert_object_equals(result2, { value: 'b', done: false }, 'second chunk read should be correct');
|
||||
|
||||
assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the second read');
|
||||
controller.enqueue('c');
|
||||
assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the third enqueue');
|
||||
|
||||
return reader.read();
|
||||
}).then(result3 => {
|
||||
assert_object_equals(result3, { value: 'c', done: false }, 'third chunk read should be correct');
|
||||
|
||||
assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the third read');
|
||||
controller.enqueue('d');
|
||||
assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the fourth enqueue');
|
||||
});
|
||||
|
||||
}, 'ReadableStream strategies: the default strategy should continue giving desiredSize of 1 if the chunks are read immediately');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const randomSource = new RandomPushSource(8);
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(typeof c, 'object', 'c should be an object in start');
|
||||
assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in start');
|
||||
assert_equals(typeof c.close, 'function', 'close should be a function in start');
|
||||
assert_equals(typeof c.error, 'function', 'error should be a function in start');
|
||||
|
||||
randomSource.ondata = t.step_func(chunk => {
|
||||
if (!c.enqueue(chunk) <= 0) {
|
||||
randomSource.readStop();
|
||||
}
|
||||
});
|
||||
|
||||
randomSource.onend = c.close.bind(c);
|
||||
randomSource.onerror = c.error.bind(c);
|
||||
},
|
||||
|
||||
pull(c) {
|
||||
assert_equals(typeof c, 'object', 'c should be an object in pull');
|
||||
assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in pull');
|
||||
assert_equals(typeof c.close, 'function', 'close should be a function in pull');
|
||||
|
||||
randomSource.readStart();
|
||||
}
|
||||
});
|
||||
|
||||
return readableStreamToArray(rs).then(chunks => {
|
||||
assert_equals(chunks.length, 8, '8 chunks should be read');
|
||||
for (let chunk of chunks) {
|
||||
assert_equals(chunk.length, 128, 'chunk should have 128 bytes');
|
||||
}
|
||||
});
|
||||
|
||||
}, 'ReadableStream integration test: adapting a random push source');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = sequentialReadableStream(10);
|
||||
|
||||
return readableStreamToArray(rs).then(chunks => {
|
||||
assert_true(rs.source.closed, 'source should be closed after all chunks are read');
|
||||
assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
|
||||
});
|
||||
|
||||
}, 'ReadableStream integration test: adapting a sync pull source');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = sequentialReadableStream(10, { async: true });
|
||||
|
||||
return readableStreamToArray(rs).then(chunks => {
|
||||
assert_true(rs.source.closed, 'source should be closed after all chunks are read');
|
||||
assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
|
||||
});
|
||||
|
||||
}, 'ReadableStream integration test: adapting an async pull source');
|
||||
|
||||
done();
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="pipe-through.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('pipe-through.js');
|
||||
</script>
|
|
@ -0,0 +1,108 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
let pipeToArguments;
|
||||
const thisValue = {
|
||||
pipeTo() {
|
||||
pipeToArguments = arguments;
|
||||
}
|
||||
};
|
||||
|
||||
const input = { readable: {}, writable: {} };
|
||||
const options = {};
|
||||
const result = ReadableStream.prototype.pipeThrough.call(thisValue, input, options);
|
||||
|
||||
assert_array_equals(pipeToArguments, [input.writable, options],
|
||||
'correct arguments should be passed to thisValue.pipeTo');
|
||||
assert_equals(result, input.readable, 'return value should be the passed readable property');
|
||||
|
||||
}, 'ReadableStream.prototype.pipeThrough should work generically on its this and its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const thisValue = {
|
||||
pipeTo() {
|
||||
assert_unreached('pipeTo should not be called');
|
||||
}
|
||||
};
|
||||
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', thisValue, [undefined, {}]);
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', thisValue, [null, {}]);
|
||||
|
||||
}, 'ReadableStream.prototype.pipeThrough should throw when its first argument is not convertible to an object');
|
||||
|
||||
test(() => {
|
||||
|
||||
const args = [{ readable: {}, writable: {} }, {}];
|
||||
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', undefined, args);
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', null, args);
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', 1, args);
|
||||
methodThrows(ReadableStream.prototype, 'pipeThrough', { pipeTo: 'test' }, args);
|
||||
|
||||
}, 'ReadableStream.prototype.pipeThrough should throw when "this" has no pipeTo method');
|
||||
|
||||
test(() => {
|
||||
const error = new Error('potato');
|
||||
|
||||
const throwingPipeTo = {
|
||||
get pipeTo() {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
assert_throws(error,
|
||||
() => ReadableStream.prototype.pipeThrough.call(throwingPipeTo, { readable: { }, writable: { } }, {}),
|
||||
'pipeThrough should rethrow the error thrown by pipeTo');
|
||||
|
||||
const thisValue = {
|
||||
pipeTo() {
|
||||
assert_unreached('pipeTo should not be called');
|
||||
}
|
||||
};
|
||||
|
||||
const throwingWritable = {
|
||||
readable: {},
|
||||
get writable() {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
assert_throws(error,
|
||||
() => ReadableStream.prototype.pipeThrough.call(thisValue, throwingWritable, {}),
|
||||
'pipeThrough should rethrow the error thrown by the writable getter');
|
||||
|
||||
const throwingReadable = {
|
||||
get readable() {
|
||||
throw error;
|
||||
},
|
||||
writable: {}
|
||||
};
|
||||
assert_throws(error,
|
||||
() => ReadableStream.prototype.pipeThrough.call(thisValue, throwingReadable, {}),
|
||||
'pipeThrough should rethrow the error thrown by the readable getter');
|
||||
|
||||
}, 'ReadableStream.prototype.pipeThrough should rethrow errors from accessing pipeTo, readable, or writable');
|
||||
|
||||
test(() => {
|
||||
|
||||
let count = 0;
|
||||
const thisValue = {
|
||||
pipeTo() {
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
ReadableStream.prototype.pipeThrough.call(thisValue, { readable: {}, writable: {} });
|
||||
ReadableStream.prototype.pipeThrough.call(thisValue, { readable: {} }, {});
|
||||
ReadableStream.prototype.pipeThrough.call(thisValue, { writable: {} }, {});
|
||||
|
||||
assert_equals(count, 3, 'pipeTo was called 3 times');
|
||||
|
||||
}, 'ReadableStream.prototype.pipeThrough should work with missing readable, writable, or options');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="readable-stream-reader.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('readable-stream-reader.js');
|
||||
</script>
|
|
@ -0,0 +1,485 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/rs-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
let ReadableStreamReader;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader('potato'));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader({}));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader());
|
||||
|
||||
}, 'ReadableStreamReader constructor should get a ReadableStream object as argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['cancel', 'constructor', 'read', 'releaseLock'];
|
||||
const properties = methods.concat(['closed']).sort();
|
||||
|
||||
const rsReader = new ReadableStreamReader(new ReadableStream());
|
||||
const proto = Object.getPrototypeOf(rsReader);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
||||
for (let m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
|
||||
assert_equals(propDesc.configurable, true, 'method should be configurable');
|
||||
assert_equals(propDesc.writable, true, 'method should be writable');
|
||||
assert_equals(typeof rsReader[m], 'function', 'should have be a method');
|
||||
}
|
||||
|
||||
const closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed');
|
||||
assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerable');
|
||||
assert_equals(closedPropDesc.configurable, true, 'closed should be configurable');
|
||||
assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter');
|
||||
assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter');
|
||||
|
||||
assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter');
|
||||
assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed property');
|
||||
assert_equals(typeof rsReader.closed.then, 'function', 'closed property is thenable');
|
||||
assert_equals(typeof rsReader.constructor, 'function', 'has a constructor method');
|
||||
assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter');
|
||||
assert_equals(typeof rsReader.read, 'function', 'has a getReader method');
|
||||
assert_equals(rsReader.read.length, 0, 'read has no parameters');
|
||||
assert_equals(typeof rsReader.releaseLock, 'function', 'has a releaseLock method');
|
||||
assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters');
|
||||
|
||||
}, 'ReadableStreamReader instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rsReader = new ReadableStreamReader(new ReadableStream());
|
||||
assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise');
|
||||
|
||||
}, 'ReadableStreamReader closed should always return the same promise object');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
new ReadableStreamReader(rs); // Constructing directly the first time should be fine.
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader(rs),
|
||||
'constructing directly the second time should fail');
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via direct ' +
|
||||
'construction)');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
new ReadableStreamReader(rs); // Constructing directly should be fine.
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail');
|
||||
|
||||
}, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via direct ' +
|
||||
'construction)');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
rs.getReader(); // getReader() should be fine.
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader(rs), 'constructing directly should fail');
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via getReader)');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
rs.getReader(); // getReader() should be fine.
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail');
|
||||
|
||||
}, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via getReader)');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
new ReadableStreamReader(rs); // Constructing directly should not throw.
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should be OK if the stream is closed');
|
||||
|
||||
test(() => {
|
||||
|
||||
const theError = new Error('don\'t say i didn\'t warn ya');
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
}
|
||||
});
|
||||
|
||||
new ReadableStreamReader(rs); // Constructing directly should not throw.
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should be OK if the stream is errored');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
const promise = reader.read().then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false }, 'read() should fulfill with the enqueued chunk');
|
||||
});
|
||||
|
||||
controller.enqueue('a');
|
||||
return promise;
|
||||
|
||||
}, 'Reading from a reader for an empty stream will wait until a chunk is available');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let cancelCalled = false;
|
||||
const passedReason = new Error('it wasn\'t the right time, sorry');
|
||||
const rs = new ReadableStream({
|
||||
cancel(reason) {
|
||||
assert_true(rs.locked, 'the stream should still be locked');
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'should not be able to get another reader');
|
||||
assert_equals(reason, passedReason, 'the cancellation reason is passed through to the underlying source');
|
||||
cancelCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return reader.cancel(passedReason).then(() => assert_true(cancelCalled));
|
||||
|
||||
}, 'cancel() on a reader does not release the reader');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
const promise = reader.closed;
|
||||
|
||||
controller.close();
|
||||
return promise;
|
||||
|
||||
}, 'closed should be fulfilled after stream is closed (.closed access before acquiring)');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const reader1 = rs.getReader();
|
||||
|
||||
reader1.releaseLock();
|
||||
|
||||
const reader2 = rs.getReader();
|
||||
controller.close();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), reader1.closed),
|
||||
reader2.closed
|
||||
]);
|
||||
|
||||
}, 'closed should be rejected after reader releases its lock (multiple stream locks)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const reader1 = rs.getReader();
|
||||
const promise1 = reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'reading the first chunk from reader1 works');
|
||||
});
|
||||
reader1.releaseLock();
|
||||
|
||||
const reader2 = rs.getReader();
|
||||
const promise2 = reader2.read().then(r => {
|
||||
assert_object_equals(r, { value: 'b', done: false }, 'reading the second chunk from reader2 works');
|
||||
});
|
||||
reader2.releaseLock();
|
||||
|
||||
return Promise.all([promise1, promise2]);
|
||||
|
||||
}, 'Multiple readers can access the stream in sequence');
|
||||
|
||||
promise_test(() => {
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
}
|
||||
});
|
||||
|
||||
const reader1 = rs.getReader();
|
||||
reader1.releaseLock();
|
||||
|
||||
const reader2 = rs.getReader();
|
||||
|
||||
// Should be a no-op
|
||||
reader1.releaseLock();
|
||||
|
||||
return reader2.read().then(result => {
|
||||
assert_object_equals(result, { value: 'a', done: false },
|
||||
'read() should still work on reader2 even after reader1 is released');
|
||||
});
|
||||
|
||||
}, 'Cannot use an already-released reader to unlock a stream again');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
},
|
||||
cancel() {
|
||||
assert_unreached('underlying source cancel should not be called');
|
||||
}
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
reader.releaseLock();
|
||||
const cancelPromise = reader.cancel();
|
||||
|
||||
const reader2 = rs.getReader();
|
||||
const readPromise = reader2.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be able to read a chunk');
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), cancelPromise),
|
||||
readPromise
|
||||
]);
|
||||
|
||||
}, 'cancel() on a released reader is a no-op and does not pass through');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const promiseAsserts = [];
|
||||
|
||||
let controller;
|
||||
const theError = { name: 'unique error' };
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const reader1 = rs.getReader();
|
||||
|
||||
promiseAsserts.push(
|
||||
promise_rejects(t, theError, reader1.closed),
|
||||
promise_rejects(t, theError, reader1.read())
|
||||
);
|
||||
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'trying to get another reader before erroring should throw');
|
||||
|
||||
controller.error(theError);
|
||||
|
||||
reader1.releaseLock();
|
||||
|
||||
const reader2 = rs.getReader();
|
||||
|
||||
promiseAsserts.push(
|
||||
promise_rejects(t, theError, reader2.closed),
|
||||
promise_rejects(t, theError, reader2.read())
|
||||
);
|
||||
|
||||
return Promise.all(promiseAsserts);
|
||||
|
||||
}, 'Getting a second reader after erroring the stream and releasing the reader should succeed');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const promise = rs.getReader().closed.then(
|
||||
t.unreached_func('closed promise should not be fulfilled when stream is errored'),
|
||||
err => {
|
||||
assert_equals(err, undefined, 'passed error should be undefined as it was');
|
||||
}
|
||||
);
|
||||
|
||||
controller.error();
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStreamReader closed promise should be rejected with undefined if that is the error');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
|
||||
return rs.getReader().read().then(
|
||||
t.unreached_func('read promise should not be fulfilled when stream is errored'),
|
||||
err => {
|
||||
assert_equals(err, undefined, 'passed error should be undefined as it was');
|
||||
}
|
||||
);
|
||||
|
||||
}, 'ReadableStreamReader: if start rejects with no parameter, it should error the stream with an undefined error');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = { name: 'unique string' };
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const promise = promise_rejects(t, theError, rs.getReader().closed);
|
||||
|
||||
controller.error(theError);
|
||||
return promise;
|
||||
|
||||
}, 'Erroring a ReadableStream after checking closed should reject ReadableStreamReader closed promise');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = { name: 'unique string' };
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
controller.error(theError);
|
||||
|
||||
// Let's call getReader twice for extra test coverage of this code path.
|
||||
rs.getReader().releaseLock();
|
||||
|
||||
return promise_rejects(t, theError, rs.getReader().closed);
|
||||
|
||||
}, 'Erroring a ReadableStream before checking closed should reject ReadableStreamReader closed promise');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
const promise = Promise.all([
|
||||
reader.read().then(result => {
|
||||
assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
|
||||
}),
|
||||
reader.read().then(result => {
|
||||
assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
|
||||
}),
|
||||
reader.closed
|
||||
]);
|
||||
|
||||
controller.close();
|
||||
return promise;
|
||||
|
||||
}, 'Reading twice on a stream that gets closed');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
controller.close();
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(result => {
|
||||
assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
|
||||
}),
|
||||
reader.read().then(result => {
|
||||
assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
|
||||
}),
|
||||
reader.closed
|
||||
]);
|
||||
|
||||
}, 'Reading twice on a closed stream');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const myError = { name: 'mashed potatoes' };
|
||||
controller.error(myError);
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, myError, reader.read()),
|
||||
promise_rejects(t, myError, reader.read()),
|
||||
promise_rejects(t, myError, reader.closed)
|
||||
]);
|
||||
|
||||
}, 'Reading twice on an errored stream');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const myError = { name: 'mashed potatoes' };
|
||||
const reader = rs.getReader();
|
||||
|
||||
const promise = Promise.all([
|
||||
promise_rejects(t, myError, reader.read()),
|
||||
promise_rejects(t, myError, reader.read()),
|
||||
promise_rejects(t, myError, reader.closed)
|
||||
]);
|
||||
|
||||
controller.error(myError);
|
||||
return promise;
|
||||
|
||||
}, 'Reading twice on a stream that gets errored');
|
||||
|
||||
done();
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/rs-utils.js"></script>
|
||||
<script src="tee.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('tee.js');
|
||||
</script>
|
254
tests/wpt/web-platform-tests/streams/readable-streams/tee.js
Normal file
254
tests/wpt/web-platform-tests/streams/readable-streams/tee.js
Normal file
|
@ -0,0 +1,254 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('../resources/rs-utils.js');
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
const result = rs.tee();
|
||||
|
||||
assert_true(Array.isArray(result), 'return value should be an array');
|
||||
assert_equals(result.length, 2, 'array should have length 2');
|
||||
assert_equals(result[0].constructor, ReadableStream, '0th element should be a ReadableStream');
|
||||
assert_equals(result[1].constructor, ReadableStream, '1st element should be a ReadableStream');
|
||||
|
||||
}, 'ReadableStream teeing: rs.tee() returns an array of two ReadableStreams');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const branch = rs.tee();
|
||||
const branch1 = branch[0];
|
||||
const branch2 = branch[1];
|
||||
const reader1 = branch1.getReader();
|
||||
const reader2 = branch2.getReader();
|
||||
|
||||
reader2.closed.then(t.unreached_func('branch2 should not be closed'));
|
||||
|
||||
return Promise.all([
|
||||
reader1.closed,
|
||||
reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch1 should be correct');
|
||||
}),
|
||||
reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: 'b', done: false }, 'second chunk from branch1 should be correct');
|
||||
}),
|
||||
reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: undefined, done: true }, 'third read() from branch1 should be done');
|
||||
}),
|
||||
reader2.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch2 should be correct');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'ReadableStream teeing: should be able to read one branch to the end without affecting the other');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const theObject = { the: 'test object' };
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(theObject);
|
||||
}
|
||||
});
|
||||
|
||||
const branch = rs.tee();
|
||||
const branch1 = branch[0];
|
||||
const branch2 = branch[1];
|
||||
const reader1 = branch1.getReader();
|
||||
const reader2 = branch2.getReader();
|
||||
|
||||
return Promise.all([reader1.read(), reader2.read()]).then(values => {
|
||||
assert_object_equals(values[0], values[1], 'the values should be equal');
|
||||
});
|
||||
|
||||
}, 'ReadableStream teeing: values should be equal across each branch');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = { name: 'boo!' };
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
},
|
||||
pull() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
const branches = rs.tee();
|
||||
const reader1 = branches[0].getReader();
|
||||
const reader2 = branches[1].getReader();
|
||||
|
||||
reader1.label = 'reader1';
|
||||
reader2.label = 'reader2';
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, theError, reader1.closed),
|
||||
promise_rejects(t, theError, reader2.closed),
|
||||
reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: 'a', done: false }, 'should be able to read the first chunk in branch1');
|
||||
}),
|
||||
reader1.read().then(r => {
|
||||
assert_object_equals(r, { value: 'b', done: false }, 'should be able to read the second chunk in branch1');
|
||||
|
||||
return promise_rejects(t, theError, reader2.read());
|
||||
})
|
||||
.then(() => promise_rejects(t, theError, reader1.read()))
|
||||
]);
|
||||
|
||||
}, 'ReadableStream teeing: errors in the source should propagate to both branches');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const branches = rs.tee();
|
||||
const branch1 = branches[0];
|
||||
const branch2 = branches[1];
|
||||
branch1.cancel();
|
||||
|
||||
return Promise.all([
|
||||
readableStreamToArray(branch1).then(chunks => {
|
||||
assert_array_equals(chunks, [], 'branch1 should have no chunks');
|
||||
}),
|
||||
readableStreamToArray(branch2).then(chunks => {
|
||||
assert_array_equals(chunks, ['a', 'b'], 'branch2 should have two chunks');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'ReadableStream teeing: canceling branch1 should not impact branch2');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const branches = rs.tee();
|
||||
const branch1 = branches[0];
|
||||
const branch2 = branches[1];
|
||||
branch2.cancel();
|
||||
|
||||
return Promise.all([
|
||||
readableStreamToArray(branch1).then(chunks => {
|
||||
assert_array_equals(chunks, ['a', 'b'], 'branch1 should have two chunks');
|
||||
}),
|
||||
readableStreamToArray(branch2).then(chunks => {
|
||||
assert_array_equals(chunks, [], 'branch2 should have no chunks');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'ReadableStream teeing: canceling branch2 should not impact branch2');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reason1 = new Error('We\'re wanted men.');
|
||||
const reason2 = new Error('I have the death sentence on twelve systems.');
|
||||
|
||||
let resolve;
|
||||
const promise = new Promise(r => resolve = r);
|
||||
const rs = new ReadableStream({
|
||||
cancel(reason) {
|
||||
assert_array_equals(reason, [reason1, reason2],
|
||||
'the cancel reason should be an array containing those from the branches');
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const branch = rs.tee();
|
||||
const branch1 = branch[0];
|
||||
const branch2 = branch[1];
|
||||
branch1.cancel(reason1);
|
||||
branch2.cancel(reason2);
|
||||
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStream teeing: canceling both branches should aggregate the cancel reasons into an array');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const theError = { name: 'I\'ll be careful.' };
|
||||
const rs = new ReadableStream({
|
||||
cancel() {
|
||||
throw theError;
|
||||
}
|
||||
});
|
||||
|
||||
const branch = rs.tee();
|
||||
const branch1 = branch[0];
|
||||
const branch2 = branch[1];
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, theError, branch1.cancel()),
|
||||
promise_rejects(t, theError, branch2.cancel())
|
||||
]);
|
||||
|
||||
}, 'ReadableStream teeing: failing to cancel the original stream should cause cancel() to reject on branches');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const branches = rs.tee();
|
||||
const reader1 = branches[0].getReader();
|
||||
const reader2 = branches[1].getReader();
|
||||
|
||||
const promise = Promise.all([reader1.closed, reader2.closed]);
|
||||
|
||||
controller.close();
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStream teeing: closing the original should immediately close the branches');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
let controller;
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
|
||||
const branches = rs.tee();
|
||||
const reader1 = branches[0].getReader();
|
||||
const reader2 = branches[1].getReader();
|
||||
|
||||
const theError = { name: 'boo!' };
|
||||
const promise = Promise.all([
|
||||
promise_rejects(t, theError, reader1.closed),
|
||||
promise_rejects(t, theError, reader2.closed)
|
||||
]);
|
||||
|
||||
controller.error(theError);
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStream teeing: erroring the original should immediately error the branches');
|
||||
|
||||
done();
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-workers/resources/test-helpers.js"></script>
|
||||
<script src="../resources/test-initializer.js"></script>
|
||||
|
||||
<script src="../resources/test-utils.js"></script>
|
||||
<script src="../resources/rs-test-templates.js"></script>
|
||||
<script src="templated.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
worker_test('templated.js');
|
||||
</script>
|
|
@ -0,0 +1,148 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
self.importScripts('../resources/test-utils.js');
|
||||
self.importScripts('../resources/rs-test-templates.js');
|
||||
}
|
||||
|
||||
// Run the readable stream test templates against readable streams created directly using the constructor
|
||||
|
||||
const theError = { name: 'boo!' };
|
||||
const chunks = ['a', 'b'];
|
||||
|
||||
templatedRSEmpty('ReadableStream (empty)', () => {
|
||||
return new ReadableStream();
|
||||
});
|
||||
|
||||
templatedRSEmptyReader('ReadableStream (empty) reader', () => {
|
||||
return streamAndDefaultReader(new ReadableStream());
|
||||
});
|
||||
|
||||
templatedRSClosed('ReadableStream (closed via call in start)', () => {
|
||||
return new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
templatedRSClosedReader('ReadableStream reader (closed before getting reader)', () => {
|
||||
let controller;
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
controller.close();
|
||||
const result = streamAndDefaultReader(stream);
|
||||
return result;
|
||||
});
|
||||
|
||||
templatedRSClosedReader('ReadableStream reader (closed after getting reader)', () => {
|
||||
let controller;
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
const result = streamAndDefaultReader(stream);
|
||||
controller.close();
|
||||
return result;
|
||||
});
|
||||
|
||||
templatedRSClosed('ReadableStream (closed via cancel)', () => {
|
||||
const stream = new ReadableStream();
|
||||
stream.cancel();
|
||||
return stream;
|
||||
});
|
||||
|
||||
templatedRSClosedReader('ReadableStream reader (closed via cancel after getting reader)', () => {
|
||||
const stream = new ReadableStream();
|
||||
const result = streamAndDefaultReader(stream);
|
||||
result.reader.cancel();
|
||||
return result;
|
||||
});
|
||||
|
||||
templatedRSErrored('ReadableStream (errored via call in start)', () => {
|
||||
return new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
}
|
||||
});
|
||||
}, theError);
|
||||
|
||||
templatedRSErroredSyncOnly('ReadableStream (errored via call in start)', () => {
|
||||
return new ReadableStream({
|
||||
start(c) {
|
||||
c.error(theError);
|
||||
}
|
||||
});
|
||||
}, theError);
|
||||
|
||||
templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', () => {
|
||||
return new ReadableStream({
|
||||
start() {
|
||||
return Promise.reject(theError);
|
||||
}
|
||||
});
|
||||
}, theError);
|
||||
|
||||
templatedRSErroredReader('ReadableStream (errored via returning a rejected promise in start) reader', () => {
|
||||
return streamAndDefaultReader(new ReadableStream({
|
||||
start() {
|
||||
return Promise.reject(theError);
|
||||
}
|
||||
}));
|
||||
}, theError);
|
||||
|
||||
templatedRSErroredReader('ReadableStream reader (errored before getting reader)', () => {
|
||||
let controller;
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
controller.error(theError);
|
||||
return streamAndDefaultReader(stream);
|
||||
}, theError);
|
||||
|
||||
templatedRSErroredReader('ReadableStream reader (errored after getting reader)', () => {
|
||||
let controller;
|
||||
const result = streamAndDefaultReader(new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
}));
|
||||
controller.error(theError);
|
||||
return result;
|
||||
}, theError);
|
||||
|
||||
templatedRSTwoChunksOpenReader('ReadableStream (two chunks enqueued, still open) reader', () => {
|
||||
return streamAndDefaultReader(new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(chunks[0]);
|
||||
c.enqueue(chunks[1]);
|
||||
}
|
||||
}));
|
||||
}, chunks);
|
||||
|
||||
templatedRSTwoChunksClosedReader('ReadableStream (two chunks enqueued, then closed) reader', () => {
|
||||
let doClose;
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(chunks[0]);
|
||||
c.enqueue(chunks[1]);
|
||||
doClose = c.close.bind(c);
|
||||
}
|
||||
});
|
||||
const result = streamAndDefaultReader(stream);
|
||||
doClose();
|
||||
return result;
|
||||
}, chunks);
|
||||
|
||||
function streamAndDefaultReader(stream) {
|
||||
return { stream, reader: stream.getReader() };
|
||||
}
|
||||
|
||||
done();
|
|
@ -0,0 +1,624 @@
|
|||
'use strict';
|
||||
|
||||
// These tests can be run against any readable stream produced by the web platform that meets the given descriptions.
|
||||
// For readable stream tests, the factory should return the stream. For reader tests, the factory should return a
|
||||
// { stream, reader } object. (You can use this to vary the time at which you acquire a reader.)
|
||||
|
||||
self.templatedRSEmpty = (label, factory) => {
|
||||
test(() => {}, 'Running templatedRSEmpty with ' + label);
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
|
||||
assert_equals(typeof rs.locked, 'boolean', 'has a boolean locked getter');
|
||||
assert_equals(typeof rs.cancel, 'function', 'has a cancel method');
|
||||
assert_equals(typeof rs.getReader, 'function', 'has a getReader method');
|
||||
assert_equals(typeof rs.pipeThrough, 'function', 'has a pipeThrough method');
|
||||
assert_equals(typeof rs.pipeTo, 'function', 'has a pipeTo method');
|
||||
assert_equals(typeof rs.tee, 'function', 'has a tee method');
|
||||
|
||||
}, 'instances have the correct methods and properties');
|
||||
};
|
||||
|
||||
self.templatedRSClosed = (label, factory) => {
|
||||
test(() => {}, 'Running templatedRSClosed with ' + label);
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = factory();
|
||||
const cancelPromise1 = rs.cancel();
|
||||
const cancelPromise2 = rs.cancel();
|
||||
|
||||
assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
|
||||
|
||||
return Promise.all([
|
||||
cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() call should fulfill with undefined')),
|
||||
cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() call should fulfill with undefined'))
|
||||
]);
|
||||
|
||||
}, 'cancel() should return a distinct fulfilled promise each time');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
assert_false(rs.locked, 'locked getter should return false');
|
||||
|
||||
}, 'locked should be false');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
rs.getReader(); // getReader() should not throw.
|
||||
|
||||
}, 'getReader() should be OK');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
|
||||
const reader = rs.getReader();
|
||||
reader.releaseLock();
|
||||
|
||||
const reader2 = rs.getReader(); // Getting a second reader should not throw.
|
||||
reader2.releaseLock();
|
||||
|
||||
rs.getReader(); // Getting a third reader should not throw.
|
||||
|
||||
}, 'should be able to acquire multiple readers if they are released in succession');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
|
||||
rs.getReader();
|
||||
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getting a second reader should throw');
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getting a third reader should throw');
|
||||
|
||||
}, 'should not be able to acquire a second reader if we don\'t release the first one');
|
||||
};
|
||||
|
||||
self.templatedRSErrored = (label, factory, error) => {
|
||||
test(() => {}, 'Running templatedRSErrored with ' + label);
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = factory();
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error, reader.closed),
|
||||
promise_rejects(t, error, reader.read())
|
||||
]);
|
||||
|
||||
}, 'getReader() should return a reader that acts errored');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = factory();
|
||||
const reader = rs.getReader();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error, reader.read()),
|
||||
promise_rejects(t, error, reader.read()),
|
||||
promise_rejects(t, error, reader.closed)
|
||||
]);
|
||||
|
||||
}, 'read() twice should give the error each time');
|
||||
|
||||
test(() => {
|
||||
const rs = factory();
|
||||
|
||||
assert_false(rs.locked, 'locked getter should return false');
|
||||
}, 'locked should be false');
|
||||
};
|
||||
|
||||
self.templatedRSErroredSyncOnly = (label, factory, error) => {
|
||||
test(() => {}, 'Running templatedRSErroredSyncOnly with ' + label);
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = factory();
|
||||
rs.getReader().releaseLock();
|
||||
const reader = rs.getReader(); // Calling getReader() twice does not throw (the stream is not locked).
|
||||
|
||||
return promise_rejects(t, error, reader.closed);
|
||||
|
||||
}, 'should be able to obtain a second reader, with the correct closed promise');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = factory();
|
||||
rs.getReader();
|
||||
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getting a second reader should throw a TypeError');
|
||||
assert_throws(new TypeError(), () => rs.getReader(), 'getting a third reader should throw a TypeError');
|
||||
|
||||
}, 'should not be able to obtain additional readers if we don\'t release the first lock');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = factory();
|
||||
const cancelPromise1 = rs.cancel();
|
||||
const cancelPromise2 = rs.cancel();
|
||||
|
||||
assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error, cancelPromise1),
|
||||
promise_rejects(t, error, cancelPromise2)
|
||||
]);
|
||||
|
||||
}, 'cancel() should return a distinct rejected promise each time');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = factory();
|
||||
const reader = rs.getReader();
|
||||
const cancelPromise1 = reader.cancel();
|
||||
const cancelPromise2 = reader.cancel();
|
||||
|
||||
assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error, cancelPromise1),
|
||||
promise_rejects(t, error, cancelPromise2)
|
||||
]);
|
||||
|
||||
}, 'reader cancel() should return a distinct rejected promise each time');
|
||||
};
|
||||
|
||||
self.templatedRSEmptyReader = (label, factory) => {
|
||||
test(() => {}, 'Running templatedRSEmptyReader with ' + label);
|
||||
|
||||
test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
assert_true('closed' in reader, 'has a closed property');
|
||||
assert_equals(typeof reader.closed.then, 'function', 'closed property is thenable');
|
||||
|
||||
assert_equals(typeof reader.cancel, 'function', 'has a cancel method');
|
||||
assert_equals(typeof reader.read, 'function', 'has a read method');
|
||||
assert_equals(typeof reader.releaseLock, 'function', 'has a releaseLock method');
|
||||
|
||||
}, 'instances have the correct methods and properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const stream = factory().stream;
|
||||
|
||||
assert_true(stream.locked, 'locked getter should return true');
|
||||
|
||||
}, 'locked should be true');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
reader.read().then(
|
||||
t.unreached_func('read() should not fulfill'),
|
||||
t.unreached_func('read() should not reject')
|
||||
);
|
||||
|
||||
return delay(500);
|
||||
|
||||
}, 'read() should never settle');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
reader.read().then(
|
||||
t.unreached_func('read() should not fulfill'),
|
||||
t.unreached_func('read() should not reject')
|
||||
);
|
||||
|
||||
reader.read().then(
|
||||
t.unreached_func('read() should not fulfill'),
|
||||
t.unreached_func('read() should not reject')
|
||||
);
|
||||
|
||||
return delay(500);
|
||||
|
||||
}, 'two read()s should both never settle');
|
||||
|
||||
test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct');
|
||||
|
||||
}, 'read() should return distinct promises each time');
|
||||
|
||||
test(() => {
|
||||
|
||||
const stream = factory().stream;
|
||||
assert_throws(new TypeError(), () => stream.getReader(), 'stream.getReader() should throw a TypeError');
|
||||
|
||||
}, 'getReader() again on the stream should fail');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const streamAndReader = factory();
|
||||
const stream = streamAndReader.stream;
|
||||
const reader = streamAndReader.reader;
|
||||
|
||||
reader.read().then(
|
||||
t.unreached_func('first read() should not fulfill'),
|
||||
t.unreached_func('first read() should not reject')
|
||||
);
|
||||
|
||||
reader.read().then(
|
||||
t.unreached_func('second read() should not fulfill'),
|
||||
t.unreached_func('second read() should not reject')
|
||||
);
|
||||
|
||||
reader.closed.then(
|
||||
t.unreached_func('closed should not fulfill'),
|
||||
t.unreached_func('closed should not reject')
|
||||
);
|
||||
|
||||
assert_throws(new TypeError(), () => reader.releaseLock(), 'releaseLock should throw a TypeError');
|
||||
|
||||
assert_true(stream.locked, 'the stream should still be locked');
|
||||
|
||||
return delay(500);
|
||||
|
||||
}, 'releasing the lock with pending read requests should throw but the read requests should stay pending');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
reader.releaseLock();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), reader.read()),
|
||||
promise_rejects(t, new TypeError(), reader.read())
|
||||
]);
|
||||
|
||||
}, 'releasing the lock should cause further read() calls to reject with a TypeError');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
const closedBefore = reader.closed;
|
||||
reader.releaseLock();
|
||||
const closedAfter = reader.closed;
|
||||
|
||||
assert_equals(closedBefore, closedAfter, 'the closed promise should not change identity');
|
||||
|
||||
return promise_rejects(t, new TypeError(), closedBefore);
|
||||
|
||||
}, 'releasing the lock should cause closed calls to reject with a TypeError');
|
||||
|
||||
test(() => {
|
||||
|
||||
const streamAndReader = factory();
|
||||
const stream = streamAndReader.stream;
|
||||
const reader = streamAndReader.reader;
|
||||
|
||||
reader.releaseLock();
|
||||
assert_false(stream.locked, 'locked getter should return false');
|
||||
|
||||
}, 'releasing the lock should cause locked to become false');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
reader.cancel();
|
||||
|
||||
return reader.read().then(r => {
|
||||
assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result');
|
||||
});
|
||||
|
||||
}, 'canceling via the reader should cause the reader to act closed');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const stream = factory().stream;
|
||||
return promise_rejects(t, new TypeError(), stream.cancel());
|
||||
|
||||
}, 'canceling via the stream should fail');
|
||||
};
|
||||
|
||||
self.templatedRSClosedReader = (label, factory) => {
|
||||
test(() => {}, 'Running templatedRSClosedReader with ' + label);
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return reader.read().then(v => {
|
||||
assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
|
||||
});
|
||||
|
||||
}, 'read() should fulfill with { value: undefined, done: true }');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(v => {
|
||||
assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
|
||||
}),
|
||||
reader.read().then(v => {
|
||||
assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'read() multiple times should fulfill with { value: undefined, done: true }');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return reader.read().then(() => reader.read()).then(v => {
|
||||
assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
|
||||
});
|
||||
|
||||
}, 'read() should work when used within another read() fulfill callback');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return reader.closed.then(v => assert_equals(v, undefined, 'reader closed should fulfill with undefined'));
|
||||
|
||||
}, 'closed should fulfill with undefined');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
const closedBefore = reader.closed;
|
||||
reader.releaseLock();
|
||||
const closedAfter = reader.closed;
|
||||
|
||||
assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
|
||||
|
||||
return Promise.all([
|
||||
closedBefore.then(v => assert_equals(v, undefined, 'reader.closed acquired before release should fulfill')),
|
||||
promise_rejects(t, new TypeError(), closedAfter)
|
||||
]);
|
||||
|
||||
}, 'releasing the lock should cause closed to reject and change identity');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
const cancelPromise1 = reader.cancel();
|
||||
const cancelPromise2 = reader.cancel();
|
||||
const closedReaderPromise = reader.closed;
|
||||
|
||||
assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
|
||||
assert_not_equals(cancelPromise1, closedReaderPromise, 'cancel() promise 1 should be distinct from reader.closed');
|
||||
assert_not_equals(cancelPromise2, closedReaderPromise, 'cancel() promise 2 should be distinct from reader.closed');
|
||||
|
||||
return Promise.all([
|
||||
cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() should fulfill with undefined')),
|
||||
cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() should fulfill with undefined'))
|
||||
]);
|
||||
|
||||
}, 'cancel() should return a distinct fulfilled promise each time');
|
||||
};
|
||||
|
||||
self.templatedRSErroredReader = (label, factory, error) => {
|
||||
test(() => {}, 'Running templatedRSErroredReader with ' + label);
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
return promise_rejects(t, error, reader.closed);
|
||||
|
||||
}, 'closed should reject with the error');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
const closedBefore = reader.closed;
|
||||
|
||||
return promise_rejects(t, error, closedBefore).then(() => {
|
||||
reader.releaseLock();
|
||||
|
||||
const closedAfter = reader.closed;
|
||||
assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
|
||||
|
||||
return promise_rejects(t, new TypeError(), closedAfter);
|
||||
});
|
||||
|
||||
}, 'releasing the lock should cause closed to reject and change identity');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
return promise_rejects(t, error, reader.read());
|
||||
|
||||
}, 'read() should reject with the error');
|
||||
};
|
||||
|
||||
self.templatedRSTwoChunksOpenReader = (label, factory, chunks) => {
|
||||
test(() => {}, 'Running templatedRSTwoChunksOpenReader with ' + label);
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
|
||||
}),
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'calling read() twice without waiting will eventually give both chunks (sequential)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
|
||||
|
||||
return reader.read().then(r2 => {
|
||||
assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
|
||||
});
|
||||
});
|
||||
|
||||
}, 'calling read() twice without waiting will eventually give both chunks (nested)');
|
||||
|
||||
test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct');
|
||||
|
||||
}, 'read() should return distinct promises each time');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
const promise1 = reader.closed.then(v => {
|
||||
assert_equals(v, undefined, 'reader closed should fulfill with undefined');
|
||||
});
|
||||
|
||||
const promise2 = reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[0], done: false },
|
||||
'promise returned before cancellation should fulfill with a chunk');
|
||||
});
|
||||
|
||||
reader.cancel();
|
||||
|
||||
const promise3 = reader.read().then(r => {
|
||||
assert_object_equals(r, { value: undefined, done: true },
|
||||
'promise returned after cancellation should fulfill with an end-of-stream signal');
|
||||
});
|
||||
|
||||
return Promise.all([promise1, promise2, promise3]);
|
||||
|
||||
}, 'cancel() after a read() should still give that single read result');
|
||||
};
|
||||
|
||||
self.templatedRSTwoChunksClosedReader = function (label, factory, chunks) {
|
||||
test(() => {}, 'Running templatedRSTwoChunksClosedReader with ' + label);
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return Promise.all([
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
|
||||
}),
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
|
||||
}),
|
||||
reader.read().then(r => {
|
||||
assert_object_equals(r, { value: undefined, done: true }, 'third result should be correct');
|
||||
})
|
||||
]);
|
||||
|
||||
}, 'third read(), without waiting, should give { value: undefined, done: true } (sequential)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
return reader.read().then(r => {
|
||||
assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
|
||||
|
||||
return reader.read().then(r2 => {
|
||||
assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
|
||||
|
||||
return reader.read().then(r3 => {
|
||||
assert_object_equals(r3, { value: undefined, done: true }, 'third result should be correct');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}, 'third read(), without waiting, should give { value: undefined, done: true } (nested)');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const streamAndReader = factory();
|
||||
const stream = streamAndReader.stream;
|
||||
const reader = streamAndReader.reader;
|
||||
|
||||
assert_true(stream.locked, 'stream should start locked');
|
||||
|
||||
const promise = reader.closed.then(v => {
|
||||
assert_equals(v, undefined, 'reader closed should fulfill with undefined');
|
||||
assert_true(stream.locked, 'stream should remain locked');
|
||||
});
|
||||
|
||||
reader.read();
|
||||
reader.read();
|
||||
|
||||
return promise;
|
||||
|
||||
}, 'draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const streamAndReader = factory();
|
||||
const stream = streamAndReader.stream;
|
||||
const reader = streamAndReader.reader;
|
||||
|
||||
const promise = reader.closed.then(() => {
|
||||
assert_true(stream.locked, 'the stream should start locked');
|
||||
reader.releaseLock(); // Releasing the lock after reader closed should not throw.
|
||||
assert_false(stream.locked, 'the stream should end unlocked');
|
||||
});
|
||||
|
||||
reader.read();
|
||||
reader.read();
|
||||
|
||||
return promise;
|
||||
|
||||
}, 'releasing the lock after the stream is closed should cause locked to become false');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const reader = factory().reader;
|
||||
|
||||
reader.releaseLock();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), reader.read()),
|
||||
promise_rejects(t, new TypeError(), reader.read()),
|
||||
promise_rejects(t, new TypeError(), reader.read())
|
||||
]);
|
||||
|
||||
}, 'releasing the lock should cause further read() calls to reject with a TypeError');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const streamAndReader = factory();
|
||||
const stream = streamAndReader.stream;
|
||||
const reader = streamAndReader.reader;
|
||||
|
||||
const readerClosed = reader.closed;
|
||||
|
||||
assert_equals(reader.closed, readerClosed, 'accessing reader.closed twice in succession gives the same value');
|
||||
|
||||
const promise = reader.read().then(() => {
|
||||
assert_equals(reader.closed, readerClosed, 'reader.closed is the same after read() fulfills');
|
||||
|
||||
reader.releaseLock();
|
||||
|
||||
assert_equals(reader.closed, readerClosed, 'reader.closed is the same after releasing the lock');
|
||||
|
||||
const newReader = stream.getReader();
|
||||
return newReader.read();
|
||||
});
|
||||
|
||||
assert_equals(reader.closed, readerClosed, 'reader.closed is the same after calling read()');
|
||||
|
||||
return promise;
|
||||
|
||||
}, 'reader\'s closed property always returns the same promise');
|
||||
};
|
185
tests/wpt/web-platform-tests/streams/resources/rs-utils.js
Normal file
185
tests/wpt/web-platform-tests/streams/resources/rs-utils.js
Normal file
|
@ -0,0 +1,185 @@
|
|||
'use strict';
|
||||
(function () {
|
||||
|
||||
class RandomPushSource {
|
||||
constructor(toPush) {
|
||||
this.pushed = 0;
|
||||
this.toPush = toPush;
|
||||
this.started = false;
|
||||
this.paused = false;
|
||||
this.closed = false;
|
||||
|
||||
this._intervalHandle = null;
|
||||
}
|
||||
|
||||
readStart() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.started) {
|
||||
this._intervalHandle = setInterval(writeChunk, 2);
|
||||
this.started = true;
|
||||
}
|
||||
|
||||
if (this.paused) {
|
||||
this._intervalHandle = setInterval(writeChunk, 2);
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
const source = this;
|
||||
function writeChunk() {
|
||||
if (source.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
source.pushed++;
|
||||
|
||||
if (source.toPush > 0 && source.pushed > source.toPush) {
|
||||
if (source._intervalHandle) {
|
||||
clearInterval(source._intervalHandle);
|
||||
source._intervalHandle = undefined;
|
||||
}
|
||||
source.closed = true;
|
||||
source.onend();
|
||||
} else {
|
||||
source.ondata(randomChunk(128));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readStop() {
|
||||
if (this.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.started) {
|
||||
this.paused = true;
|
||||
clearInterval(this._intervalHandle);
|
||||
this._intervalHandle = undefined;
|
||||
} else {
|
||||
throw new Error('Can\'t pause reading an unstarted source.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function randomChunk(size) {
|
||||
let chunk = '';
|
||||
|
||||
for (let i = 0; i < size; ++i) {
|
||||
// Add a random character from the basic printable ASCII set.
|
||||
chunk += String.fromCharCode(Math.round(Math.random() * 84) + 32);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
function readableStreamToArray(readable, reader) {
|
||||
if (reader === undefined) {
|
||||
reader = readable.getReader();
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
|
||||
return pump();
|
||||
|
||||
function pump() {
|
||||
return reader.read().then(result => {
|
||||
if (result.done) {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
chunks.push(result.value);
|
||||
return pump();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SequentialPullSource {
|
||||
constructor(limit, options) {
|
||||
const async = options && options.async;
|
||||
|
||||
this.current = 0;
|
||||
this.limit = limit;
|
||||
this.opened = false;
|
||||
this.closed = false;
|
||||
|
||||
this._exec = f => f();
|
||||
if (async) {
|
||||
this._exec = f => setTimeout(f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
open(cb) {
|
||||
this._exec(() => {
|
||||
this.opened = true;
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
read(cb) {
|
||||
this._exec(() => {
|
||||
if (++this.current <= this.limit) {
|
||||
cb(null, false, this.current);
|
||||
} else {
|
||||
cb(null, true, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
close(cb) {
|
||||
this._exec(() => {
|
||||
this.closed = true;
|
||||
cb();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function sequentialReadableStream(limit, options) {
|
||||
const sequentialSource = new SequentialPullSource(limit, options);
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start() {
|
||||
return new Promise((resolve, reject) => {
|
||||
sequentialSource.open(err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
pull(c) {
|
||||
return new Promise((resolve, reject) => {
|
||||
sequentialSource.read((err, done, chunk) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (done) {
|
||||
sequentialSource.close(err2 => {
|
||||
if (err2) {
|
||||
reject(err2);
|
||||
}
|
||||
c.close();
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
c.enqueue(chunk);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
stream.source = sequentialSource;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
self.RandomPushSource = RandomPushSource;
|
||||
self.readableStreamToArray = readableStreamToArray;
|
||||
self.sequentialReadableStream = sequentialReadableStream;
|
||||
|
||||
}());
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
function worker_test(file) {
|
||||
fetch_tests_from_worker(new Worker(file));
|
||||
if (typeof SharedWorker === 'function') {
|
||||
fetch_tests_from_worker(new SharedWorker(file));
|
||||
} else {
|
||||
test(() => {
|
||||
assert_unreached('SharedWorker is unavailable');
|
||||
}, 'Load ' + file + ' with SharedWorker');
|
||||
}
|
||||
service_worker_test(file);
|
||||
}
|
||||
|
43
tests/wpt/web-platform-tests/streams/resources/test-utils.js
Normal file
43
tests/wpt/web-platform-tests/streams/resources/test-utils.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
self.getterRejects = (t, obj, getterName, target) => {
|
||||
const getter = Object.getOwnPropertyDescriptor(obj, getterName).get;
|
||||
|
||||
return promise_rejects(t, new TypeError(), getter.call(target));
|
||||
};
|
||||
|
||||
self.methodRejects = (t, obj, methodName, target) => {
|
||||
const method = obj[methodName];
|
||||
|
||||
return promise_rejects(t, new TypeError(), method.call(target));
|
||||
};
|
||||
|
||||
self.getterThrows = (obj, getterName, target) => {
|
||||
const getter = Object.getOwnPropertyDescriptor(obj, getterName).get;
|
||||
|
||||
assert_throws(new TypeError(), () => getter.call(target), getterName + ' should throw a TypeError');
|
||||
};
|
||||
|
||||
self.methodThrows = (obj, methodName, target, args) => {
|
||||
const method = obj[methodName];
|
||||
|
||||
assert_throws(new TypeError(), () => method.apply(target, args), methodName + ' should throw a TypeError');
|
||||
};
|
||||
|
||||
self.garbageCollect = () => {
|
||||
if (self.gc) {
|
||||
// Use --expose_gc for V8 (and Node.js)
|
||||
// Exposed in SpiderMonkey shell as well
|
||||
self.gc();
|
||||
} else if (self.GCController) {
|
||||
// Present in some WebKit development environments
|
||||
GCController.collect();
|
||||
} else {
|
||||
/* eslint-disable no-console */
|
||||
console.warn('Tests are running without the ability to do manual garbage collection. They will still work, but ' +
|
||||
'coverage will be suboptimal.');
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
};
|
||||
|
||||
self.delay = ms => new Promise(resolve => step_timeout(resolve, ms));
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
@ -12,6 +13,19 @@ from manifest.sourcefile import SourceFile
|
|||
here = os.path.abspath(os.path.split(__file__)[0])
|
||||
repo_root = localpaths.repo_root
|
||||
|
||||
ERROR_MSG = """You must fix all errors; for details on how to fix them, see
|
||||
https://github.com/w3c/web-platform-tests/blob/master/docs/lint-tool.md
|
||||
|
||||
However, instead of fixing a particular error, it's sometimes
|
||||
OK to add a line to the lint.whitelist file in the root of the
|
||||
web-platform-tests directory to make the lint tool ignore it.
|
||||
|
||||
For example, to make the lint tool ignore all '%s'
|
||||
errors in the %s file,
|
||||
you could add the following line to the lint.whitelist file."
|
||||
|
||||
%s:%s"""
|
||||
|
||||
def git(command, *args):
|
||||
args = list(args)
|
||||
|
||||
|
@ -25,7 +39,7 @@ def git(command, *args):
|
|||
raise
|
||||
|
||||
|
||||
def iter_files():
|
||||
def all_git_paths():
|
||||
for item in git("ls-tree", "-r", "--name-only", "HEAD").split("\n"):
|
||||
yield item
|
||||
|
||||
|
@ -236,7 +250,18 @@ def output_error_count(error_count):
|
|||
else:
|
||||
print "There were %d errors (%s)" % (count, by_type)
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("paths", nargs="*",
|
||||
help="List of paths to lint")
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
paths = args.paths if args.paths else all_git_paths()
|
||||
return lint(paths)
|
||||
|
||||
def lint(paths):
|
||||
error_count = defaultdict(int)
|
||||
last = None
|
||||
|
||||
|
@ -250,9 +275,9 @@ def main():
|
|||
error_count[error_type] += 1
|
||||
return last
|
||||
|
||||
for path in iter_files():
|
||||
for path in paths:
|
||||
abs_path = os.path.join(repo_root, path)
|
||||
if not os.path.exists(path):
|
||||
if not os.path.exists(abs_path):
|
||||
continue
|
||||
for path_fn in path_lints:
|
||||
last = run_lint(path, path_fn, last)
|
||||
|
@ -265,19 +290,7 @@ def main():
|
|||
|
||||
output_error_count(error_count)
|
||||
if error_count:
|
||||
print
|
||||
print "You must fix all errors; for details on how to fix them, see"
|
||||
print "https://github.com/w3c/web-platform-tests/blob/master/docs/lint-tool.md"
|
||||
print
|
||||
print "However, instead of fixing a particular error, it's sometimes"
|
||||
print "OK to add a line to the lint.whitelist file in the root of the"
|
||||
print "web-platform-tests directory to make the lint tool ignore it."
|
||||
print
|
||||
print "For example, to make the lint tool ignore all '%s'" % last[0]
|
||||
print "errors in the %s file," % last[1]
|
||||
print "you could add the following line to the lint.whitelist file."
|
||||
print
|
||||
print "%s:%s" % (last[0], last[1])
|
||||
print ERROR_MSG % (last[0], last[1], last[0], last[1])
|
||||
return sum(error_count.itervalues())
|
||||
|
||||
path_lints = [check_path_length]
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Clicking on the body element itself fires a click event</title>
|
||||
<link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
|
||||
<link rel="help" href="https://w3c.github.io/uievents/#event-type-click">
|
||||
<meta name="flags" content="interact">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
html {
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
background-color: blue;
|
||||
margin: 0;
|
||||
}
|
||||
#guineapig {
|
||||
background-color: white;
|
||||
margin-bottom: 100px;/* Expose an area of the body element itself */
|
||||
}
|
||||
#other {
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="guineapig">Click on the blue area below. If a "PASS" result appears, the test passes; otherwise, it fails.</p>
|
||||
<p id="other"> </p><!-- Needed to prevent the margin from collapsing -->
|
||||
<script>
|
||||
setup({explicit_timeout: true});
|
||||
async_test(function(t) {
|
||||
document.body.addEventListener('click', function (event) {
|
||||
t.step(function () {
|
||||
assert_equals(event.target, document.body, 'target of click event must be the body element');
|
||||
assert_equals(event.eventPhase, Event.AT_TARGET, 'click event must propagate to the body element at the Target Phase');
|
||||
var passed = event.target === document.body && event.eventPhase === Event.AT_TARGET;
|
||||
document.body.style.backgroundColor = 'white';
|
||||
var guineapig = document.getElementById('guineapig');
|
||||
guineapig.style.marginBottom = '16px';
|
||||
if (passed) {
|
||||
guineapig.textContent = 'PASS';
|
||||
guineapig.style.backgroundColor = 'green';
|
||||
}
|
||||
t.done();
|
||||
});
|
||||
}, false);
|
||||
}, "Clicking on the body element itself should fire a click event targeted at the body element");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Clicking on the html element itself fires a click event</title>
|
||||
<link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
|
||||
<link rel="help" href="https://w3c.github.io/uievents/#event-type-click">
|
||||
<meta name="flags" content="interact">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
html {
|
||||
background-color: blue;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
background-color: red;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#guineapig {
|
||||
background-color: white;
|
||||
padding-top: 50px;/* Text is easier to see when it's not at the exact top of the viewport */
|
||||
margin-top: 0;/* Ensure there's no exposed html element above this */
|
||||
margin-bottom: 100px;/* Expose an area of the html element itself */
|
||||
}
|
||||
#other {
|
||||
background-color: white;
|
||||
height: 100vh;/* Push the rest of the html element outside of the viewport */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="guineapig">Click on the blue area below. If a "PASS" result appears, the test passes; otherwise, it fails.</p>
|
||||
<p id="other"> </p><!-- Needed to prevent the margin from collapsing -->
|
||||
<script>
|
||||
setup({explicit_timeout: true});
|
||||
async_test(function(t) {
|
||||
document.documentElement.addEventListener('click', function (event) {
|
||||
t.step(function () {
|
||||
assert_equals(event.target, document.documentElement, 'target of click event must be the html element');
|
||||
assert_equals(event.eventPhase, Event.AT_TARGET, 'click event must propagate to the html element at the Target Phase');
|
||||
var passed = event.target === document.documentElement && event.eventPhase === Event.AT_TARGET;
|
||||
document.documentElement.style.backgroundColor = 'white';
|
||||
document.getElementById('other').style.height = 'auto';
|
||||
var guineapig = document.getElementById('guineapig');
|
||||
guineapig.style.marginBottom = '16px';
|
||||
if (passed) {
|
||||
guineapig.textContent = 'PASS';
|
||||
guineapig.style.backgroundColor = 'green';
|
||||
}
|
||||
t.done();
|
||||
});
|
||||
}, false);
|
||||
}, "Clicking on the html element itself should fire a click event targeted at the html element");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Animation constructor tests</title>
|
||||
<link rel="help" href="http://w3c.github.io/web-animations/#dom-animation-animation">
|
||||
<link rel="author" title="Hiroyuki Ikezoe" href="mailto:hiikezoe@mozilla-japan.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<link rel="stylesheet" href="/resources/testharness.css">
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<div id="target"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var gTarget = document.getElementById("target");
|
||||
var gEffect = new KeyframeEffectReadOnly(gTarget, { opacity: [0, 1] });
|
||||
|
||||
var gTestArguments = [
|
||||
{
|
||||
effect: null,
|
||||
timeline: null,
|
||||
description: "with null effect and null timeline"
|
||||
},
|
||||
{
|
||||
effect: null,
|
||||
timeline: document.timeline,
|
||||
description: "with null effect and non-null timeline"
|
||||
},
|
||||
{
|
||||
effect: gEffect,
|
||||
timeline: null,
|
||||
description: "with non-null effect and null timeline"
|
||||
},
|
||||
{
|
||||
effect: gEffect,
|
||||
timeline: document.timeline,
|
||||
description: "with non-null effect and non-null timeline"
|
||||
},
|
||||
];
|
||||
|
||||
gTestArguments.forEach(function(args) {
|
||||
test(function(t) {
|
||||
var animation = new Animation(args.effect, args.timeline);
|
||||
|
||||
assert_not_equals(animation, null,
|
||||
"An animation sohuld be created");
|
||||
assert_equals(animation.effect, args.effect,
|
||||
"Animation returns the same effect passed to " +
|
||||
"the Constructor");
|
||||
assert_equals(animation.timeline, args.timeline,
|
||||
"Animation returns the same timeline passed to " +
|
||||
"the Constructor");
|
||||
assert_equals(animation.playState, "idle",
|
||||
"Animation.playState should be initially 'idle'");
|
||||
}, "Animation can be constructed " + args.description);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -450,17 +450,19 @@ gKeyframeSequenceTests.forEach(function(subtest) {
|
|||
});
|
||||
|
||||
|
||||
// KeyframeEffectOptions
|
||||
test(function(t) {
|
||||
var effect = new KeyframeEffectReadOnly(
|
||||
target, {left: ["10px", "20px"]});
|
||||
var effect = new KeyframeEffectReadOnly(target,
|
||||
{left: ["10px", "20px"]});
|
||||
|
||||
var ct = effect.getComputedTiming();
|
||||
assert_equals(ct.delay, 0, "default delay");
|
||||
assert_equals(ct.fill, "none", "default fill");
|
||||
assert_equals(ct.iterations, 1.0, "default iterations");
|
||||
assert_equals(ct.duration, 0, "default duration");
|
||||
assert_equals(ct.direction, "normal", "default direction");
|
||||
var timing = effect.timing;
|
||||
assert_equals(timing.delay, 0, "default delay");
|
||||
assert_equals(timing.endDelay, 0, "default endDelay");
|
||||
assert_equals(timing.fill, "auto", "default fill");
|
||||
assert_equals(timing.iterations, 1.0, "default iterations");
|
||||
assert_equals(timing.iterationStart, 0.0, "default iterationStart");
|
||||
assert_equals(timing.duration, "auto", "default duration");
|
||||
assert_equals(timing.direction, "normal", "default direction");
|
||||
assert_equals(timing.easing, "linear", "default easing");
|
||||
|
||||
assert_equals(effect.composite, "replace", "default composite");
|
||||
assert_equals(effect.iterationComposite, "replace",
|
||||
|
@ -474,9 +476,9 @@ var gKeyframeEffectOptionTests = [
|
|||
{ desc: "an empty KeyframeEffectOption",
|
||||
input: {},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "a normal KeyframeEffectOption",
|
||||
input: {delay: 1000,
|
||||
|
@ -485,142 +487,145 @@ var gKeyframeEffectOptionTests = [
|
|||
duration: "auto",
|
||||
direction: "alternate"},
|
||||
expected: {delay: 1000,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 5.5,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "alternate"} },
|
||||
{ desc: "a double value",
|
||||
input: 3000,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 3000,
|
||||
direction: "normal"} },
|
||||
{ desc: "+Infinity",
|
||||
input: Infinity,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "-Infinity",
|
||||
input: -Infinity,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: -Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "NaN",
|
||||
input: NaN,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: NaN,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative value",
|
||||
input: -1,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: -1,
|
||||
direction: "normal"} },
|
||||
{ desc: "an Infinity duration",
|
||||
input: {duration: Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative Infinity duration",
|
||||
input: {duration: -Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: -Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "a NaN duration",
|
||||
input: {duration: NaN},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: NaN,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative duration",
|
||||
input: {duration: -1},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: -1,
|
||||
direction: "normal"} },
|
||||
{ desc: "a string duration",
|
||||
input: {duration: "cabbage"},
|
||||
input: {duration: "merrychristmas"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: "merrychristmas",
|
||||
direction: "normal"} },
|
||||
{ desc: "an auto duration",
|
||||
input: {duration: "auto"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "an Infinity iterations",
|
||||
input: {iterations: Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: Infinity,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative Infinity iterations",
|
||||
input: {iterations: -Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
fill: "auto",
|
||||
iterations: -Infinity,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "a NaN iterations",
|
||||
input: {iterations: NaN},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
fill: "auto",
|
||||
iterations: NaN,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative iterations",
|
||||
input: {iterations: -1},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
fill: "auto",
|
||||
iterations: -1,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "an auto fill",
|
||||
input: {fill: "auto"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "normal"} },
|
||||
{ desc: "a forwards fill",
|
||||
input: {fill: "forwards"},
|
||||
expected: {delay: 0,
|
||||
fill: "forwards",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
duration: "auto",
|
||||
direction: "normal"} }
|
||||
];
|
||||
|
||||
gKeyframeEffectOptionTests.forEach(function(stest) {
|
||||
test(function(t) {
|
||||
var effect = new KeyframeEffectReadOnly(
|
||||
target, {left: ["10px", "20px"]}, stest.input);
|
||||
var effect = new KeyframeEffectReadOnly(target,
|
||||
{left: ["10px", "20px"]},
|
||||
stest.input);
|
||||
|
||||
var ct = effect.getComputedTiming();
|
||||
assert_equals(ct.delay, stest.expected.delay, "initial delay");
|
||||
assert_equals(ct.fill, stest.expected.fill, "initial fill");
|
||||
assert_equals(ct.iterations, stest.expected.iterations, "initial iterations");
|
||||
assert_equals(ct.duration, stest.expected.duration, "initial duration");
|
||||
assert_equals(ct.direction, stest.expected.direction, "initial direction");
|
||||
var timing = effect.timing;
|
||||
assert_equals(timing.delay, stest.expected.delay, "timing delay");
|
||||
assert_equals(timing.fill, stest.expected.fill, "timing fill");
|
||||
assert_equals(timing.iterations, stest.expected.iterations,
|
||||
"timing iterations");
|
||||
assert_equals(timing.duration, stest.expected.duration, "timing duration");
|
||||
assert_equals(timing.direction, stest.expected.direction,
|
||||
"timing direction");
|
||||
}, "a KeyframeEffectReadOnly constructed by " + stest.desc);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>KeyframeEffectReadOnly getComputedTiming() tests</title>
|
||||
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffectreadonly-getcomputedtiming">
|
||||
<link rel="author" title="Boris Chiou" href="mailto:boris.chiou@gmail.com">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<link rel="stylesheet" href="/resources/testharness.css">
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<div id="target"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var target = document.getElementById("target");
|
||||
|
||||
test(function(t) {
|
||||
var effect = new KeyframeEffectReadOnly(target,
|
||||
{left: ["10px", "20px"]});
|
||||
|
||||
var ct = effect.getComputedTiming();
|
||||
assert_equals(ct.delay, 0, "computed delay");
|
||||
assert_equals(ct.fill, "none", "computed fill");
|
||||
assert_equals(ct.iterations, 1.0, "computed iterations");
|
||||
assert_equals(ct.duration, 0, "computed duration");
|
||||
assert_equals(ct.direction, "normal", "computed direction");
|
||||
}, "values of getComputedTiming() when a KeyframeEffectReadOnly is " +
|
||||
"constructed without any KeyframeEffectOptions object");
|
||||
|
||||
var gGetComputedTimingTests = [
|
||||
{ desc: "an empty KeyframeEffectOption",
|
||||
input: {},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a normal KeyframeEffectOption",
|
||||
input: {delay: 1000,
|
||||
fill: "auto",
|
||||
iterations: 5.5,
|
||||
duration: "auto",
|
||||
direction: "alternate"},
|
||||
expected: {delay: 1000,
|
||||
fill: "none",
|
||||
iterations: 5.5,
|
||||
duration: 0,
|
||||
direction: "alternate"} },
|
||||
{ desc: "a double value",
|
||||
input: 3000,
|
||||
timing: {delay: 0,
|
||||
fill: "auto",
|
||||
iterations: 1,
|
||||
duration: 3000,
|
||||
direction: "normal"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 3000,
|
||||
direction: "normal"} },
|
||||
{ desc: "+Infinity",
|
||||
input: Infinity,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "-Infinity",
|
||||
input: -Infinity,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "NaN",
|
||||
input: NaN,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative value",
|
||||
input: -1,
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "an Infinity duration",
|
||||
input: {duration: Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: Infinity,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative Infinity duration",
|
||||
input: {duration: -Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a NaN duration",
|
||||
input: {duration: NaN},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative duration",
|
||||
input: {duration: -1},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a string duration",
|
||||
input: {duration: "merrychristmas"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "an auto duration",
|
||||
input: {duration: "auto"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "an Infinity iterations",
|
||||
input: {iterations: Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: Infinity,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative Infinity iterations",
|
||||
input: {iterations: -Infinity},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a NaN iterations",
|
||||
input: {iterations: NaN},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a negative iterations",
|
||||
input: {iterations: -1},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "an auto fill",
|
||||
input: {fill: "auto"},
|
||||
expected: {delay: 0,
|
||||
fill: "none",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} },
|
||||
{ desc: "a forwards fill",
|
||||
input: {fill: "forwards"},
|
||||
expected: {delay: 0,
|
||||
fill: "forwards",
|
||||
iterations: 1,
|
||||
duration: 0,
|
||||
direction: "normal"} }
|
||||
];
|
||||
|
||||
gGetComputedTimingTests.forEach(function(stest) {
|
||||
test(function(t) {
|
||||
var effect = new KeyframeEffectReadOnly(target,
|
||||
{left: ["10px", "20px"]},
|
||||
stest.input);
|
||||
|
||||
var ct = effect.getComputedTiming();
|
||||
assert_equals(ct.delay, stest.expected.delay, "computed delay");
|
||||
assert_equals(ct.fill, stest.expected.fill, "computed fill");
|
||||
assert_equals(ct.iterations, stest.expected.iterations,
|
||||
"computed iterations");
|
||||
assert_equals(ct.duration, stest.expected.duration, "computed duration");
|
||||
assert_equals(ct.direction, stest.expected.direction, "computed direction");
|
||||
}, "values of getComputedTiming() when a KeyframeEffectReadOnly is " +
|
||||
"constructed by " + stest.desc);
|
||||
});
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -17,7 +17,7 @@
|
|||
var isOpenCalled = false;
|
||||
|
||||
wsocket.addEventListener('open', testOpen.step_func(function (evt) {
|
||||
wsocket.send(".close");
|
||||
wsocket.send("Goodbye");
|
||||
isOpenCalled = true;
|
||||
testOpen.done();
|
||||
}), true);
|
||||
|
|
|
@ -11,11 +11,10 @@ async_test(function(t){
|
|||
ws.onopen = t.step_func(function(e) {
|
||||
ws.send('Goodbye');
|
||||
})
|
||||
ws.onclose = function(e) {
|
||||
ws.onclose = t.step_func(function(e) {
|
||||
assert_equals(e.wasClean, true, 'e.wasClean');
|
||||
ws.onclose = t.step_func(function() {assert_unreached()});
|
||||
|
||||
setTimeout(t.step_func(function() {t.done();}), 50)
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
async_test(function(t) {
|
||||
var ws = new WebSocket(SCHEME_DOMAIN_PORT + '/echo');
|
||||
ws.onmessage = ws.onerror = t.step_func(function() {assert_unreached()});
|
||||
ws.onopen = function(e) {
|
||||
ws.onopen = t.step_func(function(e) {
|
||||
ws.close();
|
||||
}
|
||||
ws.onclose = function(e) {
|
||||
});
|
||||
ws.onclose = t.step_func(function(e) {
|
||||
assert_equals(e.wasClean, true, 'e.wasClean');
|
||||
ws.onclose = t.step_func(function() {assert_unreached()});
|
||||
setTimeout(t.step_func(function() {t.done()}), 50);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
var i = 0;
|
||||
async_test(function(t) {
|
||||
var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/');
|
||||
ws.onclose = function(e) {
|
||||
ws.onclose = t.step_func(function(e) {
|
||||
i++;
|
||||
}
|
||||
});
|
||||
ws.close();
|
||||
ws.close();
|
||||
ws.close();
|
||||
|
|
|
@ -32,8 +32,8 @@ async_test(function(outer) {
|
|||
ws.send(stuffToSend[i]);
|
||||
}
|
||||
}
|
||||
ws.onopen = function(e) {
|
||||
ws.onopen = outer.step_func(function(e) {
|
||||
sendNext();
|
||||
}
|
||||
});
|
||||
}, "Constructor succeeds");
|
||||
</script>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<script>
|
||||
async_test(function(t) {
|
||||
var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/wrong_accept_key');
|
||||
ws.onclose = function(e) {
|
||||
t.done();
|
||||
}
|
||||
ws.onclose = t.step_func(function(e) {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue