diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCIceTransport.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCIceTransport.html | 282 |
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> |