blob: b96b36fc369f1dbb929ce0f44dcf7ae18ffc8e22 [file] [log] [blame]
// META: global=window,worker
// META: script=/common/get-host-info.sub.js
// META: script=resources/webtransport-test-helpers.sub.js
// Write datagrams until the producer receives the AbortSignal.
async function write_datagrams(writer, signal) {
const encoder = new TextEncoder();
let counter = 0;
const sentTokens = [];
const aborted = new Promise((resolve) => {
signal.addEventListener('abort', resolve);
});
while (true) {
await Promise.race([writer.ready, aborted]);
if (signal.aborted) {
break;
}
var token = counter.toString();
sentTokens.push(token);
writer.write(encoder.encode(token));
counter++;
}
return sentTokens;
}
// Read datagrams until the consumer has received enough i.e. N datagrams. Call
// abort() after reading.
async function read_datagrams(reader, controller, N) {
const decoder = new TextDecoder();
const receivedTokens = [];
while (receivedTokens.length < N) {
const { value: token, done } = await reader.read();
assert_false(done);
receivedTokens.push(decoder.decode(token));
}
controller.abort();
return receivedTokens;
}
// Write numbers until the producer receives the AbortSignal.
async function write_numbers(writer, signal) {
let counter = 0;
const sentNumbers = [];
const aborted =
new Promise((resolve) => signal.addEventListener('abort', resolve));
// Counter should be less than 256 because reader stores numbers in Uint8Array.
while (counter < 256) {
await Promise.race([writer.ready, aborted])
if (signal.aborted) {
break;
}
sentNumbers.push(counter);
chunk = new Uint8Array(1);
chunk[0] = counter;
writer.write(chunk);
counter++;
}
return sentNumbers;
}
// Write large datagrams of size 10 until the producer receives the AbortSignal.
async function write_large_datagrams(writer, signal) {
const aborted = new Promise((resolve) => {
signal.addEventListener('abort', resolve);
});
while (true) {
await Promise.race([writer.ready, aborted]);
if (signal.aborted) {
break;
}
writer.write(new Uint8Array(10));
}
}
// Read datagrams with BYOB reader until the consumer has received enough i.e. N
// datagrams. Call abort() after reading.
async function read_numbers_byob(reader, controller, N) {
let buffer = new ArrayBuffer(N);
buffer = await readInto(reader, buffer);
controller.abort();
return Array.from(new Uint8Array(buffer));
}
promise_test(async t => {
// Establish a WebTransport session.
const wt = new WebTransport(webtransport_url('echo.py'));
await wt.ready;
const writer = wt.datagrams.writable.getWriter();
const reader = wt.datagrams.readable.getReader();
const controller = new AbortController();
const signal = controller.signal;
// Write and read datagrams.
const N = 5;
const [sentTokens, receivedTokens] = await Promise.all([
write_datagrams(writer, signal),
read_datagrams(reader, controller, N)
]);
// Check receivedTokens is a subset of sentTokens.
const subset = receivedTokens.every(token => sentTokens.includes(token));
assert_true(subset);
}, 'Datagrams are echoed successfully');
promise_test(async t => {
// Establish a WebTransport session.
const wt = new WebTransport(webtransport_url('echo.py'));
await wt.ready;
const writer = wt.datagrams.writable.getWriter();
const reader = wt.datagrams.readable.getReader({ mode: 'byob' });
const controller = new AbortController();
const signal = controller.signal;
// Write and read datagrams.
// Numbers are less than 256, consider N to be a small number.
const N = 5;
const [sentNumbers, receiveNumbers] = await Promise.all([
write_numbers(writer, signal),
read_numbers_byob(reader, controller, N)
]);
// No duplicated numbers received.
assert_equals((new Set(receiveNumbers)).size, N);
// Check receiveNumbers is a subset of sentNumbers.
const subset = receiveNumbers.every(token => sentNumbers.includes(token));
assert_true(subset);
}, 'Successfully reading datagrams with BYOB reader.');
promise_test(async t => {
// Establish a WebTransport session.
const wt = new WebTransport(webtransport_url('echo.py'));
await wt.ready;
const writer = wt.datagrams.writable.getWriter();
const reader = wt.datagrams.readable.getReader({ mode: 'byob' });
const controller = new AbortController();
const signal = controller.signal;
// Write datagrams of size 10, but only 1 byte buffer is provided for BYOB
// reader. To avoid splitting a datagram, stream will be errored.
const buffer = new ArrayBuffer(1);
const [error, _] = await Promise.all([
reader.read(new Uint8Array(buffer)).catch(e => {
controller.abort();
return e;
}),
write_large_datagrams(writer, signal)
]);
assert_equals(error.name, 'RangeError');
}, 'Reading datagrams with insufficient buffer should be rejected.');
promise_test(async t => {
// Make a WebTransport connection, but session is not necessarily established.
const wt = new WebTransport(webtransport_url('echo.py'));
const writer = wt.datagrams.writable.getWriter();
const reader = wt.datagrams.readable.getReader();
const controller = new AbortController();
const signal = controller.signal;
// Write and read datagrams.
const N = 1;
const [sentTokens, receivedTokens] = await Promise.all([
write_datagrams(writer, signal),
read_datagrams(reader, controller, N)
]);
// Check receivedTokens is a subset of sentTokens.
const subset = receivedTokens.every(token => sentTokens.includes(token));
assert_true(subset);
// Make sure WebTransport session is established.
await wt.ready;
}, 'Sending and receiving datagrams is ready to use before session is established');
promise_test(async t => {
// Establish a WebTransport session.
const wt = new WebTransport(webtransport_url('echo.py'));
await wt.ready;
const N = 5;
wt.datagrams.outgoingHighWaterMark = N;
const writer = wt.datagrams.writable.getWriter();
const encoder = new TextEncoder();
// Write N-1 datagrams.
let counter;
for (counter = 0; counter < N-1; counter++) {
var datagram = counter.toString();
let resolved = false;
writer.write(encoder.encode(datagram));
// Check writer.ready resolves immediately.
writer.ready.then(() => resolved = true);
// TODO(nidhijaju): The number of `await Promise.resolve()` calls is
// implementation dependent, so we should not have this as the final
// solution.
for (let i = 0; i < 10; i++) {
await Promise.resolve();
}
assert_true(resolved);
}
// Write one more datagram.
resolved = false;
const last_datagram = counter.toString();
writer.write(encoder.encode(last_datagram));
// Check writer.ready does not resolve immediately.
writer.ready.then(() => resolved = true);
for (let i = 0; i < 10; i++) {
await Promise.resolve();
}
assert_false(resolved);
// Make sure writer.ready is resolved eventually.
await writer.ready;
}, 'Datagram\'s outgoingHighWaterMark correctly regulates written datagrams');
promise_test(async t => {
// Establish a WebTransport session.
const wt = new WebTransport(webtransport_url('echo.py'));
await wt.ready;
const N = 5;
wt.datagrams.incomingHighWaterMark = N;
const writer = wt.datagrams.writable.getWriter();
const encoder = new TextEncoder();
// Write 10*N datagrams.
let counter;
for (counter = 0; counter < 10*N; counter++) {
var datagram = counter.toString();
writer.write(encoder.encode(datagram));
await writer.ready;
}
// Wait for incoming datagrams to arrive.
wait(500);
const reader = wt.datagrams.readable.getReader();
// Read all of the immediately available datagrams.
let receivedDatagrams = 0;
while (true) {
let resolved = false;
reader.read().then(() => resolved = true);
// TODO(nidhijaju): Find a better solution instead of just having numerous
// `await Promise.resolve()` calls.
for (let i = 0; i < 10; i++) {
await Promise.resolve();
}
if (!resolved) {
break;
}
receivedDatagrams++;
}
// Check that the receivedDatagrams is less than or equal to the
// incomingHighWaterMark.
assert_less_than_equal(receivedDatagrams, N);
}, 'Datagrams read is less than or equal to the incomingHighWaterMark');