[Readable Streams API] Enable creation of ReadableStreamBYOBReader
[WebKit-https.git] / LayoutTests / streams / readable-byte-stream-controller.js
1 'use strict';
2
3 if (self.importScripts) {
4     self.importScripts('../resources/testharness.js');
5 }
6
7 test(function() {
8     const rs = new ReadableStream({
9         type: "bytes"
10     });
11 }, "Creating a ReadableStream with an underlyingSource with type property set to 'bytes' should succeed");
12
13 test(() => {
14     const methods = ['close', 'constructor', 'enqueue', 'error'];
15     const properties = methods.concat(['byobRequest', 'desiredSize']).sort();
16
17     let controller;
18
19     const rs = new ReadableStream({
20         start: function(c) {
21             controller = c;
22         },
23         type: "bytes"
24     });
25
26     const proto = Object.getPrototypeOf(controller);
27
28     assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
29
30     for (const m of methods) {
31         const propDesc = Object.getOwnPropertyDescriptor(proto, m);
32         assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
33         assert_equals(propDesc.configurable, true, 'method should be configurable');
34         assert_equals(propDesc.writable, true, 'method should be writable');
35         assert_equals(typeof controller[m], 'function', 'should have be a method');
36     }
37
38     const byobRequestPropDesc = Object.getOwnPropertyDescriptor(proto, 'byobRequest');
39     assert_equals(byobRequestPropDesc.enumerable, false, 'byobRequest should be non-enumerable');
40     assert_equals(byobRequestPropDesc.configurable, true, 'byobRequest should be configurable');
41     assert_not_equals(byobRequestPropDesc.get, undefined, 'byobRequest should have a getter');
42     assert_equals(byobRequestPropDesc.set, undefined, 'byobRequest should not have a setter');
43
44     const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desiredSize');
45     assert_equals(desiredSizePropDesc.enumerable, false, 'desiredSize should be non-enumerable');
46     assert_equals(desiredSizePropDesc.configurable, true, 'desiredSize should be configurable');
47     assert_not_equals(desiredSizePropDesc.get, undefined, 'desiredSize should have a getter');
48     assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not have a setter');
49
50     assert_equals(controller.close.length, 0, 'close has 0 parameter');
51     assert_equals(controller.constructor.length, 3, 'constructor has 3 parameters');
52     assert_equals(controller.enqueue.length, 1, 'enqueue has 1 parameter');
53     assert_equals(controller.error.length, 1, 'error has 1 parameter');
54
55 }, 'ReadableByteStreamController instances should have the correct list of properties');
56
57 test(function() {
58     let controller;
59
60     const rs = new ReadableStream({
61         start: function(c) {
62             controller = c;
63         },
64         type: "bytes"
65     });
66
67     assert_throws(new TypeError("Can only call ReadableByteStreamController.error on instances of ReadableByteStreamController"),
68         function() { controller.error.apply(rs); });
69 }, "Calling error() with a this object different from ReadableByteStreamController should throw a TypeError");
70
71 test(function() {
72     let controller;
73
74     const rs = new ReadableStream({
75         start: function(c) {
76             controller = c;
77         },
78         type: "bytes"
79     });
80
81     assert_throws(new TypeError("Can only call ReadableByteStreamController.close on instances of ReadableByteStreamController"),
82         function() { controller.close.apply(rs); });
83 }, "Calling close() with a this object different from ReadableByteStreamController should throw a TypeError");
84
85 test(function() {
86     let controller;
87
88     const rs = new ReadableStream({
89         start: function(c) {
90             controller = c;
91         },
92         type: "bytes"
93     });
94
95     assert_throws(new TypeError("Can only call ReadableByteStreamController.enqueue on instances of ReadableByteStreamController"),
96         function() { controller.enqueue.apply(rs, new Int8Array(1)); });
97 }, "Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError");
98
99 test(function() {
100     let controller;
101
102     const rs = new ReadableStream({
103         start: function(c) {
104             controller = c;
105         },
106         type: "bytes"
107     });
108
109     controller.enqueue(new Int8Array(2));
110     controller.close();
111
112     assert_throws(new TypeError("ReadableByteStreamController is requested to close"),
113         function() { controller.enqueue(new Int8Array(1)); });
114 }, "Calling enqueue() when close has been requested but not yet performed should throw a TypeError");
115
116 test(function() {
117     let controller;
118
119     const rs = new ReadableStream({
120         start: function(c) {
121             controller = c;
122         },
123         type: "bytes"
124     });
125     controller.close();
126
127     assert_throws(new TypeError("ReadableStream is not readable"),
128         function() {
129             controller.enqueue(new Int8Array(1));
130         });
131 }, "Calling enqueue() when stream is not readable should throw a TypeError");
132
133 test(function() {
134     let controller;
135
136     const rs = new ReadableStream({
137         start: function(c) {
138             controller = c;
139         },
140         type: "bytes"
141     });
142
143     const invalidChunk = function() {};
144
145     assert_throws(new TypeError("Provided chunk is not an object"),
146         function() { controller.enqueue(invalidChunk); });
147 }, "Calling enqueue() with a chunk that is not an object should trhow a TypeError");
148
149 test(function() {
150     let controller;
151
152     const rs = new ReadableStream({
153         start: function(c) {
154             controller = c;
155         },
156         type: "bytes"
157     });
158
159     const invalidChunk = {};
160
161     assert_throws(new TypeError("Provided chunk is not an object"),
162         function() { controller.enqueue(invalidChunk); });
163 }, "Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError");
164
165 test(function() {
166     let controller;
167
168     const rs = new ReadableStream({
169         start: function(c) {
170             controller = c;
171         },
172         type: "bytes"
173     });
174
175     assert_throws(new TypeError("ReadableStream is not readable"),
176         function() {
177             controller.close();
178             controller.error();
179         });
180 }, "Calling error() after calling close() should throw a TypeError");
181
182 test(function() {
183     let controller;
184
185     const rs = new ReadableStream({
186         start: function(c) {
187             controller = c;
188         },
189         type: "bytes"
190     });
191
192     assert_throws(new TypeError("ReadableStream is not readable"),
193         function() {
194             controller.error();
195             controller.error();
196         });
197 }, "Calling error() after calling error() should throw a TypeError");
198
199 test(function() {
200     let controller;
201
202     const rs = new ReadableStream({
203         start: function(c) {
204             controller = c;
205         },
206         type: "bytes"
207     });
208
209     assert_throws(new TypeError("Close has already been requested"),
210         function() {
211             controller.close();
212             controller.close();
213         });
214 }, "Calling close() after calling close() should throw a TypeError");
215
216 test(function() {
217     let controller;
218
219     const rs = new ReadableStream({
220         start: function(c) {
221             controller = c;
222         },
223         type: "bytes"
224     });
225
226     assert_throws(new TypeError("ReadableStream is not readable"),
227         function() {
228             controller.error();
229             controller.close();
230         });
231 }, "Calling close() after calling error() should throw a TypeError");
232
233 promise_test(function(test) {
234     let controller;
235
236     const rs = new ReadableStream({
237         start: function(c) {
238             controller = c;
239         },
240         type: "bytes"
241     });
242     const myError = new Error("my error");
243     controller.error(myError);
244
245     return promise_rejects(test, myError, rs.getReader().read());
246 }, "Calling read() on a reader associated to a controller that has been errored should fail with provided error");
247
248 promise_test(function() {
249     let controller;
250
251     const rs = new ReadableStream({
252         start: function(c) {
253             controller = c;
254         },
255         type: "bytes"
256     });
257
258     controller.close();
259
260     return rs.getReader().read().then(
261         function(res) {
262             assert_object_equals(res, {value: undefined, done: true});
263         }
264     );
265 }, "Calling read() on a reader associated to a controller that has been closed should not be rejected");
266
267 promise_test(function(test) {
268     let controller;
269
270     const rs = new ReadableStream({
271         start: function(c) {
272             controller = c;
273         },
274         type: "bytes"
275     });
276
277     const myError = new Error("My error");
278     let readingPromise = rs.getReader().read();
279     controller.error(myError);
280
281     return promise_rejects(test, myError, readingPromise);
282 }, "Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is undefined)");
283
284 promise_test(function(test) {
285     let controller;
286
287     const rs = new ReadableStream({
288         autoAllocateChunkSize: 128,
289         start: function(c) {
290             controller = c;
291         },
292         type: "bytes"
293     });
294
295     const myError = new Error("My error");
296     let readingPromise = rs.getReader().read();
297     controller.error(myError);
298
299     return promise_rejects(test, myError, readingPromise);
300 }, "Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is specified)");
301
302 promise_test(function() {
303     let controller;
304
305     const rs = new ReadableStream({
306         start: function(c) {
307             controller = c;
308         },
309         type: "bytes"
310     });
311
312     const buffer = new Uint8Array([3]);
313     controller.enqueue(buffer);
314
315     return rs.getReader().read().then(
316         function(res) {
317             assert_object_equals(res, {value: buffer, done: false});
318         }
319     );
320 }, "Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk");
321
322 promise_test(function() {
323     let controller;
324
325     const rs = new ReadableStream({
326         start: function(c) {
327             controller = c;
328         },
329         type: "bytes"
330     });
331
332     let promise = rs.getReader().read();
333     const buffer = new Uint8Array([1]);
334     controller.enqueue(buffer);
335     return promise.then(
336         function(res) {
337             assert_object_equals(res, {value: buffer, done: false});
338         }
339     );
340 }, "Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk");
341
342 promise_test(function() {
343     let controller;
344
345     const rs = new ReadableStream({
346         start: function(c) {
347             controller = c;
348         },
349         type: "bytes"
350     });
351
352     const reader = rs.getReader();
353     const buffer = new Uint8Array([1]);
354     controller.enqueue(buffer);
355     return reader.read().then(
356         function(res) {
357             assert_object_equals(res, {value: buffer, done: false});
358         }
359     );
360 }, "Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk");
361
362 test(function() {
363     let controller;
364
365     const rs = new ReadableStream({
366         start: function(c) {
367             controller = c;
368         },
369         type: "bytes"
370     });
371
372     assert_equals(controller.desiredSize, 1, "by default initial value of desiredSize should be 1");
373 }, "By default initial value of desiredSize should be 1");
374
375 promise_test(function() {
376     const rs = new ReadableStream({
377         type: "bytes"
378     });
379
380     return rs.cancel().then(
381         function(res) {
382             assert_object_equals(res, undefined);
383         }
384     );
385 }, "Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined");
386
387 promise_test(function() {
388     let pullCalls = 0;
389     const rs = new ReadableStream({
390         pull: function () {
391             pullCalls++;
392         },
393         type: "bytes"
394     });
395     return new Promise(function(resolve, reject) {
396         setTimeout(function() {
397             if (pullCalls === 1)
398                 resolve("ok");
399             else
400                 reject("1 call should have been made to pull function");
401         }, 200);
402     });
403 }, "Test that pull is called once when a new ReadableStream is created with a ReadableByteStreamController");
404
405 promise_test(function() {
406     const myError = new Error("Pull failed");
407     const rs = new ReadableStream({
408         pull: function () {
409             throw myError;
410         },
411         type: "bytes"
412     });
413
414     return new Promise(function(resolve, reject) {
415         setTimeout(function() {
416             rs.cancel().then(
417                 function (res) { reject("Cancel should return a promise resolved with rejection"); },
418                 function (err) {
419                     if (err === myError)
420                         resolve();
421                     else
422                         reject("Reason for rejection should be the error that was thrown in pull");
423                 }
424             )
425         }, 200)});
426 }, "Calling cancel after pull has thrown an error should result in a promise rejected with the same error");
427
428 promise_test(function() {
429     const myError = new Error("Start failed");
430     const rs = new ReadableStream({
431         start: function () {
432             return new Promise(function(resolve, reject) { reject(myError); });
433         },
434         type: "bytes"
435     });
436
437     return new Promise(function(resolve, reject) {
438         setTimeout(function() {
439             rs.cancel().then(
440                 function (res) { reject("An error should have been thrown"); },
441                 function (err) {
442                     if (err === myError)
443                         resolve();
444                     else
445                         reject("Reason for rejection should be the error that led the promise returned by start to fail");
446                 }
447             )
448         }, 200)});
449 }, "Calling cancel after creating a ReadableStream with an underlyingByteStream's start function returning a rejected promise should result in a promise rejected with the same error");
450
451 done();