From febeadd6a24782c4fac4a54b291a87b49813c578 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 24 Mar 2023 23:21:18 +0000 Subject: [PATCH] Add more WebTransport web-platform tests to cover more of the API Differential Revision: https://phabricator.services.mozilla.com/D172427 bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1822058 gecko-commit: af3732371d385722defee38e18cb018e1834bec2 gecko-reviewers: jib --- webtransport/datagrams.https.any.js | 103 +++++++++++++++++- .../webtransport-test-helpers.sub.js | 17 +++ webtransport/streams-echo.https.any.js | 62 +++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) diff --git a/webtransport/datagrams.https.any.js b/webtransport/datagrams.https.any.js index b96b36fc369f1db..dc7133bb867e203 100644 --- a/webtransport/datagrams.https.any.js +++ b/webtransport/datagrams.https.any.js @@ -23,6 +23,20 @@ async function write_datagrams(writer, signal) { return sentTokens; } +// Write N datagrams without waiting, then wait for them +async function write_N_datagrams(writer, n) { + const encoder = new TextEncoder(); + const sentTokens = []; + const promises = []; + while (sentTokens.length < n) { + const token = sentTokens.length.toString(); + sentTokens.push(token); + promises.push(writer.write(encoder.encode(token))); + } + await Promise.all(promises); + 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) { @@ -155,6 +169,37 @@ promise_test(async t => { assert_equals(error.name, 'RangeError'); }, 'Reading datagrams with insufficient buffer should be rejected.'); +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(); + + // Write and read max-size datagram. + await writer.write(new Uint8Array(wt.datagrams.maxDatagramSize)); + const { value: token, done } = await reader.read(); + assert_false(done); + assert_equals(token.length, wt.datagrams.maxDatagramSize); +}, 'Transfer max-size datagram'); + +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(); + + // Write and read max-size datagram. + await writer.write(new Uint8Array(wt.datagrams.maxDatagramSize+1)); + // This should resolve with no datagram sent, which is hard to test for. + // Wait for incoming datagrams to arrive, and if they do, fail. + const result = await Promise.race([reader.read(), wait(500)]); + assert_equals(result, undefined); +}, 'Fail to transfer max-size+1 datagram'); + promise_test(async t => { // Make a WebTransport connection, but session is not necessarily established. const wt = new WebTransport(webtransport_url('echo.py')); @@ -166,9 +211,10 @@ promise_test(async t => { const signal = controller.signal; // Write and read datagrams. - const N = 1; + const N = 5; + wt.datagrams.outgoingHighWaterMark = N; const [sentTokens, receivedTokens] = await Promise.all([ - write_datagrams(writer, signal), + write_N_datagrams(writer, N), read_datagrams(reader, controller, N) ]); @@ -269,3 +315,56 @@ promise_test(async t => { // incomingHighWaterMark. assert_less_than_equal(receivedDatagrams, N); }, 'Datagrams read is less than or equal to the incomingHighWaterMark'); + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + await wt.ready; + + assert_equals(wt.datagrams.incomingMaxAge, Infinity); + assert_equals(wt.datagrams.outgoingMaxAge, Infinity); + + wt.datagrams.incomingMaxAge = 5; + assert_equals(wt.datagrams.incomingMaxAge, 5); + wt.datagrams.outgoingMaxAge = 5; + assert_equals(wt.datagrams.outgoingMaxAge, 5); + + assert_throws_js(RangeError, () => { wt.datagrams.incomingMaxAge = -1; }); + assert_throws_js(RangeError, () => { wt.datagrams.outgoingMaxAge = -1; }); + assert_throws_js(RangeError, () => { wt.datagrams.incomingMaxAge = NaN; }); + assert_throws_js(RangeError, () => { wt.datagrams.outgoingMaxAge = NaN; }); + + wt.datagrams.incomingMaxAge = 0; + assert_equals(wt.datagrams.incomingMaxAge, Infinity); + wt.datagrams.outgoingMaxAge = 0; + assert_equals(wt.datagrams.outgoingMaxAge, Infinity); +}, 'Datagram MaxAge getters/setters work correctly'); + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + await wt.ready; + + // Initial values are implementation-defined + assert_greater_than_equal(wt.datagrams.incomingHighWaterMark, 1); + assert_greater_than_equal(wt.datagrams.outgoingHighWaterMark, 1); + + wt.datagrams.incomingHighWaterMark = 5; + assert_equals(wt.datagrams.incomingHighWaterMark, 5); + wt.datagrams.outgoingHighWaterMark = 5; + assert_equals(wt.datagrams.outgoingHighWaterMark, 5); + + assert_throws_js(RangeError, () => { wt.datagrams.incomingHighWaterMark = -1; }); + assert_throws_js(RangeError, () => { wt.datagrams.outgoingHighWaterMark = -1; }); + assert_throws_js(RangeError, () => { wt.datagrams.incomingHighWaterMark = NaN; }); + assert_throws_js(RangeError, () => { wt.datagrams.outgoingHighWaterMark = NaN; }); + + wt.datagrams.incomingHighWaterMark = 0.5; + assert_equals(wt.datagrams.incomingHighWaterMark, 1); + wt.datagrams.outgoingHighWaterMark = 0.5; + assert_equals(wt.datagrams.outgoingHighWaterMark, 1); + wt.datagrams.incomingHighWaterMark = 0; + assert_equals(wt.datagrams.incomingHighWaterMark, 1); + wt.datagrams.outgoingHighWaterMark = 0; + assert_equals(wt.datagrams.outgoingHighWaterMark, 1); +}, 'Datagram HighWaterMark getters/setters work correctly'); diff --git a/webtransport/resources/webtransport-test-helpers.sub.js b/webtransport/resources/webtransport-test-helpers.sub.js index 733153e12027d1e..9f9127b22f96da1 100644 --- a/webtransport/resources/webtransport-test-helpers.sub.js +++ b/webtransport/resources/webtransport-test-helpers.sub.js @@ -20,6 +20,23 @@ function webtransport_code_to_http_code(n) { return first + n + Math.floor(n / 0x1e); } +// Read all chunks from |readable_stream| and return as an array of arrays +async function read_stream(readable_stream) { + const reader = readable_stream.getReader(); + + let chunks = []; + while (true) { + const {value: chunk, done} = await reader.read(); + if (done) { + break; + } + chunks.push(chunk); + } + reader.releaseLock(); + + return chunks; +} + // Read all chunks from |readable_stream|, decode chunks to a utf-8 string, then // return the string. async function read_stream_as_string(readable_stream) { diff --git a/webtransport/streams-echo.https.any.js b/webtransport/streams-echo.https.any.js index 32781419ebf7bd8..18ebe123a417a91 100644 --- a/webtransport/streams-echo.https.any.js +++ b/webtransport/streams-echo.https.any.js @@ -151,3 +151,65 @@ promise_test(async t => { } reader.releaseLock(); }, 'Can read data from a unidirectional stream with BYOB reader'); + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + await wt.ready; + + // Create a bidirectional stream. + const bidi_stream = await wt.createBidirectionalStream(); + + // Write a message to the writable end, and close it. + const writer = bidi_stream.writable.getWriter(); + const bytes = new Uint8Array(16384); + const [reply] = await Promise.all([ + read_stream(bidi_stream.readable), + writer.write(bytes), + writer.write(bytes), + writer.write(bytes), + writer.close() + ]); + let len = 0; + for (chunk of reply) { + len += chunk.length; + } + // Check that the message from the readable end matches the writable end. + assert_equals(len, 3*bytes.length); +}, 'Transfer large chunks of data on a bidirectional stream'); + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + await wt.ready; + + // Create a unidirectional stream. + const uni_stream = await wt.createUnidirectionalStream(); + + // Write a message to the writable end, and close it. + const writer = uni_stream.getWriter(); + const bytes = new Uint8Array(16384); + await Promise.all([ + writer.write(bytes), + writer.write(bytes), + writer.write(bytes), + writer.close() + ]); + // XXX Update once chrome fixes https://crbug.com/929585 + // The echo handler creates a new unidirectional stream to echo back data from + // the server to client. Accept the unidirectional stream. + const readable = wt.incomingUnidirectionalStreams; + const stream_reader = readable.getReader(); + const { value: recv_stream } = await stream_reader.read(); + stream_reader.releaseLock(); + + // Read the data on the readable end. + const reply = await read_stream(recv_stream); + let len = 0; + for (chunk of reply) { + len += chunk.length; + } + // Check that the message from the readable end matches the writable end. + assert_equals(len, 3*bytes.length); +}, 'Transfer large chunks of data on a unidirectional stream'); +