• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// META: global=window,worker
2// META: script=../resources/test-utils.js
3// META: script=../resources/recording-streams.js
4'use strict';
5
6const error1 = new Error('error1');
7error1.name = 'error1';
8
9const error2 = new Error('error2');
10error2.name = 'error2';
11
12function writeArrayToStream(array, writableStreamWriter) {
13  array.forEach(chunk => writableStreamWriter.write(chunk));
14  return writableStreamWriter.close();
15}
16
17promise_test(() => {
18  let storage;
19  const ws = new WritableStream({
20    start() {
21      storage = [];
22    },
23
24    write(chunk) {
25      return delay(0).then(() => storage.push(chunk));
26    },
27
28    close() {
29      return delay(0);
30    }
31  });
32
33  const writer = ws.getWriter();
34
35  const input = [1, 2, 3, 4, 5];
36  return writeArrayToStream(input, writer)
37      .then(() => assert_array_equals(storage, input, 'correct data should be relayed to underlying sink'));
38}, 'WritableStream should complete asynchronous writes before close resolves');
39
40promise_test(() => {
41  const ws = recordingWritableStream();
42
43  const writer = ws.getWriter();
44
45  const input = [1, 2, 3, 4, 5];
46  return writeArrayToStream(input, writer)
47      .then(() => assert_array_equals(ws.events, ['write', 1, 'write', 2, 'write', 3, 'write', 4, 'write', 5, 'close'],
48                                      'correct data should be relayed to underlying sink'));
49}, 'WritableStream should complete synchronous writes before close resolves');
50
51promise_test(() => {
52  const ws = new WritableStream({
53    write() {
54      return 'Hello';
55    }
56  });
57
58  const writer = ws.getWriter();
59
60  const writePromise = writer.write('a');
61  return writePromise
62      .then(value => assert_equals(value, undefined, 'fulfillment value must be undefined'));
63}, 'fulfillment value of ws.write() call should be undefined even if the underlying sink returns a non-undefined ' +
64    'value');
65
66promise_test(() => {
67  let resolveSinkWritePromise;
68  const ws = new WritableStream({
69    write() {
70      return new Promise(resolve => {
71        resolveSinkWritePromise = resolve;
72      });
73    }
74  });
75
76  const writer = ws.getWriter();
77
78  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
79
80  return writer.ready.then(() => {
81    const writePromise = writer.write('a');
82    let writePromiseResolved = false;
83    assert_not_equals(resolveSinkWritePromise, undefined, 'resolveSinkWritePromise should not be undefined');
84
85    assert_equals(writer.desiredSize, 0, 'desiredSize should be 0 after writer.write()');
86
87    return Promise.all([
88      writePromise.then(value => {
89        writePromiseResolved = true;
90        assert_equals(resolveSinkWritePromise, undefined, 'sinkWritePromise should be fulfilled before writePromise');
91
92        assert_equals(value, undefined, 'writePromise should be fulfilled with undefined');
93      }),
94      writer.ready.then(value => {
95        assert_equals(resolveSinkWritePromise, undefined, 'sinkWritePromise should be fulfilled before writer.ready');
96        assert_true(writePromiseResolved, 'writePromise should be fulfilled before writer.ready');
97
98        assert_equals(writer.desiredSize, 1, 'desiredSize should be 1 again');
99
100        assert_equals(value, undefined, 'writePromise should be fulfilled with undefined');
101      }),
102      flushAsyncEvents().then(() => {
103        resolveSinkWritePromise();
104        resolveSinkWritePromise = undefined;
105      })
106    ]);
107  });
108}, 'WritableStream should transition to waiting until write is acknowledged');
109
110promise_test(t => {
111  let sinkWritePromiseRejectors = [];
112  const ws = new WritableStream({
113    write() {
114      const sinkWritePromise = new Promise((r, reject) => sinkWritePromiseRejectors.push(reject));
115      return sinkWritePromise;
116    }
117  });
118
119  const writer = ws.getWriter();
120
121  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
122
123  return writer.ready.then(() => {
124    const writePromise = writer.write('a');
125    assert_equals(sinkWritePromiseRejectors.length, 1, 'there should be 1 rejector');
126    assert_equals(writer.desiredSize, 0, 'desiredSize should be 0');
127
128    const writePromise2 = writer.write('b');
129    assert_equals(sinkWritePromiseRejectors.length, 1, 'there should be still 1 rejector');
130    assert_equals(writer.desiredSize, -1, 'desiredSize should be -1');
131
132    const closedPromise = writer.close();
133
134    assert_equals(writer.desiredSize, -1, 'desiredSize should still be -1');
135
136    return Promise.all([
137      promise_rejects_exactly(t, error1, closedPromise,
138                              'closedPromise should reject with the error returned from the sink\'s write method')
139          .then(() => assert_equals(sinkWritePromiseRejectors.length, 0,
140                                    'sinkWritePromise should reject before closedPromise')),
141      promise_rejects_exactly(t, error1, writePromise,
142                              'writePromise should reject with the error returned from the sink\'s write method')
143          .then(() => assert_equals(sinkWritePromiseRejectors.length, 0,
144                                    'sinkWritePromise should reject before writePromise')),
145      promise_rejects_exactly(t, error1, writePromise2,
146                              'writePromise2 should reject with the error returned from the sink\'s write method')
147          .then(() => assert_equals(sinkWritePromiseRejectors.length, 0,
148                                    'sinkWritePromise should reject before writePromise2')),
149      flushAsyncEvents().then(() => {
150        sinkWritePromiseRejectors[0](error1);
151        sinkWritePromiseRejectors = [];
152      })
153    ]);
154  });
155}, 'when write returns a rejected promise, queued writes and close should be cleared');
156
157promise_test(t => {
158  const ws = new WritableStream({
159    write() {
160      throw error1;
161    }
162  });
163
164  const writer = ws.getWriter();
165
166  return promise_rejects_exactly(t, error1, writer.write('a'),
167                                 'write() should reject with the error returned from the sink\'s write method')
168      .then(() => promise_rejects_js(t, TypeError, writer.close(), 'close() should be rejected'));
169}, 'when sink\'s write throws an error, the stream should become errored and the promise should reject');
170
171promise_test(t => {
172  const ws = new WritableStream({
173    write(chunk, controller) {
174      controller.error(error1);
175      throw error2;
176    }
177  });
178
179  const writer = ws.getWriter();
180
181  return promise_rejects_exactly(t, error2, writer.write('a'),
182                                 'write() should reject with the error returned from the sink\'s write method ')
183  .then(() => {
184    return Promise.all([
185      promise_rejects_exactly(t, error1, writer.ready,
186                              'writer.ready must reject with the error passed to the controller'),
187      promise_rejects_exactly(t, error1, writer.closed,
188                              'writer.closed must reject with the error passed to the controller')
189    ]);
190  });
191}, 'writer.write(), ready and closed reject with the error passed to controller.error() made before sink.write' +
192    ' rejection');
193
194promise_test(() => {
195  const numberOfWrites = 1000;
196
197  let resolveFirstWritePromise;
198  let writeCount = 0;
199  const ws = new WritableStream({
200    write() {
201      ++writeCount;
202      if (!resolveFirstWritePromise) {
203        return new Promise(resolve => {
204          resolveFirstWritePromise = resolve;
205        });
206      }
207      return Promise.resolve();
208    }
209  });
210
211  const writer = ws.getWriter();
212  return writer.ready.then(() => {
213    for (let i = 1; i < numberOfWrites; ++i) {
214      writer.write('a');
215    }
216    const writePromise = writer.write('a');
217
218    assert_equals(writeCount, 1, 'should have called sink\'s write once');
219
220    resolveFirstWritePromise();
221
222    return writePromise
223        .then(() =>
224        assert_equals(writeCount, numberOfWrites, `should have called sink's write ${numberOfWrites} times`));
225  });
226}, 'a large queue of writes should be processed completely');
227
228promise_test(() => {
229  const stream = recordingWritableStream();
230  const w = stream.getWriter();
231  const WritableStreamDefaultWriter = w.constructor;
232  w.releaseLock();
233  const writer = new WritableStreamDefaultWriter(stream);
234  return writer.ready.then(() => {
235    writer.write('a');
236    assert_array_equals(stream.events, ['write', 'a'], 'write() should be passed to sink');
237  });
238}, 'WritableStreamDefaultWriter should work when manually constructed');
239
240promise_test(() => {
241  let thenCalled = false;
242  const ws = new WritableStream({
243    write() {
244      return {
245        then(onFulfilled) {
246          thenCalled = true;
247          onFulfilled();
248        }
249      };
250    }
251  });
252  return ws.getWriter().write('a').then(() => assert_true(thenCalled, 'thenCalled should be true'));
253}, 'returning a thenable from write() should work');
254
255promise_test(() => {
256  const stream = new WritableStream();
257  const writer = stream.getWriter();
258  const WritableStreamDefaultWriter = writer.constructor;
259  assert_throws_js(TypeError, () => new WritableStreamDefaultWriter(stream),
260                   'should not be able to construct on locked stream');
261  // If stream.[[writer]] no longer points to |writer| then the closed Promise
262  // won't work properly.
263  return Promise.all([writer.close(), writer.closed]);
264}, 'failing DefaultWriter constructor should not release an existing writer');
265
266promise_test(t => {
267  const ws = new WritableStream({
268    start() {
269      return Promise.reject(error1);
270    }
271  }, { highWaterMark: 0 });
272  const writer = ws.getWriter();
273  return Promise.all([
274    promise_rejects_exactly(t, error1, writer.ready, 'ready should be rejected'),
275    promise_rejects_exactly(t, error1, writer.write(), 'write() should be rejected')
276  ]);
277}, 'write() on a stream with HWM 0 should not cause the ready Promise to resolve');
278
279promise_test(t => {
280  const ws = new WritableStream();
281  const writer = ws.getWriter();
282  writer.releaseLock();
283  return promise_rejects_js(t, TypeError, writer.write(), 'write should reject');
284}, 'writing to a released writer should reject the returned promise');
285