summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCPeerConnection-addIceCandidate-timing.https.html
blob: 9793844f56900927447f080ebbc92011d601fb9d (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>

'use strict';

// In this test, the promises should resolve in the execution order
// (setLocalDescription, setLocalDescription, addIceCandidate) as is ensured by
// the Operations Chain; if an operation is pending, executing another operation
// will queue it. This test will fail if an Operations Chain is not implemented,
// but it gives the implementation some slack: it only ensures that
// addIceCandidate() is not resolved first, allowing timing issues in resolving
// promises where the test still passes even if addIceCandidate() is resolved
// *before* the second setLocalDescription().
//
// This test covers Chrome issue (https://crbug.com/1019222), but does not
// require setLocalDescription-promises to resolve immediately which is another
// Chrome bug (https://crbug.com/1019232). The true order is covered by the next
// test.
// TODO(https://crbug.com/1019232): Delete this test when the next test passes
// in Chrome.
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');

  const candidatePromise = new Promise(resolve => {
    caller.onicecandidate = e => resolve(e.candidate);
  });
  await caller.setLocalDescription(await caller.createOffer());
  await callee.setRemoteDescription(caller.localDescription);
  const candidate = await candidatePromise;

  // Chain setLocalDescription(), setLocalDescription() and addIceCandidate()
  // without performing await between the calls.
  const pendingPromises = [];
  const resolveOrder = [];
  pendingPromises.push(callee.setLocalDescription().then(() => {
    resolveOrder.push('setLocalDescription 1');
  }));
  pendingPromises.push(callee.setLocalDescription().then(() => {
    resolveOrder.push('setLocalDescription 2');
  }));
  pendingPromises.push(callee.addIceCandidate(candidate).then(() => {
    resolveOrder.push('addIceCandidate');
  }));
  await Promise.all(pendingPromises);

  assert_equals(resolveOrder[0], 'setLocalDescription 1');
}, 'addIceCandidate is not resolved first if 2x setLocalDescription ' +
   'operations are pending');

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');

  const candidatePromise = new Promise(resolve => {
    caller.onicecandidate = e => resolve(e.candidate);
  });
  await caller.setLocalDescription(await caller.createOffer());
  await callee.setRemoteDescription(caller.localDescription);
  const candidate = await candidatePromise;

  // Chain setLocalDescription(), setLocalDescription() and addIceCandidate()
  // without performing await between the calls.
  const pendingPromises = [];
  const resolveOrder = [];
  pendingPromises.push(callee.setLocalDescription().then(() => {
    resolveOrder.push('setLocalDescription 1');
  }));
  pendingPromises.push(callee.setLocalDescription().then(() => {
    resolveOrder.push('setLocalDescription 2');
  }));
  pendingPromises.push(callee.addIceCandidate(candidate).then(() => {
    resolveOrder.push('addIceCandidate');
  }));
  await Promise.all(pendingPromises);

  // This test verifies that both issues described in https://crbug.com/1019222
  // and https://crbug.com/1019232 are fixed. If this test passes in Chrome, the
  // ICE candidate exchange issues described in
  // https://github.com/web-platform-tests/wpt/issues/19866 should be resolved.
  assert_array_equals(
      resolveOrder,
      ['setLocalDescription 1', 'setLocalDescription 2', 'addIceCandidate']);
}, 'addIceCandidate and setLocalDescription are resolved in the correct ' +
   'order, as defined by the operations chain specification');

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');
  let events = [];
  let pendingPromises = [];

  const onCandidatePromise = new Promise(resolve => {
    caller.onicecandidate = () => {
      events.push('candidate generated');
      resolve();
    }
  });
  pendingPromises.push(onCandidatePromise);
  pendingPromises.push(caller.setLocalDescription().then(() => {
    events.push('setLocalDescription');
  }));
  await Promise.all(pendingPromises);
  assert_array_equals(events, ['setLocalDescription', 'candidate generated']);
}, 'onicecandidate fires after resolving setLocalDescription in offerer');

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');
  let events = [];
  let pendingPromises = [];

  caller.onicecandidate = (ev) => {
    if (ev.candidate) {
      callee.addIceCandidate(ev.candidate);
    }
  }
  const offer = await caller.createOffer();
  const onCandidatePromise = new Promise(resolve => {
    callee.onicecandidate = () => {
      events.push('candidate generated');
      resolve();
    }
  });
  await callee.setRemoteDescription(offer);
  const answer = await callee.createAnswer();
  pendingPromises.push(onCandidatePromise);
  pendingPromises.push(callee.setLocalDescription(answer).then(() => {
    events.push('setLocalDescription');
  }));
  await Promise.all(pendingPromises);
  assert_array_equals(events, ['setLocalDescription', 'candidate generated']);
}, 'onicecandidate fires after resolving setLocalDescription in answerer');

</script>