1// META: global=window,worker 2// META: script=../resources/test-utils.js 3// META: script=../resources/recording-streams.js 4'use strict'; 5 6test(() => { 7 8 const rs = new ReadableStream(); 9 const ws = new WritableStream(); 10 11 assert_false(rs.locked, 'sanity check: the ReadableStream must not start locked'); 12 assert_false(ws.locked, 'sanity check: the WritableStream must not start locked'); 13 14 rs.pipeTo(ws); 15 16 assert_true(rs.locked, 'the ReadableStream must become locked'); 17 assert_true(ws.locked, 'the WritableStream must become locked'); 18 19}, 'Piping must lock both the ReadableStream and WritableStream'); 20 21promise_test(() => { 22 23 const rs = new ReadableStream({ 24 start(controller) { 25 controller.close(); 26 } 27 }); 28 const ws = new WritableStream(); 29 30 return rs.pipeTo(ws).then(() => { 31 assert_false(rs.locked, 'the ReadableStream must become unlocked'); 32 assert_false(ws.locked, 'the WritableStream must become unlocked'); 33 }); 34 35}, 'Piping finishing must unlock both the ReadableStream and WritableStream'); 36 37promise_test(t => { 38 39 const fakeRS = Object.create(ReadableStream.prototype); 40 const ws = new WritableStream(); 41 42 return methodRejects(t, ReadableStream.prototype, 'pipeTo', fakeRS, [ws]); 43 44}, 'pipeTo must check the brand of its ReadableStream this value'); 45 46promise_test(t => { 47 48 const rs = new ReadableStream(); 49 const fakeWS = Object.create(WritableStream.prototype); 50 51 return methodRejects(t, ReadableStream.prototype, 'pipeTo', rs, [fakeWS]); 52 53}, 'pipeTo must check the brand of its WritableStream argument'); 54 55promise_test(t => { 56 57 const rs = new ReadableStream(); 58 const ws = new WritableStream(); 59 60 rs.getReader(); 61 62 assert_true(rs.locked, 'sanity check: the ReadableStream starts locked'); 63 assert_false(ws.locked, 'sanity check: the WritableStream does not start locked'); 64 65 return promise_rejects_js(t, TypeError, rs.pipeTo(ws)).then(() => { 66 assert_false(ws.locked, 'the WritableStream must still be unlocked'); 67 }); 68 69}, 'pipeTo must fail if the ReadableStream is locked, and not lock the WritableStream'); 70 71promise_test(t => { 72 73 const rs = new ReadableStream(); 74 const ws = new WritableStream(); 75 76 ws.getWriter(); 77 78 assert_false(rs.locked, 'sanity check: the ReadableStream does not start locked'); 79 assert_true(ws.locked, 'sanity check: the WritableStream starts locked'); 80 81 return promise_rejects_js(t, TypeError, rs.pipeTo(ws)).then(() => { 82 assert_false(rs.locked, 'the ReadableStream must still be unlocked'); 83 }); 84 85}, 'pipeTo must fail if the WritableStream is locked, and not lock the ReadableStream'); 86 87promise_test(() => { 88 89 const CHUNKS = 10; 90 91 const rs = new ReadableStream({ 92 start(c) { 93 for (let i = 0; i < CHUNKS; ++i) { 94 c.enqueue(i); 95 } 96 c.close(); 97 } 98 }); 99 100 const written = []; 101 const ws = new WritableStream({ 102 write(chunk) { 103 written.push(chunk); 104 }, 105 close() { 106 written.push('closed'); 107 } 108 }, new CountQueuingStrategy({ highWaterMark: CHUNKS })); 109 110 return rs.pipeTo(ws).then(() => { 111 const targetValues = []; 112 for (let i = 0; i < CHUNKS; ++i) { 113 targetValues.push(i); 114 } 115 targetValues.push('closed'); 116 117 assert_array_equals(written, targetValues, 'the correct values must be written'); 118 119 // Ensure both readable and writable are closed by the time the pipe finishes. 120 return Promise.all([ 121 rs.getReader().closed, 122 ws.getWriter().closed 123 ]); 124 }); 125 126 // NOTE: no requirement on *when* the pipe finishes; that is left to implementations. 127 128}, 'Piping from a ReadableStream from which lots of chunks are synchronously readable'); 129 130promise_test(t => { 131 132 let controller; 133 const rs = recordingReadableStream({ 134 start(c) { 135 controller = c; 136 } 137 }); 138 139 const ws = recordingWritableStream(); 140 141 const pipePromise = rs.pipeTo(ws).then(() => { 142 assert_array_equals(ws.events, ['write', 'Hello', 'close']); 143 }); 144 145 t.step_timeout(() => { 146 controller.enqueue('Hello'); 147 t.step_timeout(() => controller.close(), 10); 148 }, 10); 149 150 return pipePromise; 151 152}, 'Piping from a ReadableStream for which a chunk becomes asynchronously readable after the pipeTo'); 153 154for (const preventAbort of [true, false]) { 155 promise_test(() => { 156 157 const rs = new ReadableStream({ 158 pull() { 159 return Promise.reject(undefined); 160 } 161 }); 162 163 return rs.pipeTo(new WritableStream(), { preventAbort }).then( 164 () => assert_unreached('pipeTo promise should be rejected'), 165 value => assert_equals(value, undefined, 'rejection value should be undefined')); 166 167 }, `an undefined rejection from pull should cause pipeTo() to reject when preventAbort is ${preventAbort}`); 168} 169 170for (const preventCancel of [true, false]) { 171 promise_test(() => { 172 173 const rs = new ReadableStream({ 174 pull(controller) { 175 controller.enqueue(0); 176 } 177 }); 178 179 const ws = new WritableStream({ 180 write() { 181 return Promise.reject(undefined); 182 } 183 }); 184 185 return rs.pipeTo(ws, { preventCancel }).then( 186 () => assert_unreached('pipeTo promise should be rejected'), 187 value => assert_equals(value, undefined, 'rejection value should be undefined')); 188 189 }, `an undefined rejection from write should cause pipeTo() to reject when preventCancel is ${preventCancel}`); 190} 191 192promise_test(t => { 193 const rs = new ReadableStream(); 194 const ws = new WritableStream(); 195 return promise_rejects_js(t, TypeError, rs.pipeTo(ws, { 196 get preventAbort() { 197 ws.getWriter(); 198 } 199 }), 'pipeTo should reject'); 200}, 'pipeTo() should reject if an option getter grabs a writer'); 201 202promise_test(t => { 203 const rs = new ReadableStream({ 204 start(controller) { 205 controller.close(); 206 } 207 }); 208 const ws = new WritableStream(); 209 210 return rs.pipeTo(ws, null); 211}, 'pipeTo() promise should resolve if null is passed'); 212