mirror of
https://github.com/servo/servo.git
synced 2025-06-23 16:44:33 +01:00
406 lines
12 KiB
JavaScript
406 lines
12 KiB
JavaScript
'use strict';
|
|
|
|
if (self.importScripts) {
|
|
self.importScripts('/resources/testharness.js');
|
|
self.importScripts('../resources/test-utils.js');
|
|
self.importScripts('../resources/recording-streams.js');
|
|
}
|
|
|
|
const error1 = new Error('error1');
|
|
error1.name = 'error1';
|
|
|
|
const error2 = new Error('error2');
|
|
error2.name = 'error2';
|
|
|
|
promise_test(() => {
|
|
const ws = new WritableStream({
|
|
close() {
|
|
return 'Hello';
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
const closePromise = writer.close();
|
|
return closePromise.then(value => assert_equals(value, undefined, 'fulfillment value must be undefined'));
|
|
}, 'fulfillment value of ws.close() call must be undefined even if the underlying sink returns a non-undefined ' +
|
|
'value');
|
|
|
|
promise_test(() => {
|
|
let controller;
|
|
let resolveClose;
|
|
const ws = new WritableStream({
|
|
start(c) {
|
|
controller = c;
|
|
},
|
|
close() {
|
|
return new Promise(resolve => {
|
|
resolveClose = resolve;
|
|
});
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
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(() => {
|
|
let controller;
|
|
const passedError = new Error('error me');
|
|
const ws = new WritableStream({
|
|
start(c) {
|
|
controller = c;
|
|
},
|
|
close() {
|
|
controller.error(passedError);
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
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);
|
|
return new Promise(() => {});
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
writer.write('a');
|
|
|
|
return delay(0).then(() => {
|
|
writer.releaseLock();
|
|
});
|
|
}, 'releaseLock on a stream with a pending write in which the stream has been errored');
|
|
|
|
promise_test(() => {
|
|
let controller;
|
|
const ws = new WritableStream({
|
|
start(c) {
|
|
controller = c;
|
|
},
|
|
close() {
|
|
controller.error(error1);
|
|
return new Promise(() => {});
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
writer.close();
|
|
|
|
return delay(0).then(() => {
|
|
writer.releaseLock();
|
|
});
|
|
}, 'releaseLock on a stream with a pending close in which controller.error() was called');
|
|
|
|
promise_test(() => {
|
|
const ws = recordingWritableStream();
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
return writer.ready.then(() => {
|
|
assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
|
|
|
|
writer.close();
|
|
assert_equals(writer.desiredSize, 1, 'desiredSize should be still 1');
|
|
|
|
return writer.ready.then(v => {
|
|
assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
|
|
assert_array_equals(ws.events, ['close'], 'write and abort should not be called');
|
|
});
|
|
});
|
|
}, 'when close is called on a WritableStream in writable state, ready should return a fulfilled promise');
|
|
|
|
promise_test(() => {
|
|
const ws = recordingWritableStream({
|
|
write() {
|
|
return new Promise(() => {});
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
return writer.ready.then(() => {
|
|
writer.write('a');
|
|
|
|
assert_equals(writer.desiredSize, 0, 'desiredSize should be 0');
|
|
|
|
let calledClose = false;
|
|
return Promise.all([
|
|
writer.ready.then(v => {
|
|
assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
|
|
assert_true(calledClose, 'ready should not be fulfilled before writer.close() is called');
|
|
assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called');
|
|
}),
|
|
flushAsyncEvents().then(() => {
|
|
writer.close();
|
|
calledClose = true;
|
|
})
|
|
]);
|
|
});
|
|
}, 'when close is called on a WritableStream in waiting state, ready promise should be fulfilled');
|
|
|
|
promise_test(() => {
|
|
let asyncCloseFinished = false;
|
|
const ws = recordingWritableStream({
|
|
close() {
|
|
return flushAsyncEvents().then(() => {
|
|
asyncCloseFinished = true;
|
|
});
|
|
}
|
|
});
|
|
|
|
const writer = ws.getWriter();
|
|
return writer.ready.then(() => {
|
|
writer.write('a');
|
|
|
|
writer.close();
|
|
|
|
return writer.ready.then(v => {
|
|
assert_false(asyncCloseFinished, 'ready promise should be fulfilled before async close completes');
|
|
assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
|
|
assert_array_equals(ws.events, ['write', 'a', 'close'], 'sink abort() should not be called');
|
|
});
|
|
});
|
|
}, 'when close is called on a WritableStream in waiting state, ready should be fulfilled immediately even if close ' +
|
|
'takes a long time');
|
|
|
|
promise_test(t => {
|
|
const rejection = { name: 'letter' };
|
|
const ws = new WritableStream({
|
|
close() {
|
|
return {
|
|
then(onFulfilled, onRejected) { onRejected(rejection); }
|
|
};
|
|
}
|
|
});
|
|
return promise_rejects(t, rejection, ws.getWriter().close(), 'close() should return a rejection');
|
|
}, 'returning a thenable from close() should work');
|
|
|
|
promise_test(t => {
|
|
const ws = new WritableStream();
|
|
const writer = ws.getWriter();
|
|
return writer.ready.then(() => {
|
|
const closePromise = writer.close();
|
|
const closedPromise = writer.closed;
|
|
writer.releaseLock();
|
|
return Promise.all([
|
|
closePromise,
|
|
promise_rejects(t, new TypeError(), closedPromise, '.closed promise should be rejected')
|
|
]);
|
|
});
|
|
}, 'releaseLock() should not change the result of sync close()');
|
|
|
|
promise_test(t => {
|
|
const ws = new WritableStream({
|
|
close() {
|
|
return flushAsyncEvents();
|
|
}
|
|
});
|
|
const writer = ws.getWriter();
|
|
return writer.ready.then(() => {
|
|
const closePromise = writer.close();
|
|
const closedPromise = writer.closed;
|
|
writer.releaseLock();
|
|
return Promise.all([
|
|
closePromise,
|
|
promise_rejects(t, new TypeError(), closedPromise, '.closed promise should be rejected')
|
|
]);
|
|
});
|
|
}, 'releaseLock() should not change the result of async close()');
|
|
|
|
promise_test(() => {
|
|
let resolveClose;
|
|
const ws = new WritableStream({
|
|
close() {
|
|
const promise = new Promise(resolve => {
|
|
resolveClose = resolve;
|
|
});
|
|
return promise;
|
|
}
|
|
});
|
|
const writer = ws.getWriter();
|
|
const closePromise = writer.close();
|
|
writer.releaseLock();
|
|
return delay(0).then(() => {
|
|
resolveClose();
|
|
return closePromise.then(() => {
|
|
assert_equals(ws.getWriter().desiredSize, 0, 'desiredSize should be 0');
|
|
});
|
|
});
|
|
}, 'close() should set state to CLOSED even if writer has detached');
|
|
|
|
promise_test(() => {
|
|
let resolveClose;
|
|
const ws = new WritableStream({
|
|
close() {
|
|
const promise = new Promise(resolve => {
|
|
resolveClose = resolve;
|
|
});
|
|
return promise;
|
|
}
|
|
});
|
|
const writer = ws.getWriter();
|
|
writer.close();
|
|
writer.releaseLock();
|
|
return delay(0).then(() => {
|
|
const abortingWriter = ws.getWriter();
|
|
const abortPromise = abortingWriter.abort();
|
|
abortingWriter.releaseLock();
|
|
resolveClose();
|
|
return abortPromise;
|
|
});
|
|
}, 'the promise returned by async abort during close should resolve');
|
|
|
|
// Though the order in which the promises are fulfilled or rejected is arbitrary, we're checking it for
|
|
// interoperability. We can change the order as long as we file bugs on all implementers to update to the latest tests
|
|
// to keep them interoperable.
|
|
|
|
promise_test(() => {
|
|
const ws = new WritableStream({});
|
|
|
|
const writer = ws.getWriter();
|
|
|
|
const closePromise = writer.close();
|
|
|
|
const events = [];
|
|
return Promise.all([
|
|
closePromise.then(() => {
|
|
events.push('closePromise');
|
|
}),
|
|
writer.closed.then(() => {
|
|
events.push('closed');
|
|
})
|
|
]).then(() => {
|
|
assert_array_equals(events, ['closePromise', 'closed'],
|
|
'promises must fulfill/reject in the expected order');
|
|
});
|
|
}, 'promises must fulfill/reject in the expected order on closure');
|
|
|
|
promise_test(() => {
|
|
const ws = new WritableStream({});
|
|
|
|
// Wait until the WritableStream starts so that the close() call gets processed. Otherwise, abort() will be
|
|
// processed without waiting for completion of the close().
|
|
return delay(0).then(() => {
|
|
const writer = ws.getWriter();
|
|
|
|
const closePromise = writer.close();
|
|
const abortPromise = writer.abort(error1);
|
|
|
|
const events = [];
|
|
return Promise.all([
|
|
closePromise.then(() => {
|
|
events.push('closePromise');
|
|
}),
|
|
abortPromise.then(() => {
|
|
events.push('abortPromise');
|
|
}),
|
|
writer.closed.then(() => {
|
|
events.push('closed');
|
|
})
|
|
]).then(() => {
|
|
assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
|
|
'promises must fulfill/reject in the expected order');
|
|
});
|
|
});
|
|
}, 'promises must fulfill/reject in the expected order on aborted closure');
|
|
|
|
promise_test(t => {
|
|
const ws = new WritableStream({
|
|
close() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
// Wait until the WritableStream starts so that the close() call gets processed.
|
|
return delay(0).then(() => {
|
|
const writer = ws.getWriter();
|
|
|
|
const closePromise = writer.close();
|
|
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'),
|
|
promise_rejects(t, error1, abortPromise,
|
|
'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');
|
|
});
|
|
});
|
|
}, '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();
|