MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 388 lines 12 kB view raw
1import { test, testDeep, testThrows, summary } from './helpers.js'; 2 3console.log('TransformStream / TransformStreamDefaultController Tests\n'); 4 5const READABLE_NO_BACKPRESSURE = { highWaterMark: 1 }; 6 7test('TS typeof', typeof TransformStream, 'function'); 8test('TS toStringTag', Object.prototype.toString.call(new TransformStream()), '[object TransformStream]'); 9 10testThrows('TS requires new', () => TransformStream()); 11testThrows('TS rejects readableType', () => new TransformStream({ readableType: 'bytes' })); 12testThrows('TS rejects writableType', () => new TransformStream({ writableType: 'bytes' })); 13 14const ts0 = new TransformStream(); 15test('TS readable is ReadableStream', ts0.readable instanceof ReadableStream, true); 16test('TS writable is WritableStream', ts0.writable instanceof WritableStream, true); 17 18testThrows('TSController cannot be constructed', () => new TransformStreamDefaultController()); 19test('TSController toStringTag', typeof TransformStreamDefaultController, 'function'); 20 21test('TS can be constructed with no transform', true, (() => { new TransformStream({}); return true; })()); 22 23let startCtrl = null; 24const ts1 = new TransformStream({ start(c) { startCtrl = c; } }); 25test('start called with controller', startCtrl !== null, true); 26test('controller toStringTag', Object.prototype.toString.call(startCtrl), '[object TransformStreamDefaultController]'); 27 28async function testIdentity() { 29 const ts = new TransformStream(); 30 const writer = ts.writable.getWriter(); 31 writer.write('a'); 32 const reader = ts.readable.getReader(); 33 const result = await reader.read(); 34 test('identity value', result.value, 'a'); 35 test('identity done', result.done, false); 36} 37 38async function testTransformUppercase() { 39 let c; 40 const ts = new TransformStream({ 41 start(controller) { c = controller; }, 42 transform(chunk) { c.enqueue(chunk.toUpperCase()); } 43 }); 44 const writer = ts.writable.getWriter(); 45 writer.write('hello'); 46 const reader = ts.readable.getReader(); 47 const result = await reader.read(); 48 test('uppercase transform', result.value, 'HELLO'); 49} 50 51async function testTransformDoubler() { 52 let c; 53 const ts = new TransformStream({ 54 start(controller) { c = controller; }, 55 transform(chunk) { 56 c.enqueue(chunk.toUpperCase()); 57 c.enqueue(chunk.toUpperCase()); 58 } 59 }); 60 const writer = ts.writable.getWriter(); 61 writer.write('x'); 62 const reader = ts.readable.getReader(); 63 const r1 = await reader.read(); 64 const r2 = await reader.read(); 65 test('doubler chunk 1', r1.value, 'X'); 66 test('doubler chunk 2', r2.value, 'X'); 67} 68 69async function testFlush() { 70 let flushed = false; 71 const ts = new TransformStream({ 72 transform() {}, 73 flush() { flushed = true; } 74 }); 75 await ts.writable.getWriter().close(); 76 test('flush called on close', flushed, true); 77} 78 79async function testFlushEnqueue() { 80 let c; 81 const ts = new TransformStream({ 82 start(controller) { c = controller; }, 83 transform() {}, 84 flush() { c.enqueue('flushed'); } 85 }); 86 const writer = ts.writable.getWriter(); 87 writer.write('a'); 88 writer.close(); 89 const reader = ts.readable.getReader(); 90 const r1 = await reader.read(); 91 test('flush enqueue value', r1.value, 'flushed'); 92} 93 94async function testCloseClosesReadable() { 95 const ts = new TransformStream({ transform() {} }); 96 const writer = ts.writable.getWriter(); 97 writer.close(); 98 await Promise.all([writer.closed, ts.readable.getReader().closed]); 99 test('close propagates to readable', true, true); 100} 101 102async function testTransformError() { 103 const err = new Error('transform boom'); 104 const ts = new TransformStream({ 105 transform() { throw err; } 106 }, undefined, READABLE_NO_BACKPRESSURE); 107 const writer = ts.writable.getWriter(); 108 const reader = ts.readable.getReader(); 109 try { 110 await writer.write('a'); 111 test('write should reject on transform error', false, true); 112 } catch (e) { 113 test('write rejects with transform error', e, err); 114 } 115 try { 116 await reader.read(); 117 test('read should reject on transform error', false, true); 118 } catch (e) { 119 test('read rejects with transform error', e, err); 120 } 121} 122 123async function testFlushError() { 124 const err = new Error('flush boom'); 125 const ts = new TransformStream({ 126 transform() {}, 127 flush() { throw err; } 128 }, undefined, READABLE_NO_BACKPRESSURE); 129 const writer = ts.writable.getWriter(); 130 await writer.write('a'); 131 try { 132 await writer.close(); 133 test('close should reject on flush error', false, true); 134 } catch (e) { 135 test('close rejects with flush error', e, err); 136 } 137} 138 139async function testControllerError() { 140 let ctrl; 141 const ts = new TransformStream({ 142 start(c) { ctrl = c; } 143 }); 144 ctrl.error(new Error('controller error')); 145 const reader = ts.readable.getReader(); 146 try { 147 await reader.read(); 148 test('read should reject after controller.error', false, true); 149 } catch (e) { 150 test('controller.error errors readable', e.message, 'controller error'); 151 } 152} 153 154async function testControllerTerminate() { 155 let ctrl; 156 const ts = new TransformStream({ 157 start(c) { ctrl = c; } 158 }); 159 ctrl.terminate(); 160 const reader = ts.readable.getReader(); 161 const result = await reader.read(); 162 test('terminate closes readable', result.done, true); 163} 164 165async function testDesiredSize() { 166 let ctrl; 167 const ts = new TransformStream({ 168 start(c) { ctrl = c; } 169 }); 170 test('desiredSize initially 0', ctrl.desiredSize, 0); 171} 172 173async function testDefaultHWM() { 174 const ts = new TransformStream(); 175 const writer = ts.writable.getWriter(); 176 test('writable default HWM is 1', writer.desiredSize, 1); 177} 178 179async function testCustomWritableHWM() { 180 const ts = new TransformStream({}, { highWaterMark: 17 }); 181 const writer = ts.writable.getWriter(); 182 test('writable custom HWM', writer.desiredSize, 17); 183} 184 185async function testBackpressure() { 186 const ts = new TransformStream(undefined, undefined, { highWaterMark: 0 }); 187 const writer = ts.writable.getWriter(); 188 const reader = ts.readable.getReader(); 189 const readPromise = reader.read(); 190 writer.write('a'); 191 const result = await readPromise; 192 test('backpressure read value', result.value, 'a'); 193 test('backpressure read done', result.done, false); 194} 195 196async function testAsyncTransform() { 197 let c; 198 const ts = new TransformStream({ 199 start(controller) { c = controller; }, 200 transform(chunk) { 201 return new Promise(resolve => { 202 setTimeout(() => { 203 c.enqueue(chunk.toUpperCase()); 204 resolve(); 205 }, 10); 206 }); 207 } 208 }); 209 const writer = ts.writable.getWriter(); 210 writer.write('a'); 211 const reader = ts.readable.getReader(); 212 const result = await reader.read(); 213 test('async transform value', result.value, 'A'); 214} 215 216async function testStartThrows() { 217 try { 218 new TransformStream({ 219 start() { throw new URIError('start thrown'); } 220 }); 221 test('TS ctor should throw on start error', false, true); 222 } catch (e) { 223 test('TS ctor throws start error', e instanceof URIError, true); 224 } 225} 226 227async function testCancelCallable() { 228 let cancelled = null; 229 const reason = new Error('cancel reason'); 230 const ts = new TransformStream({ 231 cancel(r) { cancelled = r; } 232 }); 233 await ts.readable.cancel(reason); 234 test('cancel called with reason', cancelled, reason); 235} 236 237async function testAbortCallsCancel() { 238 let aborted = null; 239 const reason = new Error('abort reason'); 240 const ts = new TransformStream({ 241 cancel(r) { aborted = r; } 242 }); 243 await ts.writable.abort(reason); 244 test('abort calls cancel with reason', aborted, reason); 245} 246 247async function testTransformThisBinding() { 248 let c; 249 const ts = new TransformStream({ 250 suffix: '-suffix', 251 start(controller) { c = controller; }, 252 transform(chunk) { c.enqueue(chunk + this.suffix); }, 253 flush() { c.enqueue('flushed' + this.suffix); } 254 }); 255 const writer = ts.writable.getWriter(); 256 writer.write('a'); 257 writer.close(); 258 const reader = ts.readable.getReader(); 259 const r1 = await reader.read(); 260 test('this binding transform', r1.value, 'a-suffix'); 261 const r2 = await reader.read(); 262 test('this binding flush', r2.value, 'flushed-suffix'); 263} 264 265async function testSubclass() { 266 class MyTS extends TransformStream { 267 myMethod() { return 42; } 268 } 269 const ts = new MyTS(); 270 test('subclass instanceof', ts instanceof TransformStream, true); 271 test('subclass method', ts.myMethod(), 42); 272 test('subclass readable', ts.readable instanceof ReadableStream, true); 273} 274 275async function testReadableHWM() { 276 let ctrl; 277 new TransformStream({ 278 start(c) { ctrl = c; } 279 }, undefined, { highWaterMark: 9 }); 280 test('readable custom HWM desiredSize', ctrl.desiredSize, 9); 281} 282 283async function testNegativeHWMThrows() { 284 testThrows('negative writable HWM', () => new TransformStream(undefined, { highWaterMark: -1 })); 285 testThrows('negative readable HWM', () => new TransformStream(undefined, undefined, { highWaterMark: -1 })); 286 testThrows('NaN writable HWM', () => new TransformStream(undefined, { highWaterMark: NaN })); 287 testThrows('NaN readable HWM', () => new TransformStream(undefined, undefined, { highWaterMark: NaN })); 288} 289 290async function testCancelReadableErrorsWritable() { 291 const err = new Error('cancel reason'); 292 const ts = new TransformStream(); 293 const writer = ts.writable.getWriter(); 294 ts.readable.cancel(err); 295 try { 296 await writer.closed; 297 test('writer.closed should reject after cancel', false, true); 298 } catch (e) { 299 test('writer.closed rejects with cancel reason', e, err); 300 } 301} 302 303async function testWritableStrategySize() { 304 let writableSizeCalled = false; 305 let readableSizeCalled = false; 306 const ts = new TransformStream( 307 { 308 transform(chunk, controller) { 309 controller.enqueue(chunk); 310 } 311 }, 312 { 313 size() { writableSizeCalled = true; return 1; } 314 }, 315 { 316 size() { readableSizeCalled = true; return 1; }, 317 highWaterMark: Infinity 318 } 319 ); 320 await ts.writable.getWriter().write('x'); 321 test('writable size called', writableSizeCalled, true); 322 test('readable size called', readableSizeCalled, true); 323} 324 325async function testPipeThrough() { 326 const rs = new ReadableStream({ 327 start(c) { c.enqueue(1); c.enqueue(2); c.enqueue(3); c.close(); } 328 }); 329 const ts = new TransformStream({ 330 transform(chunk, controller) { 331 controller.enqueue(chunk * 10); 332 } 333 }); 334 const result = rs.pipeThrough(ts); 335 test('pipeThrough returns readable', result instanceof ReadableStream, true); 336 const reader = result.getReader(); 337 const chunks = []; 338 while (true) { 339 const { value, done } = await reader.read(); 340 if (done) break; 341 chunks.push(value); 342 } 343 testDeep('pipeThrough transforms data', chunks, [10, 20, 30]); 344} 345 346async function testEnqueueAfterTerminateThrows() { 347 let threw = false; 348 new TransformStream({ 349 start(controller) { 350 controller.terminate(); 351 try { 352 controller.enqueue('x'); 353 } catch (e) { 354 threw = true; 355 } 356 } 357 }); 358 test('enqueue after terminate throws', threw, true); 359} 360 361await testIdentity(); 362await testTransformUppercase(); 363await testTransformDoubler(); 364await testFlush(); 365await testFlushEnqueue(); 366await testCloseClosesReadable(); 367await testTransformError(); 368await testFlushError(); 369await testControllerError(); 370await testControllerTerminate(); 371await testDesiredSize(); 372await testDefaultHWM(); 373await testCustomWritableHWM(); 374await testBackpressure(); 375await testAsyncTransform(); 376await testStartThrows(); 377await testCancelCallable(); 378await testAbortCallsCancel(); 379await testTransformThisBinding(); 380await testSubclass(); 381await testReadableHWM(); 382testNegativeHWMThrows(); 383await testCancelReadableErrorsWritable(); 384await testWritableStrategySize(); 385await testPipeThrough(); 386await testEnqueueAfterTerminateThrows(); 387 388summary();