diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html new file mode 100644 index 0000000000..5083be6cdf --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html @@ -0,0 +1,396 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>RTCPeerConnection.prototype.iceConnectionState</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> + 'use strict'; + + // Test is based on the following editor draft: + // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html + + /* + 4.3.2. Interface Definition + interface RTCPeerConnection : EventTarget { + ... + readonly attribute RTCIceConnectionState iceConnectionState; + attribute EventHandler oniceconnectionstatechange; + }; + + 4.4.4 RTCIceConnectionState Enum + enum RTCIceConnectionState { + "new", + "checking", + "connected", + "completed", + "failed", + "disconnected", + "closed" + }; + + 5.6. RTCIceTransport Interface + interface RTCIceTransport { + readonly attribute RTCIceTransportState state; + attribute EventHandler onstatechange; + + ... + }; + + enum RTCIceTransportState { + "new", + "checking", + "connected", + "completed", + "failed", + "disconnected", + "closed" + }; + */ + + /* + 4.4.4 RTCIceConnectionState Enum + new + Any of the RTCIceTransports are in the new state and none of them + are in the checking, failed or disconnected state, or all + RTCIceTransport s are in the closed state. + */ + test(t => { + const pc = new RTCPeerConnection(); + assert_equals(pc.iceConnectionState, 'new'); + }, 'Initial iceConnectionState should be new'); + + test(t => { + const pc = new RTCPeerConnection(); + pc.close(); + assert_equals(pc.iceConnectionState, 'closed'); + }, 'Closing the connection should set iceConnectionState to closed'); + + /* + 4.4.4 RTCIceConnectionState Enum + checking + Any of the RTCIceTransport s are in the checking state and none of + them are in the failed or disconnected state. + + connected + All RTCIceTransport s are in the connected, completed or closed state + and at least one of them is in the connected state. + + completed + All RTCIceTransport s are in the completed or closed state and at least + one of them is in the completed state. + + checking + The RTCIceTransport has received at least one remote candidate and + is checking candidate pairs and has either not yet found a connection + or consent checks [RFC7675] have failed on all previously successful + candidate pairs. In addition to checking, it may also still be gathering. + + 5.6. enum RTCIceTransportState + connected + The RTCIceTransport has found a usable connection, but is still + checking other candidate pairs to see if there is a better connection. + It may also still be gathering and/or waiting for additional remote + candidates. If consent checks [RFC7675] fail on the connection in use, + and there are no other successful candidate pairs available, then the + state transitions to "checking" (if there are candidate pairs remaining + to be checked) or "disconnected" (if there are no candidate pairs to + check, but the peer is still gathering and/or waiting for additional + remote candidates). + + completed + The RTCIceTransport has finished gathering, received an indication that + there are no more remote candidates, finished checking all candidate + pairs and found a connection. If consent checks [RFC7675] subsequently + fail on all successful candidate pairs, the state transitions to "failed". + */ + async_test(t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + let had_checking = false; + + const onIceConnectionStateChange = t.step_func(() => { + const {iceConnectionState} = pc1; + if (iceConnectionState === 'checking') { + had_checking = true; + } else if (iceConnectionState === 'connected' || + iceConnectionState === 'completed') { + assert_true(had_checking, 'state should pass checking before' + + ' reaching connected or completed'); + t.done(); + } else if (iceConnectionState === 'failed') { + assert_unreached("ICE should not fail"); + } + }); + + pc1.createDataChannel('test'); + + pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange); + + exchangeIceCandidates(pc1, pc2); + exchangeOfferAnswer(pc1, pc2); + }, 'connection with one data channel should eventually have connected or ' + + 'completed connection state'); + +async_test(t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + + t.add_cleanup(() => pc2.close()); + + const onIceConnectionStateChange = t.step_func(() => { + const { iceConnectionState } = pc1; + + if(iceConnectionState === 'checking') { + const iceTransport = pc1.sctp.transport.iceTransport; + + assert_equals(iceTransport.state, 'checking', + 'Expect ICE transport to be in checking state when' + + ' iceConnectionState is checking'); + + } else if(iceConnectionState === 'connected') { + const iceTransport = pc1.sctp.transport.iceTransport; + + assert_equals(iceTransport.state, 'connected', + 'Expect ICE transport to be in connected state when' + + ' iceConnectionState is connected'); + t.done(); + } else if(iceConnectionState === 'completed') { + const iceTransport = pc1.sctp.transport.iceTransport; + + assert_equals(iceTransport.state, 'completed', + 'Expect ICE transport to be in connected state when' + + ' iceConnectionState is completed'); + t.done(); + } else if (iceConnectionState === 'failed') { + assert_unreached("ICE should not fail"); + } + }); + + pc1.createDataChannel('test'); + + assert_equals(pc1.oniceconnectionstatechange, null, + 'Expect connection to have iceconnectionstatechange event'); + + pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange); + + exchangeIceCandidates(pc1, pc2); + exchangeOfferAnswer(pc1, pc2); + }, 'connection with one data channel should eventually ' + + 'have connected connection state'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + stream.getTracks().forEach(track => pc1.addTrack(track, stream)); + + exchangeIceCandidates(pc1, pc2); + exchangeOfferAnswer(pc1, pc2); + await listenToIceConnected(pc1); + }, 'connection with audio track should eventually ' + + 'have connected connection state'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const stream = await getNoiseStream({audio: true, video:true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + stream.getTracks().forEach(track => pc1.addTrack(track, stream)); + + exchangeIceCandidates(pc1, pc2); + exchangeOfferAnswer(pc1, pc2); + await listenToIceConnected(pc1); + }, 'connection with audio and video tracks should eventually ' + + 'have connected connection state'); + + promise_test(async t => { + const caller = new RTCPeerConnection(); + t.add_cleanup(() => caller.close()); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + + caller.addTransceiver('audio', {direction:'recvonly'}); + const stream = await getNoiseStream({audio:true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + callee.addTrack(track, stream); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + + assert_equals(caller.getTransceivers().length, 1); + const [transceiver] = caller.getTransceivers(); + assert_equals(transceiver.currentDirection, 'recvonly'); + + await listenToIceConnected(caller); + }, 'ICE can connect in a recvonly usecase'); + + /* + TODO + 4.4.4 RTCIceConnectionState Enum + failed + Any of the RTCIceTransport s are in the failed state. + + disconnected + Any of the RTCIceTransport s are in the disconnected state and none of + them are in the failed state. + + closed + The RTCPeerConnection object's [[ isClosed]] slot is true. + + 5.6. enum RTCIceTransportState + new + The RTCIceTransport is gathering candidates and/or waiting for + remote candidates to be supplied, and has not yet started checking. + + failed + The RTCIceTransport has finished gathering, received an indication that + there are no more remote candidates, finished checking all candidate pairs, + and all pairs have either failed connectivity checks or have lost consent. + + disconnected + The ICE Agent has determined that connectivity is currently lost for this + RTCIceTransport . This is more aggressive than failed, and may trigger + intermittently (and resolve itself without action) on a flaky network. + The way this state is determined is implementation dependent. + + Examples include: + Losing the network interface for the connection in use. + Repeatedly failing to receive a response to STUN requests. + + Alternatively, the RTCIceTransport has finished checking all existing + candidates pairs and failed to find a connection (or consent checks + [RFC7675] once successful, have now failed), but it is still gathering + and/or waiting for additional remote candidates. + + closed + The RTCIceTransport has shut down and is no longer responding to STUN requests. + */ + +for (let bundle_policy of ['balanced', 'max-bundle', 'max-compat']) { + + + promise_test(async t => { + const caller = new RTCPeerConnection({bundlePolicy: bundle_policy}); + t.add_cleanup(() => caller.close()); + const stream = await getNoiseStream( + {audio: true, video:true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track1, track2] = stream.getTracks(); + const sender1 = caller.addTrack(track1); + const sender2 = caller.addTrack(track2); + caller.createDataChannel('datachannel'); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + exchangeIceCandidates(caller, callee); + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + const [caller_transceiver1, caller_transceiver2] = caller.getTransceivers(); + assert_equals(sender1.transport, caller_transceiver1.sender.transport); + await callee.setRemoteDescription(offer); + const [callee_transceiver1, callee_transceiver2] = callee.getTransceivers(); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + // At this point, we should have a single ICE transport, and it + // should eventually get to the "connected" state. + await waitForState(caller_transceiver1.receiver.transport.iceTransport, + 'connected'); + // The PeerConnection's iceConnectionState should therefore be 'connected' + assert_equals(caller.iceConnectionState, 'connected', + 'PC.iceConnectionState:'); + }, 'iceConnectionState changes at the right time, with bundle policy ' + + bundle_policy); +} + +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate); + pc1.candidateBuffer = []; + pc2.onicecandidate = e => { + // Don't add candidate if candidate buffer is already used + if (pc1.candidateBuffer) { + pc1.candidateBuffer.push(e.candidate) + } + }; + pc1.iceStates = [pc1.iceConnectionState]; + pc2.iceStates = [pc2.iceConnectionState]; + pc1.oniceconnectionstatechange = () => { + pc1.iceStates.push(pc1.iceConnectionState); + }; + pc2.oniceconnectionstatechange = () => { + pc2.iceStates.push(pc2.iceConnectionState); + }; + + const localStream = await getNoiseStream({audio: true, video: true}); + const localStream2 = await getNoiseStream({audio: true, video: true}); + const remoteStream = await getNoiseStream({audio: true, video: true}); + for (const stream of [localStream, localStream2, remoteStream]) { + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + } + localStream.getTracks().forEach(t => pc1.addTrack(t, localStream)); + localStream2.getTracks().forEach(t => pc1.addTrack(t, localStream2)); + remoteStream.getTracks().forEach(t => pc2.addTrack(t, remoteStream)); + const offer = await pc1.createOffer(); + await pc2.setRemoteDescription(offer); + await pc1.setLocalDescription(offer); + const answer = await pc2.createAnswer(); + await pc2.setLocalDescription(answer); + await pc1.setRemoteDescription(answer); + pc1.candidateBuffer.forEach(c => pc1.addIceCandidate(c)); + delete pc1.candidateBuffer; + await listenToIceConnected(pc1); + await listenToIceConnected(pc2); + // While we're waiting for pc2, pc1 may or may not have transitioned + // to "completed" state, so allow for both cases. + if (pc1.iceStates.length == 3) { + assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected']); + } else { + assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected', + 'completed']); + } + assert_array_equals(pc2.iceStates, ['new', 'checking', 'connected']); +}, 'Responder ICE connection state behaves as expected'); + +/* + Test case for step 11 of PeerConnection.close(). + ... + 11. Set connection's ICE connection state to "closed". This does not invoke + the "update the ICE connection state" procedure, and does not fire any + event. + ... +*/ +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + + stream.getTracks().forEach(track => pc1.addTrack(track, stream)); + exchangeIceCandidates(pc1, pc2); + exchangeOfferAnswer(pc1, pc2); + await listenToIceConnected(pc2); + + pc2.oniceconnectionstatechange = t.unreached_func(); + pc2.close(); + assert_equals(pc2.iceConnectionState, 'closed'); + await new Promise(r => t.step_timeout(r, 100)); +}, 'Closing a PeerConnection should not fire iceconnectionstatechange event'); + +</script> |