mirror of
https://github.com/servo/servo.git
synced 2025-06-29 03:23:41 +01:00
399 lines
16 KiB
HTML
399 lines
16 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCQuicTransport.https.html</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="../webrtc/RTCIceTransport-extension-helper.js"></script>
|
|
<script src="../webrtc/RTCPeerConnection-helper.js"></script>
|
|
<script src="RTCQuicTransport-helper.js"></script>
|
|
<script src="../webrtc/dictionary-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
|
|
// RTCIceTransport-extension-helper.js:
|
|
// makeIceTransport
|
|
// makeAndGatherTwoIceTransports
|
|
|
|
// The following helper functions are called from RTCQuicTransport-helper.js:
|
|
// makeQuicTransport
|
|
// makeStandaloneQuicTransport
|
|
// makeAndStartTwoQuicTransports
|
|
// makeTwoConnectedQuicTransports
|
|
// sleep
|
|
|
|
test(t => {
|
|
const iceTransport = makeIceTransport(t);
|
|
const quicTransport = makeQuicTransport(t, iceTransport);
|
|
assert_equals(quicTransport.transport, iceTransport,
|
|
'Expect transport to be the same as the one passed in the constructor.');
|
|
assert_equals(quicTransport.state, 'new', `Expect state to be 'new'.`);
|
|
}, 'RTCQuicTransport initial properties are set.');
|
|
|
|
test(t => {
|
|
const iceTransport = makeIceTransport(t);
|
|
iceTransport.stop();
|
|
assert_throws('InvalidStateError',
|
|
() => makeQuicTransport(t, iceTransport));
|
|
}, 'RTCQuicTransport constructor throws if passed a closed RTCIceTransport.');
|
|
|
|
test(t => {
|
|
const iceTransport = makeIceTransport(t);
|
|
const firstQuicTransport =
|
|
makeQuicTransport(t, iceTransport);
|
|
assert_throws('InvalidStateError',
|
|
() => makeQuicTransport(t, iceTransport));
|
|
}, 'RTCQuicTransport constructor throws if passed an RTCIceTransport that ' +
|
|
'already has an active RTCQuicTransport.');
|
|
|
|
promise_test(async t => {
|
|
const pc1 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc1.close());
|
|
const pc2 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc2.close());
|
|
|
|
pc1.createDataChannel('test');
|
|
await doSignalingHandshake(pc1, pc2);
|
|
const iceTransport = pc1.sctp.transport.iceTransport;
|
|
|
|
assert_throws('InvalidStateError',
|
|
() => makeQuicTransport(t, iceTransport));
|
|
}, 'RTCQuicTransport constructor throws if passed an RTCIceTransport that ' +
|
|
'came from an RTCPeerConnection.');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.stop();
|
|
assert_equals(quicTransport.state, 'closed');
|
|
}, `stop() changes state to 'closed'.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.transport.stop();
|
|
assert_equals(quicTransport.state, 'closed');
|
|
}, `RTCIceTransport.stop() changes RTCQuicTransport.state to 'closed'.`);
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
makeAndStartTwoQuicTransports(t);
|
|
const localWatcher = new EventWatcher(t, localQuicTransport, 'statechange');
|
|
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
|
|
await Promise.all([
|
|
localWatcher.wait_for('statechange').then(() => {
|
|
assert_equals(localQuicTransport.state, 'connected');
|
|
}),
|
|
remoteWatcher.wait_for('statechange').then(() => {
|
|
assert_equals(remoteQuicTransport.state, 'connected');
|
|
}),
|
|
]);
|
|
}, 'Two RTCQuicTransports connect to each other.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
localQuicTransport.stop();
|
|
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
|
|
await remoteWatcher.wait_for('statechange');
|
|
assert_equals(remoteQuicTransport.state, 'closed');
|
|
}, `stop() fires a statechange event to 'closed' on the remote transport`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.connect();
|
|
assert_equals(quicTransport.state, 'connecting');
|
|
}, `connect() changes state to 'connecting'.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.connect();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.connect());
|
|
}, 'connect() throws if already called connect().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.listen(new Uint8Array([12345]));
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.connect());
|
|
}, 'connect() throws if already called listen().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.stop();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.connect());
|
|
}, 'connect() throws after stop().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.transport.stop();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.connect());
|
|
}, 'connect() throws if called after RTCIceTransport has stopped.');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.listen(new Uint8Array([12345]));
|
|
assert_equals(quicTransport.state, 'connecting');
|
|
}, `listen() changes state to 'connecting'.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.connect();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.listen(new Uint8Array([12345])));
|
|
}, 'listen() throws if already called connect().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.listen(new Uint8Array([12345]));
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.listen(new Uint8Array([12345])));
|
|
}, 'listen() throws if already called listen().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.stop();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.listen(new Uint8Array([12345])));
|
|
}, 'listen() throws after stop().');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.transport.stop();
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.listen(new Uint8Array([12345])));
|
|
}, 'listen() throws if called after RTCIceTransport has stopped.');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const key = quicTransport.getKey();
|
|
assert_equals(key.byteLength, 16);
|
|
}, 'RTCQuicTransport.getKey() attribute is 16 bytes.');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const key = new Uint8Array();
|
|
assert_throws('NotSupportedError',
|
|
() => quicTransport.listen(key));
|
|
}, 'listen() throws if given an empty key.');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const key = quicTransport.getKey();
|
|
let update_key = new Uint8Array(key);
|
|
for (let i = 0; i < update_key.length; i++) {
|
|
update_key[i] = 0;
|
|
}
|
|
const new_key = quicTransport.getKey();
|
|
assert_not_equals(update_key, new Uint8Array(new_key));
|
|
}, 'Cannot mutate key retrieved from getKey().');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
makeAndStartTwoQuicTransports(t);
|
|
const stats = await localQuicTransport.getStats();
|
|
assert_number_field(stats, 'timestamp');
|
|
assert_unsigned_int_field(stats, 'bytesSent');
|
|
assert_unsigned_int_field(stats, 'packetsSent');
|
|
assert_unsigned_int_field(stats, 'streamBytesSent');
|
|
assert_unsigned_int_field(stats, 'streamBytesReceived');
|
|
assert_unsigned_int_field(stats, 'numOutgoingStreamsCreated');
|
|
assert_unsigned_int_field(stats, 'numIncomingStreamsCreated');
|
|
assert_unsigned_int_field(stats, 'bytesReceived');
|
|
assert_unsigned_int_field(stats, 'packetsReceived');
|
|
assert_unsigned_int_field(stats, 'packetsProcessed');
|
|
assert_unsigned_int_field(stats, 'bytesRetransmitted');
|
|
assert_unsigned_int_field(stats, 'packetsRetransmitted');
|
|
assert_unsigned_int_field(stats, 'packetsLost');
|
|
assert_unsigned_int_field(stats, 'packetsDropped');
|
|
assert_unsigned_int_field(stats, 'cryptoRetransmitCount');
|
|
assert_unsigned_int_field(stats, 'minRttUs');
|
|
assert_unsigned_int_field(stats, 'smoothedRttUs');
|
|
assert_unsigned_int_field(stats, 'maxPacketSize');
|
|
assert_unsigned_int_field(stats, 'maxReceivedPacketSize');
|
|
assert_unsigned_int_field(stats, 'estimatedBandwidthBps');
|
|
assert_unsigned_int_field(stats, 'packetsReordered');
|
|
assert_unsigned_int_field(stats, 'blockedFramesReceived');
|
|
assert_unsigned_int_field(stats, 'blockedFramesSent');
|
|
assert_unsigned_int_field(stats, 'connectivityProbingPacketsReceived');
|
|
}, 'Stats returned by getStats() are present.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const localStream = localQuicTransport.createStream();
|
|
localStream.write({ finish: true });
|
|
const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
|
|
await remoteWatcher.wait_for('quicstream');
|
|
const localStats = await localQuicTransport.getStats();
|
|
const remoteStats = await remoteQuicTransport.getStats();
|
|
assert_equals(localStats.numOutgoingStreamsCreated, 1);
|
|
assert_equals(localStats.numIncomingStreamsCreated, 0);
|
|
assert_equals(remoteStats.numOutgoingStreamsCreated, 0);
|
|
assert_equals(remoteStats.numIncomingStreamsCreated, 1);
|
|
}, 'getStats() returns proper stream counts after creating streams.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
makeAndStartTwoQuicTransports(t);
|
|
const stats1 = await localQuicTransport.getStats();
|
|
await new Promise(resolve => t.step_timeout(resolve, 20));
|
|
const stats2 = await localQuicTransport.getStats();
|
|
assert_greater_than(stats2.timestamp, stats1.timestamp);
|
|
}, 'Two separate stats returned by getStats() give different timestamps.');
|
|
|
|
promise_test(async t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const promise = quicTransport.getStats();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'getStats() promises immediately rejected with InvalidStateError ' +
|
|
`if called before 'connecting'.`);
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const promise = localQuicTransport.getStats();
|
|
localQuicTransport.stop();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'getStats() promises rejected with InvalidStateError if stop() ' +
|
|
'is called before being fulfilled.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const promise = localQuicTransport.getStats();
|
|
localQuicTransport.transport.stop();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'getStats() promises rejected with InvalidStateError if ' +
|
|
'RTCIceTransport calls stop() before being fulfilled.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
localQuicTransport.transport.stop();
|
|
const promise = localQuicTransport.getStats();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'getStats() promises immediately rejected if called after ' +
|
|
`'closed' state.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.sendDatagram(new Uint8Array([1])));
|
|
}, `sendDatagram() throws InvalidStateError if called before 'connected'.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
quicTransport.stop();
|
|
assert_equals(quicTransport.state, 'closed');
|
|
assert_throws('InvalidStateError',
|
|
() => quicTransport.sendDatagram(new Uint8Array([1])));
|
|
}, `sendDatagram() throws InvalidStateError if called when 'closed'.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
assert_equals(quicTransport.maxDatagramLength, null);
|
|
}, 'maxDatagramLength 0 before connected.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
assert_greater_than(localQuicTransport.maxDatagramLength, 0);
|
|
}, 'maxDatagramLength larger than 0 after connected.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const bigData = new Uint8Array(localQuicTransport.maxDatagramLength + 1);
|
|
assert_throws('InvalidStateError',
|
|
() => localQuicTransport.sendDatagram(bigData));
|
|
}, 'sendDatagram() throws InvalidStateError if called with data larger ' +
|
|
'than maxDatagramLength()');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const datagram = new Uint8Array([42]);
|
|
await localQuicTransport.readyToSendDatagram();
|
|
localQuicTransport.sendDatagram(datagram);
|
|
const receiveDatagrams = await remoteQuicTransport.receiveDatagrams();
|
|
assert_equals(receiveDatagrams.length, 1);
|
|
const receiveDatagram = new Uint8Array(receiveDatagrams[0]);
|
|
assert_array_equals(receiveDatagram, datagram);
|
|
}, 'sendDatagram() sends a datagram to remote side');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const datagram = new Uint8Array([42]);
|
|
const datagram2 = new Uint8Array([43]);
|
|
await localQuicTransport.readyToSendDatagram();
|
|
localQuicTransport.sendDatagram(datagram);
|
|
const receiveDatagrams = await remoteQuicTransport.receiveDatagrams();
|
|
assert_equals(receiveDatagrams.length, 1);
|
|
const receiveDatagram = new Uint8Array(receiveDatagrams[0]);
|
|
assert_array_equals(receiveDatagram, datagram);
|
|
await localQuicTransport.readyToSendDatagram();
|
|
localQuicTransport.sendDatagram(datagram2);
|
|
const receiveDatagrams2 = await remoteQuicTransport.receiveDatagrams();
|
|
assert_equals(receiveDatagrams2.length, 1);
|
|
const receiveDatagram2 = new Uint8Array(receiveDatagrams2[0]);
|
|
assert_array_equals(receiveDatagram2, datagram2);
|
|
}, 'sendDatagram() sends a multiple datagrams to remote side');
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const promise = quicTransport.readyToSendDatagram();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'readyToSendDatagram() promise immediately rejected if called before ' +
|
|
'connecting');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
localQuicTransport.stop();
|
|
const promise = localQuicTransport.readyToSendDatagram();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'readyToSendDatagram() promise immediately rejected if called after ' +
|
|
`'closed' state.`);
|
|
|
|
test(t => {
|
|
const quicTransport = makeStandaloneQuicTransport(t);
|
|
const promise = quicTransport.receiveDatagrams();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'receiveDatagrams() promise immediately rejected if called before ' +
|
|
'connecting.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
localQuicTransport.stop();
|
|
const promise = localQuicTransport.receiveDatagrams();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'receiveDatagrams() promise immediately rejected if called after ' +
|
|
`'closed' state.`);
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const promise = localQuicTransport.receiveDatagrams();
|
|
localQuicTransport.stop();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'receiveDatagrams() promise rejected with InvalidStateError if stop() ' +
|
|
'is called before being fulfilled.');
|
|
|
|
promise_test(async t => {
|
|
const [ localQuicTransport, remoteQuicTransport ] =
|
|
await makeTwoConnectedQuicTransports(t);
|
|
const promise = localQuicTransport.receiveDatagrams();
|
|
localQuicTransport.transport.stop();
|
|
promise_rejects(t, 'InvalidStateError', promise);
|
|
}, 'receiveDatagrams() promises rejected with InvalidStateError if ' +
|
|
'RTCIceTransport calls stop() before being fulfilled.');
|
|
|
|
</script>
|
|
|