summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html')
-rw-r--r--testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html338
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>