blob: d7bed6da0144347b3f749173b427c0c4f88e05ba [file] [log] [blame]
<!doctype html>
<meta charset=utf-8>
<title>RTCQuicStream.https.html</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCIceTransport-extension-helper.js"></script>
<script src="RTCQuicTransport-helper.js"></script>
<script src="RTCQuicStream-helper.js"></script>
<script>
'use strict';
// These tests are based on the following specification:
// https://w3c.github.io/webrtc-quic/
// The following helper functions are called from RTCQuicTransport-helper.js:
// makeStandaloneQuicTransport
// makeTwoConnectedQuicTransports
// The following helper functions are called from RTCQuicStream-helper.js:
// closed_stream_test
promise_test(async t => {
const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
const quicStream = quicTransport.createStream();
assert_equals(quicStream.transport, quicTransport,
'Expect transport to be set to the creating RTCQuicTransport.');
assert_equals(quicStream.state, 'open', `Expect state to be 'open'.`);
assert_equals(quicStream.readBufferedAmount, 0,
'Expect read buffered amount to be 0.');
assert_equals(quicStream.writeBufferedAmount, 0,
'Expect write buffered amount to be 0.');
assert_greater_than(quicStream.maxWriteBufferedAmount, 0,
'Expect max write buffered amount to be greater than 0.');
}, 'createStream() returns an RTCQuicStream with initial properties set.');
promise_test(async t => {
const quicTransport = await makeStandaloneQuicTransport(t);
assert_throws('InvalidStateError', () => quicTransport.createStream());
}, 'createStream() throws if the transport is not connected.');
promise_test(async t => {
const quicTransport = await makeStandaloneQuicTransport(t);
quicTransport.stop();
assert_throws('InvalidStateError', () => quicTransport.createStream());
}, 'createStream() throws if the transport is closed.');
promise_test(async t => {
const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
const firstQuicStream = quicTransport.createStream();
const secondQuicStream = quicTransport.createStream();
quicTransport.stop();
assert_equals(firstQuicStream.state, 'closed');
assert_equals(secondQuicStream.state, 'closed');
}, 'RTCQuicTransport.stop() closes all local streams.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
localQuicTransport.createStream().finish();
localQuicTransport.createStream().finish();
const remoteWatcher =
new EventWatcher(t, remoteQuicTransport, [ 'quicstream', 'statechange' ]);
const { stream: firstRemoteStream } =
await remoteWatcher.wait_for('quicstream');
const { stream: secondRemoteStream } =
await remoteWatcher.wait_for('quicstream');
localQuicTransport.stop();
await remoteWatcher.wait_for('statechange');
assert_equals(firstRemoteStream.state, 'closed');
assert_equals(secondRemoteStream.state, 'closed');
}, 'RTCQuicTransport.stop() closes all remote streams.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.finish();
assert_equals(localStream.state, 'closing');
}, `finish() changes state to 'closing'.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.finish();
localStream.finish();
assert_equals(localStream.state, 'closing');
}, `finish() twice does not change state.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.reset();
assert_equals(localStream.state, 'closed');
}, `reset() changes state to 'closed'.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.finish();
localStream.reset();
assert_equals(localStream.state, 'closed');
}, `reset() following finish() changes state to 'closed'.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.reset();
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
assert_equals(remoteStream.state, 'open');
const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
await remoteStreamWatcher.wait_for('statechange');
assert_equals(remoteStream.state, 'closed');
}, 'createStream() followed by reset() fires a quicstream event followed ' +
`by a statechange event to 'closed' on the remote side.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
const promise = localStream.waitForReadable(1);
localStream.reset();
await promise_rejects(t, 'InvalidStateError', promise);
}, 'reset() rejects pending waitForReadable() promises.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(0));
assert_equals(localStream.writeBufferedAmount, 0);
}, 'write() with an empty array does nothing.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array([65]));
assert_equals(localStream.writeBufferedAmount, 1);
localStream.write(new Uint8Array([66, 67]));
assert_equals(localStream.writeBufferedAmount, 3);
localStream.write(new Uint8Array([68, 69, 70]));
assert_equals(localStream.writeBufferedAmount, 6);
}, 'write() adds to writeBufferedAmount each call.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(localStream.maxWriteBufferedAmount));
assert_equals(localStream.writeBufferedAmount,
localStream.maxWriteBufferedAmount);
}, 'write() can write exactly maxWriteBufferedAmount.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
assert_throws('OperationError',
() =>
localStream.write(
new Uint8Array(localStream.maxWriteBufferedAmount + 1)));
assert_equals(localStream.writeBufferedAmount, 0);
}, 'write() throws if data longer than maxWriteBufferedAmount.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(10));
assert_throws('OperationError',
() =>
localStream.write(
new Uint8Array(localStream.maxWriteBufferedAmount)));
assert_equals(localStream.writeBufferedAmount, 10);
}, 'write() throws if total write buffered amount would be greater than ' +
'maxWriteBufferedAmount.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(10));
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
await remoteWatcher.wait_for('quicstream');
}, 'write() causes quicstream event to fire on the remote transport.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.finish();
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
await remoteWatcher.wait_for('quicstream');
}, 'finish() causes quicstream event to fire on the remote transport.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.finish();
assert_throws('InvalidStateError',
() => localStream.write(new Uint8Array()));
}, 'write() throws InvalidStateError if finish() has been called.');
closed_stream_test(async (t, stream) => {
assert_throws('InvalidStateError', () => stream.write(new Uint8Array()));
}, 'write() throws InvalidStateError.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(10));
localStream.reset();
assert_equals(localStream.writeBufferedAmount, 0);
}, 'writeBufferedAmount set to 0 after local reset().');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(10));
localQuicTransport.stop();
assert_equals(localStream.writeBufferedAmount, 0);
}, 'writeBufferedAmount set to 0 after local RTCQuicTransport stop().');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(10));
localStream.finish();
assert_equals(localStream.writeBufferedAmount, 10);
}, 'writeBufferedAmount maintained after finish() has been called.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
await localStream.waitForWriteBufferedAmountBelow(0);
}, 'waitForWriteBufferedAmountBelow(0) resolves immediately.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
await localStream.waitForWriteBufferedAmountBelow(
localStream.maxWriteBufferedAmount);
}, 'waitForWriteBufferedAmountBelow(maxWriteBufferedAmount) resolves ' +
'immediately.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(localStream.maxWriteBufferedAmount));
const promise1 = localStream.waitForWriteBufferedAmountBelow(0);
const promise2 = localStream.waitForWriteBufferedAmountBelow(0);
localStream.finish();
await Promise.all([
promise_rejects(t, 'InvalidStateError', promise1),
promise_rejects(t, 'InvalidStateError', promise2)]);
}, 'Pending waitForWriteBufferedAmountBelow() promises rejected after ' +
'finish().');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(localStream.maxWriteBufferedAmount));
const promise1 = localStream.waitForWriteBufferedAmountBelow(0);
const promise2 = localStream.waitForWriteBufferedAmountBelow(0);
localStream.reset();
await Promise.all([
promise_rejects(t, 'InvalidStateError', promise1),
promise_rejects(t, 'InvalidStateError', promise2)]);
}, 'Pending waitForWriteBufferedAmountBelow() promises rejected after ' +
'reset().');
closed_stream_test(async (t, stream) => {
await promise_rejects(t, 'InvalidStateError',
stream.waitForWriteBufferedAmountBelow(0));
}, 'waitForWriteBufferedBelow() rejects with InvalidStateError.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
assert_object_equals(
localStream.readInto(new Uint8Array(10)),
{ amount: 0, finished: false });
}, 'readInto() on new local stream returns amount 0.');
closed_stream_test(async (t, stream) => {
assert_throws('InvalidStateError', () => stream.readInto(new Uint8Array(1)));
}, 'readInto() throws InvalidStateError.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array([ 65 ]));
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
await remoteStream.waitForReadable(1);
assert_equals(remoteStream.readBufferedAmount, 1);
const readBuffer = new Uint8Array(3);
assert_object_equals(
remoteStream.readInto(readBuffer),
{ amount: 1, finished: false });
assert_array_equals(readBuffer, [ 65, 0, 0 ]);
assert_equals(remoteStream.readBufferedAmount, 0);
}, 'Read 1 byte.');
// Returns a Uint8Array of length |amount| with generated data.
function generateData(amount) {
const data = new Uint8Array(amount);
for (let i = 0; i < data.length; i++) {
data[i] = i % 256;
}
return data;
}
// Writes |amount| of bytes to the given RTCQuicStream in maxWriteBufferedAmount
// chunks.
async function writeGeneratedData(stream, amount) {
const data = generateData(Math.min(stream.maxWriteBufferedAmount, amount));
while (amount > 0) {
const chunkSize = Math.min(stream.maxWriteBufferedAmount, amount);
await stream.waitForWriteBufferedAmountBelow(0);
stream.write(data.subarray(0, chunkSize));
amount -= chunkSize;
}
}
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
writeGeneratedData(localStream, localStream.maxReadBufferedAmount);
const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
await remoteStream.waitForReadable(localStream.maxReadBufferedAmount);
const readBuffer = new Uint8Array(localStream.maxReadBufferedAmount);
assert_object_equals(
remoteStream.readInto(readBuffer),
{ amount: localStream.maxReadBufferedAmount, finished: false });
assert_array_equals(
readBuffer, generateData(localStream.maxReadBufferedAmount));
}, 'Read maxReadBufferedAmount bytes all at once.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
const writeData = generateData(10);
localStream.write(writeData);
localStream.finish();
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
await remoteStream.waitForReadable(11);
assert_equals(remoteStream.readBufferedAmount, 10);
const readBuffer = new Uint8Array(10);
assert_object_equals(
remoteStream.readInto(readBuffer), { amount: 10, finished: true });
assert_array_equals(readBuffer, writeData);
}, 'waitForReadable() resolves early if remote fin is received.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
await promise_rejects(t, new TypeError(),
localStream.waitForReadable(localStream.maxReadBufferedAmount + 1));
}, 'waitForReadable() rejects with TypeError if amount is more than ' +
'maxReadBufferedAmount.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
const promise1 = localStream.waitForReadable(10);
const promise2 = localStream.waitForReadable(10);
localStream.reset();
await Promise.all([
promise_rejects(t, 'InvalidStateError', promise1),
promise_rejects(t, 'InvalidStateError', promise2)]);
}, 'Pending waitForReadable() promises rejected after reset().');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const localStream = localQuicTransport.createStream();
localStream.write(new Uint8Array(1));
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
const { stream : remoteStream} = await remoteWatcher.wait_for('quicstream');
const promise1 = remoteStream.waitForReadable(10);
const promise2 = remoteStream.waitForReadable(10);
localStream.reset();
await Promise.all([
promise_rejects(t, 'InvalidStateError', promise1),
promise_rejects(t, 'InvalidStateError', promise2)]);
}, 'Pending waitForReadable() promises rejected after remote reset().');
closed_stream_test(async (t, stream) => {
await promise_rejects(t, 'InvalidStateError',
stream.waitForReadable(1));
}, 'waitForReadable() rejects with InvalidStateError.');
</script>