diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html new file mode 100644 index 0000000000..83095c085a --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html @@ -0,0 +1,338 @@ +<!doctype html> +<meta charset=utf-8> +<title>RTCPeerConnection.prototype.removeTrack</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> + 'use strict'; + + // Test is based on the following editor draft: + // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html + + // The following helper functions are called from RTCPeerConnection-helper.js: + // generateAnswer + + /* + 5.1. RTCPeerConnection Interface Extensions + partial interface RTCPeerConnection { + ... + void removeTrack(RTCRtpSender sender); + RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind, + optional RTCRtpTransceiverInit init); + }; + */ + + // Before calling removeTrack can be tested, one needs to add MediaStreamTracks to + // a peer connection. There are two ways for adding MediaStreamTrack: addTrack and + // addTransceiver. addTransceiver is a newer API while addTrack has been implemented + // in current browsers for some time. As a result some of the removeTrack tests have + // two versions so that removeTrack can be partially tested without addTransceiver + // and the transceiver APIs being implemented. + + /* + 5.1. removeTrack + 3. If connection's [[isClosed]] slot is true, throw an InvalidStateError. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track); + const { sender } = transceiver; + + pc.close(); + assert_throws_dom('InvalidStateError', () => pc.removeTrack(sender)); + }, 'addTransceiver - Calling removeTrack when connection is closed should throw InvalidStateError'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + pc.close(); + assert_throws_dom('InvalidStateError', () => pc.removeTrack(sender)); + }, 'addTrack - Calling removeTrack when connection is closed should throw InvalidStateError'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track); + const { sender } = transceiver; + + const pc2 = new RTCPeerConnection(); + pc2.close(); + assert_throws_dom('InvalidStateError', () => pc2.removeTrack(sender)); + }, 'addTransceiver - Calling removeTrack on different connection that is closed should throw InvalidStateError'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + const pc2 = new RTCPeerConnection(); + pc2.close(); + assert_throws_dom('InvalidStateError', () => pc2.removeTrack(sender)); + }, 'addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError'); + + /* + 5.1. removeTrack + 4. If sender was not created by connection, throw an InvalidAccessError. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track); + const { sender } = transceiver; + + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + assert_throws_dom('InvalidAccessError', () => pc2.removeTrack(sender)); + }, 'addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + assert_throws_dom('InvalidAccessError', () => pc2.removeTrack(sender)); + }, 'addTrack - Calling removeTrack on different connection should throw InvalidAccessError') + + /* + 5.1. removeTrack + 7. Set sender.track to null. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track); + const { sender } = transceiver; + + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'sendrecv'); + assert_equals(transceiver.currentDirection, null); + + pc.removeTrack(sender); + assert_equals(sender.track, null); + assert_equals(transceiver.direction, 'recvonly'); + }, 'addTransceiver - Calling removeTrack with valid sender should set sender.track to null'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + assert_equals(sender.track, track); + + pc.removeTrack(sender); + assert_equals(sender.track, null); + }, 'addTrack - Calling removeTrack with valid sender should set sender.track to null'); + + /* + 5.1. removeTrack + 7. Set sender.track to null. + 10. If transceiver.currentDirection is sendrecv set transceiver.direction + to recvonly. + */ + promise_test(async t => { + const caller = new RTCPeerConnection(); + t.add_cleanup(() => caller.close()); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = caller.addTransceiver(track); + const { sender } = transceiver; + + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'sendrecv'); + assert_equals(transceiver.currentDirection, null); + + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + callee.addTrack(track, stream); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + assert_equals(transceiver.currentDirection, 'sendrecv'); + + caller.removeTrack(sender); + assert_equals(sender.track, null); + assert_equals(transceiver.direction, 'recvonly'); + assert_equals(transceiver.currentDirection, 'sendrecv', + 'Expect currentDirection to not change'); + }, 'Calling removeTrack with currentDirection sendrecv should set direction to recvonly'); + + /* + 5.1. removeTrack + 7. Set sender.track to null. + 11. If transceiver.currentDirection is sendonly set transceiver.direction + to inactive. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track, { direction: 'sendonly' }); + const { sender } = transceiver; + + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'sendonly'); + assert_equals(transceiver.currentDirection, null); + + const offer = await pc.createOffer(); + await pc.setLocalDescription(offer); + const answer = await generateAnswer(offer); + await pc.setRemoteDescription(answer); + assert_equals(transceiver.currentDirection, 'sendonly'); + + pc.removeTrack(sender); + assert_equals(sender.track, null); + assert_equals(transceiver.direction, 'inactive'); + assert_equals(transceiver.currentDirection, 'sendonly', + 'Expect currentDirection to not change'); + }, 'Calling removeTrack with currentDirection sendonly should set direction to inactive'); + + /* + 5.1. removeTrack + 7. Set sender.track to null. + 9. If transceiver.currentDirection is recvonly or inactive, + then abort these steps. + */ + promise_test(async t => { + const caller = new RTCPeerConnection(); + t.add_cleanup(() => caller.close()); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = caller.addTransceiver(track, { direction: 'recvonly' }); + const { sender } = transceiver; + + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'recvonly'); + assert_equals(transceiver.currentDirection, null); + + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + callee.addTrack(track, stream); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + assert_equals(transceiver.currentDirection, 'recvonly'); + + caller.removeTrack(sender); + assert_equals(sender.track, null); + assert_equals(transceiver.direction, 'recvonly'); + assert_equals(transceiver.currentDirection, 'recvonly'); + }, 'Calling removeTrack with currentDirection recvonly should not change direction'); + + /* + 5.1. removeTrack + 7. Set sender.track to null. + 9. If transceiver.currentDirection is recvonly or inactive, + then abort these steps. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = pc.addTransceiver(track, { direction: 'inactive' }); + const { sender } = transceiver; + + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'inactive'); + assert_equals(transceiver.currentDirection, null); + + const offer = await pc.createOffer(); + await pc.setLocalDescription(offer); + const answer = await generateAnswer(offer); + await pc.setRemoteDescription(answer); + assert_equals(transceiver.currentDirection, 'inactive'); + + pc.removeTrack(sender); + assert_equals(sender.track, null); + assert_equals(transceiver.direction, 'inactive'); + assert_equals(transceiver.currentDirection, 'inactive'); + }, 'Calling removeTrack with currentDirection inactive should not change direction'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + pc.getTransceivers()[0].stop(); + pc.removeTrack(sender); + assert_equals(sender.track, track); + }, "Calling removeTrack on a stopped transceiver should be a no-op"); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track, stream); + + await sender.replaceTrack(null); + pc.removeTrack(sender); + assert_equals(sender.track, null); +}, "Calling removeTrack on a null track should have no effect"); + + + /* + TODO + 5.1. removeTrack + Stops sending media from sender. The RTCRtpSender will still appear + in getSenders. Doing so will cause future calls to createOffer to + mark the media description for the corresponding transceiver as + recvonly or inactive, as defined in [JSEP] (section 5.2.2.). + + When the other peer stops sending a track in this manner, an ended + event is fired at the MediaStreamTrack object. + + 6. If sender is not in senders (which indicates that it was removed + due to setting an RTCSessionDescription of type "rollback"), + then abort these steps. + 12. Update the negotiation-needed flag for connection. + */ +</script> |