1// META: global=window,worker
2'use strict';
3
4
5test(() => {
6
7  const theError = new Error('a unique string');
8
9  assert_throws_exactly(theError, () => {
10    new ReadableStream({
11      get start() {
12        throw theError;
13      }
14    });
15  }, 'constructing the stream should re-throw the error');
16
17}, 'Underlying source start: throwing getter');
18
19
20test(() => {
21
22  const theError = new Error('a unique string');
23
24  assert_throws_exactly(theError, () => {
25    new ReadableStream({
26      start() {
27        throw theError;
28      }
29    });
30  }, 'constructing the stream should re-throw the error');
31
32}, 'Underlying source start: throwing method');
33
34
35test(() => {
36
37  const theError = new Error('a unique string');
38  assert_throws_exactly(theError, () => new ReadableStream({
39    get pull() {
40      throw theError;
41    }
42  }), 'constructor should throw');
43
44}, 'Underlying source: throwing pull getter (initial pull)');
45
46
47promise_test(t => {
48
49  const theError = new Error('a unique string');
50  const rs = new ReadableStream({
51    pull() {
52      throw theError;
53    }
54  });
55
56  return promise_rejects_exactly(t, theError, rs.getReader().closed);
57
58}, 'Underlying source: throwing pull method (initial pull)');
59
60
61promise_test(t => {
62
63  const theError = new Error('a unique string');
64
65  let counter = 0;
66  const rs = new ReadableStream({
67    get pull() {
68      ++counter;
69      if (counter === 1) {
70        return c => c.enqueue('a');
71      }
72
73      throw theError;
74    }
75  });
76  const reader = rs.getReader();
77
78  return Promise.all([
79    reader.read().then(r => {
80      assert_object_equals(r, { value: 'a', done: false }, 'the first chunk read should be correct');
81    }),
82    reader.read().then(r => {
83      assert_object_equals(r, { value: 'a', done: false }, 'the second chunk read should be correct');
84      assert_equals(counter, 1, 'counter should be 1');
85    })
86  ]);
87
88}, 'Underlying source pull: throwing getter (second pull does not result in a second get)');
89
90promise_test(t => {
91
92  const theError = new Error('a unique string');
93
94  let counter = 0;
95  const rs = new ReadableStream({
96    pull(c) {
97      ++counter;
98      if (counter === 1) {
99        c.enqueue('a');
100        return;
101      }
102
103      throw theError;
104    }
105  });
106  const reader = rs.getReader();
107
108  return Promise.all([
109    reader.read().then(r => {
110      assert_object_equals(r, { value: 'a', done: false }, 'the chunk read should be correct');
111    }),
112    promise_rejects_exactly(t, theError, reader.closed)
113  ]);
114
115}, 'Underlying source pull: throwing method (second pull)');
116
117test(() => {
118
119  const theError = new Error('a unique string');
120  assert_throws_exactly(theError, () => new ReadableStream({
121    get cancel() {
122      throw theError;
123    }
124  }), 'constructor should throw');
125
126}, 'Underlying source cancel: throwing getter');
127
128promise_test(t => {
129
130  const theError = new Error('a unique string');
131  const rs = new ReadableStream({
132    cancel() {
133      throw theError;
134    }
135  });
136
137  return promise_rejects_exactly(t, theError, rs.cancel());
138
139}, 'Underlying source cancel: throwing method');
140
141promise_test(() => {
142
143  let controller;
144  const rs = new ReadableStream({
145    start(c) {
146      controller = c;
147    }
148  });
149
150  rs.cancel();
151  assert_throws_js(TypeError, () => controller.enqueue('a'), 'Calling enqueue after canceling should throw');
152
153  return rs.getReader().closed;
154
155}, 'Underlying source: calling enqueue on an empty canceled stream should throw');
156
157promise_test(() => {
158
159  let controller;
160  const rs = new ReadableStream({
161    start(c) {
162      c.enqueue('a');
163      c.enqueue('b');
164      controller = c;
165    }
166  });
167
168  rs.cancel();
169  assert_throws_js(TypeError, () => controller.enqueue('c'), 'Calling enqueue after canceling should throw');
170
171  return rs.getReader().closed;
172
173}, 'Underlying source: calling enqueue on a non-empty canceled stream should throw');
174
175promise_test(() => {
176
177  return new ReadableStream({
178    start(c) {
179      c.close();
180      assert_throws_js(TypeError, () => c.enqueue('a'), 'call to enqueue should throw a TypeError');
181    }
182  }).getReader().closed;
183
184}, 'Underlying source: calling enqueue on a closed stream should throw');
185
186promise_test(t => {
187
188  const theError = new Error('boo');
189  const closed = new ReadableStream({
190    start(c) {
191      c.error(theError);
192      assert_throws_js(TypeError, () => c.enqueue('a'), 'call to enqueue should throw the error');
193    }
194  }).getReader().closed;
195
196  return promise_rejects_exactly(t, theError, closed);
197
198}, 'Underlying source: calling enqueue on an errored stream should throw');
199
200promise_test(() => {
201
202  return new ReadableStream({
203    start(c) {
204      c.close();
205      assert_throws_js(TypeError, () => c.close(), 'second call to close should throw a TypeError');
206    }
207  }).getReader().closed;
208
209}, 'Underlying source: calling close twice on an empty stream should throw the second time');
210
211promise_test(() => {
212
213  let startCalled = false;
214  let readCalled = false;
215  const reader = new ReadableStream({
216    start(c) {
217      c.enqueue('a');
218      c.close();
219      assert_throws_js(TypeError, () => c.close(), 'second call to close should throw a TypeError');
220      startCalled = true;
221    }
222  }).getReader();
223
224  return Promise.all([
225    reader.read().then(r => {
226      assert_object_equals(r, { value: 'a', done: false }, 'read() should read the enqueued chunk');
227      readCalled = true;
228    }),
229    reader.closed.then(() => {
230      assert_true(startCalled);
231      assert_true(readCalled);
232    })
233  ]);
234
235}, 'Underlying source: calling close twice on a non-empty stream should throw the second time');
236
237promise_test(() => {
238
239  let controller;
240  let startCalled = false;
241  const rs = new ReadableStream({
242    start(c) {
243      controller = c;
244      startCalled = true;
245    }
246  });
247
248  rs.cancel();
249  assert_throws_js(TypeError, () => controller.close(), 'Calling close after canceling should throw');
250
251  return rs.getReader().closed.then(() => {
252    assert_true(startCalled);
253  });
254
255}, 'Underlying source: calling close on an empty canceled stream should throw');
256
257promise_test(() => {
258
259  let controller;
260  let startCalled = false;
261  const rs = new ReadableStream({
262    start(c) {
263      controller = c;
264      c.enqueue('a');
265      startCalled = true;
266    }
267  });
268
269  rs.cancel();
270  assert_throws_js(TypeError, () => controller.close(), 'Calling close after canceling should throw');
271
272  return rs.getReader().closed.then(() => {
273    assert_true(startCalled);
274  });
275
276}, 'Underlying source: calling close on a non-empty canceled stream should throw');
277
278promise_test(() => {
279
280  const theError = new Error('boo');
281  let startCalled = false;
282
283  const closed = new ReadableStream({
284    start(c) {
285      c.error(theError);
286      assert_throws_js(TypeError, () => c.close(), 'call to close should throw a TypeError');
287      startCalled = true;
288    }
289  }).getReader().closed;
290
291  return closed.catch(e => {
292    assert_true(startCalled);
293    assert_equals(e, theError, 'closed should reject with the error');
294  });
295
296}, 'Underlying source: calling close after error should throw');
297
298promise_test(() => {
299
300  const theError = new Error('boo');
301  let startCalled = false;
302
303  const closed = new ReadableStream({
304    start(c) {
305      c.error(theError);
306      c.error();
307      startCalled = true;
308    }
309  }).getReader().closed;
310
311  return closed.catch(e => {
312    assert_true(startCalled);
313    assert_equals(e, theError, 'closed should reject with the error');
314  });
315
316}, 'Underlying source: calling error twice should not throw');
317
318promise_test(() => {
319
320  let startCalled = false;
321
322  const closed = new ReadableStream({
323    start(c) {
324      c.close();
325      c.error();
326      startCalled = true;
327    }
328  }).getReader().closed;
329
330  return closed.then(() => assert_true(startCalled));
331
332}, 'Underlying source: calling error after close should not throw');
333
334promise_test(() => {
335
336  let startCalled = false;
337  const firstError = new Error('1');
338  const secondError = new Error('2');
339
340  const closed = new ReadableStream({
341    start(c) {
342      c.error(firstError);
343      startCalled = true;
344      return Promise.reject(secondError);
345    }
346  }).getReader().closed;
347
348  return closed.catch(e => {
349    assert_true(startCalled);
350    assert_equals(e, firstError, 'closed should reject with the first error');
351  });
352
353}, 'Underlying source: calling error and returning a rejected promise from start should cause the stream to error ' +
354   'with the first error');
355
356promise_test(() => {
357
358  let startCalled = false;
359  const firstError = new Error('1');
360  const secondError = new Error('2');
361
362  const closed = new ReadableStream({
363    pull(c) {
364      c.error(firstError);
365      startCalled = true;
366      return Promise.reject(secondError);
367    }
368  }).getReader().closed;
369
370  return closed.catch(e => {
371    assert_true(startCalled);
372    assert_equals(e, firstError, 'closed should reject with the first error');
373  });
374
375}, 'Underlying source: calling error and returning a rejected promise from pull should cause the stream to error ' +
376   'with the first error');
377
378const error1 = { name: 'error1' };
379
380promise_test(t => {
381
382  let pullShouldThrow = false;
383  const rs = new ReadableStream({
384    pull(controller) {
385      if (pullShouldThrow) {
386        throw error1;
387      }
388      controller.enqueue(0);
389    }
390  }, new CountQueuingStrategy({highWaterMark: 1}));
391  const reader = rs.getReader();
392  return Promise.resolve().then(() => {
393    pullShouldThrow = true;
394    return Promise.all([
395      reader.read(),
396      promise_rejects_exactly(t, error1, reader.closed, '.closed promise should reject')
397    ]);
398  });
399
400}, 'read should not error if it dequeues and pull() throws');
401