| <!doctype html> | |
| <meta charset=utf-8> | |
| <meta name="timeout" content="long"> | |
| <title>RTCPeerConnection.prototype.iceConnectionState - disconnection</title> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="RTCPeerConnection-helper.js"></script> | |
| <script> | |
| 'use strict'; | |
| promise_test(async t => { | |
| const caller = new RTCPeerConnection(); | |
| t.add_cleanup(() => caller.close()); | |
| const callee = new RTCPeerConnection(); | |
| t.add_cleanup(() => callee.close()); | |
| const stream = await getNoiseStream({audio:true}); | |
| t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); | |
| const [track] = stream.getTracks(); | |
| caller.addTrack(track, stream); | |
| exchangeIceCandidates(caller, callee); | |
| await exchangeOfferAnswer(caller, callee); | |
| await listenToIceConnected(caller); | |
| callee.close(); | |
| await waitForIceStateChange(caller, ['disconnected', 'failed']); | |
| // TODO: this should eventually transition to failed but that takes | |
| // somewhat long (15-30s) so is not testable. | |
| }, 'ICE goes to disconnected if the other side goes away'); | |
| 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: 'sendrecv'}); | |
| exchangeIceCandidates(caller, callee); | |
| await exchangeOfferAnswer(caller, callee); | |
| await listenToIceConnected(caller); | |
| // Now, we pull a fast one, and convince callee to abandon the transport | |
| // without telling caller. | |
| await caller.setLocalDescription(); | |
| await callee.setRemoteDescription(caller.localDescription); | |
| const staleAnswer = await callee.createAnswer(); | |
| await callee.setRemoteDescription({type: 'rollback', sdp: ''}); | |
| const mlineRegex = /m=audio [0-9]+ /; | |
| const mungedOfferSdp = caller.localDescription.sdp | |
| .replace(mlineRegex, 'm=audio 0 ') | |
| .replace('BUNDLE', 'BUNGLE'); // Avoid "But that mid is rejected!" errors | |
| // callee gets the munged reoffer with a rejected m-section, caller gets a | |
| // stale answer that was made before callee saw the rejected m-section. | |
| await callee.setRemoteDescription({type: 'offer', sdp: mungedOfferSdp}); | |
| await callee.setLocalDescription(); | |
| await caller.setRemoteDescription(staleAnswer); | |
| assert_equals(await nextIceConnectionState(caller), 'disconnected'); | |
| // Now, let's fix this with an ICE restart! callee has already negotiated | |
| // a rejection of the first m-section, so it will tolerate it being | |
| // revived. | |
| caller.restartIce(); | |
| await caller.setLocalDescription(); | |
| await callee.setRemoteDescription(caller.localDescription); | |
| await callee.setLocalDescription(); | |
| await caller.setRemoteDescription(callee.localDescription); | |
| assert_equals(await nextIceConnectionState(caller), 'checking'); | |
| assert_equals(await nextIceConnectionState(caller), 'connected'); | |
| }, 'ICE restart when ICE is disconnected results in checking, then connected'); | |
| </script> |