|
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