MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1import { test, testDeep, testThrows, summary } from './helpers.js';
2
3console.log('ReadableStream / DefaultController / DefaultReader Tests\n');
4
5test('RS typeof', typeof ReadableStream, 'function');
6test('RS toStringTag', Object.prototype.toString.call(new ReadableStream()), '[object ReadableStream]');
7
8const rs = new ReadableStream();
9test('RS locked initially false', rs.locked, false);
10
11testThrows('RS requires new', () => ReadableStream());
12testThrows('RS rejects null source', () => new ReadableStream(null));
13testThrows('RS rejects invalid type', () => new ReadableStream({ type: 'invalid' }));
14testThrows('RS rejects null type', () => new ReadableStream({ type: null }));
15
16const rs2 = new ReadableStream({ type: undefined });
17test('RS accepts undefined type', rs2.locked, false);
18
19let startCalled = false;
20let startController = null;
21const rs3 = new ReadableStream({
22 start(c) { startCalled = true; startController = c; }
23});
24test('start called synchronously', startCalled, true);
25test('controller has desiredSize', startController.desiredSize, 1);
26test('controller toStringTag', Object.prototype.toString.call(startController), '[object ReadableStreamDefaultController]');
27
28testThrows('controller cannot be constructed', () => new ReadableStreamDefaultController());
29
30const rs4 = new ReadableStream({
31 start(c) { c.enqueue('a'); c.enqueue('b'); c.close(); }
32});
33
34const reader = rs4.getReader();
35test('locked after getReader', rs4.locked, true);
36test('reader toStringTag', Object.prototype.toString.call(reader), '[object ReadableStreamDefaultReader]');
37
38testThrows('cannot get second reader', () => rs4.getReader());
39testThrows('getReader rejects unknown mode', () => new ReadableStream().getReader({ mode: 'potato' }));
40
41async function testReadSequence() {
42 const r1 = await reader.read();
43 test('read 1 value', r1.value, 'a');
44 test('read 1 done', r1.done, false);
45
46 const r2 = await reader.read();
47 test('read 2 value', r2.value, 'b');
48 test('read 2 done', r2.done, false);
49
50 const r3 = await reader.read();
51 test('read 3 done', r3.done, true);
52 test('read 3 value', r3.value, undefined);
53}
54
55async function testClosedPromise() {
56 const rs = new ReadableStream({
57 start(c) { c.close(); }
58 });
59 const reader = rs.getReader();
60 await reader.closed;
61 test('closed resolves on close', true, true);
62}
63
64async function testCancel() {
65 let cancelReason = null;
66 const rs = new ReadableStream({
67 cancel(reason) { cancelReason = reason; }
68 });
69 await rs.cancel('test reason');
70 test('cancel passes reason', cancelReason, 'test reason');
71}
72
73async function testPullBackpressure() {
74 let pullCount = 0;
75 const rs = new ReadableStream({
76 pull(c) {
77 pullCount++;
78 c.enqueue(pullCount);
79 }
80 }, { highWaterMark: 1 });
81
82 await new Promise(r => setTimeout(r, 50));
83 test('pull called once at start', pullCount, 1);
84
85 const reader = rs.getReader();
86 const r1 = await reader.read();
87 test('pull backpressure value', r1.value, 1);
88
89 await new Promise(r => setTimeout(r, 50));
90 test('pull called again after read', pullCount, 2);
91}
92
93async function testReleaseLock() {
94 const rs = new ReadableStream({
95 start(c) { c.enqueue('x'); }
96 });
97 const reader = rs.getReader();
98 reader.releaseLock();
99 test('locked false after release', rs.locked, false);
100
101 const reader2 = rs.getReader();
102 const r = await reader2.read();
103 test('new reader can read', r.value, 'x');
104}
105
106async function testDesiredSize() {
107 let ctrl;
108 new ReadableStream({
109 start(c) {
110 ctrl = c;
111 }
112 });
113 await new Promise(r => setTimeout(r, 0));
114 test('desiredSize initially 1', ctrl.desiredSize, 1);
115 ctrl.enqueue('a');
116 test('desiredSize after enqueue', ctrl.desiredSize, 0);
117 ctrl.close();
118 test('desiredSize after close', ctrl.desiredSize, 0);
119}
120
121async function testErrorStream() {
122 const err = new Error('boom');
123 const rs = new ReadableStream({
124 start(c) { c.error(err); }
125 });
126 const reader = rs.getReader();
127 try {
128 await reader.read();
129 test('errored stream should reject read', false, true);
130 } catch (e) {
131 test('errored stream rejects with error', e, err);
132 }
133}
134
135async function testCustomStrategy() {
136 const rs = new ReadableStream({
137 start(c) {
138 c.enqueue(new Uint8Array(10));
139 c.enqueue(new Uint8Array(20));
140 }
141 }, {
142 highWaterMark: 64,
143 size(chunk) { return chunk.byteLength; }
144 });
145
146 const reader = rs.getReader();
147 const r1 = await reader.read();
148 test('custom strategy chunk size', r1.value.byteLength, 10);
149}
150
151async function testSubclass() {
152 class MyStream extends ReadableStream {
153 myMethod() { return 42; }
154 }
155 const ms = new MyStream({ start(c) { c.close(); } });
156 test('subclass instanceof', ms instanceof ReadableStream, true);
157 test('subclass method', ms.myMethod(), 42);
158}
159
160await testReadSequence();
161await testClosedPromise();
162await testCancel();
163await testPullBackpressure();
164await testReleaseLock();
165await testDesiredSize();
166await testErrorStream();
167await testCustomStrategy();
168await testSubclass();
169
170summary();