mirror of
https://github.com/servo/servo.git
synced 2025-09-05 12:38:21 +01:00
Update web-platform-tests to revision 58eb04cecbbec2e18531ab440225e38944a9c444
This commit is contained in:
parent
25e8bf69e6
commit
665817d2a6
35333 changed files with 1818077 additions and 16036 deletions
|
@ -424,4 +424,38 @@ promise_test(() => {
|
|||
|
||||
}, 'Closing must be propagated forward: shutdown must not occur until the final write completes');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const rs = recordingReadableStream();
|
||||
|
||||
let resolveWritePromise;
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
return new Promise(resolve => {
|
||||
resolveWritePromise = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let pipeComplete = false;
|
||||
const pipePromise = rs.pipeTo(ws, { preventClose: true }).then(() => {
|
||||
pipeComplete = true;
|
||||
});
|
||||
|
||||
rs.controller.enqueue('a');
|
||||
rs.controller.close();
|
||||
|
||||
// Flush async events and verify that no shutdown occurs.
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'a'],
|
||||
'the chunk must have been written, but close must not have happened yet');
|
||||
assert_equals(pipeComplete, false, 'the pipe must not be complete');
|
||||
|
||||
resolveWritePromise();
|
||||
|
||||
return pipePromise;
|
||||
});
|
||||
|
||||
}, 'Closing must be propagated forward: shutdown must not occur until the final write completes; preventClose = true');
|
||||
|
||||
done();
|
||||
|
|
|
@ -124,6 +124,52 @@ promise_test(() => {
|
|||
}, 'Piping from an empty ReadableStream into a WritableStream that does not desire chunks, but then the readable ' +
|
||||
'stream becomes non-empty and the writable stream starts desiring chunks');
|
||||
|
||||
promise_test(() => {
|
||||
const unreadChunks = ['b', 'c', 'd'];
|
||||
|
||||
const rs = recordingReadableStream({
|
||||
pull(controller) {
|
||||
controller.enqueue(unreadChunks.shift());
|
||||
if (unreadChunks.length === 0) {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
}, new CountQueuingStrategy({ highWaterMark: 0 }));
|
||||
|
||||
let resolveWritePromise;
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
if (!resolveWritePromise) {
|
||||
// first write
|
||||
return new Promise(resolve => {
|
||||
resolveWritePromise = resolve;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, new CountQueuingStrategy({ highWaterMark: 3 }));
|
||||
|
||||
const writer = ws.getWriter();
|
||||
const firstWritePromise = writer.write('a');
|
||||
assert_equals(writer.desiredSize, 2, 'after writing the writer\'s desiredSize must be 2');
|
||||
writer.releaseLock();
|
||||
|
||||
// firstWritePromise won't settle until we call resolveWritePromise.
|
||||
|
||||
const pipePromise = rs.pipeTo(ws);
|
||||
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'a']);
|
||||
assert_equals(unreadChunks.length, 1, 'chunks should continue to be enqueued until the HWM is reached');
|
||||
}).then(() => resolveWritePromise())
|
||||
.then(() => Promise.all([firstWritePromise, pipePromise]))
|
||||
.then(() => {
|
||||
assert_array_equals(rs.events, ['pull', 'pull', 'pull']);
|
||||
assert_array_equals(ws.events, ['write', 'a', 'write', 'b','write', 'c','write', 'd', 'close']);
|
||||
});
|
||||
|
||||
}, 'Piping from a ReadableStream to a WritableStream that desires more chunks before finishing with previous ones');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
const desiredSizes = [];
|
||||
|
|
|
@ -155,7 +155,7 @@ promise_test(() => {
|
|||
}, 'Piping from a ReadableStream for which a chunk becomes asynchronously readable after the pipeTo');
|
||||
|
||||
for (const preventAbort of [true, false]) {
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
pull() {
|
||||
|
@ -171,7 +171,7 @@ for (const preventAbort of [true, false]) {
|
|||
}
|
||||
|
||||
for (const preventCancel of [true, false]) {
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
pull(controller) {
|
||||
|
|
|
@ -71,19 +71,48 @@ promise_test(t => {
|
|||
});
|
||||
const ws = recordingWritableStream();
|
||||
const writer = ws.getWriter();
|
||||
writer.close();
|
||||
const closePromise = writer.close();
|
||||
writer.releaseLock();
|
||||
|
||||
return promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the readable stream\'s error').then(() => {
|
||||
assert_array_equals(rs.events, []);
|
||||
assert_array_equals(ws.events, ['close']);
|
||||
assert_array_equals(ws.events, ['abort', error1]);
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, rs.getReader().closed, 'the readable stream must be errored with error1'),
|
||||
ws.getWriter().closed
|
||||
promise_rejects(t, new TypeError(), ws.getWriter().closed,
|
||||
'closed must reject with a TypeError indicating the writable stream was aborted'),
|
||||
promise_rejects(t, new TypeError(), closePromise,
|
||||
'close() must reject with a TypeError indicating the writable stream was aborted'),
|
||||
]);
|
||||
});
|
||||
|
||||
}, 'Piping from an errored readable stream to a closing writable stream');
|
||||
|
||||
promise_test(t => {
|
||||
const rs = recordingReadableStream({
|
||||
start(c) {
|
||||
c.error(error1);
|
||||
}
|
||||
});
|
||||
const ws = recordingWritableStream();
|
||||
const writer = ws.getWriter();
|
||||
const closePromise = writer.close();
|
||||
writer.releaseLock();
|
||||
|
||||
return flushAsyncEvents().then(() => {
|
||||
return promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the readable stream\'s error').then(() => {
|
||||
assert_array_equals(rs.events, []);
|
||||
assert_array_equals(ws.events, ['close']);
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, rs.getReader().closed, 'the readable stream must be errored with error1'),
|
||||
ws.getWriter().closed,
|
||||
closePromise
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
}, 'Piping from an errored readable stream to a closed writable stream');
|
||||
|
||||
promise_test(t => {
|
||||
|
|
|
@ -37,7 +37,7 @@ promise_test(() => {
|
|||
assert_array_equals(chunks, [1, 2, 3, 4, 5]), 'chunks should match');
|
||||
}, 'Piping through a duck-typed pass-through transform stream should work');
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
const transform = {
|
||||
writable: new WritableStream({
|
||||
start(c) {
|
||||
|
@ -77,7 +77,7 @@ test(() => {
|
|||
|
||||
test(() => {
|
||||
const dummy = {
|
||||
pipeTo(args) {
|
||||
pipeTo() {
|
||||
return { not: 'a promise' };
|
||||
}
|
||||
};
|
||||
|
@ -90,7 +90,7 @@ test(() => {
|
|||
|
||||
test(() => {
|
||||
const dummy = {
|
||||
pipeTo(args) {
|
||||
pipeTo() {
|
||||
return {
|
||||
then() {},
|
||||
this: 'is not a real promise'
|
||||
|
@ -104,4 +104,44 @@ test(() => {
|
|||
|
||||
}, 'pipeThrough can handle calling a pipeTo that returns a non-promise thenable object');
|
||||
|
||||
promise_test(() => {
|
||||
const dummy = {
|
||||
pipeTo() {
|
||||
return Promise.reject(new Error('this rejection should not be reported as unhandled'));
|
||||
}
|
||||
};
|
||||
|
||||
ReadableStream.prototype.pipeThrough.call(dummy, { });
|
||||
|
||||
// The test harness should complain about unhandled rejections by then.
|
||||
return flushAsyncEvents();
|
||||
|
||||
}, 'pipeThrough should mark a real promise from a fake readable as handled');
|
||||
|
||||
test(() => {
|
||||
let thenCalled = false
|
||||
let catchCalled = false;
|
||||
const dummy = {
|
||||
pipeTo() {
|
||||
const fakePromise = Object.create(Promise.prototype);
|
||||
fakePromise.then = () => {
|
||||
thenCalled = true;
|
||||
};
|
||||
fakePromise.catch = () => {
|
||||
catchCalled = true;
|
||||
};
|
||||
assert_true(fakePromise instanceof Promise, 'fakePromise fools instanceof');
|
||||
return fakePromise;
|
||||
}
|
||||
};
|
||||
|
||||
// An incorrect implementation which uses an internal method to mark the promise as handled will throw or crash here.
|
||||
ReadableStream.prototype.pipeThrough.call(dummy, { });
|
||||
|
||||
// An incorrect implementation that tries to mark the promise as handled by calling .then() or .catch() on the object
|
||||
// will fail these tests.
|
||||
assert_false(thenCalled, 'then should not be called');
|
||||
assert_false(catchCalled, 'catch should not be called');
|
||||
}, 'pipeThrough should not be fooled by an object whose instanceof Promise returns true');
|
||||
|
||||
done();
|
||||
|
|
|
@ -15,9 +15,30 @@ test(() => {
|
|||
|
||||
test(() => {
|
||||
// Constructing ReadableStream with an empty underlying byte source object as parameter shouldn't throw.
|
||||
new ReadableStream({ type: 'bytes' });
|
||||
new ReadableStream({ type: 'bytes' }).getReader({ mode: 'byob' });
|
||||
// Constructor must perform ToString(type).
|
||||
new ReadableStream({ type: { toString() {return 'bytes';} } })
|
||||
.getReader({ mode: 'byob' });
|
||||
new ReadableStream({ type: { toString: null, valueOf() {return 'bytes';} } })
|
||||
.getReader({ mode: 'byob' });
|
||||
}, 'ReadableStream with byte source can be constructed with no errors');
|
||||
|
||||
test(() => {
|
||||
const ReadableStreamBYOBReader = new ReadableStream({ type: 'bytes' }).getReader({ mode: 'byob' }).constructor;
|
||||
const rs = new ReadableStream({ type: 'bytes' });
|
||||
|
||||
let reader = rs.getReader({ mode: { toString() { return 'byob'; } } });
|
||||
assert_true(reader instanceof ReadableStreamBYOBReader, 'must give a BYOB reader');
|
||||
reader.releaseLock();
|
||||
|
||||
reader = rs.getReader({ mode: { toString: null, valueOf() {return 'byob';} } });
|
||||
assert_true(reader instanceof ReadableStreamBYOBReader, 'must give a BYOB reader');
|
||||
reader.releaseLock();
|
||||
|
||||
reader = rs.getReader({ mode: 'byob', notmode: 'ignored' });
|
||||
assert_true(reader instanceof ReadableStreamBYOBReader, 'must give a BYOB reader');
|
||||
}, 'getReader({mode}) must perform ToString()');
|
||||
|
||||
promise_test(() => {
|
||||
let startCalled = false;
|
||||
let startCalledBeforePull = false;
|
||||
|
@ -101,7 +122,7 @@ promise_test(t => {
|
|||
}, 'ReadableStream with byte source: Construct with highWaterMark of 0');
|
||||
|
||||
test(() => {
|
||||
const rs = new ReadableStream({
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.desiredSize, 10, 'desiredSize must start at the highWaterMark');
|
||||
c.close();
|
||||
|
@ -114,7 +135,7 @@ test(() => {
|
|||
}, 'ReadableStream with byte source: desiredSize when closed');
|
||||
|
||||
test(() => {
|
||||
const rs = new ReadableStream({
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.desiredSize, 10, 'desiredSize must start at the highWaterMark');
|
||||
c.error();
|
||||
|
|
|
@ -153,7 +153,7 @@ promise_test(() => {
|
|||
});
|
||||
|
||||
rs.cancel();
|
||||
assert_throws(new TypeError, () => controller.enqueue('a'), 'Calling enqueue after canceling should throw');
|
||||
assert_throws(new TypeError(), () => controller.enqueue('a'), 'Calling enqueue after canceling should throw');
|
||||
|
||||
return rs.getReader().closed;
|
||||
|
||||
|
@ -171,7 +171,7 @@ promise_test(() => {
|
|||
});
|
||||
|
||||
rs.cancel();
|
||||
assert_throws(new TypeError, () => controller.enqueue('c'), 'Calling enqueue after canceling should throw');
|
||||
assert_throws(new TypeError(), () => controller.enqueue('c'), 'Calling enqueue after canceling should throw');
|
||||
|
||||
return rs.getReader().closed;
|
||||
|
||||
|
|
|
@ -5,15 +5,15 @@ if (self.importScripts) {
|
|||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
let ReadableStreamReader;
|
||||
let ReadableStreamDefaultReader;
|
||||
let ReadableStreamController;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamReader = (new ReadableStream()).getReader().constructor;
|
||||
ReadableStreamDefaultReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamReader constructor indirectly');
|
||||
}, 'Can get the ReadableStreamDefaultReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
|
@ -29,7 +29,7 @@ test(() => {
|
|||
function fakeReadableStream() {
|
||||
return {
|
||||
cancel() { return Promise.resolve(); },
|
||||
getReader() { return new ReadableStreamReader(new ReadableStream()); },
|
||||
getReader() { return new ReadableStreamDefaultReader(new ReadableStream()); },
|
||||
pipeThrough(obj) { return obj.readable; },
|
||||
pipeTo() { return Promise.resolve(); },
|
||||
tee() { return [realReadableStream(), realReadableStream()]; }
|
||||
|
@ -40,7 +40,7 @@ function realReadableStream() {
|
|||
return new ReadableStream();
|
||||
}
|
||||
|
||||
function fakeReadableStreamReader() {
|
||||
function fakeReadableStreamDefaultReader() {
|
||||
return {
|
||||
get closed() { return Promise.resolve(); },
|
||||
cancel() { return Promise.resolve(); },
|
||||
|
@ -77,44 +77,44 @@ test(() => {
|
|||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader(fakeReadableStream()),
|
||||
'Constructing a ReadableStreamReader should throw');
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader(fakeReadableStream()),
|
||||
'Constructing a ReadableStreamDefaultReader should throw');
|
||||
|
||||
}, 'ReadableStreamReader enforces a brand check on its argument');
|
||||
}, 'ReadableStreamDefaultReader 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())
|
||||
getterRejects(t, ReadableStreamDefaultReader.prototype, 'closed', fakeReadableStreamDefaultReader()),
|
||||
getterRejects(t, ReadableStreamDefaultReader.prototype, 'closed', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.closed enforces a brand check');
|
||||
}, 'ReadableStreamDefaultReader.prototype.closed enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return Promise.all([
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'cancel', fakeReadableStreamReader()),
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'cancel', realReadableStream())
|
||||
methodRejects(t, ReadableStreamDefaultReader.prototype, 'cancel', fakeReadableStreamDefaultReader()),
|
||||
methodRejects(t, ReadableStreamDefaultReader.prototype, 'cancel', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.cancel enforces a brand check');
|
||||
}, 'ReadableStreamDefaultReader.prototype.cancel enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return Promise.all([
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'read', fakeReadableStreamReader()),
|
||||
methodRejects(t, ReadableStreamReader.prototype, 'read', realReadableStream())
|
||||
methodRejects(t, ReadableStreamDefaultReader.prototype, 'read', fakeReadableStreamDefaultReader()),
|
||||
methodRejects(t, ReadableStreamDefaultReader.prototype, 'read', realReadableStream())
|
||||
]);
|
||||
|
||||
}, 'ReadableStreamReader.prototype.read enforces a brand check');
|
||||
}, 'ReadableStreamDefaultReader.prototype.read enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrows(ReadableStreamReader.prototype, 'releaseLock', fakeReadableStreamReader());
|
||||
methodThrows(ReadableStreamReader.prototype, 'releaseLock', realReadableStream());
|
||||
methodThrows(ReadableStreamDefaultReader.prototype, 'releaseLock', fakeReadableStreamDefaultReader());
|
||||
methodThrows(ReadableStreamDefaultReader.prototype, 'releaseLock', realReadableStream());
|
||||
|
||||
}, 'ReadableStreamReader.prototype.releaseLock enforces a brand check');
|
||||
}, 'ReadableStreamDefaultReader.prototype.releaseLock enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
|
|
|
@ -39,10 +39,10 @@ promise_test(() => {
|
|||
// 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();
|
||||
const 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');
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>readable-stream-reader.js dedicated worker wrapper file</title>
|
||||
<title>default-reader.js dedicated worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new Worker('readable-stream-reader.js'));
|
||||
fetch_tests_from_worker(new Worker('default-reader.js'));
|
||||
</script>
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>readable-stream-reader.js browser context wrapper file</title>
|
||||
<title>default-reader.js browser context wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script src="../resources/rs-utils.js"></script>
|
||||
|
||||
<script src="readable-stream-reader.js"></script>
|
||||
<script src="default-reader.js"></script>
|
|
@ -5,29 +5,29 @@ if (self.importScripts) {
|
|||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
let ReadableStreamReader;
|
||||
let ReadableStreamDefaultReader;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamReader = (new ReadableStream()).getReader().constructor;
|
||||
ReadableStreamDefaultReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamReader constructor indirectly');
|
||||
}, 'Can get the ReadableStreamDefaultReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader('potato'));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader({}));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamReader());
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader('potato'));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader({}));
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader());
|
||||
|
||||
}, 'ReadableStreamReader constructor should get a ReadableStream object as argument');
|
||||
}, 'ReadableStreamDefaultReader 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 rsReader = new ReadableStreamDefaultReader(new ReadableStream());
|
||||
const proto = Object.getPrototypeOf(rsReader);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
@ -56,41 +56,41 @@ test(() => {
|
|||
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');
|
||||
}, 'ReadableStreamDefaultReader instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rsReader = new ReadableStreamReader(new ReadableStream());
|
||||
const rsReader = new ReadableStreamDefaultReader(new ReadableStream());
|
||||
assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise');
|
||||
|
||||
}, 'ReadableStreamReader closed should always return the same promise object');
|
||||
}, 'ReadableStreamDefaultReader 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),
|
||||
new ReadableStreamDefaultReader(rs); // Constructing directly the first time should be fine.
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader(rs),
|
||||
'constructing directly the second time should fail');
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via direct ' +
|
||||
}, 'Constructing a ReadableStreamDefaultReader 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.
|
||||
new ReadableStreamDefaultReader(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 ' +
|
||||
}, 'Getting a ReadableStreamDefaultReader 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');
|
||||
assert_throws(new TypeError(), () => new ReadableStreamDefaultReader(rs), 'constructing directly should fail');
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via getReader)');
|
||||
}, 'Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via getReader)');
|
||||
|
||||
test(() => {
|
||||
|
||||
|
@ -98,7 +98,7 @@ test(() => {
|
|||
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)');
|
||||
}, 'Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via getReader)');
|
||||
|
||||
test(() => {
|
||||
|
||||
|
@ -108,9 +108,9 @@ test(() => {
|
|||
}
|
||||
});
|
||||
|
||||
new ReadableStreamReader(rs); // Constructing directly should not throw.
|
||||
new ReadableStreamDefaultReader(rs); // Constructing directly should not throw.
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should be OK if the stream is closed');
|
||||
}, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is closed');
|
||||
|
||||
test(() => {
|
||||
|
||||
|
@ -121,9 +121,9 @@ test(() => {
|
|||
}
|
||||
});
|
||||
|
||||
new ReadableStreamReader(rs); // Constructing directly should not throw.
|
||||
new ReadableStreamDefaultReader(rs); // Constructing directly should not throw.
|
||||
|
||||
}, 'Constructing a ReadableStreamReader directly should be OK if the stream is errored');
|
||||
}, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is errored');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
|
@ -332,7 +332,7 @@ promise_test(t => {
|
|||
controller.error();
|
||||
return promise;
|
||||
|
||||
}, 'ReadableStreamReader closed promise should be rejected with undefined if that is the error');
|
||||
}, 'ReadableStreamDefaultReader closed promise should be rejected with undefined if that is the error');
|
||||
|
||||
|
||||
promise_test(t => {
|
||||
|
@ -350,7 +350,8 @@ promise_test(t => {
|
|||
}
|
||||
);
|
||||
|
||||
}, 'ReadableStreamReader: if start rejects with no parameter, it should error the stream with an undefined error');
|
||||
}, 'ReadableStreamDefaultReader: if start rejects with no parameter, it should error the stream with an undefined ' +
|
||||
'error');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
|
@ -367,7 +368,7 @@ promise_test(t => {
|
|||
controller.error(theError);
|
||||
return promise;
|
||||
|
||||
}, 'Erroring a ReadableStream after checking closed should reject ReadableStreamReader closed promise');
|
||||
}, 'Erroring a ReadableStream after checking closed should reject ReadableStreamDefaultReader closed promise');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
|
@ -386,7 +387,7 @@ promise_test(t => {
|
|||
|
||||
return promise_rejects(t, theError, rs.getReader().closed);
|
||||
|
||||
}, 'Erroring a ReadableStream before checking closed should reject ReadableStreamReader closed promise');
|
||||
}, 'Erroring a ReadableStream before checking closed should reject ReadableStreamDefaultReader closed promise');
|
||||
|
||||
promise_test(() => {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>readable-stream-reader.js service worker wrapper file</title>
|
||||
<title>default-reader.js service worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
@ -8,5 +8,5 @@
|
|||
|
||||
<script>
|
||||
'use strict';
|
||||
service_worker_test('readable-stream-reader.js', 'Service worker test setup');
|
||||
service_worker_test('default-reader.js', 'Service worker test setup');
|
||||
</script>
|
|
@ -1,11 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>readable-stream-reader.js shared worker wrapper file</title>
|
||||
<title>default-reader.js shared worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new SharedWorker('readable-stream-reader.js'));
|
||||
fetch_tests_from_worker(new SharedWorker('default-reader.js'));
|
||||
</script>
|
|
@ -70,6 +70,6 @@ promise_test(() => {
|
|||
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');
|
||||
}, 'Garbage-collecting a ReadableStreamDefaultReader should not unlock its stream');
|
||||
|
||||
done();
|
||||
|
|
|
@ -6,6 +6,9 @@ if (self.importScripts) {
|
|||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
const error1 = new Error('error1');
|
||||
error1.name = 'error1';
|
||||
|
||||
test(() => {
|
||||
|
||||
new ReadableStream(); // ReadableStream constructed with no parameters
|
||||
|
@ -32,6 +35,8 @@ test(() => {
|
|||
'constructor should throw when the type is empty string');
|
||||
assert_throws(new RangeError(), () => new ReadableStream({ type: 'asdf' }),
|
||||
'constructor should throw when the type is asdf');
|
||||
assert_throws(error1, () => new ReadableStream({ type: { get toString() {throw error1;} } }), 'constructor should throw when ToString() throws');
|
||||
assert_throws(error1, () => new ReadableStream({ type: { toString() {throw error1;} } }), 'constructor should throw when ToString() throws');
|
||||
|
||||
}, 'ReadableStream can\'t be constructed with an invalid type');
|
||||
|
||||
|
@ -157,6 +162,13 @@ test(() => {
|
|||
|
||||
}, 'ReadableStream start controller parameter should be extensible');
|
||||
|
||||
test(() => {
|
||||
(new ReadableStream()).getReader(undefined);
|
||||
(new ReadableStream()).getReader({});
|
||||
(new ReadableStream()).getReader({ mode: undefined, notmode: 'ignored' });
|
||||
assert_throws(new RangeError(), () => (new ReadableStream()).getReader({ mode: 'potato' }));
|
||||
}, 'default ReadableStream getReader() should only accept mode:undefined');
|
||||
|
||||
promise_test(() => {
|
||||
|
||||
function SimpleStreamSource() {}
|
||||
|
@ -195,7 +207,9 @@ promise_test(() => {
|
|||
const theError = new Error('rejected!');
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return delay(1).then(() => { throw theError; });
|
||||
return delay(1).then(() => {
|
||||
throw theError;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -733,7 +747,7 @@ promise_test(() => {
|
|||
}, 'ReadableStream: should call underlying source methods as methods');
|
||||
|
||||
test(() => {
|
||||
const rs = new ReadableStream({
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.desiredSize, 10, 'desiredSize must start at highWaterMark');
|
||||
c.close();
|
||||
|
@ -745,7 +759,7 @@ test(() => {
|
|||
}, 'ReadableStream: desiredSize when closed');
|
||||
|
||||
test(() => {
|
||||
const rs = new ReadableStream({
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
assert_equals(c.desiredSize, 10, 'desiredSize must start at highWaterMark');
|
||||
c.error();
|
||||
|
|
|
@ -9,8 +9,8 @@ test(() => {
|
|||
|
||||
let pipeToArguments;
|
||||
const thisValue = {
|
||||
pipeTo() {
|
||||
pipeToArguments = arguments;
|
||||
pipeTo(...args) {
|
||||
pipeToArguments = args;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -52,18 +52,16 @@ self.recordingWritableStream = (extras = {}, strategy) => {
|
|||
|
||||
return undefined;
|
||||
},
|
||||
write(chunk) {
|
||||
write(chunk, controller) {
|
||||
stream.events.push('write', chunk);
|
||||
|
||||
if (extras.write) {
|
||||
return extras.write(chunk);
|
||||
return extras.write(chunk, controller);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
close(...args) {
|
||||
assert_array_equals(args, [controllerToCopyOver], 'close must always be called with the controller');
|
||||
|
||||
close() {
|
||||
stream.events.push('close');
|
||||
|
||||
if (extras.close) {
|
||||
|
|
|
@ -14,9 +14,7 @@ error2.name = 'error2';
|
|||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
write() {
|
||||
return new Promise(() => { }); // forever-pending, so normally .ready would not fulfill.
|
||||
}
|
||||
write: t.unreached_func('write() should not be called')
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
@ -66,11 +64,12 @@ promise_test(t => {
|
|||
.then(() => {
|
||||
const writer = ws.getWriter();
|
||||
|
||||
writer.abort();
|
||||
const abortPromise = writer.abort();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.write(1), 'write(1) must reject with a TypeError'),
|
||||
promise_rejects(t, new TypeError(), writer.write(2), 'write(2) must reject with a TypeError')
|
||||
promise_rejects(t, new TypeError(), writer.write(2), 'write(2) must reject with a TypeError'),
|
||||
abortPromise
|
||||
]);
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -169,20 +168,20 @@ promise_test(t => {
|
|||
}, 'WritableStream if sink\'s abort throws, for an abort performed during a write, the promise returned by ' +
|
||||
'ws.abort() rejects');
|
||||
|
||||
test(() => {
|
||||
promise_test(() => {
|
||||
const ws = recordingWritableStream();
|
||||
const writer = ws.getWriter();
|
||||
|
||||
writer.abort(error1);
|
||||
|
||||
assert_array_equals(ws.events, ['abort', error1]);
|
||||
return writer.abort(error1).then(() => {
|
||||
assert_array_equals(ws.events, ['abort', error1]);
|
||||
});
|
||||
}, 'Aborting a WritableStream passes through the given reason');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream();
|
||||
const writer = ws.getWriter();
|
||||
|
||||
writer.abort(error1);
|
||||
const abortPromise = writer.abort(error1);
|
||||
|
||||
const events = [];
|
||||
writer.ready.catch(() => {
|
||||
|
@ -193,6 +192,7 @@ promise_test(t => {
|
|||
});
|
||||
|
||||
return Promise.all([
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), writer.write(), 'writing should reject with a TypeError'),
|
||||
promise_rejects(t, new TypeError(), writer.close(), 'closing should reject with a TypeError'),
|
||||
promise_rejects(t, new TypeError(), writer.abort(), 'aborting should reject with a TypeError'),
|
||||
|
@ -216,19 +216,22 @@ promise_test(t => {
|
|||
}, 'Aborting a WritableStream causes any outstanding write() promises to be rejected with a TypeError');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream();
|
||||
const ws = recordingWritableStream();
|
||||
const writer = ws.getWriter();
|
||||
|
||||
const closePromise = writer.close();
|
||||
writer.abort(error1);
|
||||
const abortPromise = writer.abort(error1);
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.closed, 'closed should reject with a TypeError'),
|
||||
promise_rejects(t, new TypeError(), closePromise, 'close() should reject with a TypeError')
|
||||
]);
|
||||
promise_rejects(t, new TypeError(), closePromise, 'close() should reject with a TypeError'),
|
||||
abortPromise
|
||||
]).then(() => {
|
||||
assert_array_equals(ws.events, ['abort', error1]);
|
||||
});
|
||||
}, 'Closing but then immediately aborting a WritableStream causes the stream to error');
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
let resolveClose;
|
||||
const ws = new WritableStream({
|
||||
close() {
|
||||
|
@ -245,12 +248,12 @@ promise_test(t => {
|
|||
const abortPromise = writer.abort(error1);
|
||||
resolveClose();
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.closed, 'closed should reject with a TypeError'),
|
||||
writer.closed,
|
||||
abortPromise,
|
||||
closePromise
|
||||
]);
|
||||
});
|
||||
}, 'Closing a WritableStream and aborting it while it closes causes the stream to error');
|
||||
}, 'Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = new WritableStream();
|
||||
|
@ -308,11 +311,11 @@ promise_test(t => {
|
|||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('a');
|
||||
writer.abort(error1);
|
||||
let closedResolved = false;
|
||||
let closedRejected = false;
|
||||
return Promise.all([
|
||||
writePromise.then(() => assert_false(closedResolved, '.closed should not resolve before write()')),
|
||||
writePromise.then(() => assert_false(closedRejected, '.closed should not resolve before write()')),
|
||||
promise_rejects(t, new TypeError(), writer.closed, '.closed should reject').then(() => {
|
||||
closedResolved = true;
|
||||
closedRejected = true;
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
@ -328,17 +331,18 @@ promise_test(t => {
|
|||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('a');
|
||||
const abortPromise = writer.abort(error2);
|
||||
let closedResolved = false;
|
||||
let closedRejected = false;
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, writePromise, 'write() should reject')
|
||||
.then(() => assert_false(closedResolved, '.closed should not resolve before write()')),
|
||||
promise_rejects(t, error1, writer.closed, '.closed should reject')
|
||||
.then(() => assert_false(closedRejected, '.closed should not resolve before write()')),
|
||||
promise_rejects(t, new TypeError(), writer.closed, '.closed should reject')
|
||||
.then(() => {
|
||||
closedResolved = true;
|
||||
closedRejected = true;
|
||||
}),
|
||||
promise_rejects(t, error1, abortPromise, 'abort() should reject')]);
|
||||
abortPromise
|
||||
]);
|
||||
});
|
||||
}, '.closed should not resolve before rejected write(); write() error should overwrite abort() error');
|
||||
}, '.closed should not resolve before rejected write(); write() error should not overwrite abort() error');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
|
@ -370,13 +374,13 @@ promise_test(t => {
|
|||
return writer.ready.then(() => {
|
||||
const settlementOrder = [];
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, writer.write('1'), 'pending write should be rejected')
|
||||
promise_rejects(t, error1, writer.write('1'), 'in-flight write should be rejected')
|
||||
.then(() => settlementOrder.push(1)),
|
||||
promise_rejects(t, error1, writer.write('2'), 'first queued write should be rejected')
|
||||
promise_rejects(t, new TypeError(), writer.write('2'), 'first queued write should be rejected')
|
||||
.then(() => settlementOrder.push(2)),
|
||||
promise_rejects(t, error1, writer.write('3'), 'second queued write should be rejected')
|
||||
promise_rejects(t, new TypeError(), writer.write('3'), 'second queued write should be rejected')
|
||||
.then(() => settlementOrder.push(3)),
|
||||
promise_rejects(t, error1, writer.abort(error1), 'abort should be rejected')
|
||||
writer.abort(error2)
|
||||
]).then(() => assert_array_equals([1, 2, 3], settlementOrder, 'writes should be satisfied in order'));
|
||||
});
|
||||
}, 'writes should be satisfied in order after rejected write when aborting');
|
||||
|
@ -391,49 +395,44 @@ promise_test(t => {
|
|||
return writer.ready.then(() => {
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, writer.write('a'), 'writer.write() should reject with error from underlying write()'),
|
||||
promise_rejects(t, error1, writer.close(), 'writer.close() should reject with error from underlying write()'),
|
||||
promise_rejects(t, error1, writer.abort(), 'writer.abort() should reject with error from underlying write()')
|
||||
promise_rejects(t, new TypeError(), writer.close(),
|
||||
'writer.close() should reject with error from underlying write()'),
|
||||
writer.abort()
|
||||
]);
|
||||
});
|
||||
}, 'close() should use error from underlying write() on abort');
|
||||
}, 'close() should reject with TypeError when abort() is first error');
|
||||
|
||||
promise_test(() => {
|
||||
let resolveWrite;
|
||||
let abortCalled = false;
|
||||
const ws = new WritableStream({
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
return new Promise(resolve => {
|
||||
resolveWrite = resolve;
|
||||
});
|
||||
},
|
||||
abort() {
|
||||
abortCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
writer.write('a');
|
||||
const abortPromise = writer.abort();
|
||||
const abortPromise = writer.abort('b');
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_false(abortCalled, 'abort should not be called while write is pending');
|
||||
assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight');
|
||||
resolveWrite();
|
||||
return abortPromise.then(() => assert_true(abortCalled, 'abort should be called'));
|
||||
return abortPromise.then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'a', 'abort', 'b'], 'abort should be called after the write finishes');
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 'underlying abort() should not be called until underlying write() completes');
|
||||
|
||||
promise_test(() => {
|
||||
let resolveClose;
|
||||
let abortCalled = false;
|
||||
const ws = new WritableStream({
|
||||
const ws = recordingWritableStream({
|
||||
close() {
|
||||
return new Promise(resolve => {
|
||||
resolveClose = resolve;
|
||||
});
|
||||
},
|
||||
abort() {
|
||||
abortCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -442,10 +441,10 @@ promise_test(() => {
|
|||
writer.close();
|
||||
const abortPromise = writer.abort();
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_false(abortCalled, 'underlying abort should not be called while close is pending');
|
||||
assert_array_equals(ws.events, ['close'], 'abort should not be called while close is in-flight');
|
||||
resolveClose();
|
||||
return abortPromise.then(() => {
|
||||
assert_false(abortCalled, 'underlying abort should not be called after close completes');
|
||||
assert_array_equals(ws.events, ['close'], 'abort should not be called');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -470,7 +469,7 @@ promise_test(t => {
|
|||
const closePromise = writer.close();
|
||||
const abortPromise = writer.abort();
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_false(abortCalled, 'underlying abort should not be called while close is pending');
|
||||
assert_false(abortCalled, 'underlying abort should not be called while close is in-flight');
|
||||
rejectClose(error1);
|
||||
return promise_rejects(t, error1, abortPromise, 'abort should reject with the same reason').then(() => {
|
||||
return promise_rejects(t, error1, closePromise, 'close should reject with the same reason');
|
||||
|
@ -484,15 +483,11 @@ promise_test(t => {
|
|||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
let abortCalled = false;
|
||||
const ws = new WritableStream({
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
return new Promise(resolve => {
|
||||
resolveWrite = resolve;
|
||||
});
|
||||
},
|
||||
abort() {
|
||||
abortCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -500,17 +495,36 @@ promise_test(t => {
|
|||
return writer.ready.then(() => {
|
||||
writer.write('a');
|
||||
const closePromise = writer.close();
|
||||
const abortPromise = writer.abort();
|
||||
const abortPromise = writer.abort('b');
|
||||
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_false(abortCalled, 'abort should not be called while write is pending');
|
||||
assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight');
|
||||
resolveWrite();
|
||||
return abortPromise.then(() => {
|
||||
assert_true(abortCalled, 'abort should be called after write completes');
|
||||
assert_array_equals(ws.events, ['write', 'a', 'abort', 'b'], 'abort should be called after write completes');
|
||||
return promise_rejects(t, new TypeError(), closePromise, 'promise returned by close() should be rejected');
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 'underlying abort() should be called while closing if underlying close() has not started yet');
|
||||
}, 'an abort() that happens during a write() should trigger the underlying abort() even with a close() queued');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
write() {
|
||||
return new Promise(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
writer.write('a');
|
||||
writer.abort();
|
||||
writer.releaseLock();
|
||||
const writer2 = ws.getWriter();
|
||||
return promise_rejects(t, new TypeError(), writer2.ready,
|
||||
'ready of the second writer should be rejected with a TypeError');
|
||||
});
|
||||
}, 'if a writer is created for a stream with a pending abort, its ready should be rejected with a TypeError');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = new WritableStream();
|
||||
|
@ -518,11 +532,13 @@ promise_test(() => {
|
|||
return writer.ready.then(() => {
|
||||
const closePromise = writer.close();
|
||||
const abortPromise = writer.abort();
|
||||
let closeResolved = false;
|
||||
Promise.all([
|
||||
closePromise.then(() => { closeResolved = true; }),
|
||||
abortPromise.then(() => { assert_true(closeResolved, 'close() promise should resolve before abort() promise'); })
|
||||
]);
|
||||
const events = [];
|
||||
return Promise.all([
|
||||
closePromise.then(() => { events.push('close'); }),
|
||||
abortPromise.then(() => { events.push('abort'); })
|
||||
]).then(() => {
|
||||
assert_array_equals(events, ['close', 'abort']);
|
||||
});
|
||||
});
|
||||
}, 'writer close() promise should resolve before abort() promise');
|
||||
|
||||
|
@ -569,7 +585,7 @@ promise_test(t => {
|
|||
});
|
||||
|
||||
abortPromise = writer.abort(error1);
|
||||
abortPromise.catch(() => {
|
||||
abortPromise.then(() => {
|
||||
events.push('abortPromise');
|
||||
});
|
||||
|
||||
|
@ -588,21 +604,20 @@ promise_test(t => {
|
|||
return Promise.all([
|
||||
promise_rejects(t, error2, writePromise,
|
||||
'writePromise must reject with the error returned from the sink\'s write method'),
|
||||
promise_rejects(t, error2, abortPromise,
|
||||
'abortPromise must reject with the error returned from the sink\'s write method'),
|
||||
promise_rejects(t, error2, writer.closed,
|
||||
'writer.closed must reject with the error returned from the sink\'s write method'),
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), writer.closed,
|
||||
'writer.closed must reject with an error indicating abort'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
|
||||
'writePromise, abortPromise and writer.closed must reject');
|
||||
'writePromise, abortPromise and writer.closed must settle');
|
||||
|
||||
const writePromise3 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writePromise3,
|
||||
'writePromise3 must reject with an error indicating the stream has already been errored'),
|
||||
'writePromise3 must reject with an error indicating abort'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be still rejected with the error indicating abort')
|
||||
]);
|
||||
|
@ -616,7 +631,7 @@ promise_test(t => {
|
|||
'writer.closed must be rejected with an error indicating release')
|
||||
]);
|
||||
});
|
||||
}, 'writer.abort() while there is a pending write, and then finish the write with rejection');
|
||||
}, 'writer.abort() while there is an in-flight write, and then finish the write with rejection');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
|
@ -649,7 +664,7 @@ promise_test(t => {
|
|||
});
|
||||
|
||||
abortPromise = writer.abort(error1);
|
||||
abortPromise.catch(() => {
|
||||
abortPromise.then(() => {
|
||||
events.push('abortPromise');
|
||||
});
|
||||
|
||||
|
@ -663,13 +678,14 @@ promise_test(t => {
|
|||
}).then(() => {
|
||||
assert_array_equals(events, [], 'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet');
|
||||
|
||||
// This error is too late to change anything. abort() has already changed the stream state to 'erroring'.
|
||||
controller.error(error2);
|
||||
|
||||
const writePromise3 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writePromise3,
|
||||
'writePromise3 must reject with an error indicating the stream has already been errored'),
|
||||
'writePromise3 must reject with an error indicating abort'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be still rejected with the error indicating abort'),
|
||||
flushAsyncEvents()
|
||||
|
@ -677,29 +693,28 @@ promise_test(t => {
|
|||
}).then(() => {
|
||||
assert_array_equals(
|
||||
events, [],
|
||||
'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after '
|
||||
+ 'controller.error() call');
|
||||
'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' +
|
||||
'controller.error() call');
|
||||
|
||||
resolveWrite();
|
||||
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
promise_rejects(t, error2, abortPromise,
|
||||
'abortPromise must reject with the error passed to the controller\'s error method'),
|
||||
promise_rejects(t, error2, writer.closed,
|
||||
'writer.closed must reject with the error passed to the controller\'s error method'),
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), writer.closed,
|
||||
'writer.closed must reject with an error indicating abort'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
|
||||
'writePromise, abortPromise and writer.closed must reject');
|
||||
'writePromise, abortPromise and writer.closed must settle');
|
||||
|
||||
const writePromise4 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
promise_rejects(t, new TypeError(), writePromise4,
|
||||
'writePromise4 must reject with an error indicating that the stream has already been errored'),
|
||||
'writePromise4 must reject with an error indicating abort'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be still rejected with the error indicating abort')
|
||||
]);
|
||||
|
@ -713,12 +728,105 @@ promise_test(t => {
|
|||
'writer.closed must be rejected with an error indicating release')
|
||||
]);
|
||||
});
|
||||
}, 'writer.abort(), controller.error() while there is a pending write, and then finish the write');
|
||||
}, 'writer.abort(), controller.error() while there is an in-flight write, and then finish the write');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveClose;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
close() {
|
||||
return new Promise(resolve => {
|
||||
resolveClose = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let closePromise;
|
||||
let abortPromise;
|
||||
|
||||
const events = [];
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
writer.closed.then(() => {
|
||||
events.push('closed');
|
||||
});
|
||||
|
||||
// Wait for ws to start
|
||||
return flushAsyncEvents().then(() => {
|
||||
closePromise = writer.close();
|
||||
closePromise.then(() => {
|
||||
events.push('closePromise');
|
||||
});
|
||||
|
||||
abortPromise = writer.abort(error1);
|
||||
abortPromise.then(() => {
|
||||
events.push('abortPromise');
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.close(),
|
||||
'writer.close() must reject with an error indicating already closing'),
|
||||
promise_rejects(t, new TypeError(), writer.ready, 'writer.ready must reject with an error indicating abort'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, [], 'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet');
|
||||
|
||||
controller.error(error2);
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.close(),
|
||||
'writer.close() must reject with an error indicating already closing'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be still rejected with the error indicating abort'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(
|
||||
events, [],
|
||||
'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' +
|
||||
'controller.error() call');
|
||||
|
||||
resolveClose();
|
||||
|
||||
return Promise.all([
|
||||
closePromise,
|
||||
abortPromise,
|
||||
writer.closed,
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
|
||||
'closedPromise, abortPromise and writer.closed must fulfill');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.close(),
|
||||
'writer.close() must reject with an error indicating already closing'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be still rejected with the error indicating abort')
|
||||
]);
|
||||
}).then(() => {
|
||||
writer.releaseLock();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.close(),
|
||||
'writer.close() must reject with an error indicating release'),
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be rejected with an error indicating release'),
|
||||
promise_rejects(t, new TypeError(), writer.closed,
|
||||
'writer.closed must be rejected with an error indicating release')
|
||||
]);
|
||||
});
|
||||
}, 'writer.abort(), controller.error() while there is an in-flight close, and then finish the close');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
const ws = recordingWritableStream({
|
||||
write(chunk, c) {
|
||||
controller = c;
|
||||
return new Promise(resolve => {
|
||||
|
@ -750,8 +858,8 @@ promise_test(t => {
|
|||
const writePromise2 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writePromise2,
|
||||
'writePromise2 must reject with an error indicating the stream has already been errored'),
|
||||
promise_rejects(t, error2, writePromise2,
|
||||
'writePromise2 must reject with the error passed to the controller\'s error method'),
|
||||
promise_rejects(t, error2, writer.ready,
|
||||
'writer.ready must reject with the error passed to the controller\'s error method'),
|
||||
flushAsyncEvents()
|
||||
|
@ -767,34 +875,35 @@ promise_test(t => {
|
|||
const writePromise3 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error2, abortPromise,
|
||||
'abortPromise must reject with the error passed to the controller\'s error method'),
|
||||
promise_rejects(t, new TypeError(), writePromise3,
|
||||
'writePromise3 must reject with an error indicating the stream has already been errored'),
|
||||
promise_rejects(t, error2, writePromise3,
|
||||
'writePromise3 must reject with the error passed to the controller\'s error method'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(
|
||||
events, ['abortPromise'],
|
||||
events, [],
|
||||
'writePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()');
|
||||
|
||||
resolveWrite();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error2, abortPromise,
|
||||
'abort() must reject with the error passed to the controller\'s error method'),
|
||||
promise_rejects(t, error2, writer.closed,
|
||||
'writer.closed must reject with the error passed to the controller\'s error method'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, ['abortPromise', 'writePromise', 'closed'],
|
||||
assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
|
||||
'writePromise, abortPromise and writer.closed must fulfill/reject');
|
||||
assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called');
|
||||
|
||||
const writePromise4 = writer.write('a');
|
||||
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
promise_rejects(t, new TypeError(), writePromise4,
|
||||
'writePromise4 must reject with an error indicating that the stream has already been errored'),
|
||||
promise_rejects(t, error2, writePromise4,
|
||||
'writePromise4 must reject with the error passed to the controller\'s error method'),
|
||||
promise_rejects(t, error2, writer.ready,
|
||||
'writer.ready must be still rejected with the error passed to the controller\'s error method')
|
||||
]);
|
||||
|
@ -808,7 +917,84 @@ promise_test(t => {
|
|||
'writer.closed must be rejected with an error indicating release')
|
||||
]);
|
||||
});
|
||||
}, 'controller.error(), writer.abort() while there is a pending write, and then finish the write');
|
||||
}, 'controller.error(), writer.abort() while there is an in-flight write, and then finish the write');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveClose;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
close() {
|
||||
return new Promise(resolve => {
|
||||
resolveClose = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let closePromise;
|
||||
let abortPromise;
|
||||
|
||||
const events = [];
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
writer.closed.then(() => {
|
||||
events.push('closed');
|
||||
});
|
||||
|
||||
// Wait for ws to start
|
||||
return flushAsyncEvents().then(() => {
|
||||
closePromise = writer.close();
|
||||
closePromise.then(() => {
|
||||
events.push('closePromise');
|
||||
});
|
||||
|
||||
controller.error(error2);
|
||||
|
||||
return flushAsyncEvents();
|
||||
}).then(() => {
|
||||
assert_array_equals(events, [], 'closePromise must not be fulfilled/rejected yet');
|
||||
|
||||
abortPromise = writer.abort(error1);
|
||||
abortPromise.then(() => {
|
||||
events.push('abortPromise');
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, error2, writer.ready,
|
||||
'writer.ready must reject with the error passed to the controller\'s error method'),
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(
|
||||
events, [],
|
||||
'closePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()');
|
||||
|
||||
resolveClose();
|
||||
|
||||
return Promise.all([
|
||||
closePromise,
|
||||
promise_rejects(t, error2, writer.ready,
|
||||
'writer.ready must be still rejected with the error passed to the controller\'s error method'),
|
||||
writer.closed,
|
||||
flushAsyncEvents()
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
|
||||
'abortPromise, closePromise and writer.closed must fulfill/reject');
|
||||
}).then(() => {
|
||||
writer.releaseLock();
|
||||
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writer.ready,
|
||||
'writer.ready must be rejected with an error indicating release'),
|
||||
promise_rejects(t, new TypeError(), writer.closed,
|
||||
'writer.closed must be rejected with an error indicating release')
|
||||
]);
|
||||
});
|
||||
}, 'controller.error(), writer.abort() while there is an in-flight close, and then finish the close');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
|
@ -833,6 +1019,7 @@ promise_test(t => {
|
|||
});
|
||||
}, 'releaseLock() while aborting should reject the original closed promise');
|
||||
|
||||
// TODO(ricea): Consider removing this test if it is no longer useful.
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
let resolveAbort;
|
||||
|
@ -861,15 +1048,254 @@ promise_test(t => {
|
|||
resolveWrite();
|
||||
return abortStarted.then(() => {
|
||||
writer.releaseLock();
|
||||
assert_not_equals(writer.closed, closed, 'closed promise should have changed');
|
||||
assert_equals(writer.closed, closed, 'closed promise should not have changed');
|
||||
resolveAbort();
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), closed, 'original closed should reject'),
|
||||
promise_rejects(t, new TypeError(), writer.closed, 'new closed should reject')]);
|
||||
promise_rejects(t, new TypeError(), closed, 'closed should reject')]);
|
||||
});
|
||||
});
|
||||
}, 'releaseLock() during delayed async abort() should create a new rejected closed promise');
|
||||
}, 'releaseLock() during delayed async abort() should reject the writer.closed promise');
|
||||
|
||||
promise_test(() => {
|
||||
let resolveStart;
|
||||
const ws = recordingWritableStream({
|
||||
start() {
|
||||
return new Promise(resolve => {
|
||||
resolveStart = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
const abortPromise = ws.abort('done');
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_array_equals(ws.events, [], 'abort() should not be called during start()');
|
||||
resolveStart();
|
||||
return abortPromise.then(() => {
|
||||
assert_array_equals(ws.events, ['abort', 'done'], 'abort() should be called after start() is done');
|
||||
});
|
||||
});
|
||||
}, 'sink abort() should not be called until sink start() is done');
|
||||
|
||||
promise_test(() => {
|
||||
let resolveStart;
|
||||
let controller;
|
||||
const ws = recordingWritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return new Promise(resolve => {
|
||||
resolveStart = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
const abortPromise = ws.abort('done');
|
||||
controller.error(error1);
|
||||
resolveStart();
|
||||
return abortPromise.then(() =>
|
||||
assert_array_equals(ws.events, ['abort', 'done'],
|
||||
'abort() should still be called if start() errors the controller'));
|
||||
}, 'if start attempts to error the controller after abort() has been called, then it should lose');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = recordingWritableStream({
|
||||
start() {
|
||||
return Promise.reject(error1);
|
||||
}
|
||||
});
|
||||
return ws.abort('done').then(() =>
|
||||
assert_array_equals(ws.events, ['abort', 'done'], 'abort() should still be called if start() rejects'));
|
||||
}, 'stream abort() promise should still resolve if sink start() rejects');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream();
|
||||
const writer = ws.getWriter();
|
||||
const writerReady1 = writer.ready;
|
||||
writer.abort('a');
|
||||
const writerReady2 = writer.ready;
|
||||
assert_not_equals(writerReady1, writerReady2, 'abort() should replace the ready promise with a rejected one');
|
||||
return Promise.all([writerReady1,
|
||||
promise_rejects(t, new TypeError(), writerReady2, 'writerReady2 should reject')]);
|
||||
}, 'writer abort() during sink start() should replace the writer.ready promise synchronously');
|
||||
|
||||
promise_test(t => {
|
||||
const events = [];
|
||||
const ws = recordingWritableStream();
|
||||
const writer = ws.getWriter();
|
||||
const writePromise1 = writer.write(1);
|
||||
const abortPromise = writer.abort('a');
|
||||
const writePromise2 = writer.write(2);
|
||||
const closePromise = writer.close();
|
||||
writePromise1.catch(() => events.push('write1'));
|
||||
abortPromise.then(() => events.push('abort'));
|
||||
writePromise2.catch(() => events.push('write2'));
|
||||
closePromise.catch(() => events.push('close'));
|
||||
return Promise.all([
|
||||
promise_rejects(t, new TypeError(), writePromise1, 'first write() should reject'),
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), writePromise2, 'second write() should reject'),
|
||||
promise_rejects(t, new TypeError(), closePromise, 'close() should reject')
|
||||
])
|
||||
.then(() => {
|
||||
assert_array_equals(events, ['write2', 'write1', 'abort', 'close'],
|
||||
'promises should resolve in the standard order');
|
||||
assert_array_equals(ws.events, ['abort', 'a'], 'underlying sink write() should not be called');
|
||||
});
|
||||
}, 'promises returned from other writer methods should be rejected when writer abort() happens during sink start()');
|
||||
|
||||
promise_test(t => {
|
||||
let writeReject;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
write(chunk, c) {
|
||||
controller = c;
|
||||
return new Promise((resolve, reject) => {
|
||||
writeReject = reject;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('a');
|
||||
const abortPromise = writer.abort();
|
||||
controller.error(error1);
|
||||
writeReject(error2);
|
||||
return Promise.all([
|
||||
promise_rejects(t, error2, writePromise, 'write() should reject with error2'),
|
||||
abortPromise
|
||||
]);
|
||||
});
|
||||
}, 'abort() should succeed despite rejection from write');
|
||||
|
||||
promise_test(t => {
|
||||
let closeReject;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
close() {
|
||||
return new Promise((resolve, reject) => {
|
||||
closeReject = reject;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const closePromise = writer.close();
|
||||
const abortPromise = writer.abort();
|
||||
controller.error(error1);
|
||||
closeReject(error2);
|
||||
return Promise.all([
|
||||
promise_rejects(t, error2, closePromise, 'close() should reject with error2'),
|
||||
promise_rejects(t, error2, abortPromise, 'abort() should reject with error2')
|
||||
]);
|
||||
});
|
||||
}, 'abort() should be rejected with the rejection returned from close()');
|
||||
|
||||
promise_test(t => {
|
||||
let rejectWrite;
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
return new Promise((resolve, reject) => {
|
||||
rejectWrite = reject;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('1');
|
||||
const abortPromise = writer.abort(error2);
|
||||
rejectWrite(error1);
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, writePromise, 'write should reject'),
|
||||
abortPromise,
|
||||
promise_rejects(t, new TypeError(), writer.closed, 'closed should reject with TypeError')
|
||||
]);
|
||||
}).then(() => {
|
||||
assert_array_equals(ws.events, ['write', '1', 'abort', error2], 'abort sink method should be called');
|
||||
});
|
||||
}, 'a rejecting sink.write() should not prevent sink.abort() from being called');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = recordingWritableStream({
|
||||
start() {
|
||||
return Promise.reject(error1);
|
||||
}
|
||||
});
|
||||
return ws.abort(error2)
|
||||
.then(() => {
|
||||
assert_array_equals(ws.events, ['abort', error2]);
|
||||
});
|
||||
}, 'when start errors after stream abort(), underlying sink abort() should be called anyway');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream();
|
||||
const abortPromise1 = ws.abort();
|
||||
const abortPromise2 = ws.abort();
|
||||
return Promise.all([
|
||||
abortPromise1,
|
||||
promise_rejects(t, new TypeError(), abortPromise2, 'second abort() should reject')
|
||||
]);
|
||||
}, 'when calling abort() twice on the same stream, the second call should reject');
|
||||
|
||||
promise_test(t => {
|
||||
let controller;
|
||||
let resolveWrite;
|
||||
const ws = recordingWritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
write() {
|
||||
return new Promise(resolve => {
|
||||
resolveWrite = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('chunk');
|
||||
controller.error(error1);
|
||||
const abortPromise = writer.abort(error2);
|
||||
resolveWrite();
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
promise_rejects(t, error1, abortPromise, 'abort() should reject')
|
||||
]).then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'chunk'], 'sink abort() should not be called');
|
||||
});
|
||||
});
|
||||
}, 'sink abort() should not be called if stream was erroring due to controller.error() before abort() was called');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
let size = 1;
|
||||
const ws = recordingWritableStream({
|
||||
write() {
|
||||
return new Promise(resolve => {
|
||||
resolveWrite = resolve;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
size() {
|
||||
return size;
|
||||
},
|
||||
highWaterMark: 1
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const writePromise1 = writer.write('chunk1');
|
||||
size = NaN;
|
||||
const writePromise2 = writer.write('chunk2');
|
||||
const abortPromise = writer.abort(error2);
|
||||
resolveWrite();
|
||||
return Promise.all([
|
||||
writePromise1,
|
||||
promise_rejects(t, new RangeError(), writePromise2, 'second write() should reject'),
|
||||
promise_rejects(t, new RangeError(), abortPromise, 'abort() should reject')
|
||||
]).then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'chunk1'], 'sink abort() should not be called');
|
||||
});
|
||||
});
|
||||
}, 'sink abort() should not be called if stream was erroring due to bad strategy before abort() was called');
|
||||
|
||||
done();
|
||||
|
|
|
@ -61,7 +61,7 @@ promise_test(t => {
|
|||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
const p1 = promise_rejects(t, new TypeError(), writer.write('a'), 'write should reject with a TypeError');
|
||||
const p1 = promise_rejects(t, error1, writer.write('a'), 'write should reject with the thrown error');
|
||||
|
||||
const p2 = promise_rejects(t, error1, writer.closed, 'closed should reject with the thrown error');
|
||||
|
||||
|
|
|
@ -26,41 +26,70 @@ promise_test(() => {
|
|||
}, 'fulfillment value of ws.close() call must be undefined even if the underlying sink returns a non-undefined ' +
|
||||
'value');
|
||||
|
||||
promise_test(t => {
|
||||
const passedError = new Error('error me');
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
let resolveClose;
|
||||
const ws = new WritableStream({
|
||||
close(c) {
|
||||
start(c) {
|
||||
controller = c;
|
||||
return delay(50);
|
||||
},
|
||||
close() {
|
||||
return new Promise(resolve => {
|
||||
resolveClose = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
return Promise.all([
|
||||
writer.close(),
|
||||
delay(10).then(() => controller.error(passedError)),
|
||||
promise_rejects(t, passedError, writer.closed,
|
||||
'closed promise should be rejected with the passed error'),
|
||||
delay(70).then(() => promise_rejects(t, passedError, writer.closed, 'closed should stay rejected'))
|
||||
]);
|
||||
}, 'when sink calls error asynchronously while closing, the stream should become errored');
|
||||
const closePromise = writer.close();
|
||||
return flushAsyncEvents().then(() => {
|
||||
controller.error(error1);
|
||||
return flushAsyncEvents();
|
||||
}).then(() => {
|
||||
resolveClose();
|
||||
return Promise.all([
|
||||
closePromise,
|
||||
writer.closed,
|
||||
flushAsyncEvents().then(() => writer.closed)]);
|
||||
});
|
||||
}, 'when sink calls error asynchronously while sink close is in-flight, the stream should not become errored');
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
const passedError = new Error('error me');
|
||||
const ws = new WritableStream({
|
||||
close(controller) {
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
close() {
|
||||
controller.error(passedError);
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
return writer.close().then(() => promise_rejects(t, passedError, writer.closed, 'closed should stay rejected'));
|
||||
}, 'when sink calls error synchronously while closing, the stream should become errored');
|
||||
return writer.close().then(() => writer.closed);
|
||||
}, 'when sink calls error synchronously while closing, the stream should not become errored');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
close() {
|
||||
throw error1;
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
return Promise.all([
|
||||
writer.write('y'),
|
||||
promise_rejects(t, error1, writer.close(), 'close() must reject with the error'),
|
||||
promise_rejects(t, error1, writer.closed, 'closed must reject with the error')
|
||||
]);
|
||||
}, 'when the sink throws during close, and the close is requested while a write is still in-flight, the stream should ' +
|
||||
'become errored during the close');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = new WritableStream({
|
||||
write(chunk, controller) {
|
||||
controller.error(error1);
|
||||
|
@ -76,9 +105,13 @@ promise_test(t => {
|
|||
});
|
||||
}, 'releaseLock on a stream with a pending write in which the stream has been errored');
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
close(controller) {
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
close() {
|
||||
controller.error(error1);
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
@ -90,7 +123,7 @@ promise_test(t => {
|
|||
return delay(0).then(() => {
|
||||
writer.releaseLock();
|
||||
});
|
||||
}, 'releaseLock on a stream with a pending close in which the stream has been errored');
|
||||
}, 'releaseLock on a stream with a pending close in which controller.error() was called');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = recordingWritableStream();
|
||||
|
@ -276,7 +309,7 @@ promise_test(() => {
|
|||
});
|
||||
}, 'promises must fulfill/reject in the expected order on closure');
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(() => {
|
||||
const ws = new WritableStream({});
|
||||
|
||||
// Wait until the WritableStream starts so that the close() call gets processed. Otherwise, abort() will be
|
||||
|
@ -295,8 +328,7 @@ promise_test(t => {
|
|||
abortPromise.then(() => {
|
||||
events.push('abortPromise');
|
||||
}),
|
||||
promise_rejects(t, new TypeError(), writer.closed, 'writer.closed must reject with an error indicating abort')
|
||||
.then(() => {
|
||||
writer.closed.then(() => {
|
||||
events.push('closed');
|
||||
})
|
||||
]).then(() => {
|
||||
|
@ -321,22 +353,16 @@ promise_test(t => {
|
|||
const abortPromise = writer.abort(error2);
|
||||
|
||||
const events = [];
|
||||
closePromise.catch(() => events.push('closePromise'));
|
||||
abortPromise.catch(() => events.push('abortPromise'));
|
||||
writer.closed.catch(() => events.push('closed'));
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, closePromise,
|
||||
'closePromise must reject with the error returned from the sink\'s close method')
|
||||
.then(() => {
|
||||
events.push('closePromise');
|
||||
}),
|
||||
'closePromise must reject with the error returned from the sink\'s close method'),
|
||||
promise_rejects(t, error1, abortPromise,
|
||||
'abortPromise must reject with the error returned from the sink\'s close method')
|
||||
.then(() => {
|
||||
events.push('abortPromise');
|
||||
}),
|
||||
promise_rejects(t, error1, writer.closed,
|
||||
'writer.closed must reject with the error returned from the sink\'s close method')
|
||||
.then(() => {
|
||||
events.push('closed');
|
||||
})
|
||||
'abortPromise must reject with the error returned from the sink\'s close method'),
|
||||
promise_rejects(t, new TypeError(), writer.closed,
|
||||
'writer.closed must reject with a TypeError indicating the stream was aborted')
|
||||
]).then(() => {
|
||||
assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
|
||||
'promises must fulfill/reject in the expected order');
|
||||
|
@ -344,4 +370,37 @@ promise_test(t => {
|
|||
});
|
||||
}, 'promises must fulfill/reject in the expected order on aborted and errored closure');
|
||||
|
||||
promise_test(t => {
|
||||
let resolveWrite;
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
write(chunk, c) {
|
||||
controller = c;
|
||||
return new Promise(resolve => {
|
||||
resolveWrite = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const writePromise = writer.write('c');
|
||||
controller.error(error1);
|
||||
const closePromise = writer.close();
|
||||
let closeRejected = false;
|
||||
closePromise.catch(() => {
|
||||
closeRejected = true;
|
||||
});
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_false(closeRejected);
|
||||
resolveWrite();
|
||||
return Promise.all([
|
||||
writePromise,
|
||||
promise_rejects(t, error1, closePromise, 'close() should reject')
|
||||
]).then(() => {
|
||||
assert_true(closeRejected);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 'close() should not reject until no sink methods are in flight');
|
||||
|
||||
done();
|
||||
|
|
|
@ -37,24 +37,23 @@ promise_test(t => {
|
|||
|
||||
return Promise.all([
|
||||
writer.write('a'),
|
||||
promise_rejects(t, error1, writer.closed, 'controller.error() in write() should errored the stream')
|
||||
promise_rejects(t, error1, writer.closed, 'controller.error() in write() should error the stream')
|
||||
]);
|
||||
}, 'controller argument should be passed to write method');
|
||||
|
||||
// Older versions of the standard had the controller argument passed to close(). It wasn't useful, and so has been
|
||||
// removed. This test remains to identify implementations that haven't been updated.
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
close(controller) {
|
||||
controller.error(error1);
|
||||
close(...args) {
|
||||
t.step(() => {
|
||||
assert_array_equals(args, [], 'no arguments should be passed to close');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const writer = ws.getWriter();
|
||||
|
||||
return Promise.all([
|
||||
writer.close(),
|
||||
promise_rejects(t, error1, writer.closed, 'controller.error() in close() should error the stream')
|
||||
]);
|
||||
}, 'controller argument should be passed to close method');
|
||||
return ws.getWriter().close();
|
||||
}, 'controller argument should not be passed to close method');
|
||||
|
||||
promise_test(() => {
|
||||
const ws = new WritableStream({}, {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>error.js dedicated worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new Worker('error.js'));
|
||||
</script>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>error.js browser context wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
|
||||
|
||||
<script src="error.js"></script>
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
const error1 = new Error('error1');
|
||||
error1.name = 'error1';
|
||||
|
||||
const error2 = new Error('error2');
|
||||
error2.name = 'error2';
|
||||
|
||||
promise_test(t => {
|
||||
const ws = new WritableStream({
|
||||
start(controller) {
|
||||
controller.error(error1);
|
||||
}
|
||||
});
|
||||
return promise_rejects(t, error1, ws.getWriter().closed, 'stream should be errored');
|
||||
}, 'controller.error() should error the stream');
|
||||
|
||||
test(() => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
ws.abort();
|
||||
controller.error(error1);
|
||||
}, 'controller.error() on erroring stream should not throw');
|
||||
|
||||
promise_test(t => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
controller.error(error1);
|
||||
controller.error(error2);
|
||||
return promise_rejects(t, error1, ws.getWriter().closed, 'first controller.error() should win');
|
||||
}, 'surplus calls to controller.error() should be a no-op');
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return ws.abort().then(() => {
|
||||
controller.error(error1);
|
||||
});
|
||||
}, 'controller.error() on errored stream should not throw');
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return ws.getWriter().close().then(() => {
|
||||
controller.error(error1);
|
||||
});
|
||||
}, 'controller.error() on closed stream should not throw');
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>error.js service worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
service_worker_test('error.js', 'Service worker test setup');
|
||||
</script>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>error.js shared worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new SharedWorker('error.js'));
|
||||
</script>
|
|
@ -226,4 +226,25 @@ promise_test(() => {
|
|||
return writer2.ready;
|
||||
}, 'redundant releaseLock() is no-op');
|
||||
|
||||
promise_test(() => {
|
||||
const events = [];
|
||||
const ws = new WritableStream();
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
// Force the ready promise back to a pending state.
|
||||
const writerPromise = writer.write('dummy');
|
||||
const readyPromise = writer.ready.catch(() => events.push('ready'));
|
||||
const closedPromise = writer.closed.catch(() => events.push('closed'));
|
||||
writer.releaseLock();
|
||||
return Promise.all([readyPromise, closedPromise]).then(() => {
|
||||
assert_array_equals(events, ['ready', 'closed'], 'ready promise should fire before closed promise');
|
||||
// Stop the writer promise hanging around after the test has finished.
|
||||
return Promise.all([
|
||||
writerPromise,
|
||||
ws.abort()
|
||||
]);
|
||||
});
|
||||
});
|
||||
}, 'ready promise should fire before closed on releaseLock');
|
||||
|
||||
done();
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>properties.js dedicated worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new Worker('properties.js'));
|
||||
</script>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>properties.js browser context wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
|
||||
|
||||
<script src="properties.js"></script>
|
|
@ -0,0 +1,219 @@
|
|||
'use strict';
|
||||
|
||||
if (self.importScripts) {
|
||||
self.importScripts('/resources/testharness.js');
|
||||
}
|
||||
|
||||
// The purpose of this file is to test for objects, attributes and arguments that should not exist.
|
||||
// The test cases are generated from data tables to reduce duplication.
|
||||
|
||||
// Courtesy of André Bargull. Source is https://esdiscuss.org/topic/isconstructor#content-11.
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new new Proxy(o, { construct: () => ({}) })();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const func of ['WritableStreamDefaultController', 'WritableStreamDefaultWriter']) {
|
||||
test(() => {
|
||||
assert_equals(self[func], undefined, `${func} should not be defined`);
|
||||
}, `${func} should not be exported on the global object`);
|
||||
}
|
||||
|
||||
// Now get hold of the symbols so we can test their properties.
|
||||
self.WritableStreamDefaultController = (() => {
|
||||
let controller;
|
||||
new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller.constructor;
|
||||
})();
|
||||
self.WritableStreamDefaultWriter = new WritableStream().getWriter().constructor;
|
||||
|
||||
const expected = {
|
||||
WritableStream: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 0
|
||||
},
|
||||
locked: {
|
||||
type: 'getter'
|
||||
},
|
||||
abort: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
getWriter: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
}
|
||||
},
|
||||
WritableStreamDefaultController: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 4
|
||||
},
|
||||
error: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
}
|
||||
},
|
||||
WritableStreamDefaultWriter: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 1
|
||||
},
|
||||
closed: {
|
||||
type: 'getter'
|
||||
},
|
||||
desiredSize: {
|
||||
type: 'getter'
|
||||
},
|
||||
ready: {
|
||||
type: 'getter'
|
||||
},
|
||||
abort: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
close: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
},
|
||||
releaseLock: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
},
|
||||
write: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const c in expected) {
|
||||
const properties = expected[c];
|
||||
const prototype = self[c].prototype;
|
||||
for (const name in properties) {
|
||||
const fullName = `${c}.prototype.${name}`;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
||||
test(() => {
|
||||
const { configurable, enumerable } = descriptor;
|
||||
assert_true(configurable, `${name} should be configurable`);
|
||||
assert_false(enumerable, `${name} should not be enumerable`);
|
||||
}, `${fullName} should have standard properties`);
|
||||
const type = properties[name].type;
|
||||
switch (type) {
|
||||
case 'getter':
|
||||
test(() => {
|
||||
const { writable, get, set } = descriptor;
|
||||
assert_equals(writable, undefined, `${name} should not be a data descriptor`);
|
||||
assert_equals(typeof get, 'function', `${name} should have a getter`);
|
||||
assert_equals(set, undefined, `${name} should not have a setter`);
|
||||
}, `${fullName} should be a getter`);
|
||||
break;
|
||||
|
||||
case 'constructor':
|
||||
case 'method':
|
||||
test(() => {
|
||||
assert_true(descriptor.writable, `${name} should be writable`);
|
||||
assert_equals(typeof prototype[name], 'function', `${name} should be a function`);
|
||||
assert_equals(prototype[name].length, properties[name].length,
|
||||
`${name} should take ${properties[name].length} arguments`);
|
||||
if (type === 'constructor') {
|
||||
assert_true(IsConstructor(prototype[name]), `${name} should be a constructor`);
|
||||
} else {
|
||||
assert_false(IsConstructor(prototype[name]), `${name} should not be a constructor`);
|
||||
}
|
||||
}, `${fullName} should be a ${type}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
test(() => {
|
||||
const expectedPropertyNames = Object.keys(properties).sort();
|
||||
const actualPropertyNames = Object.getOwnPropertyNames(prototype).sort();
|
||||
assert_array_equals(actualPropertyNames, expectedPropertyNames,
|
||||
`${c} properties should match expected properties`);
|
||||
}, `${c}.prototype should have exactly the expected properties`);
|
||||
}
|
||||
|
||||
const sinkMethods = {
|
||||
start: {
|
||||
length: 1,
|
||||
trigger: () => {}
|
||||
},
|
||||
write: {
|
||||
length: 2,
|
||||
trigger: writer => writer.write()
|
||||
},
|
||||
close: {
|
||||
length: 0,
|
||||
trigger: writer => writer.close()
|
||||
},
|
||||
abort: {
|
||||
length: 1,
|
||||
trigger: writer => writer.abort()
|
||||
}
|
||||
};
|
||||
|
||||
for (const method in sinkMethods) {
|
||||
const { length, trigger } = sinkMethods[method];
|
||||
|
||||
// Some semantic tests of how sink methods are called can be found in general.js, as well as in the test files
|
||||
// specific to each method.
|
||||
promise_test(() => {
|
||||
let argCount;
|
||||
const ws = new WritableStream({
|
||||
[method](...args) {
|
||||
argCount = args.length;
|
||||
}
|
||||
});
|
||||
return Promise.resolve(trigger(ws.getWriter())).then(() => {
|
||||
assert_equals(argCount, length, `${method} should be called with ${length} arguments`);
|
||||
});
|
||||
}, `sink method ${method} should be called with the right number of arguments`);
|
||||
|
||||
promise_test(() => {
|
||||
let methodWasCalled = false;
|
||||
function Sink() {}
|
||||
Sink.prototype = {
|
||||
[method]() {
|
||||
methodWasCalled = true;
|
||||
}
|
||||
};
|
||||
const ws = new WritableStream(new Sink());
|
||||
return Promise.resolve(trigger(ws.getWriter())).then(() => {
|
||||
assert_true(methodWasCalled, `${method} should be called`);
|
||||
});
|
||||
}, `sink method ${method} should be called even when it's located on the prototype chain`);
|
||||
|
||||
if (method !== 'start') {
|
||||
promise_test(t => {
|
||||
const unreachedTraps = ['getPrototypeOf', 'setPrototypeOf', 'isExtensible', 'preventExtensions',
|
||||
'getOwnPropertyDescriptor', 'defineProperty', 'has', 'set', 'deleteProperty', 'ownKeys',
|
||||
'apply', 'construct'];
|
||||
const handler = {
|
||||
get: t.step_func((target, property) => {
|
||||
if (property === 'type') {
|
||||
return undefined;
|
||||
}
|
||||
assert_in_array(property, ['start', method], `only start() and ${method}() should be called`);
|
||||
return () => Promise.resolve();
|
||||
})
|
||||
};
|
||||
for (const trap of unreachedTraps) {
|
||||
handler[trap] = t.unreached_func(`${trap} should not be trapped`);
|
||||
}
|
||||
const sink = new Proxy({}, handler);
|
||||
const ws = new WritableStream(sink);
|
||||
return trigger(ws.getWriter());
|
||||
}, `unexpected properties should not be accessed when calling sink method ${method}`);
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>properties.js service worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
service_worker_test('properties.js', 'Service worker test setup');
|
||||
</script>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>properties.js shared worker wrapper file</title>
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
fetch_tests_from_worker(new SharedWorker('properties.js'));
|
||||
</script>
|
|
@ -78,8 +78,8 @@ promise_test(t => {
|
|||
const resolved = [];
|
||||
const writer = ws.getWriter();
|
||||
const readyPromise1 = writer.ready.then(() => resolved.push('ready1'));
|
||||
const writePromise = promise_rejects(t, new TypeError(), writer.write(),
|
||||
'write() should reject with a TypeError')
|
||||
const writePromise = promise_rejects(t, error1, writer.write(),
|
||||
'write() should reject with the error')
|
||||
.then(() => resolved.push('write'));
|
||||
const readyPromise2 = promise_rejects(t, error1, writer.ready, 'ready should reject with error1')
|
||||
.then(() => resolved.push('ready2'));
|
||||
|
|
|
@ -6,6 +6,8 @@ if (self.importScripts) {
|
|||
self.importScripts('../resources/recording-streams.js');
|
||||
}
|
||||
|
||||
const error1 = { name: 'error1' };
|
||||
|
||||
promise_test(() => {
|
||||
let resolveStartPromise;
|
||||
const ws = recordingWritableStream({
|
||||
|
@ -93,15 +95,74 @@ promise_test(() => {
|
|||
}, 'underlying sink\'s write or close should not be invoked if the promise returned by start is rejected');
|
||||
|
||||
promise_test(t => {
|
||||
const rejection = { name: 'this is checked' };
|
||||
const ws = new WritableStream({
|
||||
start() {
|
||||
return {
|
||||
then(onFulfilled, onRejected) { onRejected(rejection); }
|
||||
then(onFulfilled, onRejected) { onRejected(error1); }
|
||||
};
|
||||
}
|
||||
});
|
||||
return promise_rejects(t, rejection, ws.getWriter().closed, 'closed promise should be rejected');
|
||||
return promise_rejects(t, error1, ws.getWriter().closed, 'closed promise should be rejected');
|
||||
}, 'returning a thenable from start() should work');
|
||||
|
||||
promise_test(t => {
|
||||
const ws = recordingWritableStream({
|
||||
start(controller) {
|
||||
controller.error(error1);
|
||||
}
|
||||
});
|
||||
return promise_rejects(t, error1, ws.getWriter().write('a'), 'write() should reject with the error')
|
||||
.then(() => {
|
||||
assert_array_equals(ws.events, [], 'sink write() should not have been called');
|
||||
});
|
||||
}, 'controller.error() during start should cause writes to fail');
|
||||
|
||||
promise_test(t => {
|
||||
let controller;
|
||||
let resolveStart;
|
||||
const ws = recordingWritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return new Promise(resolve => {
|
||||
resolveStart = resolve;
|
||||
});
|
||||
}
|
||||
});
|
||||
const writer = ws.getWriter();
|
||||
const writePromise = writer.write('a');
|
||||
const closePromise = writer.close();
|
||||
controller.error(error1);
|
||||
resolveStart();
|
||||
return Promise.all([
|
||||
promise_rejects(t, error1, writePromise, 'write() should fail'),
|
||||
promise_rejects(t, error1, closePromise, 'close() should fail')
|
||||
]).then(() => {
|
||||
assert_array_equals(ws.events, [], 'sink write() and close() should not have been called');
|
||||
});
|
||||
}, 'controller.error() during async start should cause existing writes to fail');
|
||||
|
||||
promise_test(t => {
|
||||
const events = [];
|
||||
const promises = [];
|
||||
function catchAndRecord(promise, name) {
|
||||
promises.push(promise.then(t.unreached_func(`promise ${name} should not resolve`),
|
||||
() => {
|
||||
events.push(name);
|
||||
}));
|
||||
}
|
||||
const ws = new WritableStream({
|
||||
start() {
|
||||
return Promise.reject();
|
||||
}
|
||||
}, new CountQueuingStrategy({ highWaterMark: 0 }));
|
||||
const writer = ws.getWriter();
|
||||
catchAndRecord(writer.ready, 'ready');
|
||||
catchAndRecord(writer.closed, 'closed');
|
||||
catchAndRecord(writer.write(), 'write');
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
assert_array_equals(events, ['ready', 'write', 'closed'], 'promises should reject in standard order');
|
||||
});
|
||||
}, 'when start() rejects, writer promises should reject in standard order');
|
||||
|
||||
done();
|
||||
|
|
|
@ -191,8 +191,8 @@ promise_test(t => {
|
|||
'writer.closed must reject with the error passed to the controller')
|
||||
]);
|
||||
});
|
||||
}, 'writer.write(), ready and closed reject with the error passed to controller.error() made before sink.write'
|
||||
+ ' rejection');
|
||||
}, 'writer.write(), ready and closed reject with the error passed to controller.error() made before sink.write' +
|
||||
' rejection');
|
||||
|
||||
promise_test(() => {
|
||||
const numberOfWrites = 1000;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue