Skip to content

Commit a8872d9

Browse files
lucacasonatoMattiasBuelensdomenic
authored
Streams: tests for Transformer.cancel (#40453)
Co-authored-by: Mattias Buelens <mattias@buelens.com> Co-authored-by: Domenic Denicola <d@domenic.me>
1 parent 3154c3f commit a8872d9

File tree

5 files changed

+182
-14
lines changed

5 files changed

+182
-14
lines changed
+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// META: global=window,worker,shadowrealm
2+
// META: script=../resources/test-utils.js
3+
'use strict';
4+
5+
const thrownError = new Error('bad things are happening!');
6+
thrownError.name = 'error1';
7+
8+
const originalReason = new Error('original reason');
9+
originalReason.name = 'error2';
10+
11+
promise_test(async t => {
12+
let cancelled = undefined;
13+
const ts = new TransformStream({
14+
cancel(reason) {
15+
cancelled = reason;
16+
}
17+
});
18+
const res = await ts.readable.cancel(thrownError);
19+
assert_equals(res, undefined, 'readable.cancel() should return undefined');
20+
assert_equals(cancelled, thrownError, 'transformer.cancel() should be called with the passed reason');
21+
}, 'cancelling the readable side should call transformer.cancel()');
22+
23+
promise_test(async t => {
24+
const ts = new TransformStream({
25+
cancel(reason) {
26+
assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason');
27+
throw thrownError;
28+
}
29+
});
30+
const writer = ts.writable.getWriter();
31+
const cancelPromise = ts.readable.cancel(originalReason);
32+
await promise_rejects_exactly(t, thrownError, cancelPromise, 'readable.cancel() should reject with thrownError');
33+
await promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject with thrownError');
34+
}, 'cancelling the readable side should reject if transformer.cancel() throws');
35+
36+
promise_test(async t => {
37+
let aborted = undefined;
38+
const ts = new TransformStream({
39+
cancel(reason) {
40+
aborted = reason;
41+
},
42+
flush: t.unreached_func('flush should not be called')
43+
});
44+
const res = await ts.writable.abort(thrownError);
45+
assert_equals(res, undefined, 'writable.abort() should return undefined');
46+
assert_equals(aborted, thrownError, 'transformer.abort() should be called with the passed reason');
47+
}, 'aborting the writable side should call transformer.abort()');
48+
49+
promise_test(async t => {
50+
const ts = new TransformStream({
51+
cancel(reason) {
52+
assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason');
53+
throw thrownError;
54+
},
55+
flush: t.unreached_func('flush should not be called')
56+
});
57+
const reader = ts.readable.getReader();
58+
const abortPromise = ts.writable.abort(originalReason);
59+
await promise_rejects_exactly(t, thrownError, abortPromise, 'writable.abort() should reject with thrownError');
60+
await promise_rejects_exactly(t, thrownError, reader.closed, 'reader.closed should reject with thrownError');
61+
}, 'aborting the writable side should reject if transformer.cancel() throws');
62+
63+
promise_test(async t => {
64+
const ts = new TransformStream({
65+
async cancel(reason) {
66+
assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason');
67+
throw thrownError;
68+
},
69+
flush: t.unreached_func('flush should not be called')
70+
});
71+
const cancelPromise = ts.readable.cancel(originalReason);
72+
const closePromise = ts.writable.close();
73+
await Promise.all([
74+
promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError'),
75+
promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'),
76+
]);
77+
}, 'closing the writable side should reject if a parallel transformer.cancel() throws');
78+
79+
promise_test(async t => {
80+
let controller;
81+
const ts = new TransformStream({
82+
start(c) {
83+
controller = c;
84+
},
85+
async cancel(reason) {
86+
assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason');
87+
controller.error(thrownError);
88+
},
89+
flush: t.unreached_func('flush should not be called')
90+
});
91+
const cancelPromise = ts.readable.cancel(originalReason);
92+
const closePromise = ts.writable.close();
93+
await Promise.all([
94+
promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError'),
95+
promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'),
96+
]);
97+
}, 'readable.cancel() and a parallel writable.close() should reject if a transformer.cancel() calls controller.error()');
98+
99+
promise_test(async t => {
100+
let controller;
101+
const ts = new TransformStream({
102+
start(c) {
103+
controller = c;
104+
},
105+
async cancel(reason) {
106+
assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason');
107+
controller.error(thrownError);
108+
},
109+
flush: t.unreached_func('flush should not be called')
110+
});
111+
const cancelPromise = ts.writable.abort(originalReason);
112+
await promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError');
113+
const closePromise = ts.readable.cancel(1);
114+
await promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError');
115+
}, 'writable.abort() and readable.cancel() should reject if a transformer.cancel() calls controller.error()');

streams/transform-streams/errors.any.js

+31-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ promise_test(t => {
99
const ts = new TransformStream({
1010
transform() {
1111
throw thrownError;
12-
}
12+
},
13+
cancel: t.unreached_func('cancel should not be called')
1314
});
1415

1516
const reader = ts.readable.getReader();
@@ -34,7 +35,8 @@ promise_test(t => {
3435
},
3536
flush() {
3637
throw thrownError;
37-
}
38+
},
39+
cancel: t.unreached_func('cancel should not be called')
3840
});
3941

4042
const reader = ts.readable.getReader();
@@ -54,13 +56,14 @@ promise_test(t => {
5456
]);
5557
}, 'TransformStream errors thrown in flush put the writable and readable in an errored state');
5658

