Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests related to ReadableStream of type 'owning' #39520

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion interfaces/streams.idl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle
callback UnderlyingSourcePullCallback = Promise<undefined> (ReadableStreamController controller);
callback UnderlyingSourceCancelCallback = Promise<undefined> (optional any reason);

enum ReadableStreamType { "bytes" };
enum ReadableStreamType { "bytes", "owning" };

interface mixin ReadableStreamGenericReader {
readonly attribute Promise<undefined> closed;
Expand Down
49 changes: 49 additions & 0 deletions streams/readable-streams/owning-type-message-port.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// META: global=window,worker
// META: script=../resources/test-utils.js
// META: script=../resources/rs-utils.js
'use strict';

promise_test(async () => {
const channel = new MessageChannel;
const port1 = channel.port1;
const port2 = channel.port2;

const source = {
start(controller) {
controller.enqueue(port1, { transfer : [ port1 ] });
},
type: 'owning'
};

const stream = new ReadableStream(source);

const chunk = await stream.getReader().read();

assert_not_equals(chunk.value, port1);

let promise = new Promise(resolve => port2.onmessage = e => resolve(e.data));
chunk.value.postMessage("toPort2");
assert_equals(await promise, "toPort2");

promise = new Promise(resolve => chunk.value.onmessage = e => resolve(e.data));
port2.postMessage("toPort1");
assert_equals(await promise, "toPort1");
}, 'Transferred MessageChannel works as expected');

promise_test(async t => {
const channel = new MessageChannel;
const port1 = channel.port1;
const port2 = channel.port2;

const source = {
start(controller) {
controller.enqueue({ port1 }, { transfer : [ port1 ] });
},
type: 'owning'
};

const stream = new ReadableStream(source);
const [clone1, clone2] = stream.tee();

await promise_rejects_dom(t, "DataCloneError", clone2.getReader().read());
}, 'Second branch of owning ReadableStream tee should end up into errors with transfer only values');
128 changes: 128 additions & 0 deletions streams/readable-streams/owning-type-video-frame.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// META: global=window,worker
// META: script=../resources/test-utils.js
// META: script=../resources/rs-utils.js
'use strict';

function createVideoFrame()
{
let init = {
format: 'I420',
timestamp: 1234,
codedWidth: 4,
codedHeight: 2
};
let data = new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8, // y
1, 2, // u
1, 2, // v
]);

return new VideoFrame(data, init);
}

promise_test(async () => {
const videoFrame = createVideoFrame();
videoFrame.test = 1;
const source = {
start(controller) {
assert_equals(videoFrame.format, 'I420');
controller.enqueue(videoFrame, { transfer : [ videoFrame ] });
assert_equals(videoFrame.format, null);
assert_equals(videoFrame.test, 1);
},
type: 'owning'
};

const stream = new ReadableStream(source);
// Cancelling the stream should close all video frames, thus no console messages of GCing VideoFrames should happen.
stream.cancel();
}, 'ReadableStream of type owning should close serialized chunks');

promise_test(async () => {
const videoFrame = createVideoFrame();
videoFrame.test = 1;
const source = {
start(controller) {
assert_equals(videoFrame.format, 'I420');
controller.enqueue({ videoFrame }, { transfer : [ videoFrame ] });
assert_equals(videoFrame.format, null);
assert_equals(videoFrame.test, 1);
},
type: 'owning'
};

const stream = new ReadableStream(source);
const reader = stream.getReader();

const chunk = await reader.read();
assert_equals(chunk.value.videoFrame.format, 'I420');
assert_equals(chunk.value.videoFrame.test, undefined);

chunk.value.videoFrame.close();
}, 'ReadableStream of type owning should transfer JS chunks with transferred values');

promise_test(async t => {
const videoFrame = createVideoFrame();
videoFrame.close();
const source = {
start(controller) {
assert_throws_dom("DataCloneError", () => controller.enqueue(videoFrame, { transfer : [ videoFrame ] }));
},
type: 'owning'
};

const stream = new ReadableStream(source);
const reader = stream.getReader();

await promise_rejects_dom(t, "DataCloneError", reader.read());
}, 'ReadableStream of type owning should error when trying to enqueue not serializable values');

