mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 09:15:35 +00:00
Bug 1630436 [wpt PR 22982] - Streams: update tests for Web IDL conversion, a=testonly
Automatic update from web-platform-tests Streams: update tests for Web IDL conversion Follows https://github.com/whatwg/streams/pull/1035. Notable changes: - Updates for the normative changes listed there. - Introduce an idlharness test - Remove various tests for things that are covered by idlharness, such as brand checks, the order of getting values from dictionaries, etc. - Updated for the fact that everything is now globally exposed, so some roundabout code to get constructors can be removed. - Slight timing updates: the pattern of returning a promise from an underlying sink/source/transformer `start()` function, then waiting on that before doing asserts, does not work with Web IDL's "promise resolved with". So instead we use `flushAsyncEvents()` to wait a little longer. - Consolidated queuing strategy tests into a single file, since after deleting the things covered by idlharness, most of the tests are shared. - Added tests that underlyingSink/underlyingSource are converted after queuingStrategy, since those dictionary conversions are done in prose. - Added a test for various updates to the Web IDL async iterators specification. -- wpt-commits: 887350c2f46def5b01c4dd1f8d2eee35dfb9c5bb wpt-pr: 22982
This commit is contained in:
parent
71bad18802
commit
1df360c380
204
testing/web-platform/tests/interfaces/streams.idl
Normal file
204
testing/web-platform/tests/interfaces/streams.idl
Normal file
@ -0,0 +1,204 @@
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableStream {
|
||||
constructor(optional object underlyingSource, optional QueuingStrategy strategy = {});
|
||||
|
||||
readonly attribute boolean locked;
|
||||
|
||||
Promise<void> cancel(optional any reason);
|
||||
ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions options = {});
|
||||
ReadableStream pipeThrough(ReadableWritablePair transform, optional StreamPipeOptions options = {});
|
||||
Promise<void> pipeTo(WritableStream destination, optional StreamPipeOptions options = {});
|
||||
sequence<ReadableStream> tee();
|
||||
|
||||
async iterable<any>(optional ReadableStreamIteratorOptions options = {});
|
||||
};
|
||||
|
||||
typedef (ReadableStreamDefaultReader or ReadableStreamBYOBReader) ReadableStreamReader;
|
||||
|
||||
enum ReadableStreamReaderMode { "byob" };
|
||||
|
||||
dictionary ReadableStreamGetReaderOptions {
|
||||
ReadableStreamReaderMode mode;
|
||||
};
|
||||
|
||||
dictionary ReadableStreamIteratorOptions {
|
||||
boolean preventCancel = false;
|
||||
};
|
||||
|
||||
dictionary ReadableWritablePair {
|
||||
required ReadableStream readable;
|
||||
required WritableStream writable;
|
||||
};
|
||||
|
||||
dictionary StreamPipeOptions {
|
||||
boolean preventClose = false;
|
||||
boolean preventAbort = false;
|
||||
boolean preventCancel = false;
|
||||
AbortSignal signal;
|
||||
};
|
||||
|
||||
dictionary UnderlyingSource {
|
||||
UnderlyingSourceStartCallback start;
|
||||
UnderlyingSourcePullCallback pull;
|
||||
UnderlyingSourceCancelCallback cancel;
|
||||
ReadableStreamType type;
|
||||
[EnforceRange] unsigned long long autoAllocateChunkSize;
|
||||
};
|
||||
|
||||
typedef (ReadableStreamDefaultController or ReadableByteStreamController) ReadableStreamController;
|
||||
|
||||
callback UnderlyingSourceStartCallback = any (ReadableStreamController controller);
|
||||
callback UnderlyingSourcePullCallback = Promise<void> (ReadableStreamController controller);
|
||||
callback UnderlyingSourceCancelCallback = Promise<void> (optional any reason);
|
||||
|
||||
enum ReadableStreamType { "bytes" };
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableStreamDefaultReader {
|
||||
constructor(ReadableStream stream);
|
||||
|
||||
readonly attribute Promise<void> closed;
|
||||
|
||||
Promise<void> cancel(optional any reason);
|
||||
Promise<any> read();
|
||||
void releaseLock();
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableStreamBYOBReader {
|
||||
constructor(ReadableStream stream);
|
||||
|
||||
readonly attribute Promise<void> closed;
|
||||
|
||||
Promise<void> cancel(optional any reason);
|
||||
Promise<any> read(ArrayBufferView view);
|
||||
void releaseLock();
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableStreamDefaultController {
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
||||
void close();
|
||||
void enqueue(optional any chunk);
|
||||
void error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableByteStreamController {
|
||||
readonly attribute ReadableStreamBYOBRequest? byobRequest;
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
||||
void close();
|
||||
void enqueue(ArrayBufferView chunk);
|
||||
void error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ReadableStreamBYOBRequest {
|
||||
readonly attribute ArrayBufferView? view;
|
||||
|
||||
void respond([EnforceRange] unsigned long long bytesWritten);
|
||||
void respondWithNewView(ArrayBufferView view);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface WritableStream {
|
||||
constructor(optional object underlyingSink, optional QueuingStrategy strategy = {});
|
||||
|
||||
readonly attribute boolean locked;
|
||||
|
||||
Promise<void> abort(optional any reason);
|
||||
Promise<void> close();
|
||||
WritableStreamDefaultWriter getWriter();
|
||||
};
|
||||
|
||||
dictionary UnderlyingSink {
|
||||
UnderlyingSinkStartCallback start;
|
||||
UnderlyingSinkWriteCallback write;
|
||||
UnderlyingSinkCloseCallback close;
|
||||
UnderlyingSinkAbortCallback abort;
|
||||
any type;
|
||||
};
|
||||
|
||||
callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller);
|
||||
callback UnderlyingSinkWriteCallback = Promise<void> (WritableStreamDefaultController controller, optional any chunk);
|
||||
callback UnderlyingSinkCloseCallback = Promise<void> ();
|
||||
callback UnderlyingSinkAbortCallback = Promise<void> (optional any reason);
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface WritableStreamDefaultWriter {
|
||||
constructor(WritableStream stream);
|
||||
|
||||
readonly attribute Promise<void> closed;
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
readonly attribute Promise<void> ready;
|
||||
|
||||
Promise<void> abort(optional any reason);
|
||||
Promise<void> close();
|
||||
void releaseLock();
|
||||
Promise<void> write(optional any chunk);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface WritableStreamDefaultController {
|
||||
void error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface TransformStream {
|
||||
constructor(optional object transformer,
|
||||
optional QueuingStrategy writableStrategy = {},
|
||||
optional QueuingStrategy readableStrategy = {});
|
||||
|
||||
readonly attribute ReadableStream readable;
|
||||
readonly attribute WritableStream writable;
|
||||
};
|
||||
|
||||
dictionary Transformer {
|
||||
TransformerStartCallback start;
|
||||
TransformerTransformCallback transform;
|
||||
TransformerFlushCallback flush;
|
||||
any readableType;
|
||||
any writableType;
|
||||
};
|
||||
|
||||
callback TransformerStartCallback = any (TransformStreamDefaultController controller);
|
||||
callback TransformerFlushCallback = Promise<void> (TransformStreamDefaultController controller);
|
||||
callback TransformerTransformCallback = Promise<void> (TransformStreamDefaultController controller, optional any chunk);
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface TransformStreamDefaultController {
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
||||
void enqueue(optional any chunk);
|
||||
void error(optional any reason);
|
||||
void terminate();
|
||||
};
|
||||
|
||||
dictionary QueuingStrategy {
|
||||
unrestricted double highWaterMark;
|
||||
QueuingStrategySize size;
|
||||
};
|
||||
|
||||
callback QueuingStrategySize = unrestricted double (optional any chunk);
|
||||
|
||||
dictionary QueuingStrategyInit {
|
||||
required unrestricted double highWaterMark;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface ByteLengthQueuingStrategy {
|
||||
constructor(QueuingStrategyInit init);
|
||||
|
||||
attribute unrestricted double highWaterMark;
|
||||
readonly attribute Function size;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
interface CountQueuingStrategy {
|
||||
constructor(QueuingStrategyInit init);
|
||||
|
||||
readonly attribute unrestricted double highWaterMark;
|
||||
readonly attribute Function size;
|
||||
};
|
@ -1,132 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
test(() => {
|
||||
|
||||
new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
}, 'Can construct a ByteLengthQueuingStrategy with a valid high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
for (const highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark });
|
||||
assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
|
||||
}
|
||||
|
||||
}, 'Can construct a ByteLengthQueuingStrategy with any value as its high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
const highWaterMark = 1;
|
||||
const highWaterMarkObjectGetter = {
|
||||
get highWaterMark() { return highWaterMark; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const highWaterMarkObjectGetterThrowing = {
|
||||
get highWaterMark() { throw error; }
|
||||
};
|
||||
|
||||
assert_throws_js(TypeError, () => new ByteLengthQueuingStrategy(), 'construction fails with undefined');
|
||||
assert_throws_js(TypeError, () => new ByteLengthQueuingStrategy(null), 'construction fails with null');
|
||||
assert_throws_js(Error, () => new ByteLengthQueuingStrategy(highWaterMarkObjectGetterThrowing),
|
||||
'construction fails with an object with a throwing highWaterMark getter');
|
||||
|
||||
// Should not fail:
|
||||
new ByteLengthQueuingStrategy('potato');
|
||||
new ByteLengthQueuingStrategy({});
|
||||
new ByteLengthQueuingStrategy(highWaterMarkObjectGetter);
|
||||
|
||||
}, 'ByteLengthQueuingStrategy constructor behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
assert_throws_js(TypeError, () => ByteLengthQueuingStrategy.prototype.size(), 'size fails with undefined');
|
||||
assert_throws_js(TypeError, () => ByteLengthQueuingStrategy.prototype.size(null), 'size fails with null');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size('potato'), undefined,
|
||||
'size succeeds with undefined with a random non-object type');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size({}), undefined,
|
||||
'size succeeds with undefined with an object without hwm property');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size(chunk), size,
|
||||
'size succeeds with the right amount with an object with a hwm');
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size(chunkGetter), size,
|
||||
'size succeeds with the right amount with an object with a hwm getter');
|
||||
assert_throws_js(Error, () => ByteLengthQueuingStrategy.prototype.size(chunkGetterThrowing),
|
||||
'size fails with the error thrown by the getter');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy size behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const thisValue = null;
|
||||
const returnValue = { 'returned from': 'byteLength getter' };
|
||||
const chunk = {
|
||||
get byteLength() { return returnValue; }
|
||||
};
|
||||
|
||||
assert_equals(ByteLengthQueuingStrategy.prototype.size.call(thisValue, chunk), returnValue);
|
||||
|
||||
}, 'ByteLengthQueuingStrategy.prototype.size should work generically on its this and its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
|
||||
{ value: 4, writable: true, enumerable: true, configurable: true },
|
||||
'highWaterMark property should be a data property with the value passed the constructor');
|
||||
assert_equals(typeof strategy.size, 'function');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy instances have the correct properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
|
||||
assert_equals(strategy.highWaterMark, 4);
|
||||
|
||||
strategy.highWaterMark = 10;
|
||||
assert_equals(strategy.highWaterMark, 10);
|
||||
|
||||
strategy.highWaterMark = 'banana';
|
||||
assert_equals(strategy.highWaterMark, 'banana');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy\'s highWaterMark property can be set to anything');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_equals(ByteLengthQueuingStrategy.name, 'ByteLengthQueuingStrategy',
|
||||
'ByteLengthQueuingStrategy.name must be "ByteLengthQueuingStrategy"');
|
||||
|
||||
}, 'ByteLengthQueuingStrategy.name is correct');
|
||||
|
||||
class SubClass extends ByteLengthQueuingStrategy {
|
||||
size() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
subClassMethod() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
const sc = new SubClass({highWaterMark: 77});
|
||||
assert_equals(sc.constructor.name, 'SubClass',
|
||||
'constructor.name should be correct');
|
||||
assert_equals(sc.highWaterMark, 77,
|
||||
'highWaterMark should come from the parent class');
|
||||
assert_equals(sc.size(), 2,
|
||||
'size() on the subclass should override the parent');
|
||||
assert_true(sc.subClassMethod(), 'subClassMethod() should work');
|
||||
|
||||
}, 'subclassing ByteLengthQueuingStrategy should work correctly');
|
@ -1,131 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
test(() => {
|
||||
|
||||
new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
}, 'Can construct a CountQueuingStrategy with a valid high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
for (const highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark });
|
||||
assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
|
||||
}
|
||||
|
||||
}, 'Can construct a CountQueuingStrategy with any value as its high water mark');
|
||||
|
||||
test(() => {
|
||||
|
||||
const highWaterMark = 1;
|
||||
const highWaterMarkObjectGetter = {
|
||||
get highWaterMark() { return highWaterMark; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const highWaterMarkObjectGetterThrowing = {
|
||||
get highWaterMark() { throw error; }
|
||||
};
|
||||
|
||||
assert_throws_js(TypeError, () => new CountQueuingStrategy(), 'construction fails with undefined');
|
||||
assert_throws_js(TypeError, () => new CountQueuingStrategy(null), 'construction fails with null');
|
||||
assert_throws_js(Error, () => new CountQueuingStrategy(highWaterMarkObjectGetterThrowing),
|
||||
'construction fails with an object with a throwing highWaterMark getter');
|
||||
|
||||
// Should not fail:
|
||||
new CountQueuingStrategy('potato');
|
||||
new CountQueuingStrategy({});
|
||||
new CountQueuingStrategy(highWaterMarkObjectGetter);
|
||||
|
||||
}, 'CountQueuingStrategy constructor behaves as expected with strange arguments');
|
||||
|
||||
|
||||
test(() => {
|
||||
|
||||
const thisValue = null;
|
||||
const chunk = {
|
||||
get byteLength() {
|
||||
throw new TypeError('shouldn\'t be called');
|
||||
}
|
||||
};
|
||||
|
||||
assert_equals(CountQueuingStrategy.prototype.size.call(thisValue, chunk), 1);
|
||||
|
||||
}, 'CountQueuingStrategy.prototype.size should work generically on its this and its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
|
||||
assert_equals(CountQueuingStrategy.prototype.size(), 1, 'size returns 1 with undefined');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(null), 1, 'size returns 1 with null');
|
||||
assert_equals(CountQueuingStrategy.prototype.size('potato'), 1, 'size returns 1 with non-object type');
|
||||
assert_equals(CountQueuingStrategy.prototype.size({}), 1, 'size returns 1 with empty object');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunk), 1, 'size returns 1 with a chunk');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunkGetter), 1, 'size returns 1 with chunk getter');
|
||||
assert_equals(CountQueuingStrategy.prototype.size(chunkGetterThrowing), 1,
|
||||
'size returns 1 with chunk getter that throws');
|
||||
|
||||
}, 'CountQueuingStrategy size behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
|
||||
assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
|
||||
{ value: 4, writable: true, enumerable: true, configurable: true },
|
||||
'highWaterMark property should be a data property with the value passed the constructor');
|
||||
assert_equals(typeof strategy.size, 'function');
|
||||
|
||||
}, 'CountQueuingStrategy instances have the correct properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
|
||||
assert_equals(strategy.highWaterMark, 4);
|
||||
|
||||
strategy.highWaterMark = 10;
|
||||
assert_equals(strategy.highWaterMark, 10);
|
||||
|
||||
strategy.highWaterMark = 'banana';
|
||||
assert_equals(strategy.highWaterMark, 'banana');
|
||||
|
||||
}, 'CountQueuingStrategy\'s highWaterMark property can be set to anything');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_equals(CountQueuingStrategy.name, 'CountQueuingStrategy',
|
||||
'CountQueuingStrategy.name must be "CountQueuingStrategy"');
|
||||
|
||||
}, 'CountQueuingStrategy.name is correct');
|
||||
|
||||
class SubClass extends CountQueuingStrategy {
|
||||
size() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
subClassMethod() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
const sc = new SubClass({highWaterMark: 77});
|
||||
assert_equals(sc.constructor.name, 'SubClass',
|
||||
'constructor.name should be correct');
|
||||
assert_equals(sc.highWaterMark, 77,
|
||||
'highWaterMark should come from the parent class');
|
||||
assert_equals(sc.size(), 2,
|
||||
'size() on the subclass should override the parent');
|
||||
assert_true(sc.subClassMethod(), 'subClassMethod() should work');
|
||||
|
||||
}, 'subclassing CountQueuingStrategy should work correctly');
|
75
testing/web-platform/tests/streams/idlharness.any.js
Normal file
75
testing/web-platform/tests/streams/idlharness.any.js
Normal file
@ -0,0 +1,75 @@
|
||||
// META: global=window,worker
|
||||
// META: script=/resources/WebIDLParser.js
|
||||
// META: script=/resources/idlharness.js
|
||||
// META: timeout=long
|
||||
|
||||
idl_test(
|
||||
['streams'],
|
||||
['dom'], // for AbortSignal
|
||||
async idl_array => {
|
||||
// Empty try/catches ensure that if something isn't implemented (e.g., readable byte streams, or writable streams)
|
||||
// the harness still sets things up correctly. Note that the corresponding interface tests will still fail.
|
||||
|
||||
try {
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
self.readableStreamDefaultController = c;
|
||||
}
|
||||
});
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
self.readableByteStreamController = c;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
const stream = new ReadableStream({
|
||||
pull() {
|
||||
self.readableStreamByobRequest = controller.byobRequest;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
await reader.read(new Uint8Array(1));
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
new WritableStream({
|
||||
start(c) {
|
||||
self.writableStreamDefaultController = c;
|
||||
}
|
||||
});
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
new TransformStream({
|
||||
start(c) {
|
||||
self.transformStreamDefaultController = c;
|
||||
}
|
||||
});
|
||||
} catch {}
|
||||
|
||||
idl_array.add_objects({
|
||||
ReadableStream: ["new ReadableStream()"],
|
||||
ReadableStreamDefaultReader: ["(new ReadableStream()).getReader()"],
|
||||
ReadableStreamBYOBReader: ["(new ReadableStream({ type: 'bytes' })).getReader('byob')"],
|
||||
ReadableStreamDefaultController: ["self.readableStreamDefaultController"],
|
||||
ReadableByteStreamController: ["self.readableByteStreamController"],
|
||||
ReadableStreamBYOBRequest: ["self.readableStreamByobRequest"],
|
||||
WritableStream: ["new WritableStream()"],
|
||||
WritableStreamDefaultWriter: ["(new WritableStream()).getWriter()"],
|
||||
WritableStreamDefaultController: ["self.writableStreamDefaultController"],
|
||||
TransformStream: ["new TransformStream()"],
|
||||
TransformStreamDefaultController: ["self.transformStreamDefaultController"],
|
||||
ByteLengthQueuingStrategy: ["new ByteLengthQueuingStrategy({ highWaterMark: 5 })"],
|
||||
CountQueuingStrategy: ["new CountQueuingStrategy({ highWaterMark: 5 })"]
|
||||
});
|
||||
}
|
||||
);
|
@ -81,12 +81,8 @@ promise_test(() => {
|
||||
|
||||
const rs = recordingReadableStream();
|
||||
|
||||
const startPromise = Promise.resolve();
|
||||
let resolveWritePromise;
|
||||
const ws = recordingWritableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
write() {
|
||||
if (!resolveWritePromise) {
|
||||
// first write
|
||||
@ -101,7 +97,7 @@ promise_test(() => {
|
||||
const writer = ws.getWriter();
|
||||
writer.write('a');
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_array_equals(ws.events, ['write', 'a']);
|
||||
assert_equals(writer.desiredSize, 0, 'after writing the writer\'s desiredSize must be 0');
|
||||
writer.releaseLock();
|
||||
|
@ -106,17 +106,19 @@ for (const readable of badReadables) {
|
||||
|
||||
test(() => {
|
||||
const rs = new ReadableStream();
|
||||
const writable = new WritableStream();
|
||||
let writableGetterCalled = false;
|
||||
assert_throws_js(TypeError, () => rs.pipeThrough({
|
||||
get writable() {
|
||||
writableGetterCalled = true;
|
||||
return new WritableStream();
|
||||
},
|
||||
readable
|
||||
}),
|
||||
'pipeThrough should brand-check readable');
|
||||
assert_true(writableGetterCalled, 'writable should have been accessed');
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
() => rs.pipeThrough({
|
||||
get writable() {
|
||||
writableGetterCalled = true;
|
||||
return new WritableStream();
|
||||
},
|
||||
readable
|
||||
}),
|
||||
'pipeThrough should brand-check readable'
|
||||
);
|
||||
assert_false(writableGetterCalled, 'writable should not have been accessed');
|
||||
}, `pipeThrough should brand-check readable and not allow '${readable}'`);
|
||||
}
|
||||
|
||||
@ -150,7 +152,7 @@ test(t => {
|
||||
}, { highWaterMark: 0 });
|
||||
|
||||
const throwingWritable = {
|
||||
readable: {},
|
||||
readable: rs,
|
||||
get writable() {
|
||||
throw error;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
class ThrowingOptions {
|
||||
constructor(whatShouldThrow) {
|
||||
@ -34,7 +35,7 @@ class ThrowingOptions {
|
||||
}
|
||||
}
|
||||
|
||||
const checkOrder = ['preventClose', 'preventAbort', 'preventCancel', 'signal'];
|
||||
const checkOrder = ['preventAbort', 'preventCancel', 'preventClose', 'signal'];
|
||||
|
||||
for (let i = 0; i < checkOrder.length; ++i) {
|
||||
const whatShouldThrow = checkOrder[i];
|
||||
|
135
testing/web-platform/tests/streams/queuing-strategies.any.js
Normal file
135
testing/web-platform/tests/streams/queuing-strategies.any.js
Normal file
@ -0,0 +1,135 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
const highWaterMarkConversions = new Map([
|
||||
[-Infinity, -Infinity],
|
||||
[-5, -5],
|
||||
[false, 0],
|
||||
[true, 1],
|
||||
[NaN, NaN],
|
||||
['foo', NaN],
|
||||
['0', 0],
|
||||
[{}, NaN],
|
||||
[() => {}, NaN]
|
||||
]);
|
||||
|
||||
for (const QueuingStrategy of [CountQueuingStrategy, ByteLengthQueuingStrategy]) {
|
||||
test(() => {
|
||||
new QueuingStrategy({ highWaterMark: 4 });
|
||||
}, `${QueuingStrategy.name}: Can construct a with a valid high water mark`);
|
||||
|
||||
test(() => {
|
||||
const highWaterMark = 1;
|
||||
const highWaterMarkObjectGetter = {
|
||||
get highWaterMark() { return highWaterMark; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const highWaterMarkObjectGetterThrowing = {
|
||||
get highWaterMark() { throw error; }
|
||||
};
|
||||
|
||||
assert_throws_js(TypeError, () => new QueuingStrategy(), 'construction fails with undefined');
|
||||
assert_throws_js(TypeError, () => new QueuingStrategy(null), 'construction fails with null');
|
||||
assert_throws_js(TypeError, () => new QueuingStrategy(true), 'construction fails with true');
|
||||
assert_throws_js(TypeError, () => new QueuingStrategy(5), 'construction fails with 5');
|
||||
assert_throws_js(TypeError, () => new QueuingStrategy({}), 'construction fails with {}');
|
||||
assert_throws_exactly(error, () => new QueuingStrategy(highWaterMarkObjectGetterThrowing),
|
||||
'construction fails with an object with a throwing highWaterMark getter');
|
||||
|
||||
assert_equals((new QueuingStrategy(highWaterMarkObjectGetter)).highWaterMark, highWaterMark);
|
||||
}, `${QueuingStrategy.name}: Constructor behaves as expected with strange arguments`);
|
||||
|
||||
test(() => {
|
||||
for (const [input, output] of highWaterMarkConversions.entries()) {
|
||||
const strategy = new QueuingStrategy({ highWaterMark: input });
|
||||
assert_equals(strategy.highWaterMark, output, `${input} gets set correctly`);
|
||||
}
|
||||
}, `${QueuingStrategy.name}: highWaterMark constructor values are converted per the unrestricted double rules`);
|
||||
|
||||
test(() => {
|
||||
const size1 = (new QueuingStrategy({ highWaterMark: 5 })).size;
|
||||
const size2 = (new QueuingStrategy({ highWaterMark: 10 })).size;
|
||||
|
||||
assert_equals(size1, size2);
|
||||
}, `${QueuingStrategy.name}: size is the same function across all instances`);
|
||||
|
||||
test(() => {
|
||||
const size = (new QueuingStrategy({ highWaterMark: 5 })).size;
|
||||
assert_equals(size.name, 'size');
|
||||
}, `${QueuingStrategy.name}: size should have the right name`);
|
||||
|
||||
test(() => {
|
||||
class SubClass extends QueuingStrategy {
|
||||
size() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
subClassMethod() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const sc = new SubClass({ highWaterMark: 77 });
|
||||
assert_equals(sc.constructor.name, 'SubClass', 'constructor.name should be correct');
|
||||
assert_equals(sc.highWaterMark, 77, 'highWaterMark should come from the parent class');
|
||||
assert_equals(sc.size(), 2, 'size() on the subclass should override the parent');
|
||||
assert_true(sc.subClassMethod(), 'subClassMethod() should work');
|
||||
}, `${QueuingStrategy.name}: subclassing should work correctly`);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
const size = (new CountQueuingStrategy({ highWaterMark: 5 })).size;
|
||||
assert_equals(size.length, 0);
|
||||
}, 'CountQueuingStrategy: size should have the right length');
|
||||
|
||||
test(() => {
|
||||
const size = (new ByteLengthQueuingStrategy({ highWaterMark: 5 })).size;
|
||||
assert_equals(size.length, 1);
|
||||
}, 'ByteLengthQueuingStrategy: size should have the right length');
|
||||
|
||||
test(() => {
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
|
||||
const sizeFunction = (new CountQueuingStrategy({ highWaterMark: 5 })).size;
|
||||
|
||||
assert_equals(sizeFunction(), 1, 'size returns 1 with undefined');
|
||||
assert_equals(sizeFunction(null), 1, 'size returns 1 with null');
|
||||
assert_equals(sizeFunction('potato'), 1, 'size returns 1 with non-object type');
|
||||
assert_equals(sizeFunction({}), 1, 'size returns 1 with empty object');
|
||||
assert_equals(sizeFunction(chunk), 1, 'size returns 1 with a chunk');
|
||||
assert_equals(sizeFunction(chunkGetter), 1, 'size returns 1 with chunk getter');
|
||||
assert_equals(sizeFunction(chunkGetterThrowing), 1,
|
||||
'size returns 1 with chunk getter that throws');
|
||||
}, 'CountQueuingStrategy: size behaves as expected with strange arguments');
|
||||
|
||||
test(() => {
|
||||
const size = 1024;
|
||||
const chunk = { byteLength: size };
|
||||
const chunkGetter = {
|
||||
get byteLength() { return size; }
|
||||
};
|
||||
const error = new Error('wow!');
|
||||
const chunkGetterThrowing = {
|
||||
get byteLength() { throw error; }
|
||||
};
|
||||
|
||||
const sizeFunction = (new ByteLengthQueuingStrategy({ highWaterMark: 5 })).size;
|
||||
|
||||
assert_throws_js(TypeError, () => sizeFunction(), 'size fails with undefined');
|
||||
assert_throws_js(TypeError, () => sizeFunction(null), 'size fails with null');
|
||||
assert_equals(sizeFunction('potato'), undefined, 'size succeeds with undefined with a random non-object type');
|
||||
assert_equals(sizeFunction({}), undefined, 'size succeeds with undefined with an object without hwm property');
|
||||
assert_equals(sizeFunction(chunk), size, 'size succeeds with the right amount with an object with a hwm');
|
||||
assert_equals(sizeFunction(chunkGetter), size,
|
||||
'size succeeds with the right amount with an object with a hwm getter');
|
||||
assert_throws_exactly(error, () => sizeFunction(chunkGetterThrowing),
|
||||
'size fails with the error thrown by the getter');
|
||||
}, 'ByteLengthQueuingStrategy: size behaves as expected with strange arguments');
|
@ -48,16 +48,36 @@ promise_test(() => {
|
||||
}, 'ReadableStream with byte source: read()ing from a stream with queued chunks still transfers the buffer');
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream({
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
const view = new Uint8Array([1, 2, 3]);
|
||||
c.enqueue(view);
|
||||
assert_throws_js(TypeError, () => c.enqueue(view), 'enqueuing an already-detached buffer must throw');
|
||||
assert_throws_js(TypeError, () => c.enqueue(view));
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
}, 'ReadableStream with byte source: enqueuing an already-detached buffer throws');
|
||||
|
||||
test(() => {
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
const view = new Uint8Array([]);
|
||||
assert_throws_js(TypeError, () => c.enqueue(view));
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
}, 'ReadableStream with byte source: enqueuing a zero-length buffer throws');
|
||||
|
||||
test(() => {
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
|
||||
assert_throws_js(TypeError, () => c.enqueue(view));
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
}, 'ReadableStream with byte source: enqueuing a zero-length view on a non-zero-length buffer throws');
|
||||
|
||||
promise_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
@ -70,11 +90,36 @@ promise_test(t => {
|
||||
const view = new Uint8Array([4, 5, 6]);
|
||||
return reader.read(view).then(() => {
|
||||
// view is now detached
|
||||
return promise_rejects_js(t, TypeError, reader.read(view),
|
||||
'read(view) must reject when given an already-detached buffer');
|
||||
return promise_rejects_js(t, TypeError, reader.read(view));
|
||||
});
|
||||
}, 'ReadableStream with byte source: reading into an already-detached buffer rejects');
|
||||
|
||||
promise_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(new Uint8Array([1, 2, 3]));
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
const view = new Uint8Array();
|
||||
return promise_rejects_js(t, TypeError, reader.read(view));
|
||||
}, 'ReadableStream with byte source: reading into a zero-length buffer rejects');
|
||||
|
||||
promise_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(new Uint8Array([1, 2, 3]));
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
|
||||
return promise_rejects_js(t, TypeError, reader.read(view));
|
||||
}, 'ReadableStream with byte source: reading into a zero-length view on a non-zero-length buffer rejects');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
@ -118,8 +163,7 @@ async_test(t => {
|
||||
const view = new Uint8Array([1, 2, 3]);
|
||||
reader.read(view);
|
||||
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view),
|
||||
'respondWithNewView() must throw if passed a detached view');
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -129,6 +173,36 @@ async_test(t => {
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer has been detached ' +
|
||||
'(in the readable state)');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
const view = new Uint8Array();
|
||||
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
reader.read(new Uint8Array([4, 5, 6]));
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer is zero-length ' +
|
||||
'(in the readable state)');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
|
||||
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
reader.read(new Uint8Array([4, 5, 6]));
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view is zero-length on a ' +
|
||||
'non-zero-length buffer (in the readable state)');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
@ -139,8 +213,7 @@ async_test(t => {
|
||||
c.close();
|
||||
|
||||
const zeroLengthView = new Uint8Array(view.buffer, 0, 0);
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(zeroLengthView),
|
||||
'respondWithNewView() must throw if passed a (zero-length) view whose buffer has been detached');
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(zeroLengthView));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -149,3 +222,37 @@ async_test(t => {
|
||||
reader.read(new Uint8Array([4, 5, 6]));
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer has been detached ' +
|
||||
'(in the closed state)');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
const view = new Uint8Array();
|
||||
|
||||
c.close();
|
||||
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
reader.read(new Uint8Array([4, 5, 6]));
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view\'s buffer is zero-length ' +
|
||||
'(in the closed state)');
|
||||
|
||||
async_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
pull: t.step_func_done(c => {
|
||||
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
|
||||
|
||||
c.close();
|
||||
|
||||
assert_throws_js(TypeError, () => c.byobRequest.respondWithNewView(view));
|
||||
}),
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
reader.read(new Uint8Array([4, 5, 6]));
|
||||
}, 'ReadableStream with byte source: respondWithNewView() throws if the supplied view is zero-length on a ' +
|
||||
'non-zero-length buffer (in the closed state)');
|
@ -1,189 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/test-utils.js
|
||||
'use strict';
|
||||
|
||||
let ReadableStreamBYOBReader;
|
||||
let ReadableByteStreamController;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamBYOBReader = realRSBYOBReader().constructor;
|
||||
|
||||
assert_equals(ReadableStreamBYOBReader.name, 'ReadableStreamBYOBReader', 'ReadableStreamBYOBReader should be set');
|
||||
|
||||
}, 'Can get the ReadableStreamBYOBReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableByteStreamController = realRBSController().constructor;
|
||||
|
||||
assert_equals(ReadableByteStreamController.name, 'ReadableByteStreamController',
|
||||
'ReadableByteStreamController should be set');
|
||||
|
||||
}, 'Can get the ReadableByteStreamController constructor indirectly');
|
||||
|
||||
function fakeRS() {
|
||||
return Object.setPrototypeOf({
|
||||
cancel() { return Promise.resolve(); },
|
||||
getReader() { return realRSBYOBReader(); },
|
||||
pipeThrough(obj) { return obj.readable; },
|
||||
pipeTo() { return Promise.resolve(); },
|
||||
tee() { return [realRS(), realRS()]; }
|
||||
}, ReadableStream.prototype);
|
||||
}
|
||||
|
||||
function realRS() {
|
||||
return new ReadableStream();
|
||||
}
|
||||
|
||||
function fakeRSBYOBReader() {
|
||||
return Object.setPrototypeOf({
|
||||
get closed() { return Promise.resolve(); },
|
||||
cancel() { return Promise.resolve(); },
|
||||
read() { return Promise.resolve({ value: undefined, done: true }); },
|
||||
releaseLock() { return; }
|
||||
}, ReadableStreamBYOBReader.prototype);
|
||||
}
|
||||
|
||||
function realRSBYOBReader() {
|
||||
return new ReadableStream({ type: 'bytes' }).getReader({ mode: 'byob' });
|
||||
}
|
||||
|
||||
function fakeRBSController() {
|
||||
return Object.setPrototypeOf({
|
||||
close() { },
|
||||
enqueue() { },
|
||||
error() { }
|
||||
}, ReadableByteStreamController.prototype);
|
||||
}
|
||||
|
||||
function realRBSController() {
|
||||
let controller;
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBReader(fakeRS()), 'constructor should throw');
|
||||
|
||||
}, 'ReadableStreamBYOBReader enforces a brand check on its argument');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return getterRejectsForAll(t, ReadableStreamBYOBReader.prototype, 'closed',
|
||||
[fakeRSBYOBReader(), realRS(), realRBSController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamBYOBReader.prototype.closed enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamBYOBReader.prototype, 'cancel',
|
||||
[fakeRSBYOBReader(), realRS(), realRBSController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamBYOBReader.prototype.cancel enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamBYOBReader.prototype, 'read',
|
||||
[fakeRSBYOBReader(), realRS(), realRBSController(), undefined, null], [new Uint8Array(1)]);
|
||||
|
||||
}, 'ReadableStreamBYOBReader.prototype.read enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStreamBYOBReader.prototype, 'releaseLock',
|
||||
[fakeRSBYOBReader(), realRS(), realRBSController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamBYOBReader.prototype.releaseLock enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableByteStreamController(fakeRS()),
|
||||
'Constructing a ReadableByteStreamController should throw');
|
||||
|
||||
}, 'ReadableByteStreamController enforces a brand check on its arguments');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableByteStreamController(realRS()),
|
||||
'Constructing a ReadableByteStreamController should throw');
|
||||
|
||||
}, 'ReadableByteStreamController can\'t be given a fully-constructed ReadableStream');
|
||||
|
||||
test(() => {
|
||||
|
||||
getterThrowsForAll(ReadableByteStreamController.prototype, 'byobRequest',
|
||||
[fakeRBSController(), realRS(), realRSBYOBReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableByteStreamController.prototype.byobRequest enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableByteStreamController.prototype, 'close',
|
||||
[fakeRBSController(), realRS(), realRSBYOBReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableByteStreamController.prototype.close enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableByteStreamController.prototype, 'enqueue',
|
||||
[fakeRBSController(), realRS(), realRSBYOBReader(), undefined, null], [new Uint8Array(1)]);
|
||||
|
||||
}, 'ReadableByteStreamController.prototype.enqueue enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableByteStreamController.prototype, 'error',
|
||||
[fakeRBSController(), realRS(), realRSBYOBReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableByteStreamController.prototype.error enforces a brand check');
|
||||
|
||||
// ReadableStreamBYOBRequest can only be accessed asynchronously, so cram everything into one test.
|
||||
promise_test(t => {
|
||||
|
||||
let ReadableStreamBYOBRequest;
|
||||
const rs = new ReadableStream({
|
||||
pull(controller) {
|
||||
return t.step(() => {
|
||||
const byobRequest = controller.byobRequest;
|
||||
ReadableStreamBYOBRequest = byobRequest.constructor;
|
||||
brandChecks();
|
||||
byobRequest.respond(1);
|
||||
});
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
const reader = rs.getReader({ mode: 'byob' });
|
||||
return reader.read(new Uint8Array(1));
|
||||
|
||||
function fakeRSBYOBRequest() {
|
||||
return Object.setPrototypeOf({
|
||||
get view() {},
|
||||
respond() {},
|
||||
respondWithNewView() {}
|
||||
}, ReadableStreamBYOBRequest.prototype);
|
||||
}
|
||||
|
||||
function brandChecks() {
|
||||
for (const badController of [fakeRBSController(), realRS(), realRSBYOBReader(), undefined, null]) {
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBRequest(badController, new Uint8Array(1)),
|
||||
'ReadableStreamBYOBRequest constructor must throw for an invalid controller argument');
|
||||
}
|
||||
getterThrowsForAll(ReadableStreamBYOBRequest.prototype, 'view',
|
||||
[fakeRSBYOBRequest(), realRS(), realRSBYOBReader(), realRBSController(), undefined, null]);
|
||||
methodThrowsForAll(ReadableStreamBYOBRequest.prototype, 'respond',
|
||||
[fakeRSBYOBRequest(), realRS(), realRSBYOBReader(), realRBSController(), undefined, null], [1]);
|
||||
methodThrowsForAll(ReadableStreamBYOBRequest.prototype, 'respondWithNewView',
|
||||
[fakeRSBYOBRequest(), realRS(), realRSBYOBReader(), realRBSController(), undefined, null],
|
||||
[new Uint8Array(1)]);
|
||||
}
|
||||
|
||||
}, 'ReadableStreamBYOBRequest enforces brand checks');
|
@ -17,8 +17,6 @@ function getRealByteStreamController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
const ReadableByteStreamController = getRealByteStreamController().constructor;
|
||||
|
||||
// Create an object pretending to have prototype |prototype|, of type |type|. |type| is one of "undefined", "null",
|
||||
// "fake", or "real". "real" will call the realObjectCreator function to get a real instance of the object.
|
||||
function createDummyObject(prototype, type, realObjectCreator) {
|
||||
@ -41,39 +39,15 @@ function createDummyObject(prototype, type, realObjectCreator) {
|
||||
|
||||
const dummyTypes = ['undefined', 'null', 'fake', 'real'];
|
||||
|
||||
function runTests(ReadableStreamBYOBRequest) {
|
||||
for (const controllerType of dummyTypes) {
|
||||
const controller = createDummyObject(ReadableByteStreamController.prototype, controllerType,
|
||||
getRealByteStreamController);
|
||||
for (const viewType of dummyTypes) {
|
||||
const view = createDummyObject(Uint8Array.prototype, viewType, () => new Uint8Array(16));
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBRequest(controller, view),
|
||||
'constructor should throw');
|
||||
}, `ReadableStreamBYOBRequest constructor should throw when passed a ${controllerType} ` +
|
||||
`ReadableByteStreamController and a ${viewType} view`);
|
||||
}
|
||||
for (const controllerType of dummyTypes) {
|
||||
const controller = createDummyObject(ReadableByteStreamController.prototype, controllerType,
|
||||
getRealByteStreamController);
|
||||
for (const viewType of dummyTypes) {
|
||||
const view = createDummyObject(Uint8Array.prototype, viewType, () => new Uint8Array(16));
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBRequest(controller, view),
|
||||
'constructor should throw');
|
||||
}, `ReadableStreamBYOBRequest constructor should throw when passed a ${controllerType} ` +
|
||||
`ReadableByteStreamController and a ${viewType} view`);
|
||||
}
|
||||
}
|
||||
|
||||
function getConstructorAndRunTests() {
|
||||
let ReadableStreamBYOBRequest;
|
||||
const rs = new ReadableStream({
|
||||
pull(controller) {
|
||||
const byobRequest = controller.byobRequest;
|
||||
ReadableStreamBYOBRequest = byobRequest.constructor;
|
||||
byobRequest.respond(4);
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
rs.getReader({ mode: 'byob' }).read(new Uint8Array(8)).then(() => {
|
||||
runTests(ReadableStreamBYOBRequest);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
// We can only get at the ReadableStreamBYOBRequest constructor asynchronously, so we need to make the test harness wait
|
||||
// for us to explicitly tell it all our tests have run.
|
||||
setup({ explicit_done: true });
|
||||
|
||||
getConstructorAndRunTests();
|
||||
|
@ -1,48 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/constructor-ordering.js
|
||||
'use strict';
|
||||
|
||||
const operations = [
|
||||
op('get', 'size'),
|
||||
op('get', 'highWaterMark'),
|
||||
op('get', 'type'),
|
||||
op('validate', 'type'),
|
||||
op('validate', 'size'),
|
||||
op('tonumber', 'highWaterMark'),
|
||||
op('validate', 'highWaterMark'),
|
||||
op('get', 'pull'),
|
||||
op('validate', 'pull'),
|
||||
op('get', 'cancel'),
|
||||
op('validate', 'cancel'),
|
||||
op('get', 'autoAllocateChunkSize'),
|
||||
op('tonumber', 'autoAllocateChunkSize'),
|
||||
op('validate', 'autoAllocateChunkSize'),
|
||||
op('get', 'start'),
|
||||
op('validate', 'start')
|
||||
];
|
||||
|
||||
for (const failureOp of operations) {
|
||||
test(() => {
|
||||
const record = new OpRecorder(failureOp);
|
||||
const underlyingSource = createRecordingObjectWithProperties(record, ['start', 'pull', 'cancel']);
|
||||
|
||||
// The valid value for "type" is "bytes", so set it separately.
|
||||
defineCheckedProperty(record, underlyingSource, 'type', () => record.check('type') ? 'invalid' : 'bytes');
|
||||
|
||||
// autoAllocateChunkSize is a special case because it has a tonumber step.
|
||||
defineCheckedProperty(record, underlyingSource, 'autoAllocateChunkSize',
|
||||
() => createRecordingNumberObject(record, 'autoAllocateChunkSize'));
|
||||
|
||||
const strategy = createRecordingStrategy(record);
|
||||
|
||||
try {
|
||||
new ReadableStream(underlyingSource, strategy);
|
||||
assert_unreached('constructor should throw');
|
||||
} catch (e) {
|
||||
assert_equals(typeof e, 'object', 'e should be an object');
|
||||
}
|
||||
|
||||
assert_equals(record.actual(), expectedAsString(operations, failureOp),
|
||||
'operations should be performed in the right order');
|
||||
}, `ReadableStream constructor should stop after ${failureOp} fails`);
|
||||
}
|
@ -307,8 +307,8 @@ promise_test(() => {
|
||||
const byobRequest = controller.byobRequest;
|
||||
const view = byobRequest.view;
|
||||
byobRequests[pullCount] = {
|
||||
defined: byobRequest !== undefined,
|
||||
viewDefined: view !== undefined,
|
||||
nonNull: byobRequest !== null,
|
||||
viewNonNull: view !== null,
|
||||
viewInfo: extractViewInfo(view)
|
||||
};
|
||||
if (pullCount === 0) {
|
||||
@ -337,8 +337,8 @@ promise_test(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull() must have been invoked once');
|
||||
const byobRequest = byobRequests[0];
|
||||
assert_true(byobRequest.defined, 'first byobRequest must not be undefined');
|
||||
assert_true(byobRequest.viewDefined, 'first byobRequest.view must not be undefined');
|
||||
assert_true(byobRequest.nonNull, 'first byobRequest must not be null');
|
||||
assert_true(byobRequest.viewNonNull, 'first byobRequest.view must not be null');
|
||||
const viewInfo = byobRequest.viewInfo;
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'first view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 16, 'first view.buffer.byteLength should be 16');
|
||||
@ -356,8 +356,8 @@ promise_test(() => {
|
||||
assert_equals(value.byteLength, 1, 'first value.byteLength should be 1');
|
||||
assert_equals(value[0], 0x01, 'first value[0] should be 0x01');
|
||||
const byobRequest = byobRequests[1];
|
||||
assert_true(byobRequest.defined, 'second byobRequest must not be undefined');
|
||||
assert_true(byobRequest.viewDefined, 'second byobRequest.view must not be undefined');
|
||||
assert_true(byobRequest.nonNull, 'second byobRequest must not be null');
|
||||
assert_true(byobRequest.viewNonNull, 'second byobRequest.view must not be null');
|
||||
const viewInfo = byobRequest.viewInfo;
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'second view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 16, 'second view.buffer.byteLength should be 16');
|
||||
@ -391,8 +391,8 @@ promise_test(() => {
|
||||
const byobRequest = controller.byobRequest;
|
||||
const view = byobRequest.view;
|
||||
byobRequests[pullCount] = {
|
||||
defined: byobRequest !== undefined,
|
||||
viewDefined: view !== undefined,
|
||||
nonNull: byobRequest !== null,
|
||||
viewNonNull: view !== null,
|
||||
viewInfo: extractViewInfo(view)
|
||||
};
|
||||
if (pullCount === 0) {
|
||||
@ -422,8 +422,8 @@ promise_test(() => {
|
||||
assert_equals(value.byteLength, 1, 'first value.byteLength should be 1');
|
||||
assert_equals(value[0], 0x01, 'first value[0] should be 0x01');
|
||||
const byobRequest = byobRequests[0];
|
||||
assert_true(byobRequest.defined, 'first byobRequest must not be undefined');
|
||||
assert_true(byobRequest.viewDefined, 'first byobRequest.view must not be undefined');
|
||||
assert_true(byobRequest.nonNull, 'first byobRequest must not be null');
|
||||
assert_true(byobRequest.viewNonNull, 'first byobRequest.view must not be null');
|
||||
const viewInfo = byobRequest.viewInfo;
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'first view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 16, 'first view.buffer.byteLength should be 16');
|
||||
@ -443,8 +443,8 @@ promise_test(() => {
|
||||
assert_equals(value[0], 0x02, 'second value[0] should be 0x02');
|
||||
assert_equals(value[1], 0x03, 'second value[1] should be 0x03');
|
||||
const byobRequest = byobRequests[1];
|
||||
assert_true(byobRequest.defined, 'second byobRequest must not be undefined');
|
||||
assert_true(byobRequest.viewDefined, 'second byobRequest.view must not be undefined');
|
||||
assert_true(byobRequest.nonNull, 'second byobRequest must not be null');
|
||||
assert_true(byobRequest.viewNonNull, 'second byobRequest.view must not be null');
|
||||
const viewInfo = byobRequest.viewInfo;
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'second view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 32, 'second view.buffer.byteLength should be 32');
|
||||
@ -695,7 +695,7 @@ promise_test(() => {
|
||||
return reader.read().then(result => {
|
||||
assert_equals(result.done, false, 'done');
|
||||
assert_equals(result.value.byteLength, 16, 'byteLength');
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_equals(byobRequest, null, 'byobRequest must be null');
|
||||
});
|
||||
}, 'ReadableStream with byte source: Respond to pull() by enqueue()');
|
||||
|
||||
@ -745,7 +745,7 @@ promise_test(() => {
|
||||
assert_equals(result[1].value.byteLength, 1, 'result[1].value.byteLength');
|
||||
assert_equals(result[2].done, false, 'result[2].done');
|
||||
assert_equals(result[2].value.byteLength, 1, 'result[2].value.byteLength');
|
||||
assert_equals(byobRequest, undefined, 'byobRequest should be undefined');
|
||||
assert_equals(byobRequest, null, 'byobRequest should be null');
|
||||
assert_equals(desiredSizes[0], 0, 'desiredSize on pull should be 0');
|
||||
assert_equals(desiredSizes[1], 0, 'desiredSize after 1st enqueue() should be 0');
|
||||
assert_equals(desiredSizes[2], 0, 'desiredSize after 2nd enqueue() should be 0');
|
||||
@ -794,7 +794,7 @@ promise_test(() => {
|
||||
assert_equals(result[1].value.byteLength, 1, 'result[1].value.byteLength');
|
||||
assert_equals(result[2].done, false, 'result[2].done');
|
||||
assert_equals(result[2].value.byteLength, 1, 'result[2].value.byteLength');
|
||||
assert_equals(byobRequest, undefined, 'byobRequest should be undefined');
|
||||
assert_equals(byobRequest, null, 'byobRequest should be null');
|
||||
assert_equals(desiredSizes[0], 256, 'desiredSize on pull should be 256');
|
||||
assert_equals(desiredSizes[1], 256, 'desiredSize after 1st enqueue() should be 256');
|
||||
assert_equals(desiredSizes[2], 256, 'desiredSize after 2nd enqueue() should be 256');
|
||||
@ -813,13 +813,13 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
byobRequestDefined.push(controller.byobRequest !== undefined);
|
||||
byobRequestDefined.push(controller.byobRequest !== null);
|
||||
|
||||
const view = controller.byobRequest.view;
|
||||
view[0] = 0x01;
|
||||
controller.byobRequest.respond(1);
|
||||
|
||||
byobRequestDefined.push(controller.byobRequest !== undefined);
|
||||
byobRequestDefined.push(controller.byobRequest !== null);
|
||||
|
||||
++pullCount;
|
||||
},
|
||||
@ -833,8 +833,8 @@ promise_test(() => {
|
||||
assert_equals(result.value.byteLength, 1, 'result.value.byteLength');
|
||||
assert_equals(result.value[0], 0x01, 'result.value[0]');
|
||||
assert_equals(pullCount, 1, 'pull() should be called only once');
|
||||
assert_true(byobRequestDefined[0], 'byobRequest must not be undefined before respond()');
|
||||
assert_false(byobRequestDefined[1], 'byobRequest must be undefined after respond()');
|
||||
assert_true(byobRequestDefined[0], 'byobRequest must not be null before respond()');
|
||||
assert_false(byobRequestDefined[1], 'byobRequest must be null after respond()');
|
||||
});
|
||||
}, 'ReadableStream with byte source: read(view), then respond()');
|
||||
|
||||
@ -849,7 +849,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
byobRequestDefined.push(controller.byobRequest !== undefined);
|
||||
byobRequestDefined.push(controller.byobRequest !== null);
|
||||
|
||||
// Emulate ArrayBuffer transfer by just creating a new ArrayBuffer and pass it. By checking the result of
|
||||
// read(view), we test that the respond()'s buffer argument is working correctly.
|
||||
@ -860,7 +860,7 @@ promise_test(() => {
|
||||
transferredView[0] = 0x01;
|
||||
controller.byobRequest.respondWithNewView(transferredView);
|
||||
|
||||
byobRequestDefined.push(controller.byobRequest !== undefined);
|
||||
byobRequestDefined.push(controller.byobRequest !== null);
|
||||
|
||||
++pullCount;
|
||||
},
|
||||
@ -874,8 +874,8 @@ promise_test(() => {
|
||||
assert_equals(result.value.byteLength, 1, 'result.value.byteLength');
|
||||
assert_equals(result.value[0], 0x01, 'result.value[0]');
|
||||
assert_equals(pullCount, 1, 'pull() should be called only once');
|
||||
assert_true(byobRequestDefined[0], 'byobRequest must not be undefined before respond()');
|
||||
assert_false(byobRequestDefined[1], 'byobRequest must be undefined after respond()');
|
||||
assert_true(byobRequestDefined[0], 'byobRequest must not be null before respond()');
|
||||
assert_false(byobRequestDefined[1], 'byobRequest must be null after respond()');
|
||||
});
|
||||
}, 'ReadableStream with byte source: read(view), then respond() with a transferred ArrayBuffer');
|
||||
|
||||
@ -889,7 +889,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
byobRequestWasDefined = controller.byobRequest !== undefined;
|
||||
byobRequestWasDefined = controller.byobRequest !== null;
|
||||
|
||||
try {
|
||||
controller.byobRequest.respond(2);
|
||||
@ -905,7 +905,7 @@ promise_test(() => {
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
return reader.read(new Uint8Array(1)).then(() => {
|
||||
assert_true(byobRequestWasDefined, 'byobRequest should be defined');
|
||||
assert_true(byobRequestWasDefined, 'byobRequest should be non-null');
|
||||
assert_not_equals(incorrectRespondException, undefined, 'respond() must throw');
|
||||
assert_equals(incorrectRespondException.name, 'RangeError', 'respond() must throw a RangeError');
|
||||
});
|
||||
@ -954,7 +954,7 @@ promise_test(() => {
|
||||
return reader.read(new Uint8Array(1));
|
||||
}).then(result => {
|
||||
assert_equals(pullCount, 1);
|
||||
assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined');
|
||||
assert_not_equals(byobRequest, null, 'byobRequest must not be null');
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 4, 'view.buffer.byteLength should be 4');
|
||||
assert_equals(viewInfo.byteOffset, 0, 'view.byteOffset should be 0');
|
||||
@ -1129,7 +1129,7 @@ promise_test(() => {
|
||||
});
|
||||
|
||||
assert_equals(pullCount, 1, '1 pull() should have been made in response to partial fill by enqueue()');
|
||||
assert_not_equals(byobRequest, undefined, 'byobRequest should not be undefined');
|
||||
assert_not_equals(byobRequest, null, 'byobRequest should not be null');
|
||||
assert_equals(viewInfos[0].byteLength, 2, 'byteLength before enqueue() shouild be 2');
|
||||
assert_equals(viewInfos[1].byteLength, 1, 'byteLength after enqueue() should be 1');
|
||||
|
||||
@ -1146,7 +1146,7 @@ promise_test(() => {
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
let byobRequest;
|
||||
let pullCalled = false;
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
@ -1157,7 +1157,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
byobRequest = controller.byobRequest;
|
||||
pullCalled = true;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -1169,7 +1169,7 @@ promise_test(() => {
|
||||
return reader.read(new Uint8Array(buffer, 8, 8)).then(result => {
|
||||
assert_equals(result.done, false);
|
||||
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_false(pullCalled, 'pull() must not have been called');
|
||||
|
||||
const view = result.value;
|
||||
assert_equals(view.constructor, Uint8Array);
|
||||
@ -1183,7 +1183,7 @@ promise_test(() => {
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
let byobRequest;
|
||||
let pullCalled = false;
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
@ -1200,7 +1200,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
byobRequest = controller.byobRequest;
|
||||
pullCalled = true;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -1210,7 +1210,7 @@ promise_test(() => {
|
||||
return reader.read(new Uint8Array(24)).then(result => {
|
||||
assert_equals(result.done, false, 'done');
|
||||
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_false(pullCalled, 'pull() must not have been called');
|
||||
|
||||
const view = result.value;
|
||||
assert_equals(view.byteOffset, 0, 'byteOffset');
|
||||
@ -1221,7 +1221,7 @@ promise_test(() => {
|
||||
}, 'ReadableStream with byte source: Multiple enqueue(), getReader(), then read(view)');
|
||||
|
||||
promise_test(() => {
|
||||
let byobRequest;
|
||||
let pullCalled = false;
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
@ -1229,8 +1229,8 @@ promise_test(() => {
|
||||
view[15] = 0x01;
|
||||
c.enqueue(view);
|
||||
},
|
||||
pull(controller) {
|
||||
byobRequest = controller.byobRequest;
|
||||
pull() {
|
||||
pullCalled = true;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -1240,7 +1240,7 @@ promise_test(() => {
|
||||
return reader.read(new Uint8Array(24)).then(result => {
|
||||
assert_equals(result.done, false);
|
||||
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_false(pullCalled, 'pull() must not have been called');
|
||||
|
||||
const view = result.value;
|
||||
assert_equals(view.byteOffset, 0);
|
||||
@ -1250,7 +1250,7 @@ promise_test(() => {
|
||||
}, 'ReadableStream with byte source: enqueue(), getReader(), then read(view) with a bigger view');
|
||||
|
||||
promise_test(() => {
|
||||
let byobRequest;
|
||||
let pullCalled = false;
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
@ -1259,8 +1259,8 @@ promise_test(() => {
|
||||
view[15] = 0x02;
|
||||
c.enqueue(view);
|
||||
},
|
||||
pull(controller) {
|
||||
byobRequest = controller.byobRequest;
|
||||
pull() {
|
||||
pullCalled = true;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -1279,14 +1279,14 @@ promise_test(() => {
|
||||
}).then(result => {
|
||||
assert_equals(result.done, false, 'done');
|
||||
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_false(pullCalled, 'pull() must not have been called');
|
||||
|
||||
const view = result.value;
|
||||
assert_equals(view.byteOffset, 0);
|
||||
assert_equals(view.byteLength, 8);
|
||||
assert_equals(view[7], 0x02);
|
||||
});
|
||||
}, 'ReadableStream with byte source: enqueue(), getReader(), then read(view) with a smaller views');
|
||||
}, 'ReadableStream with byte source: enqueue(), getReader(), then read(view) with smaller views');
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
@ -1301,7 +1301,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
if (controller.byobRequest === undefined) {
|
||||
if (controller.byobRequest === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1393,7 +1393,7 @@ promise_test(() => {
|
||||
assert_equals(view.byteLength, 2, 'byteLength');
|
||||
assert_equals(view[0], 0x0302, 'Contents are set');
|
||||
|
||||
assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined');
|
||||
assert_not_equals(byobRequest, null, 'byobRequest must not be null');
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 2, 'view.buffer.byteLength should be 2');
|
||||
assert_equals(viewInfo.byteOffset, 1, 'view.byteOffset should be 1');
|
||||
@ -1530,7 +1530,7 @@ promise_test(() => {
|
||||
assert_equals(view.byteOffset, 0);
|
||||
assert_equals(view.byteLength, 0);
|
||||
|
||||
assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined');
|
||||
assert_not_equals(byobRequest, null, 'byobRequest must not be null');
|
||||
assert_equals(viewInfo.constructor, Uint8Array, 'view.constructor should be Uint8Array');
|
||||
assert_equals(viewInfo.bufferByteLength, 16, 'view.buffer.byteLength should be 16');
|
||||
assert_equals(viewInfo.byteOffset, 0, 'view.byteOffset should be 0');
|
||||
@ -1549,7 +1549,7 @@ promise_test(() => {
|
||||
controller = c;
|
||||
},
|
||||
pull() {
|
||||
if (controller.byobRequest === undefined) {
|
||||
if (controller.byobRequest === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1638,7 +1638,7 @@ promise_test(() => {
|
||||
assert_equals(view.byteOffset, 0);
|
||||
assert_equals(view.byteLength, 2);
|
||||
|
||||
assert_equals(byobRequest, undefined, 'byobRequest must be undefined');
|
||||
assert_equals(byobRequest, null, 'byobRequest must be null');
|
||||
});
|
||||
|
||||
assert_equals(pullCount, 0, 'No pull should have been made since the startPromise has not yet been handled');
|
||||
@ -1760,10 +1760,10 @@ promise_test(t => {
|
||||
}, 'ReadableStream with byte source: Multiple read(view) and multiple enqueue()');
|
||||
|
||||
promise_test(t => {
|
||||
let byobRequest;
|
||||
let pullCalled = false;
|
||||
const stream = new ReadableStream({
|
||||
pull(controller) {
|
||||
byobRequest = controller.byobRequest;
|
||||
pullCalled = true;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
@ -1771,24 +1771,9 @@ promise_test(t => {
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
return promise_rejects_js(t, TypeError, reader.read(), 'read() must fail')
|
||||
.then(() => assert_equals(byobRequest, undefined, 'byobRequest must be undefined'));
|
||||
.then(() => assert_false(pullCalled, 'pull() must not have been called'));
|
||||
}, 'ReadableStream with byte source: read(view) with passing undefined as view must fail');
|
||||
|
||||
promise_test(t => {
|
||||
let byobRequest;
|
||||
const stream = new ReadableStream({
|
||||
pull(controller) {
|
||||
byobRequest = controller.byobRequest;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
|
||||
const reader = stream.getReader({ mode: 'byob' });
|
||||
|
||||
return promise_rejects_js(t, TypeError, reader.read(new Uint8Array(0)), 'read(view) must fail')
|
||||
.then(() => assert_equals(byobRequest, undefined, 'byobRequest must be undefined'));
|
||||
}, 'ReadableStream with byte source: read(view) with zero-length view must fail');
|
||||
|
||||
promise_test(t => {
|
||||
const stream = new ReadableStream({
|
||||
type: 'bytes'
|
||||
@ -1898,7 +1883,7 @@ promise_test(t => {
|
||||
|
||||
const promise = promise_rejects_exactly(t, testError, reader.read(), 'read() must fail');
|
||||
return promise_rejects_exactly(t, testError, promise.then(() => reader.closed))
|
||||
.then(() => assert_equals(byobRequest, undefined, 'byobRequest must be undefined'));
|
||||
.then(() => assert_equals(byobRequest, null, 'byobRequest must be null'));
|
||||
}, 'ReadableStream with byte source: Throwing in pull function must error the stream');
|
||||
|
||||
promise_test(t => {
|
||||
@ -1917,7 +1902,7 @@ promise_test(t => {
|
||||
|
||||
return promise_rejects_exactly(t, error1, reader.read(), 'read() must fail')
|
||||
.then(() => promise_rejects_exactly(t, error1, reader.closed, 'closed must fail'))
|
||||
.then(() => assert_equals(byobRequest, undefined, 'byobRequest must be undefined'));
|
||||
.then(() => assert_equals(byobRequest, null, 'byobRequest must be null'));
|
||||
}, 'ReadableStream with byte source: Throwing in pull in response to read() must be ignored if the stream is ' +
|
||||
'errored in it');
|
||||
|
||||
@ -1938,7 +1923,7 @@ promise_test(t => {
|
||||
|
||||
return promise_rejects_exactly(t, testError, reader.read(new Uint8Array(1)), 'read(view) must fail')
|
||||
.then(() => promise_rejects_exactly(t, testError, reader.closed, 'reader.closed must reject'))
|
||||
.then(() => assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined'));
|
||||
.then(() => assert_not_equals(byobRequest, null, 'byobRequest must not be null'));
|
||||
}, 'ReadableStream with byte source: Throwing in pull in response to read(view) function must error the stream');
|
||||
|
||||
promise_test(t => {
|
||||
@ -1957,7 +1942,7 @@ promise_test(t => {
|
||||
|
||||
return promise_rejects_exactly(t, error1, reader.read(new Uint8Array(1)), 'read(view) must fail')
|
||||
.then(() => promise_rejects_exactly(t, error1, reader.closed, 'closed must fail'))
|
||||
.then(() => assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined'));
|
||||
.then(() => assert_not_equals(byobRequest, null, 'byobRequest must not be null'));
|
||||
}, 'ReadableStream with byte source: Throwing in pull in response to read(view) must be ignored if the stream is ' +
|
||||
'errored in it');
|
||||
|
||||
@ -2101,9 +2086,6 @@ test(() => {
|
||||
}
|
||||
}), 'constructor should throw for size function');
|
||||
|
||||
assert_throws_js(RangeError, () => new ReadableStream({ type: 'bytes' }, { size: null }),
|
||||
'constructor should throw for size defined');
|
||||
|
||||
assert_throws_js(RangeError,
|
||||
() => new ReadableStream({ type: 'bytes' }, new CountQueuingStrategy({ highWaterMark: 1 })),
|
||||
'constructor should throw when strategy is CountQueuingStrategy');
|
||||
|
@ -1,142 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/rs-utils.js
|
||||
'use strict';
|
||||
|
||||
let ReadableStreamBYOBReader;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamBYOBReader = (new ReadableStream({ type: 'bytes' }))
|
||||
.getReader({ mode: 'byob' }).constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamBYOBReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBReader('potato'));
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBReader({}));
|
||||
assert_throws_js(TypeError, () => new ReadableStreamBYOBReader());
|
||||
|
||||
}, 'ReadableStreamBYOBReader constructor should get a ReadableStream object as argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['cancel', 'constructor', 'read', 'releaseLock'];
|
||||
const properties = methods.concat(['closed']).sort();
|
||||
|
||||
const rsReader = new ReadableStreamBYOBReader(new ReadableStream({ type: 'bytes' }));
|
||||
const proto = Object.getPrototypeOf(rsReader);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
|
||||
assert_equals(propDesc.configurable, true, 'method should be configurable');
|
||||
assert_equals(propDesc.writable, true, 'method should be writable');
|
||||
assert_equals(typeof rsReader[m], 'function', 'should have be a method');
|
||||
const expectedName = m === 'constructor' ? 'ReadableStreamBYOBReader' : m;
|
||||
assert_equals(rsReader[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
const closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed');
|
||||
assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerable');
|
||||
assert_equals(closedPropDesc.configurable, true, 'closed should be configurable');
|
||||
assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter');
|
||||
assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter');
|
||||
|
||||
assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter');
|
||||
assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed property');
|
||||
assert_equals(typeof rsReader.closed.then, 'function', 'closed property is thenable');
|
||||
assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter');
|
||||
assert_equals(rsReader.read.length, 1, 'read has 1 parameter');
|
||||
assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters');
|
||||
|
||||
}, 'ReadableStreamBYOBReader instances should have the correct list of properties');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const rs = new ReadableStream({
|
||||
pull(controller) {
|
||||
return t.step(() => {
|
||||
const byobRequest = controller.byobRequest;
|
||||
|
||||
const methods = ['constructor', 'respond', 'respondWithNewView'];
|
||||
const properties = methods.concat(['view']).sort();
|
||||
|
||||
const proto = Object.getPrototypeOf(byobRequest);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
|
||||
assert_equals(propDesc.configurable, true, 'method should be configurable');
|
||||
assert_equals(propDesc.writable, true, 'method should be writable');
|
||||
assert_equals(typeof byobRequest[m], 'function', 'should have a method');
|
||||
const expectedName = m === 'constructor' ? 'ReadableStreamBYOBRequest' : m;
|
||||
assert_equals(byobRequest[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
const viewPropDesc = Object.getOwnPropertyDescriptor(proto, 'view');
|
||||
assert_equals(viewPropDesc.enumerable, false, 'view should be non-enumerable');
|
||||
assert_equals(viewPropDesc.configurable, true, 'view should be configurable');
|
||||
assert_not_equals(viewPropDesc.get, undefined, 'view should have a getter');
|
||||
assert_equals(viewPropDesc.set, undefined, 'view should not have a setter');
|
||||
assert_not_equals(byobRequest.view, undefined, 'has a non-undefined view property');
|
||||
assert_equals(byobRequest.constructor.length, 0, 'constructor has 0 parameters');
|
||||
assert_equals(byobRequest.respond.length, 1, 'respond has 1 parameter');
|
||||
assert_equals(byobRequest.respondWithNewView.length, 1, 'releaseLock has 1 parameter');
|
||||
|
||||
byobRequest.respond(1);
|
||||
|
||||
});
|
||||
},
|
||||
type: 'bytes' });
|
||||
const reader = rs.getReader({ mode: 'byob' });
|
||||
return reader.read(new Uint8Array(1));
|
||||
|
||||
}, 'ReadableStreamBYOBRequest instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['close', 'constructor', 'enqueue', 'error'];
|
||||
const accessors = ['byobRequest', 'desiredSize'];
|
||||
const properties = methods.concat(accessors).sort();
|
||||
|
||||
let controller;
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
type: 'bytes'
|
||||
});
|
||||
const proto = Object.getPrototypeOf(controller);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
|
||||
assert_equals(propDesc.configurable, true, 'method should be configurable');
|
||||
assert_equals(propDesc.writable, true, 'method should be writable');
|
||||
assert_equals(typeof controller[m], 'function', 'should have be a method');
|
||||
const expectedName = m === 'constructor' ? 'ReadableByteStreamController' : m;
|
||||
assert_equals(controller[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
for (const a of accessors) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, a);
|
||||
assert_equals(propDesc.enumerable, false, `${a} should be non-enumerable`);
|
||||
assert_equals(propDesc.configurable, true, `${a} should be configurable`);
|
||||
assert_not_equals(propDesc.get, undefined, `${a} should have a getter`);
|
||||
assert_equals(propDesc.set, undefined, `${a} should not have a setter`);
|
||||
}
|
||||
|
||||
assert_equals(controller.close.length, 0, 'cancel has no parameters');
|
||||
assert_equals(controller.constructor.length, 0, 'constructor has no parameters');
|
||||
assert_equals(controller.enqueue.length, 1, 'enqueue has 1 parameter');
|
||||
assert_equals(controller.error.length, 1, 'releaseLock has 1 parameter');
|
||||
|
||||
}, 'ReadableByteStreamController instances should have the correct list of properties');
|
@ -4,13 +4,24 @@
|
||||
// META: script=../resources/recording-streams.js
|
||||
'use strict';
|
||||
|
||||
const error1 = new Error('error1');
|
||||
|
||||
function assert_iter_result(iterResult, value, done, message) {
|
||||
const prefix = message === undefined ? '' : `${message} `;
|
||||
assert_equals(typeof iterResult, 'object', `${prefix}type is object`);
|
||||
assert_equals(Object.getPrototypeOf(iterResult), Object.prototype, `${prefix}[[Prototype]]`);
|
||||
assert_array_equals(Object.getOwnPropertyNames(iterResult).sort(), ['done', 'value'], `${prefix}property names`);
|
||||
assert_equals(iterResult.value, value, `${prefix}value`);
|
||||
assert_equals(iterResult.done, done, `${prefix}done`);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
assert_equals(ReadableStream.prototype[Symbol.asyncIterator], ReadableStream.prototype.getIterator);
|
||||
}, '@@asyncIterator() method is === to getIterator() method');
|
||||
assert_equals(ReadableStream.prototype[Symbol.asyncIterator], ReadableStream.prototype.values);
|
||||
}, '@@asyncIterator() method is === to values() method');
|
||||
|
||||
test(() => {
|
||||
const s = new ReadableStream();
|
||||
const it = s.getIterator();
|
||||
const it = s.values();
|
||||
const proto = Object.getPrototypeOf(it);
|
||||
|
||||
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
|
||||
@ -21,7 +32,7 @@ test(() => {
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_false(propDesc.enumerable, 'method should be non-enumerable');
|
||||
assert_true(propDesc.enumerable, 'method should be enumerable');
|
||||
assert_true(propDesc.configurable, 'method should be configurable');
|
||||
assert_true(propDesc.writable, 'method should be writable');
|
||||
assert_equals(typeof it[m], 'function', 'method should be a function');
|
||||
@ -40,7 +51,7 @@ promise_test(async () => {
|
||||
c.enqueue(2);
|
||||
c.enqueue(3);
|
||||
c.close();
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const chunks = [];
|
||||
@ -59,7 +70,7 @@ promise_test(async () => {
|
||||
c.close();
|
||||
}
|
||||
i += 1;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const chunks = [];
|
||||
@ -69,6 +80,42 @@ promise_test(async () => {
|
||||
assert_array_equals(chunks, [1, 2, 3]);
|
||||
}, 'Async-iterating a pull source');
|
||||
|
||||
promise_test(async () => {
|
||||
const s = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(undefined);
|
||||
c.enqueue(undefined);
|
||||
c.enqueue(undefined);
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
|
||||
const chunks = [];
|
||||
for await (const chunk of s) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
assert_array_equals(chunks, [undefined, undefined, undefined]);
|
||||
}, 'Async-iterating a push source with undefined values');
|
||||
|
||||
promise_test(async () => {
|
||||
let i = 1;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
c.enqueue(undefined);
|
||||
if (i >= 3) {
|
||||
c.close();
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
});
|
||||
|
||||
const chunks = [];
|
||||
for await (const chunk of s) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
assert_array_equals(chunks, [undefined, undefined, undefined]);
|
||||
}, 'Async-iterating a pull source with undefined values');
|
||||
|
||||
promise_test(async () => {
|
||||
let i = 1;
|
||||
const s = recordingReadableStream({
|
||||
@ -81,27 +128,23 @@ promise_test(async () => {
|
||||
},
|
||||
}, new CountQueuingStrategy({ highWaterMark: 0 }));
|
||||
|
||||
const it = s.getIterator();
|
||||
const it = s.values();
|
||||
assert_array_equals(s.events, []);
|
||||
|
||||
const read1 = await it.next();
|
||||
assert_equals(read1.done, false);
|
||||
assert_equals(read1.value, 1);
|
||||
assert_iter_result(read1, 1, false);
|
||||
assert_array_equals(s.events, ['pull']);
|
||||
|
||||
const read2 = await it.next();
|
||||
assert_equals(read2.done, false);
|
||||
assert_equals(read2.value, 2);
|
||||
assert_iter_result(read2, 2, false);
|
||||
assert_array_equals(s.events, ['pull', 'pull']);
|
||||
|
||||
const read3 = await it.next();
|
||||
assert_equals(read3.done, false);
|
||||
assert_equals(read3.value, 3);
|
||||
assert_iter_result(read3, 3, false);
|
||||
assert_array_equals(s.events, ['pull', 'pull', 'pull']);
|
||||
|
||||
const read4 = await it.next();
|
||||
assert_equals(read4.done, true);
|
||||
assert_equals(read4.value, undefined);
|
||||
assert_iter_result(read4, undefined, true);
|
||||
assert_array_equals(s.events, ['pull', 'pull', 'pull']);
|
||||
}, 'Async-iterating a pull source manually');
|
||||
|
||||
@ -160,8 +203,7 @@ promise_test(async () => {
|
||||
|
||||
const reader = s.getReader();
|
||||
const readResult = await reader.read();
|
||||
assert_equals(readResult.done, false);
|
||||
assert_equals(readResult.value, 1);
|
||||
assert_iter_result(readResult, 1, false);
|
||||
reader.releaseLock();
|
||||
|
||||
const chunks = [];
|
||||
@ -182,7 +224,7 @@ for (const type of ['throw', 'break', 'return']) {
|
||||
|
||||
// use a separate function for the loop body so return does not stop the test
|
||||
const loop = async () => {
|
||||
for await (const c of s.getIterator({ preventCancel })) {
|
||||
for await (const c of s.values({ preventCancel })) {
|
||||
if (type === 'throw') {
|
||||
throw new Error();
|
||||
} else if (type === 'break') {
|
||||
@ -214,7 +256,7 @@ for (const preventCancel of [false, true]) {
|
||||
}
|
||||
});
|
||||
|
||||
const it = s.getIterator({ preventCancel });
|
||||
const it = s.values({ preventCancel });
|
||||
await it.return();
|
||||
|
||||
if (preventCancel) {
|
||||
@ -226,33 +268,249 @@ for (const preventCancel of [false, true]) {
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const s = new ReadableStream();
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
await it.return();
|
||||
return promise_rejects_js(t, TypeError, it.return(), 'return should reject');
|
||||
}, 'Calling return() twice rejects');
|
||||
|
||||
const iterResult1 = await it.next();
|
||||
assert_iter_result(iterResult1, 0, false, '1st next()');
|
||||
|
||||
await promise_rejects_exactly(t, error1, it.next(), '2nd next()');
|
||||
}, 'next() rejects if the stream errors');
|
||||
|
||||
promise_test(async () => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue(0);
|
||||
c.close();
|
||||
},
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
const next = await it.next();
|
||||
assert_equals(Object.getPrototypeOf(next), Object.prototype);
|
||||
assert_array_equals(Object.getOwnPropertyNames(next).sort(), ['done', 'value']);
|
||||
}, 'next()\'s fulfillment value has the right shape');
|
||||
|
||||
const iterResult = await it.return('return value');
|
||||
assert_iter_result(iterResult, 'return value', true);
|
||||
}, 'return() does not rejects if the stream has not errored yet');
|
||||
|
||||
promise_test(async t => {
|
||||
const s = recordingReadableStream();
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
it.next();
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
// Do not error in start() because doing so would prevent acquiring a reader/async iterator.
|
||||
c.error(error1);
|
||||
}
|
||||
});
|
||||
|
||||
await promise_rejects_js(t, TypeError, it.return(), 'return() should reject');
|
||||
assert_array_equals(s.events, ['pull']);
|
||||
}, 'calling return() while there are pending reads rejects');
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
await flushAsyncEvents();
|
||||
await promise_rejects_exactly(t, error1, it.return('return value'));
|
||||
}, 'return() rejects if the stream has errored');
|
||||
|
||||
promise_test(async t => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResult1 = await it.next();
|
||||
assert_iter_result(iterResult1, 0, false, '1st next()');
|
||||
|
||||
await promise_rejects_exactly(t, error1, it.next(), '2nd next()');
|
||||
|
||||
const iterResult3 = await it.next();
|
||||
assert_iter_result(iterResult3, undefined, true, '3rd next()');
|
||||
}, 'next() that succeeds; next() that reports an error; next()');
|
||||
|
||||
promise_test(async () => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResults = await Promise.allSettled([it.next(), it.next(), it.next()]);
|
||||
|
||||
assert_equals(iterResults[0].status, 'fulfilled', '1st next() promise status');
|
||||
assert_iter_result(iterResults[0].value, 0, false, '1st next()');
|
||||
|
||||
assert_equals(iterResults[1].status, 'rejected', '2nd next() promise status');
|
||||
assert_equals(iterResults[1].reason, error1, '2nd next() rejection reason');
|
||||
|
||||
assert_equals(iterResults[2].status, 'fulfilled', '3rd next() promise status');
|
||||
assert_iter_result(iterResults[2].value, undefined, true, '3rd next()');
|
||||
}, 'next() that succeeds; next() that reports an error(); next() [no awaiting]');
|
||||
|
||||
promise_test(async t => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResult1 = await it.next();
|
||||
assert_iter_result(iterResult1, 0, false, '1st next()');
|
||||
|
||||
await promise_rejects_exactly(t, error1, it.next(), '2nd next()');
|
||||
|
||||
const iterResult3 = await it.return('return value');
|
||||
assert_iter_result(iterResult3, 'return value', true, 'return()');
|
||||
}, 'next() that succeeds; next() that reports an error(); return()');
|
||||
|
||||
promise_test(async () => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResults = await Promise.allSettled([it.next(), it.next(), it.return('return value')]);
|
||||
|
||||
assert_equals(iterResults[0].status, 'fulfilled', '1st next() promise status');
|
||||
assert_iter_result(iterResults[0].value, 0, false, '1st next()');
|
||||
|
||||
assert_equals(iterResults[1].status, 'rejected', '2nd next() promise status');
|
||||
assert_equals(iterResults[1].reason, error1, '2nd next() rejection reason');
|
||||
|
||||
assert_equals(iterResults[2].status, 'fulfilled', 'return() promise status');
|
||||
assert_iter_result(iterResults[2].value, 'return value', true, 'return()');
|
||||
}, 'next() that succeeds; next() that reports an error(); return() [no awaiting]');
|
||||
|
||||
promise_test(async () => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
c.enqueue(timesPulled);
|
||||
++timesPulled;
|
||||
}
|
||||
});
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResult1 = await it.next();
|
||||
assert_iter_result(iterResult1, 0, false, 'next()');
|
||||
|
||||
const iterResult2 = await it.return('return value');
|
||||
assert_iter_result(iterResult2, 'return value', true, 'return()');
|
||||
|
||||
assert_equals(timesPulled, 2);
|
||||
}, 'next() that succeeds; return()');
|
||||
|
||||
promise_test(async () => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
c.enqueue(timesPulled);
|
||||
++timesPulled;
|
||||
}
|
||||
});
|
||||
const it = s[Symbol.asyncIterator]();
|
||||
|
||||
const iterResults = await Promise.allSettled([it.next(), it.return('return value')]);
|
||||
|
||||
assert_equals(iterResults[0].status, 'fulfilled', 'next() promise status');
|
||||
assert_iter_result(iterResults[0].value, 0, false, 'next()');
|
||||
|
||||
assert_equals(iterResults[1].status, 'fulfilled', 'return() promise status');
|
||||
assert_iter_result(iterResults[1].value, 'return value', true, 'return()');
|
||||
|
||||
assert_equals(timesPulled, 2);
|
||||
}, 'next() that succeeds; return() [no awaiting]');
|
||||
|
||||
promise_test(async () => {
|
||||
const rs = new ReadableStream();
|
||||
const it = rs.values();
|
||||
|
||||
const iterResult1 = await it.return('return value');
|
||||
assert_iter_result(iterResult1, 'return value', true, 'return()');
|
||||
|
||||
const iterResult2 = await it.next();
|
||||
assert_iter_result(iterResult2, undefined, true, 'next()');
|
||||
}, 'return(); next()');
|
||||
|
||||
promise_test(async () => {
|
||||
const rs = new ReadableStream();
|
||||
const it = rs.values();
|
||||
|
||||
const iterResults = await Promise.allSettled([it.return('return value'), it.next()]);
|
||||
|
||||
assert_equals(iterResults[0].status, 'fulfilled', 'return() promise status');
|
||||
assert_iter_result(iterResults[0].value, 'return value', true, 'return()');
|
||||
|
||||
assert_equals(iterResults[1].status, 'fulfilled', 'next() promise status');
|
||||
assert_iter_result(iterResults[1].value, undefined, true, 'next()');
|
||||
}, 'return(); next() [no awaiting]');
|
||||
|
||||
promise_test(async () => {
|
||||
const rs = new ReadableStream();
|
||||
const it = rs.values();
|
||||
|
||||
const iterResult1 = await it.return('return value 1');
|
||||
assert_iter_result(iterResult1, 'return value 1', true, '1st return()');
|
||||
|
||||
const iterResult2 = await it.return('return value 2');
|
||||
assert_iter_result(iterResult2, 'return value 2', true, '1st return()');
|
||||
}, 'return(); return()');
|
||||
|
||||
promise_test(async () => {
|
||||
const rs = new ReadableStream();
|
||||
const it = rs.values();
|
||||
|
||||
const iterResults = await Promise.allSettled([it.return('return value 1'), it.return('return value 2')]);
|
||||
|
||||
assert_equals(iterResults[0].status, 'fulfilled', '1st return() promise status');
|
||||
assert_iter_result(iterResults[0].value, 'return value 1', true, '1st return()');
|
||||
|
||||
assert_equals(iterResults[1].status, 'fulfilled', '2nd return() promise status');
|
||||
assert_iter_result(iterResults[1].value, 'return value 2', true, '1st return()');
|
||||
}, 'return(); return() [no awaiting]');
|
||||
|
||||
test(() => {
|
||||
const s = new ReadableStream({
|
||||
@ -261,9 +519,9 @@ test(() => {
|
||||
c.close();
|
||||
},
|
||||
});
|
||||
const it = s.getIterator();
|
||||
assert_throws_js(TypeError, () => s.getIterator(), 'getIterator() should throw');
|
||||
}, 'getIterator() throws if there\'s already a lock');
|
||||
s.values();
|
||||
assert_throws_js(TypeError, () => s.values(), 'values() should throw');
|
||||
}, 'values() throws if there\'s already a lock');
|
||||
|
||||
promise_test(async () => {
|
||||
const s = new ReadableStream({
|
||||
@ -272,7 +530,7 @@ promise_test(async () => {
|
||||
c.enqueue(2);
|
||||
c.enqueue(3);
|
||||
c.close();
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const chunks = [];
|
||||
@ -285,6 +543,34 @@ promise_test(async () => {
|
||||
await reader.closed;
|
||||
}, 'Acquiring a reader after exhaustively async-iterating a stream');
|
||||
|
||||
promise_test(async t => {
|
||||
let timesPulled = 0;
|
||||
const s = new ReadableStream({
|
||||
pull(c) {
|
||||
if (timesPulled === 0) {
|
||||
c.enqueue(0);
|
||||
++timesPulled;
|
||||
} else {
|
||||
c.error(error1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const it = s[Symbol.asyncIterator]({ preventCancel: true });
|
||||
|
||||
const iterResult1 = await it.next();
|
||||
assert_iter_result(iterResult1, 0, false, '1st next()');
|
||||
|
||||
await promise_rejects_exactly(t, error1, it.next(), '2nd next()');
|
||||
|
||||
const iterResult2 = await it.return('return value');
|
||||
assert_iter_result(iterResult2, 'return value', true, 'return()');
|
||||
|
||||
// i.e. it should not reject with a generic "this stream is locked" TypeError.
|
||||
const reader = s.getReader();
|
||||
await promise_rejects_exactly(t, error1, reader.closed, 'closed on the new reader should reject with the error');
|
||||
}, 'Acquiring a reader after return()ing from a stream that errors');
|
||||
|
||||
promise_test(async () => {
|
||||
const s = new ReadableStream({
|
||||
start(c) {
|
||||
@ -321,7 +607,7 @@ promise_test(async () => {
|
||||
|
||||
// read the first two chunks, then release lock
|
||||
const chunks = [];
|
||||
for await (const chunk of s.getIterator({preventCancel: true})) {
|
||||
for await (const chunk of s.values({preventCancel: true})) {
|
||||
chunks.push(chunk);
|
||||
if (chunk >= 2) {
|
||||
break;
|
||||
@ -331,22 +617,14 @@ promise_test(async () => {
|
||||
|
||||
const reader = s.getReader();
|
||||
const readResult = await reader.read();
|
||||
assert_equals(readResult.done, false, 'should not be closed yet');
|
||||
assert_equals(readResult.value, 3, 'should read remaining chunk');
|
||||
assert_iter_result(readResult, 3, false);
|
||||
await reader.closed;
|
||||
}, 'Acquiring a reader and reading the remaining chunks after partially async-iterating a stream with preventCancel = true');
|
||||
|
||||
promise_test(async t => {
|
||||
const rs = new ReadableStream();
|
||||
const it = rs.getIterator();
|
||||
await it.return();
|
||||
return promise_rejects_js(t, TypeError, it.next(), 'next() should reject');
|
||||
}, 'calling next() after return() should reject');
|
||||
|
||||
for (const preventCancel of [false, true]) {
|
||||
test(() => {
|
||||
const rs = new ReadableStream();
|
||||
rs.getIterator({ preventCancel }).return();
|
||||
rs.values({ preventCancel }).return();
|
||||
// The test passes if this line doesn't throw.
|
||||
rs.getReader();
|
||||
}, `return() should unlock the stream synchronously when preventCancel = ${preventCancel}`);
|
||||
|
@ -1,209 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/test-utils.js
|
||||
'use strict';
|
||||
|
||||
let ReadableStreamDefaultReader;
|
||||
let ReadableStreamDefaultController;
|
||||
let ReadableStreamAsyncIteratorPrototype;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamDefaultReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamDefaultReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
ReadableStreamDefaultController = c.constructor;
|
||||
}
|
||||
});
|
||||
|
||||
}, 'Can get the ReadableStreamDefaultController constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rs = new ReadableStream();
|
||||
ReadableStreamAsyncIteratorPrototype = Object.getPrototypeOf(rs.getIterator());
|
||||
|
||||
}, 'Can get ReadableStreamAsyncIteratorPrototype object indirectly');
|
||||
|
||||
function fakeRS() {
|
||||
return Object.setPrototypeOf({
|
||||
cancel() { return Promise.resolve(); },
|
||||
getReader() { return new ReadableStreamDefaultReader(new ReadableStream()); },
|
||||
pipeThrough(obj) { return obj.readable; },
|
||||
pipeTo() { return Promise.resolve(); },
|
||||
tee() { return [realRS(), realRS()]; }
|
||||
}, ReadableStream.prototype);
|
||||
}
|
||||
|
||||
function realRS() {
|
||||
return new ReadableStream();
|
||||
}
|
||||
|
||||
function fakeRSDefaultReader() {
|
||||
return Object.setPrototypeOf({
|
||||
get closed() { return Promise.resolve(); },
|
||||
cancel() { return Promise.resolve(); },
|
||||
read() { return Promise.resolve({ value: undefined, done: true }); },
|
||||
releaseLock() { return; }
|
||||
}, ReadableStreamDefaultReader.prototype);
|
||||
}
|
||||
|
||||
function realRSDefaultReader() {
|
||||
return new ReadableStream().getReader();
|
||||
}
|
||||
|
||||
function fakeRSDefaultController() {
|
||||
return Object.setPrototypeOf({
|
||||
close() { },
|
||||
enqueue() { },
|
||||
error() { }
|
||||
}, ReadableStreamDefaultController.prototype);
|
||||
}
|
||||
|
||||
function realRSDefaultController() {
|
||||
let controller;
|
||||
new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
function fakeRSAsyncIterator() {
|
||||
return Object.setPrototypeOf({
|
||||
next() { },
|
||||
return(value = undefined) { }
|
||||
}, ReadableStreamAsyncIteratorPrototype);
|
||||
}
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStream.prototype, 'cancel',
|
||||
[fakeRS(), realRSDefaultReader(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStream.prototype.cancel enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStream.prototype, 'getIterator',
|
||||
[fakeRS(), realRSDefaultReader(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStream.prototype.getIterator enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStream.prototype, 'getReader',
|
||||
[fakeRS(), realRSDefaultReader(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStream.prototype.getReader enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
getterThrowsForAll(ReadableStream.prototype, 'locked',
|
||||
[fakeRS(), realRSDefaultReader(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStream.prototype.locked enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStream.prototype, 'tee', [fakeRS(), realRSDefaultReader(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStream.prototype.tee enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamDefaultReader(fakeRS()),
|
||||
'Constructing a ReadableStreamDefaultReader should throw');
|
||||
|
||||
}, 'ReadableStreamDefaultReader enforces a brand check on its argument');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return getterRejectsForAll(t, ReadableStreamDefaultReader.prototype, 'closed',
|
||||
[fakeRSDefaultReader(), realRS(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultReader.prototype.closed enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamDefaultReader.prototype, 'cancel',
|
||||
[fakeRSDefaultReader(), realRS(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultReader.prototype.cancel enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamDefaultReader.prototype, 'read',
|
||||
[fakeRSDefaultReader(), realRS(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultReader.prototype.read enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStreamDefaultReader.prototype, 'releaseLock',
|
||||
[fakeRSDefaultReader(), realRS(), realRSDefaultController(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultReader.prototype.releaseLock enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamDefaultController(fakeRS()),
|
||||
'Constructing a ReadableStreamDefaultController should throw');
|
||||
|
||||
}, 'ReadableStreamDefaultController enforces a brand check on its argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamDefaultController(realRS()),
|
||||
'Constructing a ReadableStreamDefaultController should throw');
|
||||
|
||||
}, 'ReadableStreamDefaultController can\'t be given a fully-constructed ReadableStream');
|
||||
|
||||
test(() => {
|
||||
|
||||
getterThrowsForAll(ReadableStreamDefaultController.prototype, 'desiredSize',
|
||||
[fakeRSDefaultController(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultController.prototype.desiredSize enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStreamDefaultController.prototype, 'close',
|
||||
[fakeRSDefaultController(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultController.prototype.close enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStreamDefaultController.prototype, 'enqueue',
|
||||
[fakeRSDefaultController(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultController.prototype.enqueue enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
|
||||
methodThrowsForAll(ReadableStreamDefaultController.prototype, 'error',
|
||||
[fakeRSDefaultController(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamDefaultController.prototype.error enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamAsyncIteratorPrototype, 'next',
|
||||
[fakeRSAsyncIterator(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamAsyncIteratorPrototype.next enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
return methodRejectsForAll(t, ReadableStreamAsyncIteratorPrototype, 'return',
|
||||
[fakeRSAsyncIterator(), realRS(), realRSDefaultReader(), undefined, null]);
|
||||
|
||||
}, 'ReadableStreamAsyncIteratorPrototype.return enforces a brand check');
|
@ -1,37 +1,17 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/constructor-ordering.js
|
||||
'use strict';
|
||||
|
||||
const operations = [
|
||||
op('get', 'size'),
|
||||
op('get', 'highWaterMark'),
|
||||
op('get', 'type'),
|
||||
op('validate', 'type'),
|
||||
op('validate', 'size'),
|
||||
op('tonumber', 'highWaterMark'),
|
||||
op('validate', 'highWaterMark'),
|
||||
op('get', 'pull'),
|
||||
op('validate', 'pull'),
|
||||
op('get', 'cancel'),
|
||||
op('validate', 'cancel'),
|
||||
op('get', 'start'),
|
||||
op('validate', 'start')
|
||||
];
|
||||
const error1 = new Error('error1');
|
||||
error1.name = 'error1';
|
||||
|
||||
for (const failureOp of operations) {
|
||||
test(() => {
|
||||
const record = new OpRecorder(failureOp);
|
||||
const underlyingSource = createRecordingObjectWithProperties(record, ['type', 'start', 'pull', 'cancel']);
|
||||
const strategy = createRecordingStrategy(record);
|
||||
const error2 = new Error('error2');
|
||||
error2.name = 'error2';
|
||||
|
||||
try {
|
||||
new ReadableStream(underlyingSource, strategy);
|
||||
assert_unreached('constructor should throw');
|
||||
} catch (e) {
|
||||
assert_equals(typeof e, 'object', 'e should be an object');
|
||||
}
|
||||
test(() => {
|
||||
const underlyingSource = { get start() { throw error1; } };
|
||||
const queuingStrategy = { highWaterMark: 0, get size() { throw error2; } };
|
||||
|
||||
assert_equals(record.actual(), expectedAsString(operations, failureOp),
|
||||
'operations should be performed in the right order');
|
||||
}, `ReadableStream constructor should stop after ${failureOp} fails`);
|
||||
}
|
||||
// underlyingSource is converted in prose in the method body, whereas queuingStrategy is done at the IDL layer.
|
||||
// So the queuingStrategy exception should be encountered first.
|
||||
assert_throws_exactly(error2, () => new ReadableStream(underlyingSource, queuingStrategy));
|
||||
}, 'underlyingSource argument should be converted after queuingStrategy argument');
|
||||
|
@ -2,15 +2,6 @@
|
||||
// META: script=../resources/rs-utils.js
|
||||
'use strict';
|
||||
|
||||
let ReadableStreamDefaultReader;
|
||||
|
||||
test(() => {
|
||||
|
||||
// It's not exposed globally, but we test a few of its properties here.
|
||||
ReadableStreamDefaultReader = (new ReadableStream()).getReader().constructor;
|
||||
|
||||
}, 'Can get the ReadableStreamDefaultReader constructor indirectly');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => new ReadableStreamDefaultReader('potato'));
|
||||
@ -19,44 +10,6 @@ test(() => {
|
||||
|
||||
}, 'ReadableStreamDefaultReader constructor should get a ReadableStream object as argument');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['cancel', 'constructor', 'read', 'releaseLock'];
|
||||
const properties = methods.concat(['closed']).sort();
|
||||
|
||||
const rsReader = new ReadableStreamDefaultReader(new ReadableStream());
|
||||
const proto = Object.getPrototypeOf(rsReader);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
|
||||
assert_equals(propDesc.configurable, true, 'method should be configurable');
|
||||
assert_equals(propDesc.writable, true, 'method should be writable');
|
||||
assert_equals(typeof rsReader[m], 'function', 'should have be a method');
|
||||
const expectedName = m === 'constructor' ? 'ReadableStreamDefaultReader' : m;
|
||||
assert_equals(rsReader[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
const closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed');
|
||||
assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerable');
|
||||
assert_equals(closedPropDesc.configurable, true, 'closed should be configurable');
|
||||
assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter');
|
||||
assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter');
|
||||
|
||||
assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter');
|
||||
assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed property');
|
||||
assert_equals(typeof rsReader.closed.then, 'function', 'closed property is thenable');
|
||||
assert_equals(typeof rsReader.constructor, 'function', 'has a constructor method');
|
||||
assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter');
|
||||
assert_equals(typeof rsReader.read, 'function', 'has a getReader method');
|
||||
assert_equals(rsReader.read.length, 0, 'read has no parameters');
|
||||
assert_equals(typeof rsReader.releaseLock, 'function', 'has a releaseLock method');
|
||||
assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters');
|
||||
|
||||
}, 'ReadableStreamDefaultReader instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
const rsReader = new ReadableStreamDefaultReader(new ReadableStream());
|
||||
@ -491,7 +444,7 @@ test(() => {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
assert_throws_js(RangeError, () => rs.getReader({ mode }), 'getReader() should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader({ mode }), 'getReader() should throw');
|
||||
assert_true(toStringCalled, 'toString() should be called');
|
||||
}, 'getReader() should call ToString() on mode');
|
||||
|
||||
|
@ -26,64 +26,25 @@ test(() => {
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(RangeError, () => new ReadableStream({ type: null }),
|
||||
assert_throws_js(TypeError, () => new ReadableStream({ type: null }),
|
||||
'constructor should throw when the type is null');
|
||||
assert_throws_js(RangeError, () => new ReadableStream({ type: '' }),
|
||||
assert_throws_js(TypeError, () => new ReadableStream({ type: '' }),
|
||||
'constructor should throw when the type is empty string');
|
||||
assert_throws_js(RangeError, () => new ReadableStream({ type: 'asdf' }),
|
||||
assert_throws_js(TypeError, () => new ReadableStream({ type: 'asdf' }),
|
||||
'constructor should throw when the type is asdf');
|
||||
assert_throws_exactly(error1, () => new ReadableStream({ type: { get toString() {throw error1;} } }), 'constructor should throw when ToString() throws');
|
||||
assert_throws_exactly(error1, () => new ReadableStream({ type: { toString() {throw error1;} } }), 'constructor should throw when ToString() throws');
|
||||
assert_throws_exactly(
|
||||
error1,
|
||||
() => new ReadableStream({ type: { get toString() { throw error1; } } }),
|
||||
'constructor should throw when ToString() throws'
|
||||
);
|
||||
assert_throws_exactly(
|
||||
error1,
|
||||
() => new ReadableStream({ type: { toString() { throw error1; } } }),
|
||||
'constructor should throw when ToString() throws'
|
||||
);
|
||||
|
||||
}, 'ReadableStream can\'t be constructed with an invalid type');
|
||||
|
||||
test(() => {
|
||||
|
||||
const methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee', 'getIterator'];
|
||||
const properties = methods.concat(['locked']).sort();
|
||||
const symbols = [Symbol.asyncIterator];
|
||||
|
||||
const rs = new ReadableStream();
|
||||
const proto = Object.getPrototypeOf(rs);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, 'should have all the correct properties');
|
||||
assert_array_equals(Object.getOwnPropertySymbols(proto).sort(), symbols, 'should have all the correct symbols');
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_false(propDesc.enumerable, 'method should be non-enumerable');
|
||||
assert_true(propDesc.configurable, 'method should be configurable');
|
||||
assert_true(propDesc.writable, 'method should be writable');
|
||||
assert_equals(typeof rs[m], 'function', 'method should be a function');
|
||||
const expectedName = m === 'constructor' ? 'ReadableStream' : m;
|
||||
assert_equals(rs[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
const lockedPropDesc = Object.getOwnPropertyDescriptor(proto, 'locked');
|
||||
assert_false(lockedPropDesc.enumerable, 'locked should be non-enumerable');
|
||||
assert_equals(lockedPropDesc.writable, undefined, 'locked should not be a data property');
|
||||
assert_equals(typeof lockedPropDesc.get, 'function', 'locked should have a getter');
|
||||
assert_equals(lockedPropDesc.set, undefined, 'locked should not have a setter');
|
||||
assert_true(lockedPropDesc.configurable, 'locked should be configurable');
|
||||
|
||||
assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter');
|
||||
assert_equals(rs.constructor.length, 0, 'constructor should have no parameters');
|
||||
assert_equals(rs.getReader.length, 0, 'getReader should have no parameters');
|
||||
assert_equals(rs.pipeThrough.length, 1, 'pipeThrough should have 1 parameters');
|
||||
assert_equals(rs.pipeTo.length, 1, 'pipeTo should have 1 parameter');
|
||||
assert_equals(rs.tee.length, 0, 'tee should have no parameters');
|
||||
assert_equals(rs.getIterator.length, 0, 'getIterator should have no required parameters');
|
||||
assert_equals(rs[Symbol.asyncIterator].length, 0, '@@asyncIterator should have no required parameters');
|
||||
|
||||
const asyncIteratorPropDesc = Object.getOwnPropertyDescriptor(proto, Symbol.asyncIterator);
|
||||
assert_false(asyncIteratorPropDesc.enumerable, '@@asyncIterator should be non-enumerable');
|
||||
assert_true(asyncIteratorPropDesc.configurable, '@@asyncIterator should be configurable');
|
||||
assert_true(asyncIteratorPropDesc.writable, '@@asyncIterator should be writable');
|
||||
assert_equals(typeof rs[Symbol.asyncIterator], 'function', '@@asyncIterator should be a function');
|
||||
assert_equals(rs[Symbol.asyncIterator].name, 'getIterator', '@@asyncIterator should have the correct name');
|
||||
|
||||
}, 'ReadableStream instances should have the correct list of properties');
|
||||
|
||||
test(() => {
|
||||
|
||||
assert_throws_js(TypeError, () => {
|
||||
@ -109,38 +70,8 @@ test(() => {
|
||||
let startCalled = false;
|
||||
|
||||
const source = {
|
||||
start(controller) {
|
||||
start() {
|
||||
assert_equals(this, source, 'source is this during start');
|
||||
|
||||
const methods = ['close', 'enqueue', 'error', 'constructor'];
|
||||
const properties = ['desiredSize'].concat(methods).sort();
|
||||
const proto = Object.getPrototypeOf(controller);
|
||||
|
||||
assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties,
|
||||
'the controller should have the right properties');
|
||||
|
||||
for (const m of methods) {
|
||||
const propDesc = Object.getOwnPropertyDescriptor(proto, m);
|
||||
assert_equals(typeof controller[m], 'function', `should have a ${m} method`);
|
||||
assert_false(propDesc.enumerable, m + ' should be non-enumerable');
|
||||
assert_true(propDesc.configurable, m + ' should be configurable');
|
||||
assert_true(propDesc.writable, m + ' should be writable');
|
||||
const expectedName = m === 'constructor' ? 'ReadableStreamDefaultController' : m;
|
||||
assert_equals(controller[m].name, expectedName, 'method should have the correct name');
|
||||
}
|
||||
|
||||
const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desiredSize');
|
||||
assert_false(desiredSizePropDesc.enumerable, 'desiredSize should be non-enumerable');
|
||||
assert_equals(desiredSizePropDesc.writable, undefined, 'desiredSize should not be a data property');
|
||||
assert_equals(typeof desiredSizePropDesc.get, 'function', 'desiredSize should have a getter');
|
||||
assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not have a setter');
|
||||
assert_true(desiredSizePropDesc.configurable, 'desiredSize should be configurable');
|
||||
|
||||
assert_equals(controller.close.length, 0, 'close should have no parameters');
|
||||
assert_equals(controller.constructor.length, 0, 'constructor should have no parameters');
|
||||
assert_equals(controller.enqueue.length, 1, 'enqueue should have 1 parameter');
|
||||
assert_equals(controller.error.length, 1, 'error should have 1 parameter');
|
||||
|
||||
startCalled = true;
|
||||
}
|
||||
};
|
||||
@ -148,7 +79,7 @@ test(() => {
|
||||
new ReadableStream(source);
|
||||
assert_true(startCalled);
|
||||
|
||||
}, 'ReadableStream start should be called with the proper parameters');
|
||||
}, 'ReadableStream start should be called with the proper thisArg');
|
||||
|
||||
test(() => {
|
||||
|
||||
@ -178,7 +109,7 @@ test(() => {
|
||||
(new ReadableStream()).getReader(undefined);
|
||||
(new ReadableStream()).getReader({});
|
||||
(new ReadableStream()).getReader({ mode: undefined, notmode: 'ignored' });
|
||||
assert_throws_js(RangeError, () => (new ReadableStream()).getReader({ mode: 'potato' }));
|
||||
assert_throws_js(TypeError, () => (new ReadableStream()).getReader({ mode: 'potato' }));
|
||||
}, 'default ReadableStream getReader() should only accept mode:undefined');
|
||||
|
||||
promise_test(() => {
|
||||
@ -296,18 +227,14 @@ promise_test(() => {
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
new ReadableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
return delay(10);
|
||||
}).then(() => {
|
||||
@ -319,12 +246,8 @@ promise_test(() => {
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
pull(c) {
|
||||
// Don't enqueue immediately after start. We want the stream to be empty when we call .read() on it.
|
||||
if (pullCount > 0) {
|
||||
@ -334,7 +257,7 @@ promise_test(() => {
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
}).then(() => {
|
||||
const reader = rs.getReader();
|
||||
@ -351,12 +274,10 @@ promise_test(() => {
|
||||
promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
@ -366,7 +287,7 @@ promise_test(() => {
|
||||
const read = rs.getReader().read();
|
||||
assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should be called once start finishes');
|
||||
return read;
|
||||
}).then(r => {
|
||||
@ -387,14 +308,13 @@ promise_test(() => {
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
c.enqueue('a');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
pullCount++;
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 0, 'pull should not be called once start finishes, since the queue is full');
|
||||
|
||||
const read = rs.getReader().read();
|
||||
@ -413,12 +333,10 @@ promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
let controller;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++pullCount;
|
||||
@ -426,7 +344,7 @@ promise_test(() => {
|
||||
});
|
||||
|
||||
const reader = rs.getReader();
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
|
||||
|
||||
controller.enqueue('a');
|
||||
@ -446,12 +364,10 @@ promise_test(() => {
|
||||
|
||||
let pullCount = 0;
|
||||
let controller;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++pullCount;
|
||||
@ -460,7 +376,7 @@ promise_test(() => {
|
||||
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
|
||||
|
||||
controller.enqueue('a');
|
||||
@ -484,12 +400,8 @@ promise_test(() => {
|
||||
let resolve;
|
||||
let returnedPromise;
|
||||
let timesCalled = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
pull(c) {
|
||||
c.enqueue(++timesCalled);
|
||||
returnedPromise = new Promise(r => resolve = r);
|
||||
@ -498,9 +410,8 @@ promise_test(() => {
|
||||
});
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
return reader.read();
|
||||
}).then(result1 => {
|
||||
return reader.read()
|
||||
.then(result1 => {
|
||||
assert_equals(timesCalled, 1,
|
||||
'pull should have been called once after start, but not yet have been called a second time');
|
||||
assert_object_equals(result1, { value: 1, done: false }, 'read() should fulfill with the enqueued value');
|
||||
@ -521,7 +432,6 @@ promise_test(() => {
|
||||
promise_test(() => {
|
||||
|
||||
let timesCalled = 0;
|
||||
const startPromise = Promise.resolve();
|
||||
|
||||
const rs = new ReadableStream(
|
||||
{
|
||||
@ -529,7 +439,6 @@ promise_test(() => {
|
||||
c.enqueue('a');
|
||||
c.enqueue('b');
|
||||
c.enqueue('c');
|
||||
return startPromise;
|
||||
},
|
||||
pull() {
|
||||
++timesCalled;
|
||||
@ -544,7 +453,7 @@ promise_test(() => {
|
||||
);
|
||||
const reader = rs.getReader();
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
return reader.read();
|
||||
}).then(result1 => {
|
||||
assert_object_equals(result1, { value: 'a', done: false }, 'first chunk should be as expected');
|
||||
|
@ -106,7 +106,8 @@ promise_test(async t => {
|
||||
const reader = oldReadableStreamGetReader.call(rs);
|
||||
// stream should be cancelled
|
||||
await reader.closed;
|
||||
}, 'ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods');
|
||||
}, 'ReadableStream async iterator should use the original values of getReader() and ReadableStreamDefaultReader ' +
|
||||
'methods');
|
||||
|
||||
test(t => {
|
||||
const oldPromiseThen = Promise.prototype.then;
|
||||
|
@ -1,129 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Helpers for tests that constructors perform getting and validation of properties in the standard order.
|
||||
// See ../readable-streams/constructor.js for an example of how to use them.
|
||||
|
||||
// Describes an operation on a property. |type| is "get", "validate" or "tonumber". |name| is the name of the property
|
||||
// in question. |side| is usually undefined, but is used by TransformStream to distinguish between the readable and
|
||||
// writable strategies.
|
||||
class Op {
|
||||
constructor(type, name, side) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.side === undefined ? `${this.type} on ${this.name}` : `${this.type} on ${this.name} (${this.side})`;
|
||||
}
|
||||
|
||||
equals(otherOp) {
|
||||
return this.type === otherOp.type && this.name === otherOp.name && this.side === otherOp.side;
|
||||
}
|
||||
}
|
||||
|
||||
// Provides a concise syntax to create an Op object. |side| is used by TransformStream to distinguish between the two
|
||||
// strategies.
|
||||
function op(type, name, side = undefined) {
|
||||
return new Op(type, name, side);
|
||||
}
|
||||
|
||||
// Records a sequence of operations. Also checks each operation against |failureOp| to see if it should fail.
|
||||
class OpRecorder {
|
||||
constructor(failureOp) {
|
||||
this.ops = [];
|
||||
this.failureOp = failureOp;
|
||||
this.matched = false;
|
||||
}
|
||||
|
||||
// Record an operation. Returns true if this operation should fail.
|
||||
recordAndCheck(type, name, side = undefined) {
|
||||
const recordedOp = op(type, name, side);
|
||||
this.ops.push(recordedOp);
|
||||
return this.failureOp.equals(recordedOp);
|
||||
}
|
||||
|
||||
// Returns true if validation of this property should fail.
|
||||
check(name, side = undefined) {
|
||||
return this.failureOp.equals(op('validate', name, side));
|
||||
}
|
||||
|
||||
// Returns the sequence of recorded operations as a string.
|
||||
actual() {
|
||||
return this.ops.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Creates an object with the list of properties named in |properties|. Every property access will be recorded in
|
||||
// |record|, which will also be used to determine whether a particular property access should fail, or whether it should
|
||||
// return an invalid value that will fail validation.
|
||||
function createRecordingObjectWithProperties(record, properties) {
|
||||
const recordingObject = {};
|
||||
for (const property of properties) {
|
||||
defineCheckedProperty(record, recordingObject, property, () => record.check(property) ? 'invalid' : undefined);
|
||||
}
|
||||
return recordingObject;
|
||||
}
|
||||
|
||||
// Add a getter to |object| named |property| which throws if op('get', property) should fail, and otherwise calls
|
||||
// getter() to get the return value.
|
||||
function defineCheckedProperty(record, object, property, getter) {
|
||||
Object.defineProperty(object, property, {
|
||||
get() {
|
||||
if (record.recordAndCheck('get', property)) {
|
||||
throw new Error(`intentional failure of get ${property}`);
|
||||
}
|
||||
return getter();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Similar to createRecordingObjectWithProperties(), but with specific functionality for "highWaterMark" so that numeric
|
||||
// conversion can be recorded. Permits |side| to be specified so that TransformStream can distinguish between its two
|
||||
// strategies.
|
||||
function createRecordingStrategy(record, side = undefined) {
|
||||
return {
|
||||
get size() {
|
||||
if (record.recordAndCheck('get', 'size', side)) {
|
||||
throw new Error(`intentional failure of get size`);
|
||||
}
|
||||
return record.check('size', side) ? 'invalid' : undefined;
|
||||
},
|
||||
get highWaterMark() {
|
||||
if (record.recordAndCheck('get', 'highWaterMark', side)) {
|
||||
throw new Error(`intentional failure of get highWaterMark`);
|
||||
}
|
||||
return createRecordingNumberObject(record, 'highWaterMark', side);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Creates an object which will record when it is converted to a number. It will assert if the conversion is to some
|
||||
// other type, and will fail if op('tonumber', property, side) is set as the failure step. The object will convert to -1
|
||||
// if 'validate' is set as the failure step, and 1 otherwise.
|
||||
function createRecordingNumberObject(record, property, side = undefined) {
|
||||
return {
|
||||
[Symbol.toPrimitive](hint) {
|
||||
assert_equals(hint, 'number', `hint for ${property} should be 'number'`);
|
||||
if (record.recordAndCheck('tonumber', property, side)) {
|
||||
throw new Error(`intentional failure of ${op('tonumber', property, side)}`);
|
||||
}
|
||||
return record.check(property, side) ? -1 : 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Creates a string from everything in |operations| up to and including |failureOp|. "validate" steps are excluded from
|
||||
// the output, as we cannot record them except by making them fail.
|
||||
function expectedAsString(operations, failureOp) {
|
||||
const expected = [];
|
||||
for (const step of operations) {
|
||||
if (step.type !== 'validate') {
|
||||
expected.push(step);
|
||||
}
|
||||
if (step.equals(failureOp)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expected.toString();
|
||||
}
|
@ -23,10 +23,13 @@ self.templatedRSEmpty = (label, factory) => {
|
||||
test(() => {
|
||||
const rs = factory();
|
||||
|
||||
assert_throws_js(RangeError, () => rs.getReader({ mode: '' }), 'empty string mode should throw');
|
||||
assert_throws_js(RangeError, () => rs.getReader({ mode: null }), 'null mode should throw');
|
||||
assert_throws_js(RangeError, () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader(null), 'null should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader({ mode: '' }), 'empty string mode should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader({ mode: null }), 'null mode should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw');
|
||||
assert_throws_js(TypeError, () => rs.getReader(5), '5 should throw');
|
||||
|
||||
// Should not throw
|
||||
rs.getReader(null);
|
||||
|
||||
}, label + ': calling getReader with invalid arguments should throw appropriate errors');
|
||||
};
|
||||
|
@ -1,74 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/test-utils.js
|
||||
'use strict';
|
||||
|
||||
const TransformStreamDefaultController = getTransformStreamDefaultControllerConstructor();
|
||||
|
||||
function getTransformStreamDefaultControllerConstructor() {
|
||||
return realTSDefaultController().constructor;
|
||||
}
|
||||
|
||||
function fakeTS() {
|
||||
return Object.setPrototypeOf({
|
||||
get readable() { return new ReadableStream(); },
|
||||
get writable() { return new WritableStream(); }
|
||||
}, TransformStream.prototype);
|
||||
}
|
||||
|
||||
function realTS() {
|
||||
return new TransformStream();
|
||||
}
|
||||
|
||||
function fakeTSDefaultController() {
|
||||
return Object.setPrototypeOf({
|
||||
get desiredSize() { return 1; },
|
||||
enqueue() { },
|
||||
close() { },
|
||||
error() { }
|
||||
}, TransformStreamDefaultController.prototype);
|
||||
}
|
||||
|
||||
function realTSDefaultController() {
|
||||
let controller;
|
||||
new TransformStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
test(() => {
|
||||
getterThrowsForAll(TransformStream.prototype, 'readable',
|
||||
[fakeTS(), realTSDefaultController(), undefined, null]);
|
||||
}, 'TransformStream.prototype.readable enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
getterThrowsForAll(TransformStream.prototype, 'writable',
|
||||
[fakeTS(), realTSDefaultController(), undefined, null]);
|
||||
}, 'TransformStream.prototype.writable enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
constructorThrowsForAll(TransformStreamDefaultController,
|
||||
[fakeTS(), realTS(), realTSDefaultController(), undefined, null]);
|
||||
}, 'TransformStreamDefaultConstructor enforces a brand check and doesn\'t permit independent construction');
|
||||
|
||||
test(() => {
|
||||
getterThrowsForAll(TransformStreamDefaultController.prototype, 'desiredSize',
|
||||
[fakeTSDefaultController(), realTS(), undefined, null]);
|
||||
}, 'TransformStreamDefaultController.prototype.desiredSize enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(TransformStreamDefaultController.prototype, 'enqueue',
|
||||
[fakeTSDefaultController(), realTS(), undefined, null]);
|
||||
}, 'TransformStreamDefaultController.prototype.enqueue enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(TransformStreamDefaultController.prototype, 'terminate',
|
||||
[fakeTSDefaultController(), realTS(), undefined, null]);
|
||||
}, 'TransformStreamDefaultController.prototype.terminate enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(TransformStreamDefaultController.prototype, 'error',
|
||||
[fakeTSDefaultController(), realTS(), undefined, null]);
|
||||
}, 'TransformStreamDefaultController.prototype.error enforces a brand check');
|
@ -1,46 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/constructor-ordering.js
|
||||
'use strict';
|
||||
|
||||
const operations = [
|
||||
op('get', 'size', 'writable'),
|
||||
op('get', 'highWaterMark', 'writable'),
|
||||
op('get', 'size', 'readable'),
|
||||
op('get', 'highWaterMark', 'readable'),
|
||||
op('get', 'writableType'),
|
||||
op('validate', 'writableType'),
|
||||
op('validate', 'size', 'writable'),
|
||||
op('tonumber', 'highWaterMark', 'writable'),
|
||||
op('validate', 'highWaterMark', 'writable'),
|
||||
op('get', 'readableType'),
|
||||
op('validate', 'readableType'),
|
||||
op('validate', 'size', 'readable'),
|
||||
op('tonumber', 'highWaterMark', 'readable'),
|
||||
op('validate', 'highWaterMark', 'readable'),
|
||||
op('get', 'transform'),
|
||||
op('validate', 'transform'),
|
||||
op('get', 'flush'),
|
||||
op('validate', 'flush'),
|
||||
op('get', 'start'),
|
||||
op('validate', 'start')
|
||||
];
|
||||
|
||||
for (const failureOp of operations) {
|
||||
test(() => {
|
||||
const record = new OpRecorder(failureOp);
|
||||
const transformer = createRecordingObjectWithProperties(
|
||||
record, ['readableType', 'writableType', 'start', 'transform', 'flush']);
|
||||
const writableStrategy = createRecordingStrategy(record, 'writable');
|
||||
const readableStrategy = createRecordingStrategy(record, 'readable');
|
||||
|
||||
try {
|
||||
new TransformStream(transformer, writableStrategy, readableStrategy);
|
||||
assert_unreached('constructor should throw');
|
||||
} catch (e) {
|
||||
assert_equals(typeof e, 'object', 'e should be an object');
|
||||
}
|
||||
|
||||
assert_equals(record.actual(), expectedAsString(operations, failureOp),
|
||||
'operations should be performed in the right order');
|
||||
}, `TransformStream constructor should stop after ${failureOp} fails`);
|
||||
}
|
@ -12,31 +12,6 @@ test(() => {
|
||||
new TransformStream({});
|
||||
}, 'TransformStream can be constructed with no transform function');
|
||||
|
||||
test(() => {
|
||||
const ts = new TransformStream({ transform() { } });
|
||||
const proto = Object.getPrototypeOf(ts);
|
||||
|
||||
const writableStream = Object.getOwnPropertyDescriptor(proto, 'writable');
|
||||
assert_true(writableStream !== undefined, 'it has a writable property');
|
||||
assert_false(writableStream.enumerable, 'writable should be non-enumerable');
|
||||
assert_equals(typeof writableStream.get, 'function', 'writable should have a getter');
|
||||
assert_equals(writableStream.set, undefined, 'writable should not have a setter');
|
||||
assert_true(writableStream.configurable, 'writable should be configurable');
|
||||
assert_true(ts.writable instanceof WritableStream, 'writable is an instance of WritableStream');
|
||||
assert_not_equals(WritableStream.prototype.getWriter.call(ts.writable), undefined,
|
||||
'writable should pass WritableStream brand check');
|
||||
|
||||
const readableStream = Object.getOwnPropertyDescriptor(proto, 'readable');
|
||||
assert_true(readableStream !== undefined, 'it has a readable property');
|
||||
assert_false(readableStream.enumerable, 'readable should be non-enumerable');
|
||||
assert_equals(typeof readableStream.get, 'function', 'readable should have a getter');
|
||||
assert_equals(readableStream.set, undefined, 'readable should not have a setter');
|
||||
assert_true(readableStream.configurable, 'readable should be configurable');
|
||||
assert_true(ts.readable instanceof ReadableStream, 'readable is an instance of ReadableStream');
|
||||
assert_not_equals(ReadableStream.prototype.getReader.call(ts.readable), undefined,
|
||||
'readable should pass ReadableStream brand check');
|
||||
}, 'TransformStream instances must have writable and readable properties of the correct types');
|
||||
|
||||
test(() => {
|
||||
const ts = new TransformStream({ transform() { } });
|
||||
|
||||
@ -44,7 +19,6 @@ test(() => {
|
||||
assert_equals(writer.desiredSize, 1, 'writer.desiredSize should be 1');
|
||||
}, 'TransformStream writable starts in the writable state');
|
||||
|
||||
|
||||
promise_test(() => {
|
||||
const ts = new TransformStream();
|
||||
|
||||
|
@ -1,119 +1,6 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
// The purpose of this file is to test for objects, attributes and arguments that should not exist.
|
||||
// The test cases are generated from data tables to reduce duplication.
|
||||
|
||||
// Courtesy of André Bargull. Source is https://esdiscuss.org/topic/isconstructor#content-11.
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new new Proxy(o, { construct: () => ({}) })();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
test(() => {
|
||||
assert_equals(self['TransformStreamDefaultController'], undefined,
|
||||
`TransformStreamDefaultController should not be defined`);
|
||||
}, `TransformStreamDefaultController should not be exported on the global object`);
|
||||
|
||||
// Now get hold of the symbol so we can test its properties.
|
||||
self.TransformStreamDefaultController = (() => {
|
||||
let controller;
|
||||
new TransformStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller.constructor;
|
||||
})();
|
||||
|
||||
const expected = {
|
||||
TransformStream: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 0
|
||||
},
|
||||
readable: {
|
||||
type: 'getter'
|
||||
},
|
||||
writable: {
|
||||
type: 'getter'
|
||||
}
|
||||
},
|
||||
TransformStreamDefaultController: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 0
|
||||
},
|
||||
desiredSize: {
|
||||
type: 'getter'
|
||||
},
|
||||
enqueue: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
error: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
terminate: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const c in expected) {
|
||||
const properties = expected[c];
|
||||
const prototype = self[c].prototype;
|
||||
for (const name in properties) {
|
||||
const fullName = `${c}.prototype.${name}`;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
||||
test(() => {
|
||||
const { configurable, enumerable } = descriptor;
|
||||
assert_true(configurable, `${name} should be configurable`);
|
||||
assert_false(enumerable, `${name} should not be enumerable`);
|
||||
}, `${fullName} should have standard properties`);
|
||||
const type = properties[name].type;
|
||||
switch (type) {
|
||||
case 'getter':
|
||||
test(() => {
|
||||
const { writable, get, set } = descriptor;
|
||||
assert_equals(writable, undefined, `${name} should not be a data descriptor`);
|
||||
assert_equals(typeof get, 'function', `${name} should have a getter`);
|
||||
assert_equals(set, undefined, `${name} should not have a setter`);
|
||||
}, `${fullName} should be a getter`);
|
||||
break;
|
||||
|
||||
case 'constructor':
|
||||
case 'method':
|
||||
test(() => {
|
||||
assert_true(descriptor.writable, `${name} should be writable`);
|
||||
assert_equals(typeof prototype[name], 'function', `${name} should be a function`);
|
||||
assert_equals(prototype[name].length, properties[name].length,
|
||||
`${name} should take ${properties[name].length} arguments`);
|
||||
if (type === 'constructor') {
|
||||
assert_true(IsConstructor(prototype[name]), `${name} should be a constructor`);
|
||||
assert_equals(prototype[name].name, c, `${name}.name should be '${c}'`);
|
||||
} else {
|
||||
assert_false(IsConstructor(prototype[name]), `${name} should not be a constructor`);
|
||||
assert_equals(prototype[name].name, name, `${name}.name should be '${name}`);
|
||||
}
|
||||
}, `${fullName} should be a ${type}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
test(() => {
|
||||
const expectedPropertyNames = Object.keys(properties).sort();
|
||||
const actualPropertyNames = Object.getOwnPropertyNames(prototype).sort();
|
||||
assert_array_equals(actualPropertyNames, expectedPropertyNames,
|
||||
`${c} properties should match expected properties`);
|
||||
}, `${c}.prototype should have exactly the expected properties`);
|
||||
}
|
||||
|
||||
const transformerMethods = {
|
||||
start: {
|
||||
length: 1,
|
||||
@ -159,31 +46,4 @@ for (const method in transformerMethods) {
|
||||
assert_true(methodWasCalled, `${method} should be called`);
|
||||
});
|
||||
}, `transformer method ${method} should be called even when it's located on the prototype chain`);
|
||||
|
||||
promise_test(t => {
|
||||
const unreachedTraps = ['getPrototypeOf', 'setPrototypeOf', 'isExtensible', 'preventExtensions',
|
||||
'getOwnPropertyDescriptor', 'defineProperty', 'has', 'set', 'deleteProperty', 'ownKeys',
|
||||
'apply', 'construct'];
|
||||
const touchedProperties = [];
|
||||
const handler = {
|
||||
get: t.step_func((target, property) => {
|
||||
touchedProperties.push(property);
|
||||
if (property === 'readableType' || property === 'writableType') {
|
||||
return undefined;
|
||||
}
|
||||
return () => Promise.resolve();
|
||||
})
|
||||
};
|
||||
for (const trap of unreachedTraps) {
|
||||
handler[trap] = t.unreached_func(`${trap} should not be trapped`);
|
||||
}
|
||||
const transformer = new Proxy({}, handler);
|
||||
const ts = new TransformStream(transformer, undefined, { highWaterMark: Infinity });
|
||||
assert_array_equals(touchedProperties, ['writableType', 'readableType', 'transform', 'flush', 'start'],
|
||||
'expected properties should be got');
|
||||
return trigger(ts).then(() => {
|
||||
assert_array_equals(touchedProperties, ['writableType', 'readableType', 'transform', 'flush', 'start'],
|
||||
'no properties should be accessed on method call');
|
||||
});
|
||||
}, `unexpected properties should not be accessed when calling transformer method ${method}`);
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ promise_test(t => {
|
||||
write() {
|
||||
return flushAsyncEvents();
|
||||
}
|
||||
}, new CountQueuingStrategy(4));
|
||||
}, new CountQueuingStrategy({ highWaterMark: 4 }));
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const settlementOrder = [];
|
||||
@ -382,7 +382,7 @@ promise_test(t => {
|
||||
write() {
|
||||
return Promise.reject(error1);
|
||||
}
|
||||
}, new CountQueuingStrategy(4));
|
||||
}, new CountQueuingStrategy({ highWaterMark: 4 }));
|
||||
const writer = ws.getWriter();
|
||||
return writer.ready.then(() => {
|
||||
const settlementOrder = [];
|
||||
|
@ -102,12 +102,8 @@ promise_test(t => {
|
||||
|
||||
promise_test(t => {
|
||||
|
||||
const startPromise = Promise.resolve();
|
||||
let rejectSinkWritePromise;
|
||||
const ws = recordingWritableStream({
|
||||
start() {
|
||||
return startPromise;
|
||||
},
|
||||
write() {
|
||||
return new Promise((r, reject) => {
|
||||
rejectSinkWritePromise = reject;
|
||||
@ -115,7 +111,7 @@ promise_test(t => {
|
||||
}
|
||||
});
|
||||
|
||||
return startPromise.then(() => {
|
||||
return flushAsyncEvents().then(() => {
|
||||
const writer = ws.getWriter();
|
||||
const writePromise = writer.write('a');
|
||||
rejectSinkWritePromise(error1);
|
||||
|
@ -1,120 +0,0 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/test-utils.js
|
||||
'use strict';
|
||||
|
||||
const WritableStreamDefaultWriter = new WritableStream().getWriter().constructor;
|
||||
const WriterProto = WritableStreamDefaultWriter.prototype;
|
||||
const WritableStreamDefaultController = getWritableStreamDefaultControllerConstructor();
|
||||
|
||||
function getWritableStreamDefaultControllerConstructor() {
|
||||
return realWSDefaultController().constructor;
|
||||
}
|
||||
|
||||
function fakeWS() {
|
||||
return Object.setPrototypeOf({
|
||||
get locked() { return false; },
|
||||
abort() { return Promise.resolve(); },
|
||||
close() { return Promise.resolve(); },
|
||||
getWriter() { return fakeWSDefaultWriter(); }
|
||||
}, WritableStream.prototype);
|
||||
}
|
||||
|
||||
function realWS() {
|
||||
return new WritableStream();
|
||||
}
|
||||
|
||||
function fakeWSDefaultWriter() {
|
||||
return Object.setPrototypeOf({
|
||||
get closed() { return Promise.resolve(); },
|
||||
get desiredSize() { return 1; },
|
||||
get ready() { return Promise.resolve(); },
|
||||
abort() { return Promise.resolve(); },
|
||||
close() { return Promise.resolve(); },
|
||||
write() { return Promise.resolve(); }
|
||||
}, WritableStreamDefaultWriter.prototype);
|
||||
}
|
||||
|
||||
function realWSDefaultWriter() {
|
||||
const ws = new WritableStream();
|
||||
return ws.getWriter();
|
||||
}
|
||||
|
||||
function fakeWSDefaultController() {
|
||||
return Object.setPrototypeOf({
|
||||
error() { return Promise.resolve(); }
|
||||
}, WritableStreamDefaultController.prototype);
|
||||
}
|
||||
|
||||
function realWSDefaultController() {
|
||||
let controller;
|
||||
new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
test(() => {
|
||||
getterThrowsForAll(WritableStream.prototype, 'locked',
|
||||
[fakeWS(), realWSDefaultWriter(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStream.prototype.locked enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return methodRejectsForAll(t, WritableStream.prototype, 'abort',
|
||||
[fakeWS(), realWSDefaultWriter(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStream.prototype.abort enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return methodRejectsForAll(t, WritableStream.prototype, 'close',
|
||||
[fakeWS(), realWSDefaultWriter(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStream.prototype.close enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(WritableStream.prototype, 'getWriter',
|
||||
[fakeWS(), realWSDefaultWriter(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStream.prototype.getWriter enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WritableStreamDefaultWriter(fakeWS()), 'constructor should throw');
|
||||
}, 'WritableStreamDefaultWriter constructor enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
getterThrowsForAll(WriterProto, 'desiredSize',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.desiredSize enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return getterRejectsForAll(t, WriterProto, 'closed',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.closed enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return getterRejectsForAll(t, WriterProto, 'ready',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.ready enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return methodRejectsForAll(t, WriterProto, 'abort',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.abort enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return methodRejectsForAll(t, WriterProto, 'write',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.write enforces a brand check');
|
||||
|
||||
promise_test(t => {
|
||||
return methodRejectsForAll(t, WriterProto, 'close',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.close enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(WriterProto, 'releaseLock',
|
||||
[fakeWSDefaultWriter(), realWS(), realWSDefaultController(), undefined, null]);
|
||||
}, 'WritableStreamDefaultWriter.prototype.releaseLock enforces a brand check');
|
||||
|
||||
test(() => {
|
||||
methodThrowsForAll(WritableStreamDefaultController.prototype, 'error',
|
||||
[fakeWSDefaultController(), realWS(), realWSDefaultWriter(), undefined, null]);
|
||||
}, 'WritableStreamDefaultController.prototype.error enforces a brand check');
|
@ -1,10 +1,12 @@
|
||||
// META: global=window,worker,jsshell
|
||||
// META: script=../resources/constructor-ordering.js
|
||||
'use strict';
|
||||
|
||||
const error1 = new Error('error1');
|
||||
error1.name = 'error1';
|
||||
|
||||
const error2 = new Error('error2');
|
||||
error2.name = 'error2';
|
||||
|
||||
promise_test(() => {
|
||||
let controller;
|
||||
const ws = new WritableStream({
|
||||
@ -85,6 +87,15 @@ test(() => {
|
||||
new WritableStream();
|
||||
}, 'WritableStream should be constructible with no arguments');
|
||||
|
||||
test(() => {
|
||||
const underlyingSink = { get start() { throw error1; } };
|
||||
const queuingStrategy = { highWaterMark: 0, get size() { throw error2; } };
|
||||
|
||||
// underlyingSink is converted in prose in the method body, whereas queuingStrategy is done at the IDL layer.
|
||||
// So the queuingStrategy exception should be encountered first.
|
||||
assert_throws_exactly(error2, () => new WritableStream(underlyingSink, queuingStrategy));
|
||||
}, 'underlyingSink argument should be converted after queuingStrategy argument');
|
||||
|
||||
test(() => {
|
||||
const ws = new WritableStream({});
|
||||
|
||||
@ -102,11 +113,6 @@ test(() => {
|
||||
assert_equals(typeof writer.closed.then, 'function', 'closed property should be thenable');
|
||||
}, 'WritableStream instances should have standard methods and properties');
|
||||
|
||||
test(() => {
|
||||
['WritableStreamDefaultWriter', 'WritableStreamDefaultController'].forEach(c =>
|
||||
assert_equals(typeof self[c], 'undefined', `${c} should not be exported`));
|
||||
}, 'private constructors should not be exported');
|
||||
|
||||
test(() => {
|
||||
let WritableStreamDefaultController;
|
||||
new WritableStream({
|
||||
@ -147,39 +153,3 @@ test(() => {
|
||||
assert_throws_js(TypeError, () => new WritableStreamDefaultWriter(stream),
|
||||
'constructor should throw a TypeError exception');
|
||||
}, 'WritableStreamDefaultWriter constructor should throw when stream argument is locked');
|
||||
|
||||
const operations = [
|
||||
op('get', 'size'),
|
||||
op('get', 'highWaterMark'),
|
||||
op('get', 'type'),
|
||||
op('validate', 'type'),
|
||||
op('validate', 'size'),
|
||||
op('tonumber', 'highWaterMark'),
|
||||
op('validate', 'highWaterMark'),
|
||||
op('get', 'write'),
|
||||
op('validate', 'write'),
|
||||
op('get', 'close'),
|
||||
op('validate', 'close'),
|
||||
op('get', 'abort'),
|
||||
op('validate', 'abort'),
|
||||
op('get', 'start'),
|
||||
op('validate', 'start')
|
||||
];
|
||||
|
||||
for (const failureOp of operations) {
|
||||
test(() => {
|
||||
const record = new OpRecorder(failureOp);
|
||||
const underlyingSink = createRecordingObjectWithProperties(record, ['type', 'start', 'write', 'close', 'abort']);
|
||||
const strategy = createRecordingStrategy(record);
|
||||
|
||||
try {
|
||||
new WritableStream(underlyingSink, strategy);
|
||||
assert_unreached('constructor should throw');
|
||||
} catch (e) {
|
||||
assert_equals(typeof e, 'object', 'e should be an object');
|
||||
}
|
||||
|
||||
assert_equals(record.actual(), expectedAsString(operations, failureOp),
|
||||
'operations should be performed in the right order');
|
||||
}, `WritableStream constructor should stop after ${failureOp} fails`);
|
||||
}
|
||||
|
@ -1,150 +1,6 @@
|
||||
// META: global=window,worker,jsshell
|
||||
'use strict';
|
||||
|
||||
// The purpose of this file is to test for objects, attributes and arguments that should not exist.
|
||||
// The test cases are generated from data tables to reduce duplication.
|
||||
|
||||
// Courtesy of André Bargull. Source is https://esdiscuss.org/topic/isconstructor#content-11.
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new new Proxy(o, { construct: () => ({}) })();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const func of ['WritableStreamDefaultController', 'WritableStreamDefaultWriter']) {
|
||||
test(() => {
|
||||
assert_equals(self[func], undefined, `${func} should not be defined`);
|
||||
}, `${func} should not be exported on the global object`);
|
||||
}
|
||||
|
||||
// Now get hold of the symbols so we can test their properties.
|
||||
self.WritableStreamDefaultController = (() => {
|
||||
let controller;
|
||||
new WritableStream({
|
||||
start(c) {
|
||||
controller = c;
|
||||
}
|
||||
});
|
||||
return controller.constructor;
|
||||
})();
|
||||
self.WritableStreamDefaultWriter = new WritableStream().getWriter().constructor;
|
||||
|
||||
const expected = {
|
||||
WritableStream: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 0
|
||||
},
|
||||
locked: {
|
||||
type: 'getter'
|
||||
},
|
||||
abort: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
close: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
},
|
||||
getWriter: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
}
|
||||
},
|
||||
WritableStreamDefaultController: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 0
|
||||
},
|
||||
error: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
}
|
||||
},
|
||||
WritableStreamDefaultWriter: {
|
||||
constructor: {
|
||||
type: 'constructor',
|
||||
length: 1
|
||||
},
|
||||
closed: {
|
||||
type: 'getter'
|
||||
},
|
||||
desiredSize: {
|
||||
type: 'getter'
|
||||
},
|
||||
ready: {
|
||||
type: 'getter'
|
||||
},
|
||||
abort: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
},
|
||||
close: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
},
|
||||
releaseLock: {
|
||||
type: 'method',
|
||||
length: 0
|
||||
},
|
||||
write: {
|
||||
type: 'method',
|
||||
length: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const c in expected) {
|
||||
const properties = expected[c];
|
||||
const prototype = self[c].prototype;
|
||||
for (const name in properties) {
|
||||
const fullName = `${c}.prototype.${name}`;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
||||
test(() => {
|
||||
const { configurable, enumerable } = descriptor;
|
||||
assert_true(configurable, `${name} should be configurable`);
|
||||
assert_false(enumerable, `${name} should not be enumerable`);
|
||||
}, `${fullName} should have standard properties`);
|
||||
const type = properties[name].type;
|
||||
switch (type) {
|
||||
case 'getter':
|
||||
test(() => {
|
||||
const { writable, get, set } = descriptor;
|
||||
assert_equals(writable, undefined, `${name} should not be a data descriptor`);
|
||||
assert_equals(typeof get, 'function', `${name} should have a getter`);
|
||||
assert_equals(set, undefined, `${name} should not have a setter`);
|
||||
}, `${fullName} should be a getter`);
|
||||
break;
|
||||
|
||||
case 'constructor':
|
||||
case 'method':
|
||||
test(() => {
|
||||
assert_true(descriptor.writable, `${name} should be writable`);
|
||||
assert_equals(typeof prototype[name], 'function', `${name} should be a function`);
|
||||
assert_equals(prototype[name].length, properties[name].length,
|
||||
`${name} should take ${properties[name].length} arguments`);
|
||||
if (type === 'constructor') {
|
||||
assert_true(IsConstructor(prototype[name]), `${name} should be a constructor`);
|
||||
assert_equals(prototype[name].name, c, `${name}.name should be '${c}'`);
|
||||
} else {
|
||||
assert_false(IsConstructor(prototype[name]), `${name} should not be a constructor`);
|
||||
assert_equals(prototype[name].name, name, `${name}.name should be '${name}`);
|
||||
}
|
||||
}, `${fullName} should be a ${type}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
test(() => {
|
||||
const expectedPropertyNames = Object.keys(properties).sort();
|
||||
const actualPropertyNames = Object.getOwnPropertyNames(prototype).sort();
|
||||
assert_array_equals(actualPropertyNames, expectedPropertyNames,
|
||||
`${c} properties should match expected properties`);
|
||||
}, `${c}.prototype should have exactly the expected properties`);
|
||||
}
|
||||
|
||||
const sinkMethods = {
|
||||
start: {
|
||||
length: 1,
|
||||
@ -194,31 +50,4 @@ for (const method in sinkMethods) {
|
||||
assert_true(methodWasCalled, `${method} should be called`);
|
||||
});
|
||||
}, `sink method ${method} should be called even when it's located on the prototype chain`);
|
||||
|
||||
promise_test(t => {
|
||||
const unreachedTraps = ['getPrototypeOf', 'setPrototypeOf', 'isExtensible', 'preventExtensions',
|
||||
'getOwnPropertyDescriptor', 'defineProperty', 'has', 'set', 'deleteProperty', 'ownKeys',
|
||||
'apply', 'construct'];
|
||||
const touchedProperties = [];
|
||||
const handler = {
|
||||
get: t.step_func((target, property) => {
|
||||
touchedProperties.push(property);
|
||||
if (property === 'type') {
|
||||
return undefined;
|
||||
}
|
||||
return () => Promise.resolve();
|
||||
})
|
||||
};
|
||||
for (const trap of unreachedTraps) {
|
||||
handler[trap] = t.unreached_func(`${trap} should not be trapped`);
|
||||
}
|
||||
const sink = new Proxy({}, handler);
|
||||
const ws = new WritableStream(sink);
|
||||
assert_array_equals(touchedProperties, ['type', 'write', 'close', 'abort', 'start'],
|
||||
'expected properties should be got');
|
||||
return trigger(ws.getWriter()).then(() => {
|
||||
assert_array_equals(touchedProperties, ['type', 'write', 'close', 'abort', 'start'],
|
||||
'no properties should be accessed on method call');
|
||||
});
|
||||
}, `unexpected properties should not be accessed when calling sink method ${method}`);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user