// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js 'use strict'; // Here we just test that the strategies are correctly passed to the readable and writable sides. We assume that // ReadableStream and WritableStream will correctly apply the strategies when they are being used by a TransformStream // and so it isn't necessary to repeat their tests here. test(() => { const ts = new TransformStream({}, { highWaterMark: 17 }); assert_equals(ts.writable.getWriter().desiredSize, 17, 'desiredSize should be 17'); }, 'writableStrategy highWaterMark should work'); promise_test(() => { const ts = recordingTransformStream({}, undefined, { highWaterMark: 9 }); const writer = ts.writable.getWriter(); for (let i = 0; i < 10; ++i) { writer.write(i); } return delay(0).then(() => { assert_array_equals(ts.events, [ 'transform', 0, 'transform', 1, 'transform', 2, 'transform', 3, 'transform', 4, 'transform', 5, 'transform', 6, 'transform', 7, 'transform', 8], 'transform() should have been called 9 times'); }); }, 'readableStrategy highWaterMark should work'); promise_test(t => { let writableSizeCalled = false; let readableSizeCalled = false; let transformCalled = false; const ts = new TransformStream( { transform(chunk, controller) { t.step(() => { transformCalled = true; assert_true(writableSizeCalled, 'writableStrategy.size() should have been called'); assert_false(readableSizeCalled, 'readableStrategy.size() should not have been called'); controller.enqueue(chunk); assert_true(readableSizeCalled, 'readableStrategy.size() should have been called'); }); } }, { size() { writableSizeCalled = true; return 1; } }, { size() { readableSizeCalled = true; return 1; }, highWaterMark: Infinity }); return ts.writable.getWriter().write().then(() => { assert_true(transformCalled, 'transform() should be called'); }); }, 'writable should have the correct size() function'); test(() => { const ts = new TransformStream(); const writer = ts.writable.getWriter(); assert_equals(writer.desiredSize, 1, 'default writable HWM is 1'); writer.write(undefined); assert_equals(writer.desiredSize, 0, 'default chunk size is 1'); }, 'default writable strategy should be equivalent to { highWaterMark: 1 }'); promise_test(t => { const ts = new TransformStream({ transform(chunk, controller) { return t.step(() => { assert_equals(controller.desiredSize, 0, 'desiredSize should be 0'); controller.enqueue(undefined); // The first chunk enqueued is consumed by the pending read(). assert_equals(controller.desiredSize, 0, 'desiredSize should still be 0'); controller.enqueue(undefined); assert_equals(controller.desiredSize, -1, 'desiredSize should be -1'); }); } }); const writePromise = ts.writable.getWriter().write(); return ts.readable.getReader().read().then(() => writePromise); }, 'default readable strategy should be equivalent to { highWaterMark: 0 }'); test(() => { assert_throws_js(RangeError, () => new TransformStream(undefined, { highWaterMark: -1 }), 'should throw RangeError for negative writableHighWaterMark'); assert_throws_js(RangeError, () => new TransformStream(undefined, undefined, { highWaterMark: -1 }), 'should throw RangeError for negative readableHighWaterMark'); assert_throws_js(RangeError, () => new TransformStream(undefined, { highWaterMark: NaN }), 'should throw RangeError for NaN writableHighWaterMark'); assert_throws_js(RangeError, () => new TransformStream(undefined, undefined, { highWaterMark: NaN }), 'should throw RangeError for NaN readableHighWaterMark'); }, 'a RangeError should be thrown for an invalid highWaterMark'); const objectThatConvertsTo42 = { toString() { return '42'; } }; test(() => { const ts = new TransformStream(undefined, { highWaterMark: objectThatConvertsTo42 }); const writer = ts.writable.getWriter(); assert_equals(writer.desiredSize, 42, 'writable HWM is 42'); }, 'writableStrategy highWaterMark should be converted to a number'); test(() => { const ts = new TransformStream({ start(controller) { assert_equals(controller.desiredSize, 42, 'desiredSize should be 42'); } }, undefined, { highWaterMark: objectThatConvertsTo42 }); }, 'readableStrategy highWaterMark should be converted to a number'); promise_test(t => { const ts = new TransformStream(undefined, undefined, { size() { return NaN; }, highWaterMark: 1 }); const writer = ts.writable.getWriter(); return promise_rejects_js(t, RangeError, writer.write(), 'write should reject'); }, 'a bad readableStrategy size function should cause writer.write() to reject on an identity transform'); promise_test(t => { const ts = new TransformStream({ transform(chunk, controller) { // This assert has the important side-effect of catching the error, so transform() does not throw. assert_throws_js(RangeError, () => controller.enqueue(chunk), 'enqueue should throw'); } }, undefined, { size() { return -1; }, highWaterMark: 1 }); const writer = ts.writable.getWriter(); return writer.write().then(() => { return Promise.all([ promise_rejects_js(t, RangeError, writer.ready, 'ready should reject'), promise_rejects_js(t, RangeError, writer.closed, 'closed should reject'), promise_rejects_js(t, RangeError, ts.readable.getReader().closed, 'readable closed should reject') ]); }); }, 'a bad readableStrategy size function should error the stream on enqueue even when transformer.transform() ' + 'catches the exception');