Skip to content

Commit 0d3e62f

Browse files
Add tests related to ReadableStream of type 'owning' (#39520)
* Add tests related to ReadableStream of type 'owning' --------- Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com>
1 parent 9e699dc commit 0d3e62f

File tree

4 files changed

+269
-1
lines changed

4 files changed

+269
-1
lines changed

interfaces/streams.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle
5656
callback UnderlyingSourcePullCallback = Promise<undefined> (ReadableStreamController controller);
5757
callback UnderlyingSourceCancelCallback = Promise<undefined> (optional any reason);
5858

59-
enum ReadableStreamType { "bytes" };
59+
enum ReadableStreamType { "bytes", "owning" };
6060

6161
interface mixin ReadableStreamGenericReader {
6262
readonly attribute Promise<undefined> closed;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// META: global=window,worker
2+
// META: script=../resources/test-utils.js
3+
// META: script=../resources/rs-utils.js
4+
'use strict';
5+
6+
promise_test(async () => {
7+
const channel = new MessageChannel;
8+
const port1 = channel.port1;
9+
const port2 = channel.port2;
10+
11+
const source = {
12+
start(controller) {
13+
controller.enqueue(port1, { transfer : [ port1 ] });
14+
},
15+
type: 'owning'
16+
};
17+
18+
const stream = new ReadableStream(source);
19+
20+
const chunk = await stream.getReader().read();
21+
22+
assert_not_equals(chunk.value, port1);
23+
24+
let promise = new Promise(resolve => port2.onmessage = e => resolve(e.data));
25+
chunk.value.postMessage("toPort2");
26+
assert_equals(await promise, "toPort2");
27+
28+
promise = new Promise(resolve => chunk.value.onmessage = e => resolve(e.data));
29+
port2.postMessage("toPort1");
30+
assert_equals(await promise, "toPort1");
31+
}, 'Transferred MessageChannel works as expected');
32+
33+
promise_test(async t => {
34+
const channel = new MessageChannel;
35+
const port1 = channel.port1;
36+
const port2 = channel.port2;
37+
38+
const source = {
39+
start(controller) {
40+
controller.enqueue({ port1 }, { transfer : [ port1 ] });
41+
},
42+
type: 'owning'
43+
};
44+
45+
const stream = new ReadableStream(source);
46+
const [clone1, clone2] = stream.tee();
47+
48+
await promise_rejects_dom(t, "DataCloneError", clone2.getReader().read());
49+
}, 'Second branch of owning ReadableStream tee should end up into errors with transfer only values');
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// META: global=window,worker
2+
// META: script=../resources/test-utils.js
3+
// META: script=../resources/rs-utils.js
4+
'use strict';
5+
6+
function createVideoFrame()
7+
{
8+
let init = {
9+
format: 'I420',
10+
timestamp: 1234,
11+
codedWidth: 4,
12+
codedHeight: 2
13+
};
14+
let data = new Uint8Array([
15+
1, 2, 3, 4, 5, 6, 7, 8, // y
16+
1, 2, // u
17+
1, 2, // v
18+
]);
19+
20+
return new VideoFrame(data, init);
21+
}
22+
23+
promise_test(async () => {
24+
const videoFrame = createVideoFrame();
25+
videoFrame.test = 1;
26+
const source = {
27+
start(controller) {
28+
assert_equals(videoFrame.format, 'I420');
29+
controller.enqueue(videoFrame, { transfer : [ videoFrame ] });
30+
assert_equals(videoFrame.format, null);
31+
assert_equals(videoFrame.test, 1);
32+
},
33+
type: 'owning'
34+
};
35+
36+
const stream = new ReadableStream(source);
37+
// Cancelling the stream should close all video frames, thus no console messages of GCing VideoFrames should happen.
38+
stream.cancel();
39+
}, 'ReadableStream of type owning should close serialized chunks');
40+
41+
promise_test(async () => {
42+
const videoFrame = createVideoFrame();
43+
videoFrame.test = 1;
44+
const source = {
45+
start(controller) {
46+
assert_equals(videoFrame.format, 'I420');
47+
controller.enqueue({ videoFrame }, { transfer : [ videoFrame ] });
48+
assert_equals(videoFrame.format, null);
49+
assert_equals(videoFrame.test, 1);
50+
},
51+
type: 'owning'
52+
};
53+
54+
const stream = new ReadableStream(source);
55+
const reader = stream.getReader();
56+
57+
const chunk = await reader.read();
58+
assert_equals(chunk.value.videoFrame.format, 'I420');
59+
assert_equals(chunk.value.videoFrame.test, undefined);
60+
61+
chunk.value.videoFrame.close();
62+
}, 'ReadableStream of type owning should transfer JS chunks with transferred values');
63+
64+
promise_test(async t => {
65+
const videoFrame = createVideoFrame();
66+
videoFrame.close();
67+
const source = {
68+
start(controller) {
69+
assert_throws_dom("DataCloneError", () => controller.enqueue(videoFrame, { transfer : [ videoFrame ] }));
70+
},
71+
type: 'owning'
72+
};
73+
74+
const stream = new ReadableStream(source);
75+
const reader = stream.getReader();
76+
77+
await promise_rejects_dom(t, "DataCloneError", reader.read());
78+
}, 'ReadableStream of type owning should error when trying to enqueue not serializable values');
79+
80+
promise_test(async () => {
81+
const videoFrame = createVideoFrame();
82+
const source = {
83+
start(controller) {
84+
controller.enqueue(videoFrame, { transfer : [ videoFrame ] });
85+
},
86+
type: 'owning'
87+
};
88+
89+
const stream = new ReadableStream(source);
90+
const [clone1, clone2] = stream.tee();
91+
92+
const chunk1 = await clone1.getReader().read();
93+
const chunk2 = await clone2.getReader().read();
94+
95+
assert_equals(videoFrame.format, null);
96+
assert_equals(chunk1.value.format, 'I420');
97+
assert_equals(chunk2.value.format, 'I420');
98+
99+
chunk1.value.close();
100+
chunk2.value.close();
101+
}, 'ReadableStream of type owning should clone serializable objects when teeing');
102+
103+
promise_test(async () => {
104+
const videoFrame = createVideoFrame();
105+
videoFrame.test = 1;
106+
const source = {
107+
start(controller) {
108+
assert_equals(videoFrame.format, 'I420');
109+
controller.enqueue({ videoFrame }, { transfer : [ videoFrame ] });
110+
assert_equals(videoFrame.format, null);
111+
assert_equals(videoFrame.test, 1);
112+
},
113+
type: 'owning'
114+
};
115+
116+
const stream = new ReadableStream(source);
117+
const [clone1, clone2] = stream.tee();
118+
119+
const chunk1 = await clone1.getReader().read();
120+
const chunk2 = await clone2.getReader().read();
121+
122+
assert_equals(videoFrame.format, null);
123+
assert_equals(chunk1.value.videoFrame.format, 'I420');
124+
assert_equals(chunk2.value.videoFrame.format, 'I420');
125+
126+
chunk1.value.videoFrame.close();
127+
chunk2.value.videoFrame.close();
128+
}, 'ReadableStream of type owning should clone JS Objects with serializables when teeing');
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// META: global=window,worker
2+
// META: script=../resources/test-utils.js
3+
// META: script=../resources/rs-utils.js
4+
'use strict';
5+
6+
test(() => {
7+
new ReadableStream({ type: 'owning' }); // ReadableStream constructed with 'owning' type
8+
}, 'ReadableStream can be constructed with owning type');
9+
10+
test(() => {
11+
let startCalled = false;
12+
13+
const source = {
14+
start(controller) {
15+
assert_equals(this, source, 'source is this during start');
16+
assert_true(controller instanceof ReadableStreamDefaultController, 'default controller');
17+
startCalled = true;
18+
},
19+
type: 'owning'
20+
};
21+
22+
new ReadableStream(source);
23+
assert_true(startCalled);
24+
}, 'ReadableStream of type owning should call start with a ReadableStreamDefaultController');
25+
26+
test(() => {
27+
let startCalled = false;
28+
29+
const source = {
30+
start(controller) {
31+
controller.enqueue("a", { transfer: [] });
32+
controller.enqueue("a", { transfer: undefined });
33+
startCalled = true;
34+
},
35+
type: 'owning'
36+
};
37+
38+
new ReadableStream(source);
39+
assert_true(startCalled);
40+
}, 'ReadableStream should be able to call enqueue with an empty transfer list');
41+
42+
test(() => {
43+
let startCalled = false;
44+
45+
const uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
46+
const buffer = uint8Array.buffer;
47+
let source = {
48+
start(controller) {
49+
startCalled = true;
50+
assert_throws_js(TypeError, () => { controller.enqueue(buffer, { transfer : [ buffer ] }); }, "transfer list is not empty");
51+
}
52+
};
53+
54+
new ReadableStream(source);
55+
assert_true(startCalled);
56+
57+
startCalled = false;
58+
source = {
59+
start(controller) {
60+
startCalled = true;
61+
assert_throws_js(TypeError, () => { controller.enqueue(buffer, { get transfer() { throw new TypeError(); } }) }, "getter throws");
62+
}
63+
};
64+
65+
new ReadableStream(source);
66+
assert_true(startCalled);
67+
}, 'ReadableStream should check transfer parameter');
68+
69+
promise_test(async () => {
70+
const uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
71+
const buffer = uint8Array.buffer;
72+
buffer.test = 1;
73+
const source = {
74+
start(controller) {
75+
assert_equals(buffer.byteLength, 8);
76+
controller.enqueue(buffer, { transfer : [ buffer ] });
77+
assert_equals(buffer.byteLength, 0);
78+
assert_equals(buffer.test, 1);
79+
},
80+
type: 'owning'
81+
};
82+
83+
const stream = new ReadableStream(source);
84+
const reader = stream.getReader();
85+
86+
const chunk = await reader.read();
87+
88+
assert_not_equals(chunk.value, buffer);
89+
assert_equals(chunk.value.byteLength, 8);
90+
assert_equals(chunk.value.test, undefined);
91+
}, 'ReadableStream of type owning should transfer enqueued chunks');

0 commit comments

Comments
 (0)