| <!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> |