From 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:33 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- .../tests/webrtc/RTCConfiguration-iceServers.html | 8 - .../RTCConfiguration-iceTransportPolicy.html | 35 ++- .../web-platform/tests/webrtc/RTCIceTransport.html | 282 ++++++++++++++++++++- .../RTCPeerConnection-createDataChannel.html | 13 + .../webrtc/RTCPeerConnection-createOffer.html | 2 +- ...ection-description-attributes-timing.https.html | 4 + ...ection-explicit-rollback-iceGatheringState.html | 59 ++++- .../tests/webrtc/RTCPeerConnection-helper.js | 142 ++++++++++- ...tion-iceConnectionState-disconnected.https.html | 42 +++ ...RTCPeerConnection-iceConnectionState.https.html | 14 + .../RTCPeerConnection-iceGatheringState.html | 90 ++++++- ...CPeerConnection-setLocalDescription-answer.html | 14 +- ...TCPeerConnection-setLocalDescription-offer.html | 6 +- ...on-setLocalDescription-parameterless.https.html | 2 + ...eerConnection-setLocalDescription-rollback.html | 6 +- ...CPeerConnection-setRemoteDescription-offer.html | 37 ++- ...erConnection-setRemoteDescription-rollback.html | 2 +- .../RTCPeerConnection-setRemoteDescription.html | 2 +- ...TCRtpReceiver-getContributingSources.https.html | 7 + .../webrtc/RTCRtpReceiver-getStats.https.html | 20 +- ...tpReceiver-getSynchronizationSources.https.html | 8 + .../tests/webrtc/RTCRtpReceiver.https.html | 96 +++++++ .../tests/webrtc/RTCRtpSender-getStats.https.html | 20 +- .../tests/webrtc/RTCRtpSender.https.html | 87 +++++++ .../RTCRtpTransceiver-setCodecPreferences.html | 138 +++++----- ...che-with-open-webrtc-connection.https.window.js | 2 +- testing/web-platform/tests/webrtc/historical.html | 9 + .../codecs-filtered-by-direction.https.html | 79 ++++++ .../protocol/codecs-subsequent-offer.https.html | 50 ++++ 29 files changed, 1130 insertions(+), 146 deletions(-) create mode 100644 testing/web-platform/tests/webrtc/RTCRtpReceiver.https.html create mode 100644 testing/web-platform/tests/webrtc/protocol/codecs-filtered-by-direction.https.html create mode 100644 testing/web-platform/tests/webrtc/protocol/codecs-subsequent-offer.https.html (limited to 'testing/web-platform/tests/webrtc') diff --git a/testing/web-platform/tests/webrtc/RTCConfiguration-iceServers.html b/testing/web-platform/tests/webrtc/RTCConfiguration-iceServers.html index d344cce9f8..aa66bfbb2b 100644 --- a/testing/web-platform/tests/webrtc/RTCConfiguration-iceServers.html +++ b/testing/web-platform/tests/webrtc/RTCConfiguration-iceServers.html @@ -301,12 +301,4 @@ }] })); }, `with empty urls should throw SyntaxError`); - // Blink and Gecko fall back to url, but it's not in the spec. - config_test(makePc => { - assert_throws_js(TypeError, () => - makePc({ iceServers: [{ - url: 'stun:stun1.example.net' - }] })); - }, 'with url field should throw TypeError'); - diff --git a/testing/web-platform/tests/webrtc/RTCConfiguration-iceTransportPolicy.html b/testing/web-platform/tests/webrtc/RTCConfiguration-iceTransportPolicy.html index ebc79048a3..b84e6a30db 100644 --- a/testing/web-platform/tests/webrtc/RTCConfiguration-iceTransportPolicy.html +++ b/testing/web-platform/tests/webrtc/RTCConfiguration-iceTransportPolicy.html @@ -128,7 +128,12 @@ t.add_cleanup(() => offerer.close()); offerer.addEventListener('icecandidate', - e => assert_equals(e.candidate, null, 'Should get no ICE candidates')); + e => { + if (e.candidate) { + assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates') + } + } + ); offerer.addTransceiver('audio'); await offerer.setLocalDescription(); @@ -142,8 +147,13 @@ t.add_cleanup(() => offerer.close()); t.add_cleanup(() => answerer.close()); - answerer.addEventListener('icecandidate', - e => assert_equals(e.candidate, null, 'Should get no ICE candidates')); + offerer.addEventListener('icecandidate', + e => { + if (e.candidate) { + assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates') + } + } + ); offerer.addTransceiver('audio'); const offer = await offerer.createOffer(); @@ -175,9 +185,14 @@ offerer.setConfiguration({iceTransportPolicy: 'relay'}); offerer.addEventListener('icecandidate', - e => assert_equals(e.candidate, null, 'Should get no ICE candidates')); - - await Promise.all([ + e => { + if (e.candidate) { + assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates') + } + } + ); + + await Promise.all([ exchangeOfferAnswer(offerer, answerer), waitForIceStateChange(offerer, ['failed']), waitForIceStateChange(answerer, ['failed']), @@ -201,9 +216,13 @@ exchangeIceCandidates(offerer, answerer); const checkNoCandidate = - e => assert_equals(e.candidate, null, 'Should get no ICE candidates'); + e => { + if (e.candidate) { + assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates') + } + }; - offerer.addEventListener('icecandidate', checkNoCandidate); + offerer.addEventListener('icecandidate', checkNoCandidate); await Promise.all([ exchangeOfferAnswer(offerer, answerer), diff --git a/testing/web-platform/tests/webrtc/RTCIceTransport.html b/testing/web-platform/tests/webrtc/RTCIceTransport.html index fe12c384e5..e80418bdc4 100644 --- a/testing/web-platform/tests/webrtc/RTCIceTransport.html +++ b/testing/web-platform/tests/webrtc/RTCIceTransport.html @@ -1,9 +1,11 @@ + RTCIceTransport + diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html index cddbd02c7b..373630ff77 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html @@ -707,6 +707,19 @@ for (const options of [{}, {negotiated: true, id: 0}]) { await createDataChannelPair(t, options, pc1); }, `addTrack, then creating ${mode}, should negotiate properly when max-bundle is used`); + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + const stream = await getNoiseStream({audio: true, video: true}); + stream.getTracks().forEach((track) => pc1.addTrack(track, stream)); + await createDataChannelPair(t, options, pc1, pc2); + + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + await pc2.setLocalDescription(); + await pc1.setRemoteDescription(pc2.localDescription); + }, `Renegotiation with audio/video and ${mode} should work properly`); + /* This test is disabled until https://github.com/w3c/webrtc-pc/issues/2562 has been resolved; it presupposes that stopping the first transceiver diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-createOffer.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-createOffer.html index 704fa3c646..7287980a5b 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-createOffer.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-createOffer.html @@ -53,7 +53,7 @@ .then(() => { assert_equals(pc.signalingState, 'have-local-offer'); assert_session_desc_similar(pc.localDescription, offer); - assert_session_desc_similar(pc.pendingLocalDescription, offer); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.currentLocalDescription, null); assert_array_equals(states, ['have-local-offer']); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-description-attributes-timing.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-description-attributes-timing.https.html index 2d2565c3e1..bb23d2a39a 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-description-attributes-timing.https.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-description-attributes-timing.https.html @@ -19,6 +19,7 @@ promise_test(async t => { await promise; assert_not_equals(pc.pendingLocalDescription, null, 'pendingLocalDescription is not null after await'); + assert_equals(pc.pendingLocalDescription, pc.localDescription); }, "pendingLocalDescription is surfaced at the right time"); promise_test(async t => { @@ -34,6 +35,7 @@ promise_test(async t => { await promise; assert_not_equals(pc.pendingRemoteDescription, null, 'pendingRemoteDescription is not null after await'); + assert_equals(pc.pendingRemoteDescription, pc.remoteDescription); }, "pendingRemoteDescription is surfaced at the right time"); promise_test(async t => { @@ -55,6 +57,7 @@ promise_test(async t => { await promise; assert_not_equals(pc2.currentLocalDescription, null, 'currentLocalDescription is not null after await'); + assert_equals(pc2.currentLocalDescription, pc2.localDescription); }, "currentLocalDescription is surfaced at the right time"); promise_test(async t => { @@ -76,6 +79,7 @@ promise_test(async t => { await promise; assert_not_equals(pc1.currentRemoteDescription, null, 'currentRemoteDescription is not null after await'); + assert_equals(pc1.currentRemoteDescription, pc1.remoteDescription); }, "currentRemoteDescription is surfaced at the right time"); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState.html index e39b985bef..a28275047e 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState.html @@ -13,8 +13,7 @@ promise_test(async t => { const pc2 = new RTCPeerConnection(); t.add_cleanup(() => pc2.close()); pc1.addTransceiver('audio', { direction: 'recvonly' }); - await initialOfferAnswerWithIceGatheringStateTransitions( - pc1, pc2); + await initialOfferAnswerWithIceGatheringStateTransitions(pc1, pc2); await pc1.setLocalDescription(await pc1.createOffer({iceRestart: true})); await iceGatheringStateTransitions(pc1, 'gathering', 'complete'); expectNoMoreGatheringStateChanges(t, pc1); @@ -22,32 +21,74 @@ promise_test(async t => { await new Promise(r => t.step_timeout(r, 1000)); }, 'rolling back an ICE restart when gathering is complete should not result in iceGatheringState changes'); +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + pc1.createDataChannel('test'); + await initialOfferAnswerWithIceGatheringStateTransitions(pc1, pc2); + await pc1.setLocalDescription(await pc1.createOffer({iceRestart: true})); + await iceGatheringStateTransitions(pc1, 'gathering', 'complete'); + expectNoMoreGatheringStateChanges(t, pc1); + await pc1.setLocalDescription({type: 'rollback'}); + await new Promise(r => t.step_timeout(r, 1000)); +}, 'rolling back an ICE restart when gathering is complete should not result in iceGatheringState changes (DataChannel case)'); + promise_test(async t => { const pc = new RTCPeerConnection(); t.add_cleanup(() => pc.close()); pc.addTransceiver('audio', { direction: 'recvonly' }); - await pc.setLocalDescription( - await pc.createOffer()); + await pc.setLocalDescription(); await iceGatheringStateTransitions(pc, 'gathering', 'complete'); + const backToNew = iceGatheringStateTransitions(pc, 'new'); await pc.setLocalDescription({type: 'rollback'}); - await iceGatheringStateTransitions(pc, 'new'); + await backToNew; }, 'setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "complete"'); +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + pc.createDataChannel('test'); + await pc.setLocalDescription(); + await iceGatheringStateTransitions(pc, 'gathering', 'complete'); + const backToNew = iceGatheringStateTransitions(pc, 'new'); + await pc.setLocalDescription({type: 'rollback'}); + await backToNew; +}, 'setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "complete" (DataChannel case)'); + promise_test(async t => { const pc = new RTCPeerConnection(); t.add_cleanup(() => pc.close()); pc.addTransceiver('audio', { direction: 'recvonly' }); - await pc.setLocalDescription( - await pc.createOffer()); + await pc.setLocalDescription(); await iceGatheringStateTransitions(pc, 'gathering'); + const backToNew = Promise.allSettled([ + iceGatheringStateTransitions(pc, 'new'), + iceGatheringStateTransitions(pc, 'complete', 'new')]); await pc.setLocalDescription({type: 'rollback'}); // We might go directly to 'new', or we might go to 'complete' first, // depending on timing. Allow either. - const results = await Promise.allSettled([ + const results = await backToNew; + assert_true(results.some(result => result.status == 'fulfilled'), + 'ICE gathering state should go back to "new", possibly through "complete"'); +}, 'setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "gathering"'); + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + pc.createDataChannel('test'); + await pc.setLocalDescription(); + await iceGatheringStateTransitions(pc, 'gathering'); + const backToNew = Promise.allSettled([ iceGatheringStateTransitions(pc, 'new'), iceGatheringStateTransitions(pc, 'complete', 'new')]); + await pc.setLocalDescription({type: 'rollback'}); + // We might go directly to 'new', or we might go to 'complete' first, + // depending on timing. Allow either. + const results = await backToNew; assert_true(results.some(result => result.status == 'fulfilled'), 'ICE gathering state should go back to "new", possibly through "complete"'); -}, 'setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "gathering"'); +}, 'setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "gathering" (DataChannel case)'); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js index 5d188328e8..6f35fde76c 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js @@ -236,6 +236,11 @@ async function waitForIceGatheringState(pc, wantedStates) { } } +async function waitForTrackUnmuted(track) { + if (track.muted === false) return true; + return waitUntilEvent(track, 'unmute'); +} + // Resolves when RTP packets have been received. async function listenForSSRCs(t, receiver) { while (true) { @@ -646,7 +651,7 @@ function findTransceiverForSender(pc, sender) { } function preferCodec(transceiver, mimeType, sdpFmtpLine) { - const {codecs} = RTCRtpSender.getCapabilities(transceiver.receiver.track.kind); + const {codecs} = RTCRtpReceiver.getCapabilities(transceiver.receiver.track.kind); // sdpFmtpLine is optional, pick the first partial match if not given. const selectedCodecIndex = codecs.findIndex(c => { return c.mimeType === mimeType && (c.sdpFmtpLine === sdpFmtpLine || !sdpFmtpLine); @@ -696,6 +701,7 @@ const iceGatheringStateTransitions = async (pc, ...states) => { }, {once: true}); }); } + return states; }; const initialOfferAnswerWithIceGatheringStateTransitions = @@ -713,6 +719,14 @@ const initialOfferAnswerWithIceGatheringStateTransitions = await pc2Transitions; }; +const expectNoMoreIceConnectionStateChanges = async (t, pc) => { + pc.oniceconnectionstatechange = + t.step_func(() => { + assert_unreached( + 'Should not get an iceconnectionstatechange right now!'); + }); +}; + const expectNoMoreGatheringStateChanges = async (t, pc) => { pc.onicegatheringstatechange = t.step_func(() => { @@ -721,6 +735,132 @@ const expectNoMoreGatheringStateChanges = async (t, pc) => { }); }; +function gatheringStateReached(object, state) { + if (object instanceof RTCIceTransport) { + return new Promise(r => + object.addEventListener("gatheringstatechange", function listener() { + if (object.gatheringState == state) { + object.removeEventListener("gatheringstatechange", listener); + r(state); + } + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(r => + object.addEventListener("icegatheringstatechange", function listener() { + if (object.iceGatheringState == state) { + object.removeEventListener("icegatheringstatechange", listener); + r(state); + } + }) + ); + } else { + throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection"; + } +} + +function nextGatheringState(object) { + if (object instanceof RTCIceTransport) { + return new Promise(resolve => + object.addEventListener( + "gatheringstatechange", + () => resolve(object.gatheringState), + { once: true } + ) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener( + "icegatheringstatechange", + () => resolve(object.iceGatheringState), + { once: true } + ) + ); + } else { + throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection"; + } +} + +function emptyCandidate(pc) { + return new Promise(r => + pc.addEventListener("icecandidate", function listener(e) { + if (e.candidate && e.candidate.candidate == "") { + pc.removeEventListener("icecandidate", listener); + r(e); + } + }) + ); +} + +function nullCandidate(pc) { + return new Promise(r => + pc.addEventListener("icecandidate", function listener(e) { + if (!e.candidate) { + pc.removeEventListener("icecandidate", listener); + r(e); + } + }) + ); +} + +function connectionStateReached(object, state) { + if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) { + return new Promise(resolve => + object.addEventListener("statechange", function listener() { + if (object.state == state) { + object.removeEventListener("statechange", listener); + resolve(state); + } + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener("connectionstatechange", function listener() { + if (object.connectionState == state) { + object.removeEventListener("connectionstatechange", listener); + resolve(state); + } + }) + ); + } else { + throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection"; + } +} + +function nextConnectionState(object) { + if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) { + return new Promise(resolve => + object.addEventListener("statechange", () => resolve(object.state), { + once: true, + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener( + "connectionstatechange", + () => resolve(object.connectionState), + { once: true } + ) + ); + } else { + throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection"; + } +} + +function nextIceConnectionState(pc) { + if (pc instanceof RTCPeerConnection) { + return new Promise(resolve => + pc.addEventListener( + "iceconnectionstatechange", + () => resolve(pc.iceConnectionState), + { once: true } + ) + ); + } else { + throw "First parameter is not an RTCPeerConnection"; + } +} + async function queueAWebrtcTask() { const pc = new RTCPeerConnection(); pc.addTransceiver('audio'); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html index af55a0c003..04c2b9c333 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html @@ -27,4 +27,46 @@ // 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'); + diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html index 5361cb2c1a..9e0afb7ce6 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html @@ -411,4 +411,18 @@ promise_test(async t => { assert_true(pc1.iceStates.length >= 2); assert_equals(pc1.iceStates[1], 'checking'); }, 'iceConnectionState can go to checking without explictly calling addIceCandidate'); + +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + exchangeIceCandidates(pc1, pc2); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await exchangeOfferAnswer(pc1, pc2); + await listenToIceConnected(pc1); + expectNoMoreIceConnectionStateChanges(t, pc1); + pc1.restartIce(); + await exchangeOfferAnswer(pc1, pc2); + await new Promise(r => t.step_timeout(r, 1000)); +}, 'ICE restart does not result in a transition back to checking'); + diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceGatheringState.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceGatheringState.html index 6afaf0fbfb..32a68953bc 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceGatheringState.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceGatheringState.html @@ -1,5 +1,6 @@ + RTCPeerConnection.prototype.iceGatheringState @@ -115,6 +116,67 @@ await new Promise(r => t.step_timeout(r, 500)); }, 'setLocalDescription() with no transports should not cause iceGatheringState to change'); + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + await iceGatheringStateTransitions(pc1, 'gathering', 'complete'); + assert_true(pc1.localDescription.sdp.includes('a=end-of-candidates')); + }, 'local description should have a=end-of-candidates when gathering completes'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + const iceTransport = transceiver.sender.transport.iceTransport; + + // This test code assumes that https://github.com/w3c/webrtc-pc/pull/2894 + // will be merged. The spec will say to dispatch two tasks; one that fires + // the empty candidate, and another that fires + // RTCIceTransport.gatheringstatechange, then + // RTCPeerConnection.icegatheringstatechange, then the global null + // candidate. + while (true) { + const {candidate} = await new Promise(r => pc1.onicecandidate = r); + assert_not_equals(candidate, null, 'Global null candidate event should not fire yet'); + if (candidate.candidate == '') { + break; + } + } + assert_equals(iceTransport.gatheringState, 'gathering'); + assert_equals(pc1.iceGatheringState, 'gathering'); + + // Now, we test the stuff that happens in the second queued task. + const events = []; + await new Promise(r => { + iceTransport.ongatheringstatechange = () => { + assert_equals(iceTransport.gatheringState, 'complete'); + assert_equals(pc1.iceGatheringState, 'complete'); + events.push('gatheringstatechange'); + }; + pc1.onicegatheringstatechange = () => { + assert_equals(iceTransport.gatheringState, 'complete'); + assert_equals(pc1.iceGatheringState, 'complete'); + events.push('icegatheringstatechange'); + } + pc1.onicecandidate = e => { + assert_equals(e.candidate, null); + assert_equals(iceTransport.gatheringState, 'complete'); + assert_equals(pc1.iceGatheringState, 'complete'); + events.push('icecandidate'); + r(); + }; + }); + + assert_array_equals(events, [ + 'gatheringstatechange', + 'icegatheringstatechange', + 'icecandidate' + ], 'events must be fired on the same task in this order'); +}, 'gathering state and candidate callbacks should fire in the correct order'); + promise_test(async t => { const pc1 = new RTCPeerConnection(); t.add_cleanup(() => pc1.close()); @@ -125,7 +187,33 @@ pc1, pc2); await pc1.setLocalDescription(await pc1.createOffer({iceRestart: true})); await iceGatheringStateTransitions(pc1, 'gathering', 'complete'); - }, 'setLocalDescription(reoffer) with a new transport should cause iceGatheringState to go to "checking" and then "complete"'); + }, 'setLocalDescription(reoffer) with a restarted transport should cause iceGatheringState to go to "gathering" and then "complete"'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + pc1.addTransceiver('audio', { direction: 'recvonly' }); + pc1.addTransceiver('video', { direction: 'recvonly' }); + exchangeIceCandidates(pc1, pc2); + await pc1.setLocalDescription(); + const firstGather = Promise.all([ + iceGatheringStateTransitions(pc1, 'gathering', 'complete'), + iceGatheringStateTransitions(pc2, 'gathering', 'complete')]); + const mungedOffer = {type: 'offer', sdp: pc1.localDescription.sdp.replace('BUNDLE', 'BUNGLE')}; + await pc2.setRemoteDescription(mungedOffer); + await pc2.setLocalDescription(); + await pc1.setRemoteDescription(pc2.localDescription); + // Let gathering finish, so we don't have two generations gathering at once + // This can cause errors depending on timing. + await firstGather; + await pc1.setLocalDescription(await pc1.createOffer({iceRestart: true})); + // We only do this so we don't get errors in addCandidate. We don't want + // to wait for it, because we might miss the gathering transitions. + pc2.setRemoteDescription(pc1.localDescription); + await iceGatheringStateTransitions(pc1, 'gathering', 'complete'); + }, 'setLocalDescription(reoffer) with two restarted transports should cause iceGatheringState to go to "gathering" and then "complete"'); promise_test(async t => { const pc1 = new RTCPeerConnection(); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-answer.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-answer.html index 4c20789096..1d80eb8295 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-answer.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-answer.html @@ -75,8 +75,8 @@ assert_session_desc_similar(pc.localDescription, answer); assert_session_desc_similar(pc.remoteDescription, offer); - assert_session_desc_similar(pc.currentLocalDescription, answer); - assert_session_desc_similar(pc.currentRemoteDescription, offer); + assert_equals(pc.currentLocalDescription, pc.localDescription); + assert_equals(pc.currentRemoteDescription, pc.remoteDescription); assert_equals(pc.pendingLocalDescription, null); assert_equals(pc.pendingRemoteDescription, null); @@ -107,8 +107,8 @@ assert_session_desc_similar(pc.localDescription, answer); assert_session_desc_similar(pc.remoteDescription, offer); - assert_session_desc_similar(pc.currentLocalDescription, answer); - assert_session_desc_similar(pc.currentRemoteDescription, offer); + assert_equals(pc.currentLocalDescription, pc.localDescription); + assert_equals(pc.currentRemoteDescription, pc.remoteDescription); assert_equals(pc.pendingLocalDescription, null); assert_equals(pc.pendingRemoteDescription, null); @@ -204,7 +204,7 @@ assert_equals(pc2.pendingLocalDescription, null, "pendingLocalDescription should never be set due to sLD(answer)"); assert_not_equals(pc2.pendingRemoteDescription, null, "pendingRemoteDescription should not be set synchronously after a call to sLD"); assert_equals(pc2.pendingRemoteDescription.type, "offer"); - assert_equals(pc2.remoteDescription.sdp, pc2.pendingRemoteDescription.sdp); + assert_equals(pc2.remoteDescription, pc2.pendingRemoteDescription); assert_equals(pc2.currentLocalDescription, null, "currentLocalDescription should not be set synchronously after a call to sLD"); assert_equals(pc2.currentRemoteDescription, null, "currentRemoteDescription should not be set synchronously after a call to sLD"); @@ -219,10 +219,10 @@ assert_equals(pc2.pendingRemoteDescription, null, "pendingRemoteDescription should be updated before the signalingstatechange event"); assert_not_equals(pc2.currentLocalDescription, null, "currentLocalDescription should be updated before the signalingstatechange event"); assert_equals(pc2.currentLocalDescription.type, "answer"); - assert_equals(pc2.currentLocalDescription.sdp, pc2.localDescription.sdp); + assert_equals(pc2.currentLocalDescription, pc2.localDescription); assert_not_equals(pc2.currentRemoteDescription, null, "currentRemoteDescription should be updated before the signalingstatechange event"); assert_equals(pc2.currentRemoteDescription.type, "offer"); - assert_equals(pc2.currentRemoteDescription.sdp, pc2.remoteDescription.sdp); + assert_equals(pc2.currentRemoteDescription, pc2.remoteDescription); await sldPromise; }, "setLocalDescription(answer) should update internal state with a queued task, in the right order"); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html index 88f1de5ed8..2be83a01ce 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html @@ -68,7 +68,7 @@ .then(() => { assert_equals(pc.signalingState, 'have-local-offer'); assert_session_desc_similar(pc.localDescription, offer); - assert_session_desc_similar(pc.pendingLocalDescription, offer); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.currentLocalDescription, null); assert_array_equals(states, ['have-local-offer']); @@ -90,7 +90,7 @@ .then(() => { assert_equals(pc.signalingState, 'have-local-offer'); assert_session_desc_similar(pc.localDescription, offer); - assert_session_desc_similar(pc.pendingLocalDescription, offer); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.currentLocalDescription, null); })); }, 'setLocalDescription with type offer and null sdp should use lastOffer generated from createOffer'); @@ -219,7 +219,7 @@ assert_equals(pc.pendingRemoteDescription, null, "pendingRemoteDescription should never be set due to sLD"); assert_not_equals(pc.pendingLocalDescription, null, "pendingLocalDescription should be updated before the signalingstatechange event"); assert_equals(pc.pendingLocalDescription.type, "offer"); - assert_equals(pc.pendingLocalDescription.sdp, pc.localDescription.sdp); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.currentLocalDescription, null, "currentLocalDescription should never be updated due to sLD(offer)"); assert_equals(pc.currentRemoteDescription, null, "currentRemoteDescription should never be updated due to sLD(offer)"); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https.html index 5a7a76319a..23d1e09a9d 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https.html @@ -27,6 +27,7 @@ promise_test(async t => { await offerer.setLocalDescription(); assert_not_equals(offerer.pendingLocalDescription, null); + assert_equals(offerer.pendingLocalDescription, offerer.pendingLocalDescription); }, "Parameterless SLD() in 'stable' sets pendingLocalDescription"); promise_test(async t => { @@ -63,6 +64,7 @@ promise_test(async t => { await answerer.setRemoteDescription(await offerer.createOffer()); await answerer.setLocalDescription(); assert_not_equals(answerer.currentLocalDescription, null); + assert_equals(answerer.currentLocalDescription, answerer.currentLocalDescription); }, "Parameterless SLD() in 'have-remote-offer' sets currentLocalDescription"); promise_test(async t => { diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-rollback.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-rollback.html index 787edc92e7..08d0393cb7 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-rollback.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-rollback.html @@ -11,7 +11,6 @@ // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html // The following helper functions are called from RTCPeerConnection-helper.js: - // assert_session_desc_similar // generateAudioReceiveOnlyOffer /* @@ -68,7 +67,8 @@ .then(() => { assert_equals(pc.signalingState, 'have-local-offer'); assert_not_equals(pc.localDescription, null); - assert_not_equals(pc.pendingLocalDescription, null); + assert_equals(pc.localDescription, pc.localDescription); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.currentLocalDescription, null); return pc.setLocalDescription({ type: 'rollback' }); @@ -148,7 +148,7 @@ assert_not_equals(pc.pendingLocalDescription, null, "pendingLocalDescription should not be set synchronously after a call to sLD"); assert_equals(pc.pendingLocalDescription.type, "offer"); - assert_equals(pc.pendingLocalDescription.sdp, pc.localDescription.sdp); + assert_equals(pc.pendingLocalDescription, pc.localDescription); assert_equals(pc.pendingRemoteDescription, null, "pendingRemoteDescription should never be set due to sLD(offer)"); const stablePromise = new Promise(resolve => { diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-offer.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-offer.html index d5acb7e1c9..d95ecafc33 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-offer.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-offer.html @@ -4,6 +4,7 @@ + diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html index 0e6213d708..a72fa66738 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html @@ -69,7 +69,7 @@ .then(() => { assert_equals(pc.signalingState, 'have-remote-offer'); assert_not_equals(pc.remoteDescription, null); - assert_not_equals(pc.pendingRemoteDescription, null); + assert_equals(pc.pendingRemoteDescription, pc.remoteDescription); assert_equals(pc.currentRemoteDescription, null); return pc.setRemoteDescription({type: 'rollback'}); diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription.html index c170f766bd..e2bec7dd91 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription.html @@ -136,7 +136,7 @@ await pc2.setLocalDescription(answer); await pc.setRemoteDescription(answer); await exchangeOffer(pc2, pc); - assert_equals(pc.remoteDescription.sdp, pc.pendingRemoteDescription.sdp); + assert_equals(pc.remoteDescription, pc.pendingRemoteDescription); assert_session_desc_similar(pc.remoteDescription, offer); assert_session_desc_similar(pc.currentRemoteDescription, answer); }, 'Switching role from offerer to answerer after going back to stable state should succeed'); diff --git a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getContributingSources.https.html b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getContributingSources.https.html index 7245d477cc..a5913f6e84 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getContributingSources.https.html +++ b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getContributingSources.https.html @@ -22,6 +22,13 @@ async function connectAndExpectNoCsrcs(t, kind) { const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2); await exchangeAnswer(pc1, pc2); + // Some browsers might need an audio element attached to the DOM. + const element = document.createElement(kind); + element.autoplay = true; + element.srcObject = trackEvent.streams[0]; + document.body.appendChild(element); + t.add_cleanup(() => { document.body.removeChild(element) }); + assert_array_equals(trackEvent.receiver.getContributingSources(), []); } diff --git a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html index bfa82b979c..2fcf33dc2e 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html +++ b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html @@ -23,8 +23,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await receiver.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'inbound-rtp')); }, 'receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats'); @@ -42,8 +42,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const receiver = callee.getReceivers()[0]; - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await receiver.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'inbound-rtp')); }, 'receiver.getStats() via addTrack should return stats report containing inbound-rtp stats'); @@ -61,8 +61,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [receiver] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const [transceiver] = callee.getTransceivers(); const statsPromiseFirst = receiver.getStats(); transceiver.stop(); @@ -85,8 +85,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [receiver] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReportFirst = await receiver.getStats(); callee.close(); const statsReportSecond = await receiver.getStats(); @@ -107,8 +107,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const receiver = callee.getReceivers()[0]; - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await receiver.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'candidate-pair')); assert_true(!![...statsReport.values()].find(({type}) => type === 'local-candidate')); diff --git a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html index 8436a44ebc..cb5336f3f3 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html +++ b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html @@ -23,6 +23,14 @@ async function initiateSingleTrackCallAndReturnReceiver(t, kind) { exchangeIceCandidates(pc1, pc2); const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2); await exchangeAnswer(pc1, pc2); + + // Some browsers might need an audio element attached to the DOM. + const element = document.createElement(kind); + element.autoplay = true; + element.srcObject = trackEvent.streams[0]; + document.body.appendChild(element); + t.add_cleanup(() => { document.body.removeChild(element) }); + return trackEvent.receiver; } diff --git a/testing/web-platform/tests/webrtc/RTCRtpReceiver.https.html b/testing/web-platform/tests/webrtc/RTCRtpReceiver.https.html new file mode 100644 index 0000000000..eea013140f --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCRtpReceiver.https.html @@ -0,0 +1,96 @@ + + +RTCRtpReceiver + + + diff --git a/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html b/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html index 5c27af2134..6aeed650e5 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html +++ b/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html @@ -22,8 +22,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [ receiver ] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await sender.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp')); }, 'sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats'); @@ -41,8 +41,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [ receiver ] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await sender.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp')); }, 'sender.getStats() via addTrack should return stats report containing outbound-rtp stats'); @@ -60,8 +60,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [ receiver ] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const [sender] = caller.getSenders(); const [transceiver] = caller.getTransceivers(); @@ -85,8 +85,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [ receiver ] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const [sender] = caller.getSenders(); const statsReportFirst = await sender.getStats(); caller.close(); @@ -108,8 +108,8 @@ exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); const [ receiver ] = callee.getReceivers(); - // Wait for RTP - await new Promise(r => receiver.track.onunmute = r); + await listenToConnected(callee); + await waitForTrackUnmuted(receiver.track); const statsReport = await sender.getStats(); assert_true(!![...statsReport.values()].find(({type}) => type === 'candidate-pair')); assert_true(!![...statsReport.values()].find(({type}) => type === 'local-candidate')); diff --git a/testing/web-platform/tests/webrtc/RTCRtpSender.https.html b/testing/web-platform/tests/webrtc/RTCRtpSender.https.html index d17115c46a..21058dfeed 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpSender.https.html +++ b/testing/web-platform/tests/webrtc/RTCRtpSender.https.html @@ -17,4 +17,91 @@ test((t) => { assert_equals(t2.sender.dtmf, null); }, "Video sender @dtmf is null"); + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + assert_equals(transceiver.sender.transport, null); + }, 'RTCRtpSender should have a null transport initially'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + assert_true(transceiver.sender.transport instanceof RTCDtlsTransport); + }, 'RTCRtpSender should have a transport after sLD(offer)'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + await pc1.setLocalDescription({type: 'rollback', sdp: ''}); + assert_equals(transceiver.sender.transport, null); + }, 'RTCRtpSender should have a null transport after rollback of sLD(offer)'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + const sender = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc2.setRemoteDescription(await pc1.createOffer()); + const [transceiver] = pc2.getTransceivers(); + assert_equals(transceiver.sender.transport, null); + }, 'RTCRtpSender should have a null transport after sRD(offer)'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + assert_true(transceiver.sender.transport instanceof RTCDtlsTransport); + assert_true(transceiver.receiver.transport instanceof RTCDtlsTransport); + assert_equals(transceiver.sender.transport, transceiver.receiver.transport); + }, 'RTCRtpSender should have the same transport object as its corresponding RTCRtpReceiver'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection({bundlePolicy: 'max-bundle'}); + t.add_cleanup(() => pc1.close()); + const audioTransceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + const videoTransceiver = pc1.addTransceiver('video', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + assert_equals(videoTransceiver.sender.transport, audioTransceiver.sender.transport); + }, 'RTCRtpSenders that share a bundle transport should have the same transport object'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection({bundlePolicy: 'max-compat'}); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + const audioTransceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + const videoTransceiver = pc1.addTransceiver('video', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + assert_not_equals(videoTransceiver.sender.transport, audioTransceiver.sender.transport); + await pc2.setRemoteDescription(pc1.localDescription); + await pc1.setRemoteDescription(await pc2.createAnswer()); + // pc2 will accept the bundle, so these should be the same now + assert_equals(videoTransceiver.sender.transport, audioTransceiver.sender.transport); + }, 'RTCRtpSenders that do not necessarily share a bundle transport should not have the same transport object'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const transceiver = pc1.addTransceiver('audio', { direction: 'recvonly' }); + await pc1.setLocalDescription(); + const transportBefore = transceiver.sender.transport; + + await pc2.setRemoteDescription(pc1.localDescription); + await pc1.setRemoteDescription(await pc2.createAnswer()); + await pc1.setLocalDescription(await pc1.createOffer({iceRestart: true})); + + const transportAfter = transceiver.sender.transport; + assert_equals(transportAfter, transportBefore); + }, 'RTCRtpSender should have the same transport object after an ICE restart'); + diff --git a/testing/web-platform/tests/webrtc/RTCRtpTransceiver-setCodecPreferences.html b/testing/web-platform/tests/webrtc/RTCRtpTransceiver-setCodecPreferences.html index f779f5a94c..8142132a8c 100644 --- a/testing/web-platform/tests/webrtc/RTCRtpTransceiver-setCodecPreferences.html +++ b/testing/web-platform/tests/webrtc/RTCRtpTransceiver-setCodecPreferences.html @@ -7,41 +7,13 @@ diff --git a/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js b/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js index a516aa4c79..5cc3b745b3 100644 --- a/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js +++ b/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js @@ -16,5 +16,5 @@ promise_test(async t => { await openWebRTC(rc1); // The page should not be eligible for BFCache because of open WebRTC connection and live MediaStreamTrack. await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); - await assertNotRestoredFromBFCache(rc1, ['WebRTC', 'LiveMediaStreamTrack']); + await assertNotRestoredFromBFCache(rc1, ['webrtc', 'media-stream']); }); diff --git a/testing/web-platform/tests/webrtc/historical.html b/testing/web-platform/tests/webrtc/historical.html index ae7a29dec0..e30a7bc7d3 100644 --- a/testing/web-platform/tests/webrtc/historical.html +++ b/testing/web-platform/tests/webrtc/historical.html @@ -2,6 +2,7 @@ Historical WebRTC features +
diff --git a/testing/web-platform/tests/webrtc/protocol/codecs-filtered-by-direction.https.html b/testing/web-platform/tests/webrtc/protocol/codecs-filtered-by-direction.https.html new file mode 100644 index 0000000000..ed1d65e4b0 --- /dev/null +++ b/testing/web-platform/tests/webrtc/protocol/codecs-filtered-by-direction.https.html @@ -0,0 +1,79 @@ + + + +RTCPeerConnection Codecs in offer get filtered by direction + + + + diff --git a/testing/web-platform/tests/webrtc/protocol/codecs-subsequent-offer.https.html b/testing/web-platform/tests/webrtc/protocol/codecs-subsequent-offer.https.html new file mode 100644 index 0000000000..fc9240e950 --- /dev/null +++ b/testing/web-platform/tests/webrtc/protocol/codecs-subsequent-offer.https.html @@ -0,0 +1,50 @@ + + + +RTCPeerConnection Codecs in subsequent offer + + + + -- cgit v1.2.3