RTCPeerConnection-addIceCandidate-timing.https.html (5785B)
1 <!doctype html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 <script> 5 6 'use strict'; 7 8 // In this test, the promises should resolve in the execution order 9 // (setLocalDescription, setLocalDescription, addIceCandidate) as is ensured by 10 // the Operations Chain; if an operation is pending, executing another operation 11 // will queue it. This test will fail if an Operations Chain is not implemented, 12 // but it gives the implementation some slack: it only ensures that 13 // addIceCandidate() is not resolved first, allowing timing issues in resolving 14 // promises where the test still passes even if addIceCandidate() is resolved 15 // *before* the second setLocalDescription(). 16 // 17 // This test covers Chrome issue (https://crbug.com/1019222), but does not 18 // require setLocalDescription-promises to resolve immediately which is another 19 // Chrome bug (https://crbug.com/1019232). The true order is covered by the next 20 // test. 21 // TODO(https://crbug.com/1019232): Delete this test when the next test passes 22 // in Chrome. 23 promise_test(async t => { 24 const caller = new RTCPeerConnection(); 25 t.add_cleanup(() => caller.close()); 26 const callee = new RTCPeerConnection(); 27 t.add_cleanup(() => callee.close()); 28 caller.addTransceiver('audio'); 29 30 const candidatePromise = new Promise(resolve => { 31 caller.onicecandidate = e => resolve(e.candidate); 32 }); 33 await caller.setLocalDescription(await caller.createOffer()); 34 await callee.setRemoteDescription(caller.localDescription); 35 const candidate = await candidatePromise; 36 37 // Chain setLocalDescription(), setLocalDescription() and addIceCandidate() 38 // without performing await between the calls. 39 const pendingPromises = []; 40 const resolveOrder = []; 41 pendingPromises.push(callee.setLocalDescription().then(() => { 42 resolveOrder.push('setLocalDescription 1'); 43 })); 44 pendingPromises.push(callee.setLocalDescription().then(() => { 45 resolveOrder.push('setLocalDescription 2'); 46 })); 47 pendingPromises.push(callee.addIceCandidate(candidate).then(() => { 48 resolveOrder.push('addIceCandidate'); 49 })); 50 await Promise.all(pendingPromises); 51 52 assert_equals(resolveOrder[0], 'setLocalDescription 1'); 53 }, 'addIceCandidate is not resolved first if 2x setLocalDescription ' + 54 'operations are pending'); 55 56 promise_test(async t => { 57 const caller = new RTCPeerConnection(); 58 t.add_cleanup(() => caller.close()); 59 const callee = new RTCPeerConnection(); 60 t.add_cleanup(() => callee.close()); 61 caller.addTransceiver('audio'); 62 63 const candidatePromise = new Promise(resolve => { 64 caller.onicecandidate = e => resolve(e.candidate); 65 }); 66 await caller.setLocalDescription(await caller.createOffer()); 67 await callee.setRemoteDescription(caller.localDescription); 68 const candidate = await candidatePromise; 69 70 // Chain setLocalDescription(), setLocalDescription() and addIceCandidate() 71 // without performing await between the calls. 72 const pendingPromises = []; 73 const resolveOrder = []; 74 pendingPromises.push(callee.setLocalDescription().then(() => { 75 resolveOrder.push('setLocalDescription 1'); 76 })); 77 pendingPromises.push(callee.setLocalDescription().then(() => { 78 resolveOrder.push('setLocalDescription 2'); 79 })); 80 pendingPromises.push(callee.addIceCandidate(candidate).then(() => { 81 resolveOrder.push('addIceCandidate'); 82 })); 83 await Promise.all(pendingPromises); 84 85 // This test verifies that both issues described in https://crbug.com/1019222 86 // and https://crbug.com/1019232 are fixed. If this test passes in Chrome, the 87 // ICE candidate exchange issues described in 88 // https://github.com/web-platform-tests/wpt/issues/19866 should be resolved. 89 assert_array_equals( 90 resolveOrder, 91 ['setLocalDescription 1', 'setLocalDescription 2', 'addIceCandidate']); 92 }, 'addIceCandidate and setLocalDescription are resolved in the correct ' + 93 'order, as defined by the operations chain specification'); 94 95 promise_test(async t => { 96 const caller = new RTCPeerConnection(); 97 t.add_cleanup(() => caller.close()); 98 const callee = new RTCPeerConnection(); 99 t.add_cleanup(() => callee.close()); 100 caller.addTransceiver('audio'); 101 let events = []; 102 let pendingPromises = []; 103 104 const onCandidatePromise = new Promise(resolve => { 105 caller.onicecandidate = () => { 106 events.push('candidate generated'); 107 resolve(); 108 } 109 }); 110 pendingPromises.push(onCandidatePromise); 111 pendingPromises.push(caller.setLocalDescription().then(() => { 112 events.push('setLocalDescription'); 113 })); 114 await Promise.all(pendingPromises); 115 assert_array_equals(events, ['setLocalDescription', 'candidate generated']); 116 }, 'onicecandidate fires after resolving setLocalDescription in offerer'); 117 118 promise_test(async t => { 119 const caller = new RTCPeerConnection(); 120 t.add_cleanup(() => caller.close()); 121 const callee = new RTCPeerConnection(); 122 t.add_cleanup(() => callee.close()); 123 caller.addTransceiver('audio'); 124 let events = []; 125 let pendingPromises = []; 126 127 caller.onicecandidate = (ev) => { 128 if (ev.candidate) { 129 callee.addIceCandidate(ev.candidate); 130 } 131 } 132 const offer = await caller.createOffer(); 133 const onCandidatePromise = new Promise(resolve => { 134 callee.onicecandidate = () => { 135 events.push('candidate generated'); 136 resolve(); 137 } 138 }); 139 await callee.setRemoteDescription(offer); 140 const answer = await callee.createAnswer(); 141 pendingPromises.push(onCandidatePromise); 142 pendingPromises.push(callee.setLocalDescription(answer).then(() => { 143 events.push('setLocalDescription'); 144 })); 145 await Promise.all(pendingPromises); 146 assert_array_equals(events, ['setLocalDescription', 'candidate generated']); 147 }, 'onicecandidate fires after resolving setLocalDescription in answerer'); 148 149 </script>