[Readable Streams API] Enable creation of ReadableStreamBYOBReader
[WebKit-https.git] / LayoutTests / streams / readable-stream-byob-request.js
1 'use strict';
2
3 if (self.importScripts) {
4     self.importScripts('../resources/testharness.js');
5 }
6
7 test(() => {
8     const methods = ['constructor', 'respond', 'respondWithNewView'];
9     const properties = methods.concat(['view']).sort();
10
11     let controller;
12
13     // FIXME: Remove next line when bug https://bugs.webkit.org/show_bug.cgi?id=167697
14     // is fixed. For the moment, so that test may pass, we have to insert a reference
15     // to Uint8Array here (otherwise, the private variable cannot be resolved).
16     const d = new Uint8Array(1);
17
18     // Specifying autoAllocateChunkSize and calling read() are steps that allow
19     // getting a ReadableStreamBYOBRequest returned instead of undefined. The
20     // purpose here is just to get such an object.
21     const rs = new ReadableStream({
22         autoAllocateChunkSize: 128,
23         start: function(c) {
24             controller = c;
25         },
26         type: "bytes"
27     });
28
29     rs.getReader().read();
30     const byobReq = controller.byobRequest;
31
32     const proto = Object.getPrototypeOf(byobReq);
33
34     assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
35
36     for (const m of methods) {
37         const propDesc = Object.getOwnPropertyDescriptor(proto, m);
38         assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
39         assert_equals(propDesc.configurable, true, 'method should be configurable');
40         assert_equals(propDesc.writable, true, 'method should be writable');
41         assert_equals(typeof byobReq[m], 'function', 'should have be a method');
42     }
43
44     const viewPropDesc = Object.getOwnPropertyDescriptor(proto, 'view');
45     assert_equals(viewPropDesc.enumerable, false, 'view should be non-enumerable');
46     assert_equals(viewPropDesc.configurable, true, 'view should be configurable');
47     assert_not_equals(viewPropDesc.get, undefined, 'view should have a getter');
48     assert_equals(viewPropDesc.set, undefined, 'view should not have a setter');
49
50     assert_equals(byobReq.constructor.length, 2, 'constructor has 2 parameters');
51     assert_equals(byobReq.respond.length, 1, 'respond has 1 parameter');
52     assert_equals(byobReq.respondWithNewView.length, 1, 'respondWithNewView has 1 parameter');
53
54 }, 'ReadableStreamBYOBRequest instances should have the correct list of properties');
55
56 test(function() {
57     let controller;
58
59     const rs = new ReadableStream({
60         start: function(c) {
61             controller = c;
62         },
63         type: "bytes"
64     });
65
66     assert_equals(controller.byobRequest, undefined, "by default byobRequest should be undefined");
67 }, "By default, byobRequest should be undefined");
68
69 test(function() {
70
71     let controller;
72     const autoAllocateChunkSize = 128;
73     const rs = new ReadableStream({
74         autoAllocateChunkSize,
75         start: function(c) {
76             controller = c;
77         },
78         type: "bytes"
79     });
80
81     rs.getReader().read();
82     const byobReq = controller.byobRequest;
83
84     assert_equals(byobReq.view.length, autoAllocateChunkSize, "byobRequest length should be equal to autoAllocateChunkSize value")
85
86 }, "byobRequest.view length should be equal to autoAllocateChunkSize")
87
88 test(function() {
89
90     let controller;
91     const rs = new ReadableStream({
92         autoAllocateChunkSize: 16,
93         start: function(c) {
94             controller = c;
95         },
96         type: "bytes"
97     });
98
99     rs.getReader().read();
100     const byobReq = controller.byobRequest;
101
102     assert_throws(new TypeError("Can only call ReadableStreamBYOBRequest.respond on instances of ReadableStreamBYOBRequest"),
103         function() { byobReq.respond.apply(rs, 1); });
104
105 }, "Calling respond() with a this object different from ReadableStreamBYOBRequest should throw a TypeError");
106
107 test(function() {
108
109     let controller;
110     const rs = new ReadableStream({
111         autoAllocateChunkSize: 16,
112         start: function(c) {
113             controller = c;
114         },
115         type: "bytes"
116     });
117
118     rs.getReader().read();
119     const byobReq = controller.byobRequest;
120
121     assert_throws(new RangeError("bytesWritten has an incorrect value"),
122         function() { byobReq.respond(-1); });
123 }, "Calling respond() with a negative bytesWritten value should throw a RangeError");
124
125 test(function() {
126
127     let controller;
128     const rs = new ReadableStream({
129         autoAllocateChunkSize: 16,
130         start: function(c) {
131             controller = c;
132         },
133         type: "bytes"
134     });
135
136     rs.getReader().read();
137     const byobReq = controller.byobRequest;
138
139     assert_throws(new RangeError("bytesWritten has an incorrect value"),
140         function() { byobReq.respond("abc"); });
141 }, "Calling respond() with a bytesWritten value which is not a number should throw a RangeError");
142
143 test(function() {
144
145     let controller;
146     const rs = new ReadableStream({
147         autoAllocateChunkSize: 16,
148         start: function(c) {
149             controller = c;
150         },
151         type: "bytes"
152     });
153
154     rs.getReader().read();
155     const byobReq = controller.byobRequest;
156
157     assert_throws(new RangeError("bytesWritten has an incorrect value"),
158         function() { byobReq.respond(Number.POSITIVE_INFINITY); });
159 }, "Calling respond() with a positive infinity bytesWritten value should throw a RangeError");
160
161 test(function() {
162
163     let controller;
164     const rs = new ReadableStream({
165         autoAllocateChunkSize: 16,
166         start: function(c) {
167             controller = c;
168         },
169         type: "bytes"
170     });
171
172     rs.getReader().read();
173     const byobReq = controller.byobRequest;
174     controller.close();
175
176     assert_throws(new TypeError("bytesWritten is different from 0 even though stream is closed"),
177         function() { byobReq.respond(1); });
178 }, "Calling respond() with a bytesWritten value different from 0 when stream is closed should throw a TypeError");
179
180 test(function() {
181
182     let controller;
183     const rs = new ReadableStream({
184         autoAllocateChunkSize: 16,
185         start: function(c) {
186             controller = c;
187         },
188         type: "bytes"
189     });
190
191     // FIXME: When ReadableStreamBYOBReader is implemented, another test (or even several ones)
192     // based on this one should be added so that reader's readIntoRequests attribute is not empty
193     // and currently unreachable code is reached.
194     rs.getReader().read();
195     const byobReq = controller.byobRequest;
196     controller.close();
197     byobReq.respond(0);
198
199 }, "Calling respond() with a bytesWritten value of 0 when stream is closed should succeed");
200
201 test(function() {
202
203     let controller;
204     const rs = new ReadableStream({
205         autoAllocateChunkSize: 16,
206         start: function(c) {
207             controller = c;
208         },
209         type: "bytes"
210     });
211
212     rs.getReader().read();
213     const byobReq = controller.byobRequest;
214     assert_throws(new RangeError("bytesWritten value is too great"),
215         function() { byobReq.respond(17); });
216
217 }, "Calling respond() with a bytesWritten value greater than autoAllocateChunkSize should fail");
218
219 promise_test(function() {
220
221     const rs = new ReadableStream({
222         autoAllocateChunkSize: 16,
223         pull: function(controller) {
224             const br = controller.byobRequest;
225             br.view[0] = 1;
226             br.view[1] = 2;
227             br.respond(2);
228         },
229         type: "bytes"
230     });
231
232     return rs.getReader().read().then(result => {
233         assert_equals(result.value.byteLength, 2);
234         assert_equals(result.value.byteOffset, 0);
235         assert_equals(result.value.buffer.byteLength, 16);
236         assert_equals(result.value[0], 1);
237         assert_equals(result.value[1], 2);
238     });
239 }, "Calling respond() with a bytesWritten value lower than autoAllocateChunkSize should succeed");
240
241 // FIXME: when ReadableStreamBYOBReader is implemented, add tests with elementSize different from 1
242 // so that more code can be covered.
243
244 test(function() {
245
246     let controller;
247     const rs = new ReadableStream({
248         autoAllocateChunkSize: 16,
249         start: function(c) {
250             controller = c;
251         },
252         type: "bytes"
253     });
254
255     rs.getReader().read();
256     const byobReq = controller.byobRequest;
257
258     assert_throws(new TypeError("Can only call ReadableStreamBYOBRequest.respondWithNewView on instances of ReadableStreamBYOBRequest"),
259         function() { byobReq.respondWithNewView.apply(rs, new Uint8Array(1)); });
260
261 }, "Calling respondWithNewView() with a this object different from ReadableStreamBYOBRequest should throw a TypeError");
262
263 test(function() {
264
265     let controller;
266     const rs = new ReadableStream({
267         autoAllocateChunkSize: 16,
268         start: function(c) {
269             controller = c;
270         },
271         type: "bytes"
272     });
273
274     rs.getReader().read();
275     const byobReq = controller.byobRequest;
276
277     assert_throws(new TypeError("Provided view is not an object"),
278         function() { byobReq.respondWithNewView(function() {}); });
279
280 }, "Calling respondWithNewView() with an argument that is not an object should throw a TypeError");
281
282 test(function() {
283
284     let controller;
285     const rs = new ReadableStream({
286         autoAllocateChunkSize: 16,
287         start: function(c) {
288             controller = c;
289         },
290         type: "bytes"
291     });
292
293     rs.getReader().read();
294     const byobReq = controller.byobRequest;
295
296     assert_throws(new TypeError("Provided view is not an ArrayBufferView"),
297         function() { byobReq.respondWithNewView({}); });
298
299 }, "Calling respondWithNewView() with an argument that is not an ArrayBufferView should throw a TypeError");
300
301 promise_test(function() {
302
303     const rs = new ReadableStream({
304         autoAllocateChunkSize: 2,
305         pull: function(controller) {
306             const newView = new Uint8Array([3, 6]);
307             const br = controller.byobRequest;
308             br.respondWithNewView(newView);
309         },
310         type: "bytes"
311     });
312
313     return rs.getReader().read().then(result => {
314         assert_equals(result.value.byteLength, 2);
315         assert_equals(result.value.byteOffset, 0);
316         assert_equals(result.value.buffer.byteLength, 2);
317         assert_equals(result.value[0], 3);
318         assert_equals(result.value[1], 6);
319     });
320 }, "When using autoAllocateChunkSize, calling respondWithNewView() should succeed if view.byteLength is equal to autoAllocateChunkSize");
321
322 promise_test(function(test) {
323
324     const rs = new ReadableStream({
325         autoAllocateChunkSize: 16,
326         pull: function(controller) {
327             const newView = new Uint8Array([3, 6]);
328             const br = controller.byobRequest;
329             br.respondWithNewView(newView);
330         },
331         type: "bytes"
332     });
333
334     const error = new RangeError("Invalid value for view.byteLength");
335
336     return promise_rejects(test, error, rs.getReader().read());
337 }, "When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteOffset is different from 0");
338
339 promise_test(function(test) {
340
341     const rs = new ReadableStream({
342         autoAllocateChunkSize: 16,
343         pull: function(controller) {
344             const buffer = new ArrayBuffer(3);
345             const newView = new Uint8Array(buffer, 1); // byteOffset of 1
346             const br = controller.byobRequest;
347             br.respondWithNewView(newView);
348         },
349         type: "bytes"
350     });
351
352     const error = new RangeError("Invalid value for view.byteOffset");
353
354     return promise_rejects(test, error, rs.getReader().read());
355 }, "When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteLength is different from autoAllocateChunkSize");
356
357 done();