summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/protocol/ice-state.https.html
blob: becce59509b792dd5d63571bff065d93185ca1fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>RTCPeerConnection Failed State</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../RTCPeerConnection-helper.js"></script>
<script>
'use strict';

// Tests for correct behavior of ICE state.

// SDP copied from JSEP Example 7.1
// It contains two media streams with different ufrags, and bundle
// turned on.
const kSdp = `v=0
o=- 4962303333179871722 1 IN IP4 0.0.0.0
s=-
t=0 0
a=ice-options:trickle
a=group:BUNDLE a1 v1
a=group:LS a1 v1
m=audio 10100 UDP/TLS/RTP/SAVPF 96 0 8 97 98
c=IN IP4 203.0.113.100
a=mid:a1
a=sendrecv
a=rtpmap:96 opus/48000/2
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:97 telephone-event/8000
a=rtpmap:98 telephone-event/48000
a=maxptime:120
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:2 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=msid:47017fee-b6c1-4162-929c-a25110252400 f83006c5-a0ff-4e0a-9ed9-d3e6747be7d9
a=ice-ufrag:ETEn
a=ice-pwd:OtSK0WpNtpUjkY4+86js7ZQl
a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2
a=setup:actpass
a=dtls-id:1
a=rtcp:10101 IN IP4 203.0.113.100
a=rtcp-mux
a=rtcp-rsize
m=video 10102 UDP/TLS/RTP/SAVPF 100 101
c=IN IP4 203.0.113.100
a=mid:v1
a=sendrecv
a=rtpmap:100 VP8/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=msid:47017fee-b6c1-4162-929c-a25110252400 f30bdb4a-5db8-49b5-bcdc-e0c9a23172e0
a=ice-ufrag:BGKk
a=ice-pwd:mqyWsAjvtKwTGnvhPztQ9mIf
a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2
a=setup:actpass
a=dtls-id:1
a=rtcp:10103 IN IP4 203.0.113.100
a=rtcp-mux
a=rtcp-rsize
`;

// Returns a promise that resolves when |pc.iceConnectionState| is in one of the
// wanted states, and rejects if it is in one of the unwanted states.
// This is a variant of the function in RTCPeerConnection-helper.js.
function waitForIceStateChange(pc, wantedStates, unwantedStates=[]) {
  return new Promise((resolve, reject) => {
    if (wantedStates.includes(pc.iceConnectionState)) {
      resolve();
      return;
    } else if (unwantedStates.includes(pc.iceConnectionState)) {
      reject('Unexpected state encountered: ' + pc.iceConnectionState);
      return;
    }
    pc.addEventListener('iceconnectionstatechange', () => {
      if (wantedStates.includes(pc.iceConnectionState)) {
        resolve();
      } else if (unwantedStates.includes(pc.iceConnectionState)) {
        reject('Unexpected state encountered: ' + pc.iceConnectionState);
      }
    });
  });
}

promise_test(async t => {
  const pc1 = new RTCPeerConnection();
  const pc2 = new RTCPeerConnection();
  t.add_cleanup(() => pc1.close());
  t.add_cleanup(() => pc2.close());
  let [track, streams] = await getTrackFromUserMedia('video');
  const sender = pc1.addTrack(track);
  exchangeIceCandidates(pc1, pc2);
  await exchangeOfferAnswer(pc1, pc2);
  await waitForIceStateChange(pc1, ['connected', 'completed']);
}, 'PC should enter connected (or completed) state when candidates are sent');

promise_test(async t => {
  const pc1 = new RTCPeerConnection();
  t.add_cleanup(() => pc1.close());
  let [track, streams] = await getTrackFromUserMedia('video');
  const sender = pc1.addTrack(track);
  const offer = await pc1.createOffer();
  assert_greater_than_equal(offer.sdp.search('a=ice-options:trickle'), 0);
}, 'PC should generate offer with a=ice-options:trickle');

promise_test(async t => {
  const pc1 = new RTCPeerConnection();
  t.add_cleanup(() => pc1.close());
  await pc1.setRemoteDescription({type: 'offer', sdp: kSdp});
  const answer = await pc1.createAnswer();
  await pc1.setLocalDescription(answer);
  assert_greater_than_equal(answer.sdp.search('a=ice-options:trickle'), 0);
  // When we use trickle ICE, and don't signal end-of-caniddates, we
  // expect failure to result in 'disconnected' state rather than 'failed'.
  const stateWaiter = waitForIceStateChange(pc1, ['disconnected'],
                                            ['failed']);
  // Add a bogus candidate. The candidate is drawn from the
  // IANA "test-net-3" pool (RFC5737), so is guaranteed not to respond.
  const candidateStr1 =
      'candidate:1 1 udp 2113929471 203.0.113.100 10100 typ host';
  await pc1.addIceCandidate({candidate: candidateStr1,
                             sdpMid: 'a1',
                             usernameFragment: 'ETEn'});
  await stateWaiter;
}, 'PC should enter disconnected state when a failing candidate is sent');

</script>