57-
test(() => {
59+
test(t => {
5860
new TransformStream({
5961
start(c) {
6062
c.enqueue('a');
6163
c.error(new Error('generic error'));
6264
assert_throws_js(TypeError, () => c.enqueue('b'), 'enqueue() should throw');
63-
}
65+
},
66+
cancel: t.unreached_func('cancel should not be called')
6467
});
6568
}, 'errored TransformStream should not enqueue new chunks');
6669

@@ -72,7 +75,8 @@ promise_test(t => {
7275
});
7376
},
7477
transform: t.unreached_func('transform should not be called'),
75-
flush: t.unreached_func('flush should not be called')
78+
flush: t.unreached_func('flush should not be called'),
79+
cancel: t.unreached_func('cancel should not be called')
7680
});
7781

7882
const writer = ts.writable.getWriter();
@@ -96,7 +100,8 @@ promise_test(t => {
96100
});
97101
},
98102
transform: t.unreached_func('transform should never be called if start() fails'),
99-
flush: t.unreached_func('flush should never be called if start() fails')
103+
flush: t.unreached_func('flush should never be called if start() fails'),
104+
cancel: t.unreached_func('cancel should never be called if start() fails')
100105
});
101106

102107
const writer = ts.writable.getWriter();
@@ -202,9 +207,10 @@ promise_test(t => {
202207
return Promise.all([
203208
abortPromise,
204209
cancelPromise,
205-
promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject with thrownError')]);
206-
}, 'abort should set the close reason for the writable when it happens before cancel during start, but cancel should ' +
207-
'still succeed');
210+
promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject'),
211+
]);
212+
}, 'abort should set the close reason for the writable when it happens before cancel during start, and cancel should ' +
213+
'reject');
208214

209215
promise_test(t => {
210216
let resolveTransform;
@@ -251,13 +257,26 @@ promise_test(t => {
251257
controller = c;
252258
}
253259
});
254-
const cancelPromise = ts.readable.cancel(thrownError);
255-
controller.error(ignoredError);
260+
const cancelPromise = ts.readable.cancel(ignoredError);
261+
controller.error(thrownError);
256262
return Promise.all([
257263
cancelPromise,
258264
promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError')
259265
]);
260-
}, 'controller.error() should do nothing after readable.cancel()');
266+
}, 'controller.error() should close writable immediately after readable.cancel()');
267+
268+
promise_test(t => {
269+
let controller;
270+
const ts = new TransformStream({
271+
start(c) {
272+
controller = c;
273+
}
274+
});
275+
return ts.readable.cancel(thrownError).then(() => {
276+
controller.error(ignoredError);
277+
return promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError');
278+
});
279+
}, 'controller.error() should do nothing after readable.cancel() resolves');
261280

262281
promise_test(t => {
263282
let controller;

streams/transform-streams/flush.any.js

+15
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,18 @@ promise_test(t => {
129129
});
130130
return promise_rejects_exactly(t, error1, ts.writable.getWriter().close(), 'close() should reject');
131131
}, 'error() during flush should cause writer.close() to reject');
132+
133+
promise_test(async t => {
134+
let flushed = false;
135+
const ts = new TransformStream({
136+
flush() {
137+
flushed = true;
138+
},
139+
cancel: t.unreached_func('cancel should not be called')
140+
});
141+
const closePromise = ts.writable.close();
142+
await delay(0);
143+
const cancelPromise = ts.readable.cancel(error1);
144+
await Promise.all([closePromise, cancelPromise]);
145+
assert_equals(flushed, true, 'transformer.flush() should be called');
146+
}, 'closing the writable side should call transformer.flush() and a parallel readable.cancel() should not reject');

streams/transform-streams/general.any.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,24 @@ promise_test(t => {
388388
controller.terminate();
389389
return Promise.all([
390390
cancelPromise,
391-
promise_rejects_exactly(t, cancelReason, ts.writable.getWriter().closed, 'closed should reject with cancelReason')
391+
promise_rejects_js(t, TypeError, ts.writable.getWriter().closed, 'closed should reject with TypeError')
392392
]);
393-
}, 'terminate() should do nothing after readable.cancel()');
393+
}, 'terminate() should abort writable immediately after readable.cancel()');
394+
395+
promise_test(t => {
396+
let controller;
397+
const ts = new TransformStream({
398+
start(c) {
399+
controller = c;
400+
}
401+
});
402+
const cancelReason = { name: 'cancelReason' };
403+
return ts.readable.cancel(cancelReason).then(() => {
404+
controller.terminate();
405+
return promise_rejects_exactly(t, cancelReason, ts.writable.getWriter().closed, 'closed should reject with TypeError');
406+
})
407+
}, 'terminate() should do nothing after readable.cancel() resolves');
408+
394409

395410
promise_test(() => {
396411
let calls = 0;

streams/transform-streams/reentrant-strategies.any.js

+4
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ promise_test(t => {
314314
// call to TransformStreamDefaultSink.
315315
return delay(0).then(() => {
316316
controller.enqueue('a');
317+
return reader.read();
318+
}).then(({ value, done }) => {
319+
assert_false(done, 'done should be false');
320+
assert_equals(value, 'a', 'value should be correct');
317321
return Promise.all([promise_rejects_exactly(t, error1, reader.read(), 'read() should reject'), abortPromise]);
318322
});
319323
}, 'writer.abort() inside size() should work');

0 commit comments

Comments
 (0)