promise_test(async () => {
const videoFrame = createVideoFrame();
const source = {
start(controller) {
controller.enqueue(videoFrame, { transfer : [ videoFrame ] });
},
type: 'owning'
};

const stream = new ReadableStream(source);
const [clone1, clone2] = stream.tee();

const chunk1 = await clone1.getReader().read();
const chunk2 = await clone2.getReader().read();

assert_equals(videoFrame.format, null);
assert_equals(chunk1.value.format, 'I420');
assert_equals(chunk2.value.format, 'I420');

chunk1.value.close();
chunk2.value.close();
}, 'ReadableStream of type owning should clone serializable objects when teeing');

promise_test(async () => {
const videoFrame = createVideoFrame();
videoFrame.test = 1;
const source = {
start(controller) {
assert_equals(videoFrame.format, 'I420');
controller.enqueue({ videoFrame }, { transfer : [ videoFrame ] });
assert_equals(videoFrame.format, null);
assert_equals(videoFrame.test, 1);
},
type: 'owning'
};

const stream = new ReadableStream(source);
const [clone1, clone2] = stream.tee();

const chunk1 = await clone1.getReader().read();
const chunk2 = await clone2.getReader().read();

assert_equals(videoFrame.format, null);
assert_equals(chunk1.value.videoFrame.format, 'I420');
assert_equals(chunk2.value.videoFrame.format, 'I420');

chunk1.value.videoFrame.close();
chunk2.value.videoFrame.close();
}, 'ReadableStream of type owning should clone JS Objects with serializables when teeing');
91 changes: 91 additions & 0 deletions streams/readable-streams/owning-type.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// META: global=window,worker
// META: script=../resources/test-utils.js
// META: script=../resources/rs-utils.js
'use strict';

test(() => {
new ReadableStream({ type: 'owning' }); // ReadableStream constructed with 'owning' type
}, 'ReadableStream can be constructed with owning type');

test(() => {
let startCalled = false;

const source = {
start(controller) {
assert_equals(this, source, 'source is this during start');
assert_true(controller instanceof ReadableStreamDefaultController, 'default controller');
startCalled = true;
},
type: 'owning'
};

new ReadableStream(source);
assert_true(startCalled);
}, 'ReadableStream of type owning should call start with a ReadableStreamDefaultController');

test(() => {
let startCalled = false;

const source = {
start(controller) {
controller.enqueue("a", { transfer: [] });
controller.enqueue("a", { transfer: undefined });
startCalled = true;
},
type: 'owning'
};

new ReadableStream(source);
assert_true(startCalled);
}, 'ReadableStream should be able to call enqueue with an empty transfer list');

test(() => {
let startCalled = false;

const uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const buffer = uint8Array.buffer;
let source = {
start(controller) {
startCalled = true;
assert_throws_js(TypeError, () => { controller.enqueue(buffer, { transfer : [ buffer ] }); }, "transfer list is not empty");
}
};

new ReadableStream(source);
assert_true(startCalled);

startCalled = false;
source = {
start(controller) {
startCalled = true;
assert_throws_js(TypeError, () => { controller.enqueue(buffer, { get transfer() { throw new TypeError(); } }) }, "getter throws");
}
};

new ReadableStream(source);
assert_true(startCalled);
}, 'ReadableStream should check transfer parameter');

promise_test(async () => {
const uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const buffer = uint8Array.buffer;
buffer.test = 1;
const source = {
start(controller) {
assert_equals(buffer.byteLength, 8);
controller.enqueue(buffer, { transfer : [ buffer ] });
assert_equals(buffer.byteLength, 0);
assert_equals(buffer.test, 1);
},
type: 'owning'
};

const stream = new ReadableStream(source);
const reader = stream.getReader();

const chunk = await reader.read();

assert_not_equals(chunk.value, buffer);
assert_equals(chunk.value.byteLength, 8);
assert_equals(chunk.value.test, undefined);
}, 'ReadableStream of type owning should transfer enqueued chunks');