summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCIceTransport.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCIceTransport.html')
-rw-r--r--testing/web-platform/tests/webrtc/RTCIceTransport.html282
1 files changed, 280 insertions, 2 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCIceTransport.html b/testing/web-platform/tests/webrtc/RTCIceTransport.html
index fe12c384e5..e80418bdc4 100644
--- a/testing/web-platform/tests/webrtc/RTCIceTransport.html
+++ b/testing/web-platform/tests/webrtc/RTCIceTransport.html
@@ -1,9 +1,11 @@
<!doctype html>
<meta charset=utf-8>
+<meta name="timeout" content="long">
<title>RTCIceTransport</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
+<script src='RTCConfiguration-helper.js'></script>
<script>
'use strict';
@@ -58,7 +60,7 @@
assert_true(dtlsTransport instanceof RTCDtlsTransport,
'Expect sctp.transport to be an RTCDtlsTransport');
- const iceTransport = dtlsTransport.iceTransport;
+ const {iceTransport} = dtlsTransport;
assert_true(iceTransport instanceof RTCIceTransport,
'Expect dtlsTransport.transport to be an RTCIceTransport');
@@ -162,7 +164,7 @@
assert_equals(iceTransport2.role, 'controlled',
`Expect answerer's iceTransport to take the controlled role`);
});
- }, 'Two connected iceTransports should has matching local/remote candidates returned');
+ }, 'Two connected iceTransports should have matching local/remote candidates returned');
promise_test(t => {
const pc1 = new RTCPeerConnection();
@@ -190,4 +192,280 @@
});
}, 'Unconnected iceTransport should have empty remote candidates and selected pair');
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const transceiver = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ const {iceTransport} = transceiver.sender.transport;
+ assert_equals(iceTransport.state, 'new');
+ assert_equals(iceTransport.gatheringState, 'new');
+ }, 'RTCIceTransport should be in state "new" initially');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const transceiver = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ const {iceTransport} = transceiver.sender.transport;
+ assert_equals(await nextGatheringState(iceTransport), 'gathering');
+ assert_equals(await nextGatheringState(iceTransport), 'complete');
+ }, 'RTCIceTransport should transition to "gathering" then "complete", after sLD');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const transceiver = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ const {iceTransport} = transceiver.sender.transport;
+ assert_equals(await nextGatheringState(iceTransport), 'gathering');
+ pc1.close();
+ assert_equals(iceTransport.gatheringState, 'gathering');
+ const result = await Promise.race([
+ gatheringStateReached(iceTransport, 'complete'),
+ new Promise(r => t.step_timeout(r, 1000))]);
+ assert_equals(result, undefined, `Did not expect a statechange after PC.close(), but got one. state is "${result}"`);
+ }, 'PC.close() should not cause the RTCIceTransport gathering state to transition to "complete"');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection({bundlePolicy: 'max-bundle'});
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ pc1.createDataChannel('test');
+ // TODO: If the spec settles on exposing the sctp transport in
+ // have-local-offer, we won't need this audio transceiver hack.
+ // See https://github.com/w3c/webrtc-pc/issues/2898 and
+ // https://github.com/w3c/webrtc-pc/issues/2899
+ const transceiver = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ const {iceTransport} = transceiver.sender.transport;
+ assert_equals(await nextGatheringState(iceTransport), 'gathering');
+ assert_equals(await nextGatheringState(iceTransport), 'complete');
+ // TODO: Maybe, maybe not.
+ assert_not_equals(pc1.sctp, null, 'pc1.sctp should be set after sLD');
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ assert_equals(pc1.sctp.transport.iceTransport, transceiver.sender.transport.iceTransport);
+ }, 'RTCIceTransport should transition to "gathering", then "complete" after sLD (DataChannel case)');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const {sender} = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ // Copy the SDP before it has candidate attrs
+ const offer = pc1.localDescription;
+ const checkingReached = connectionStateReached(sender.transport.iceTransport, 'checking');
+
+ let result = await Promise.race([checkingReached, new Promise(r => t.step_timeout(r, 1000))]);
+ assert_equals(result, undefined, `Did not expect a statechange right after sLD(offer), but got one. state is "${result}"`);
+
+ await pc2.setRemoteDescription(offer);
+
+ const firstPc2CandidatePromise =
+ new Promise(r => pc2.onicecandidate = e => r(e.candidate));
+
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ result = await Promise.race([checkingReached, new Promise(r => t.step_timeout(r, 1000))]);
+ assert_equals(result, undefined, `Did not expect a statechange callback after sRD(answer), but got one. state is "${result}"`);
+
+ const candidate = await firstPc2CandidatePromise;
+ pc1.addIceCandidate(candidate);
+
+ await checkingReached;
+ }, 'RTCIceTransport should not transition to "checking" until after the answer is set _and_ the first remote candidate is received');
+
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ const {sender} = pc1.addTransceiver('audio');
+ exchangeIceCandidates(pc1, pc2);
+ const gatheringDone = Promise.all([gatheringStateReached(pc1, 'complete'), gatheringStateReached(pc2, 'complete')]);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ assert_equals(await nextConnectionState(sender.transport.iceTransport), 'checking');
+ assert_equals(await nextConnectionState(sender.transport.iceTransport), 'connected');
+ await gatheringDone;
+ pc2.close();
+ await connectionStateReached(sender.transport.iceTransport, 'disconnected');
+ }, 'RTCIceTransport should transition to "disconnected" if packets stop flowing');
+
+ 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');
+ exchangeIceCandidates(pc1, pc2);
+ const gatheringDone = Promise.all([gatheringStateReached(pc1, 'complete'), gatheringStateReached(pc2, 'complete')]);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const {sctp} = pc1;
+ assert_equals(await nextConnectionState(sctp.transport.iceTransport), 'checking');
+ assert_equals(await nextConnectionState(sctp.transport.iceTransport), 'connected');
+ await gatheringDone;
+ pc2.close();
+ await connectionStateReached(sctp.transport.iceTransport, 'disconnected');
+ }, 'RTCIceTransport should transition to "disconnected" if packets stop flowing (DataChannel case)');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ const {sender} = pc1.addTransceiver('audio');
+ await pc1.setLocalDescription();
+ const {iceTransport} = sender.transport;
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ pc1.restartIce();
+
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ assert_equals(sender.transport.iceTransport, iceTransport, 'ICE restart should not result in a different ICE transport');
+ }, 'Local ICE restart should not result in a different ICE transport');
+
+ 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 pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const {iceTransport} = pc1.sctp.transport;
+
+ pc1.restartIce();
+
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ assert_equals(pc1.sctp.transport.iceTransport, iceTransport, 'ICE restart should not result in a different ICE transport');
+ }, 'Local ICE restart should not result in a different ICE transport (DataChannel case)');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ const {sender} = pc1.addTransceiver('audio');
+
+ await pc1.setLocalDescription();
+ const {iceTransport} = sender.transport;
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ pc2.restartIce();
+
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc1.localDescription);
+
+ assert_equals(sender.transport.iceTransport, iceTransport, 'ICE restart should not result in a different ICE transport');
+ }, 'Remote ICE restart should not result in a different ICE transport');
+
+ 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 pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const {iceTransport} = pc1.sctp.transport;
+
+ pc2.restartIce();
+
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc1.localDescription);
+
+ assert_equals(pc1.sctp.transport.iceTransport, iceTransport, 'ICE restart should not result in a different ICE transport');
+ }, 'Remote ICE restart should not result in a different ICE transport (DataChannel case)');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ // Add two transceivers, one audio and one video. The default bundlePolicy
+ // ("balanced") will result in each being offered with its own transport,
+ // but allowing the answerer to bundle the second transceiver on the
+ // transport of the first, which the answerer will do by default.
+ const audioTransceiver = pc1.addTransceiver('audio');
+ const videoTransceiver = pc1.addTransceiver('video');
+ pc1.createDataChannel('test');
+
+ await pc1.setLocalDescription();
+ const audioIceTransport = audioTransceiver.sender.transport.iceTransport;
+ const videoIceTransport = videoTransceiver.sender.transport.iceTransport;
+
+ assert_not_equals(audioIceTransport, videoIceTransport, 'audio and video should start out with different transports');
+
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const sctpIceTransport = pc1.sctp.transport.iceTransport;
+
+ assert_equals(videoTransceiver.sender.transport.iceTransport, audioIceTransport, 'After negotiation, the video sender should use the bundle ICE transport from the audio sender');
+ assert_equals(pc1.sctp.transport.iceTransport, audioIceTransport, 'After negotiation, the datachannel should use the bundle ICE transport from the audio sender');
+ assert_not_equals(videoIceTransport.state, 'closed', 'Completion of offer/answer should not close the unused ICE transport immediately');
+
+ await connectionStateReached(videoIceTransport, 'closed');
+ }, 'RTCIceTransport should transition to "closed" if the underlying transport is closed because the answer used bundle');
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ const {sender} = pc1.addTransceiver('audio');
+ exchangeIceCandidates(pc1, pc2);
+ const gatheringDone = Promise.all([gatheringStateReached(pc1, 'complete'), gatheringStateReached(pc2, 'complete')]);
+ await pc1.setLocalDescription();
+ const {iceTransport} = sender.transport;
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ assert_equals(await nextConnectionState(iceTransport), 'checking');
+ assert_equals(await nextConnectionState(iceTransport), 'connected');
+ await gatheringDone;
+
+ const closedEvent = connectionStateReached(iceTransport, 'closed');
+ pc1.close();
+ assert_equals(sender.transport.iceTransport, iceTransport, 'PC.close() should not unset the sender transport');
+ assert_equals(iceTransport.state, 'closed', 'pc.close() should close the sender transport synchronously');
+ const result = await Promise.race([closedEvent, new Promise(r => t.step_timeout(r, 1000))]);
+ assert_equals(result, undefined, 'statechange event should not fire when transitioning to closed due to PC.close()');
+ }, 'RTCIceTransport should synchronously transition to "closed" with no event if the underlying transport is closed due to PC.close()');
+
</script>