|
1 | | -// META: global=window,worker,jsshell |
| 1 | +// META: global=window,worker |
2 | 2 | // META: script=../resources/recording-streams.js |
3 | 3 | // META: script=../resources/test-utils.js |
4 | 4 | 'use strict'; |
@@ -53,29 +53,28 @@ promise_test(t => { |
53 | 53 | }); |
54 | 54 | }, 'an aborted signal should cause the writable stream to reject with an AbortError'); |
55 | 55 |
|
56 | | -promise_test(() => { |
57 | | - let error; |
58 | | - const rs = recordingReadableStream(errorOnPull, hwm0); |
59 | | - const ws = new WritableStream(); |
60 | | - const abortController = new AbortController(); |
61 | | - const signal = abortController.signal; |
62 | | - abortController.abort(); |
63 | | - return rs.pipeTo(ws, { signal }) |
64 | | - .catch(e => { |
65 | | - error = e; |
66 | | - }) |
67 | | - .then(() => Promise.all([ |
68 | | - rs.getReader().closed, |
69 | | - ws.getWriter().closed.catch(e => { |
70 | | - assert_equals(e, error, 'the writable should be errored with the same object'); |
71 | | - }) |
72 | | - ])) |
73 | | - .then(() => { |
| 56 | +for (const reason of [null, undefined, error1]) { |
| 57 | + promise_test(async t => { |
| 58 | + const rs = recordingReadableStream(errorOnPull, hwm0); |
| 59 | + const ws = new WritableStream(); |
| 60 | + const abortController = new AbortController(); |
| 61 | + const signal = abortController.signal; |
| 62 | + abortController.abort(reason); |
| 63 | + const pipeToPromise = rs.pipeTo(ws, { signal }); |
| 64 | + if (reason !== undefined) { |
| 65 | + await promise_rejects_exactly(t, reason, pipeToPromise, 'pipeTo rejects with abort reason'); |
| 66 | + } else { |
| 67 | + await promise_rejects_dom(t, 'AbortError', pipeToPromise, 'pipeTo rejects with AbortError'); |
| 68 | + } |
| 69 | + const error = await pipeToPromise.catch(e => e); |
| 70 | + await rs.getReader().closed; |
| 71 | + await promise_rejects_exactly(t, error, ws.getWriter().closed, 'the writable should be errored with the same object'); |
| 72 | + assert_equals(signal.reason, error, 'signal.reason should be error'), |
74 | 73 | assert_equals(rs.events.length, 2, 'cancel should have been called'); |
75 | 74 | assert_equals(rs.events[0], 'cancel', 'first event should be cancel'); |
76 | 75 | assert_equals(rs.events[1], error, 'the readable should be canceled with the same object'); |
77 | | - }); |
78 | | -}, 'all the AbortError objects should be the same object'); |
| 76 | + }, `(reason: '${reason}') all the error objects should be the same object`); |
| 77 | +} |
79 | 78 |
|
80 | 79 | promise_test(t => { |
81 | 80 | const rs = recordingReadableStream(errorOnPull, hwm0); |
@@ -115,61 +114,74 @@ promise_test(t => { |
115 | 114 | }); |
116 | 115 | }, 'preventCancel and preventAbort should prevent canceling the readable and aborting the readable'); |
117 | 116 |
|
118 | | -promise_test(t => { |
119 | | - const rs = new ReadableStream({ |
120 | | - start(controller) { |
121 | | - controller.enqueue('a'); |
122 | | - controller.enqueue('b'); |
123 | | - controller.close(); |
124 | | - } |
125 | | - }); |
126 | | - const abortController = new AbortController(); |
127 | | - const signal = abortController.signal; |
128 | | - const ws = recordingWritableStream({ |
129 | | - write() { |
130 | | - abortController.abort(); |
| 117 | +for (const reason of [null, undefined, error1]) { |
| 118 | + promise_test(async t => { |
| 119 | + const rs = new ReadableStream({ |
| 120 | + start(controller) { |
| 121 | + controller.enqueue('a'); |
| 122 | + controller.enqueue('b'); |
| 123 | + controller.close(); |
| 124 | + } |
| 125 | + }); |
| 126 | + const abortController = new AbortController(); |
| 127 | + const signal = abortController.signal; |
| 128 | + const ws = recordingWritableStream({ |
| 129 | + write() { |
| 130 | + abortController.abort(reason); |
| 131 | + } |
| 132 | + }); |
| 133 | + const pipeToPromise = rs.pipeTo(ws, { signal }); |
| 134 | + if (reason !== undefined) { |
| 135 | + await promise_rejects_exactly(t, reason, pipeToPromise, 'pipeTo rejects with abort reason'); |
| 136 | + } else { |
| 137 | + await promise_rejects_dom(t, 'AbortError', pipeToPromise, 'pipeTo rejects with AbortError'); |
131 | 138 | } |
132 | | - }); |
133 | | - return promise_rejects_dom(t, 'AbortError', rs.pipeTo(ws, { signal }), 'pipeTo should reject') |
134 | | - .then(() => { |
135 | | - assert_equals(ws.events.length, 4, 'only chunk "a" should have been written'); |
136 | | - assert_array_equals(ws.events.slice(0, 3), ['write', 'a', 'abort'], 'events should match'); |
137 | | - assert_equals(ws.events[3].name, 'AbortError', 'abort reason should be an AbortError'); |
138 | | - }); |
139 | | -}, 'abort should prevent further reads'); |
| 139 | + const error = await pipeToPromise.catch(e => e); |
| 140 | + assert_equals(signal.reason, error, 'signal.reason should be error'); |
| 141 | + assert_equals(ws.events.length, 4, 'only chunk "a" should have been written'); |
| 142 | + assert_array_equals(ws.events.slice(0, 3), ['write', 'a', 'abort'], 'events should match'); |
| 143 | + assert_equals(ws.events[3], error, 'abort reason should be error'); |
| 144 | + }, `(reason: '${reason}') abort should prevent further reads`); |
| 145 | +} |
140 | 146 |
|
141 | | -promise_test(t => { |
142 | | - let readController; |
143 | | - const rs = new ReadableStream({ |
144 | | - start(c) { |
145 | | - readController = c; |
146 | | - c.enqueue('a'); |
147 | | - c.enqueue('b'); |
148 | | - } |
149 | | - }); |
150 | | - const abortController = new AbortController(); |
151 | | - const signal = abortController.signal; |
152 | | - let resolveWrite; |
153 | | - const writePromise = new Promise(resolve => { |
154 | | - resolveWrite = resolve; |
155 | | - }); |
156 | | - const ws = recordingWritableStream({ |
157 | | - write() { |
158 | | - return writePromise; |
| 147 | +for (const reason of [null, undefined, error1]) { |
| 148 | + promise_test(async t => { |
| 149 | + let readController; |
| 150 | + const rs = new ReadableStream({ |
| 151 | + start(c) { |
| 152 | + readController = c; |
| 153 | + c.enqueue('a'); |
| 154 | + c.enqueue('b'); |
| 155 | + } |
| 156 | + }); |
| 157 | + const abortController = new AbortController(); |
| 158 | + const signal = abortController.signal; |
| 159 | + let resolveWrite; |
| 160 | + const writePromise = new Promise(resolve => { |
| 161 | + resolveWrite = resolve; |
| 162 | + }); |
| 163 | + const ws = recordingWritableStream({ |
| 164 | + write() { |
| 165 | + return writePromise; |
| 166 | + } |
| 167 | + }, new CountQueuingStrategy({ highWaterMark: Infinity })); |
| 168 | + const pipeToPromise = rs.pipeTo(ws, { signal }); |
| 169 | + await delay(0); |
| 170 | + await abortController.abort(reason); |
| 171 | + await readController.close(); // Make sure the test terminates when signal is not implemented. |
| 172 | + await resolveWrite(); |
| 173 | + if (reason !== undefined) { |
| 174 | + await promise_rejects_exactly(t, reason, pipeToPromise, 'pipeTo rejects with abort reason'); |
| 175 | + } else { |
| 176 | + await promise_rejects_dom(t, 'AbortError', pipeToPromise, 'pipeTo rejects with AbortError'); |
159 | 177 | } |
160 | | - }, new CountQueuingStrategy({ highWaterMark: Infinity })); |
161 | | - const pipeToPromise = rs.pipeTo(ws, { signal }); |
162 | | - return delay(0).then(() => { |
163 | | - abortController.abort(); |
164 | | - readController.close(); // Make sure the test terminates when signal is not implemented. |
165 | | - resolveWrite(); |
166 | | - return promise_rejects_dom(t, 'AbortError', pipeToPromise, 'pipeTo should reject'); |
167 | | - }).then(() => { |
| 178 | + const error = await pipeToPromise.catch(e => e); |
| 179 | + assert_equals(signal.reason, error, 'signal.reason should be error'); |
168 | 180 | assert_equals(ws.events.length, 6, 'chunks "a" and "b" should have been written'); |
169 | 181 | assert_array_equals(ws.events.slice(0, 5), ['write', 'a', 'write', 'b', 'abort'], 'events should match'); |
170 | | - assert_equals(ws.events[5].name, 'AbortError', 'abort reason should be an AbortError'); |
171 | | - }); |
172 | | -}, 'all pending writes should complete on abort'); |
| 182 | + assert_equals(ws.events[5], error, 'abort reason should be error'); |
| 183 | + }, `(reason: '${reason}') all pending writes should complete on abort`); |
| 184 | +} |
173 | 185 |
|
174 | 186 | promise_test(t => { |
175 | 187 | const rs = new ReadableStream({ |
@@ -373,3 +385,24 @@ promise_test(t => { |
373 | 385 | assert_array_equals(rs.events, ['pull'], 'cancel should not have been called'); |
374 | 386 | }); |
375 | 387 | }, 'abort should do nothing after the writable is errored'); |
| 388 | + |
| 389 | +promise_test(async t => { |
| 390 | + const rs = new ReadableStream({ |
| 391 | + pull(c) { |
| 392 | + c.enqueue(new Uint8Array([])); |
| 393 | + }, |
| 394 | + type: "bytes", |
| 395 | + }); |
| 396 | + const ws = new WritableStream(); |
| 397 | + const [first, second] = rs.tee(); |
| 398 | + |
| 399 | + let aborted = false; |
| 400 | + first.pipeTo(ws, { signal: AbortSignal.abort() }).catch(() => { |
| 401 | + aborted = true; |
| 402 | + }); |
| 403 | + await delay(0); |
| 404 | + assert_true(!aborted, "pipeTo should not resolve yet"); |
| 405 | + await second.cancel(); |
| 406 | + await delay(0); |
| 407 | + assert_true(aborted, "pipeTo should be aborted now"); |
| 408 | +}, "pipeTo on a teed readable byte stream should only be aborted when both branches are aborted"); |
0 commit comments