| <!DOCTYPE html> |
| <div id=result>NOTHING</div> |
| <script> |
| // Test that ReadableStream and WritableStream still work correctly when the |
| // global object has been interfered with. |
| // |
| // This test cannot use testharness.js because it doesn't work when you remove |
| // all the global objects. |
| |
| 'use strict'; |
| |
| const testRunner = self.testRunner; |
| if (testRunner) { |
| testRunner.dumpAsText(); |
| testRunner.waitUntilDone(); |
| } |
| |
| async function runTest() { |
| const ReadableStream = self.ReadableStream; |
| const WritableStream = self.WritableStream; |
| const ByteLengthQueuingStrategy = self.ByteLengthQueuingStrategy; |
| const CountQueuingStrategy = self.CountQueuingStrategy; |
| const Error = self.Error; |
| const Promise = self.Promise; |
| const Object = self.Object; |
| const Object_getOwnPropertyNames = Object.getOwnPropertyNames; |
| |
| function undefineObjectProperties(o, exceptions = new Set()) { |
| for (const key of Object_getOwnPropertyNames(o)) { |
| if (exceptions.has(key)) { |
| continue; |
| } |
| try { |
| o[key] = undefined; |
| } catch (e) { |
| assert(e.name === 'TypeError', 'expecting a TypeError'); |
| } |
| } |
| } |
| |
| function assert(pred, message) { |
| if (pred) { |
| return; |
| } |
| throw new Error(message); |
| } |
| |
| async function testReadAndPipeTo() { |
| let done; |
| const rs = new ReadableStream({ |
| pull(controller) { |
| if (done) { |
| controller.close(); |
| } |
| assert(controller.desiredSize > 0, 'desiredSize must be greater than 0'); |
| controller.enqueue('tree'); |
| } |
| }); |
| const reader = rs.getReader(); |
| assert(rs.locked, 'ReadableStream should be locked'); |
| await reader.read(); |
| reader.releaseLock(); |
| await rs.pipeTo(new WritableStream({ |
| write() { |
| done = true; |
| } |
| })); |
| } |
| |
| async function testReadableStreamClosed() { |
| const rs = new ReadableStream({ |
| pull(controller) { |
| controller.close(); |
| } |
| }); |
| const reader = rs.getReader(); |
| await reader.read(); |
| await reader.closed |
| } |
| |
| async function testPipeThrough() { |
| const emptyRS = new ReadableStream({ |
| pull(controller) { |
| controller.close(); |
| } |
| }); |
| let resolve; |
| const p = new Promise(r => {resolve = r}); |
| const closeResolvesWS = new WritableStream({ |
| close() { |
| resolve(); |
| } |
| }, new CountQueuingStrategy({ highWaterMark: 10})); |
| emptyRS.pipeThrough({ |
| writable: closeResolvesWS, |
| readable: new ReadableStream() |
| }, { |
| preventClose: 0, |
| preventAbort: true, |
| preventCancel: 'yes' |
| }); |
| await p; |
| } |
| |
| async function testReadableStreamError() { |
| const rs = new ReadableStream({ |
| pull(controller) { |
| controller.error('my error'); |
| } |
| }); |
| const reader = rs.getReader(); |
| try { |
| await reader.read(); |
| } catch (e) { |
| assert(e === 'my error', 'my error should be thrown'); |
| } |
| } |
| |
| async function testTee() { |
| const rs = new ReadableStream({ |
| start(controller) { |
| controller.enqueue('beef'); |
| } |
| }, { size: () => 1, highWaterMark: 19 }); |
| const [left, right] = rs.tee(); |
| const left_cancel = left.cancel(); |
| const reader = right.getReader(); |
| const { value, done } = await reader.read(); |
| assert(value === 'beef', 'value should be beef'); |
| assert(done === false, 'done should be false'); |
| await reader.cancel(); |
| await left_cancel; |
| } |
| |
| async function testWritableStreamWriteAndReleaseLock() { |
| const ws = new WritableStream({}, {highWaterMark: 7}); |
| assert(!ws.locked, 'ws should not be locked'); |
| const writer = ws.getWriter(); |
| assert(writer.desiredSize > 0, 'desiredSize should be positive'); |
| await writer.ready; |
| await writer.write('something'); |
| writer.releaseLock(); |
| await ws.abort(); |
| } |
| |
| async function testWritableStreamClose() { |
| const ws = new WritableStream({}, new ByteLengthQueuingStrategy({ highWaterMark: 1024 })); |
| const writer = ws.getWriter(); |
| await writer.close(); |
| await writer.closed; |
| } |
| |
| async function testWritableStreamDefaultControllerError() { |
| const ws = new WritableStream({ |
| start(controller) { |
| controller.error('something bad'); |
| } |
| }); |
| try { |
| await ws.getWriter().closed; |
| } catch (e) { |
| assert(e === 'something bad', 'e should be something bad'); |
| } |
| } |
| |
| // Undefine various prototypes and static methods that might be used in the |
| // implementation. |
| |
| // The "await" keyword uses Promise.prototype.then. |
| // TODO(ricea): Remove all uses of await from this test. |
| undefineObjectProperties(Promise.prototype, new Set(['then'])); |
| undefineObjectProperties(Promise); |
| undefineObjectProperties(Object.prototype); |
| undefineObjectProperties(Object); |
| // Array.prototype.length is writable but assigning |undefined| to it results |
| // in a RangeError being thrown. |
| undefineObjectProperties(Array.prototype, new Set(['length'])); |
| undefineObjectProperties(Array); |
| undefineObjectProperties(Function.prototype); |
| undefineObjectProperties(Function); |
| undefineObjectProperties(Number.prototype); |
| undefineObjectProperties(Number); |
| undefineObjectProperties(String.prototype); |
| undefineObjectProperties(String); |
| undefineObjectProperties(Boolean.prototype); |
| undefineObjectProperties(Boolean); |
| undefineObjectProperties(Math); |
| |
| // Make methods that can be called during implicit type conversions throw. |
| Object.prototype.toString = () => { |
| throw new Error('Object toString() called'); |
| }; |
| Object.prototype.valueOf = () => { |
| throw new Error('Object valueOf() called'); |
| }; |
| Array.prototype.toString = () => { |
| throw new Error('Array toString() called'); |
| }; |
| Array.prototype.valueOf = () => { |
| throw new Error('Array valueOf() called'); |
| }; |
| |
| // Undefine (almost) everything on the global object. |
| // Assigning to 'location' causes a navigation which breaks the test. |
| const doNotSet = new Set(['location']); |
| undefineObjectProperties(self, doNotSet); |
| |
| await testReadAndPipeTo(); |
| await testReadableStreamClosed(); |
| await testPipeThrough(); |
| await testReadableStreamError(); |
| await testTee(); |
| await testWritableStreamWriteAndReleaseLock(); |
| await testWritableStreamClose(); |
| await testWritableStreamDefaultControllerError(); |
| |
| document.querySelector('#result').textContent = 'PASS'; |
| if (testRunner) { |
| testRunner.notifyDone(); |
| } |
| } |
| |
| runTest(); |
| </script> |