forked from LeenkxTeam/Kmake
570 lines
17 KiB
JavaScript
570 lines
17 KiB
JavaScript
// META: global=window,worker,shadowrealm
|
|
// META: script=../resources/test-utils.js
|
|
// META: script=../resources/recording-streams.js
|
|
'use strict';
|
|
|
|
const error1 = new Error('error1!');
|
|
error1.name = 'error1';
|
|
|
|
const error2 = new Error('error2!');
|
|
error2.name = 'error2';
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: starts errored; preventAbort = false; fulfilled abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream({
|
|
abort() {
|
|
throw error2;
|
|
}
|
|
});
|
|
|
|
return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the abort error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: starts errored; preventAbort = false; rejected abort promise');
|
|
|
|
for (const falsy of [undefined, null, false, +0, -0, NaN, '']) {
|
|
const stringVersion = Object.is(falsy, -0) ? '-0' : String(falsy);
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: falsy }), 'pipeTo must reject with the same error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, `Errors must be propagated forward: starts errored; preventAbort = ${stringVersion} (falsy); fulfilled abort ` +
|
|
`promise`);
|
|
}
|
|
|
|
for (const truthy of [true, 'a', 1, Symbol(), { }]) {
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: truthy }),
|
|
'pipeTo must reject with the same error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, `Errors must be propagated forward: starts errored; preventAbort = ${String(truthy)} (truthy)`);
|
|
}
|
|
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true, preventCancel: true }),
|
|
'pipeTo must reject with the same error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: starts errored; preventAbort = true, preventCancel = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream({
|
|
start() {
|
|
return Promise.reject(error1);
|
|
}
|
|
});
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true, preventCancel: true, preventClose: true }),
|
|
'pipeTo must reject with the same error')
|
|
.then(() => {
|
|
assert_array_equals(rs.events, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: starts errored; preventAbort = true, preventCancel = true, preventClose = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; preventAbort = false; fulfilled abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream({
|
|
abort() {
|
|
throw error2;
|
|
}
|
|
});
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the abort error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; preventAbort = false; rejected abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true }),
|
|
'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; preventAbort = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; dest never desires chunks; ' +
|
|
'preventAbort = false; fulfilled abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream({
|
|
abort() {
|
|
throw error2;
|
|
}
|
|
}, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the abort error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; dest never desires chunks; ' +
|
|
'preventAbort = false; rejected abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true }),
|
|
'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored while empty; dest never desires chunks; ' +
|
|
'preventAbort = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['write', 'Hello', 'abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; preventAbort = false; fulfilled abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream({
|
|
abort() {
|
|
throw error2;
|
|
}
|
|
});
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the abort error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['write', 'Hello', 'abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; preventAbort = false; rejected abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream();
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true }),
|
|
'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['write', 'Hello']);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; preventAbort = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; dest never desires chunks; ' +
|
|
'preventAbort = false; fulfilled abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream({
|
|
abort() {
|
|
throw error2;
|
|
}
|
|
}, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the abort error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, ['abort', error1]);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; dest never desires chunks; ' +
|
|
'preventAbort = false; rejected abort promise');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
|
|
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true }),
|
|
'pipeTo must reject with the same error');
|
|
|
|
t.step_timeout(() => {
|
|
rs.controller.enqueue('Hello');
|
|
t.step_timeout(() => rs.controller.error(error1), 10);
|
|
}, 10);
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(rs.eventsWithoutPulls, []);
|
|
assert_array_equals(ws.events, []);
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: becomes errored after one chunk; dest never desires chunks; ' +
|
|
'preventAbort = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
let resolveWriteCalled;
|
|
const writeCalledPromise = new Promise(resolve => {
|
|
resolveWriteCalled = resolve;
|
|
});
|
|
|
|
let resolveWritePromise;
|
|
const ws = recordingWritableStream({
|
|
write() {
|
|
resolveWriteCalled();
|
|
|
|
return new Promise(resolve => {
|
|
resolveWritePromise = resolve;
|
|
});
|
|
}
|
|
});
|
|
|
|
let pipeComplete = false;
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws)).then(() => {
|
|
pipeComplete = true;
|
|
});
|
|
|
|
rs.controller.enqueue('a');
|
|
|
|
return writeCalledPromise.then(() => {
|
|
rs.controller.error(error1);
|
|
|
|
// Flush async events and verify that no shutdown occurs.
|
|
return flushAsyncEvents();
|
|
}).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a']); // no 'abort'
|
|
assert_equals(pipeComplete, false, 'the pipe must not be complete');
|
|
|
|
resolveWritePromise();
|
|
|
|
return pipePromise.then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a', 'abort', error1]);
|
|
});
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: shutdown must not occur until the final write completes');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
let resolveWriteCalled;
|
|
const writeCalledPromise = new Promise(resolve => {
|
|
resolveWriteCalled = resolve;
|
|
});
|
|
|
|
let resolveWritePromise;
|
|
const ws = recordingWritableStream({
|
|
write() {
|
|
resolveWriteCalled();
|
|
|
|
return new Promise(resolve => {
|
|
resolveWritePromise = resolve;
|
|
});
|
|
}
|
|
});
|
|
|
|
let pipeComplete = false;
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true })).then(() => {
|
|
pipeComplete = true;
|
|
});
|
|
|
|
rs.controller.enqueue('a');
|
|
|
|
return writeCalledPromise.then(() => {
|
|
rs.controller.error(error1);
|
|
|
|
// Flush async events and verify that no shutdown occurs.
|
|
return flushAsyncEvents();
|
|
}).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a']); // no 'abort'
|
|
assert_equals(pipeComplete, false, 'the pipe must not be complete');
|
|
|
|
resolveWritePromise();
|
|
return pipePromise;
|
|
}).then(() => flushAsyncEvents()).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a']); // no 'abort'
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: shutdown must not occur until the final write completes; preventAbort = true');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
let resolveWriteCalled;
|
|
const writeCalledPromise = new Promise(resolve => {
|
|
resolveWriteCalled = resolve;
|
|
});
|
|
|
|
let resolveWritePromise;
|
|
const ws = recordingWritableStream({
|
|
write() {
|
|
resolveWriteCalled();
|
|
|
|
return new Promise(resolve => {
|
|
resolveWritePromise = resolve;
|
|
});
|
|
}
|
|
}, new CountQueuingStrategy({ highWaterMark: 2 }));
|
|
|
|
let pipeComplete = false;
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws)).then(() => {
|
|
pipeComplete = true;
|
|
});
|
|
|
|
rs.controller.enqueue('a');
|
|
rs.controller.enqueue('b');
|
|
|
|
return writeCalledPromise.then(() => flushAsyncEvents()).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a'],
|
|
'the first chunk must have been written, but abort must not have happened yet');
|
|
assert_false(pipeComplete, 'the pipe should not complete while the first write is pending');
|
|
|
|
rs.controller.error(error1);
|
|
resolveWritePromise();
|
|
return flushAsyncEvents();
|
|
}).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
|
|
'the second chunk must have been written, but abort must not have happened yet');
|
|
assert_false(pipeComplete, 'the pipe should not complete while the second write is pending');
|
|
|
|
resolveWritePromise();
|
|
return pipePromise;
|
|
}).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'abort', error1],
|
|
'all chunks must have been written and abort must have happened');
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: shutdown must not occur until the final write completes; becomes errored after first write');
|
|
|
|
promise_test(t => {
|
|
|
|
const rs = recordingReadableStream();
|
|
|
|
let resolveWriteCalled;
|
|
const writeCalledPromise = new Promise(resolve => {
|
|
resolveWriteCalled = resolve;
|
|
});
|
|
|
|
let resolveWritePromise;
|
|
const ws = recordingWritableStream({
|
|
write() {
|
|
resolveWriteCalled();
|
|
|
|
return new Promise(resolve => {
|
|
resolveWritePromise = resolve;
|
|
});
|
|
}
|
|
}, new CountQueuingStrategy({ highWaterMark: 2 }));
|
|
|
|
let pipeComplete = false;
|
|
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventAbort: true })).then(() => {
|
|
pipeComplete = true;
|
|
});
|
|
|
|
rs.controller.enqueue('a');
|
|
rs.controller.enqueue('b');
|
|
|
|
return writeCalledPromise.then(() => flushAsyncEvents()).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a'],
|
|
'the first chunk must have been written, but abort must not have happened');
|
|
assert_false(pipeComplete, 'the pipe should not complete while the first write is pending');
|
|
|
|
rs.controller.error(error1);
|
|
resolveWritePromise();
|
|
}).then(() => flushAsyncEvents()).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
|
|
'the second chunk must have been written, but abort must not have happened');
|
|
assert_false(pipeComplete, 'the pipe should not complete while the second write is pending');
|
|
|
|
resolveWritePromise();
|
|
return pipePromise;
|
|
}).then(() => flushAsyncEvents()).then(() => {
|
|
assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
|
|
'all chunks must have been written, but abort must not have happened');
|
|
});
|
|
|
|
}, 'Errors must be propagated forward: shutdown must not occur until the final write completes; becomes errored after first write; preventAbort = true');
|