summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html')
-rw-r--r--testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html217
1 files changed, 217 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html b/testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html
new file mode 100644
index 0000000000..16be25fe13
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCRtpTransceiver-stopping.https.html
@@ -0,0 +1,217 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+['audio', 'video'].forEach((kind) => {
+ 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(kind);
+
+ // Complete O/A exchange such that the transceiver gets associated.
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ assert_not_equals(transceiver.mid, null, 'mid before stop()');
+ assert_not_equals(transceiver.direction, 'stopped',
+ 'direction before stop()');
+ assert_not_equals(transceiver.currentDirection, 'stopped',
+ 'currentDirection before stop()');
+
+ // Stop makes it stopping, but not stopped.
+ transceiver.stop();
+ assert_not_equals(transceiver.mid, null, 'mid after stop()');
+ assert_equals(transceiver.direction, 'stopped', 'direction after stop()');
+ assert_not_equals(transceiver.currentDirection, 'stopped',
+ 'currentDirection after stop()');
+
+ // Negotiating makes it stopped.
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ assert_equals(transceiver.mid, null, 'mid after negotiation');
+ assert_equals(transceiver.direction, 'stopped',
+ 'direction after negotiation');
+ assert_equals(transceiver.currentDirection, 'stopped',
+ 'currentDirection after negotiation');
+ }, `[${kind}] Locally stopped transceiver goes from stopping to stopped`);
+
+ promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+
+ const transceiver = pc.addTransceiver(kind);
+ const trackEnded = new Promise(
+ r => { transceiver.receiver.track.onended = () => { r(); } });
+ assert_equals(transceiver.receiver.track.readyState, 'live');
+ transceiver.stop();
+ // Stopping triggers ending the track, but this happens asynchronously.
+ assert_equals(transceiver.receiver.track.readyState, 'live');
+ await trackEnded;
+ assert_equals(transceiver.receiver.track.readyState, 'ended');
+ }, `[${kind}] Locally stopping a transceiver ends the track`);
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const pc1Transceiver = pc1.addTransceiver(kind);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const [pc2Transceiver] = pc2.getTransceivers();
+
+ pc1Transceiver.stop();
+
+ await pc1.setLocalDescription();
+ assert_equals(pc2Transceiver.receiver.track.readyState, 'live');
+ // Applying the remote offer immediately ends the track, we don't need to
+ // create or apply an answer.
+ await pc2.setRemoteDescription(pc1.localDescription);
+ assert_equals(pc2Transceiver.receiver.track.readyState, 'ended');
+ }, `[${kind}] Remotely stopping a transceiver ends the track`);
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const pc1Transceiver = pc1.addTransceiver(kind);
+
+ // Complete O/A exchange such that the transceiver gets associated.
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+ const [pc2Transceiver] = pc2.getTransceivers();
+ assert_not_equals(pc2Transceiver.mid, null, 'mid before stop()');
+ assert_not_equals(pc2Transceiver.direction, 'stopped',
+ 'direction before stop()');
+ assert_not_equals(pc2Transceiver.currentDirection, 'stopped',
+ 'currentDirection before stop()');
+
+ // Make the remote transceiver stopped.
+ pc1Transceiver.stop();
+
+ // Negotiating makes it stopped.
+ assert_equals(pc2.getTransceivers().length, 1);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ // As soon as the remote offer is set, the transceiver is stopped but it is
+ // not disassociated or removed until setting the local answer.
+ assert_equals(pc2.getTransceivers().length, 1);
+ assert_not_equals(pc2Transceiver.mid, null, 'mid during negotiation');
+ assert_equals(pc2Transceiver.direction, 'stopped',
+ 'direction during negotiation');
+ assert_equals(pc2Transceiver.currentDirection, 'stopped',
+ 'currentDirection during negotiation');
+ await pc2.setLocalDescription();
+ assert_equals(pc2.getTransceivers().length, 0);
+ assert_equals(pc2Transceiver.mid, null, 'mid after negotiation');
+ assert_equals(pc2Transceiver.direction, 'stopped',
+ 'direction after negotiation');
+ assert_equals(pc2Transceiver.currentDirection, 'stopped',
+ 'currentDirection after negotiation');
+ }, `[${kind}] Remotely stopped transceiver goes directly to stopped`);
+
+ promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+
+ const transceiver = pc.addTransceiver(kind);
+
+ // Rollback does not end the track, because the transceiver is not removed.
+ await pc.setLocalDescription();
+ await pc.setLocalDescription({type:'rollback'});
+ assert_equals(transceiver.receiver.track.readyState, 'live');
+ }, `[${kind}] Rollback when transceiver is not removed does not end track`);
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const pc1Transceiver = pc1.addTransceiver(kind);
+
+ // Start negotiation, causing a transceiver to be created.
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ const [pc2Transceiver] = pc2.getTransceivers();
+
+ // Rollback such that the transceiver is removed.
+ await pc2.setLocalDescription({type:'rollback'});
+ assert_equals(pc2.getTransceivers().length, 0);
+ assert_equals(pc2Transceiver.receiver.track.readyState, 'ended');
+ }, `[${kind}] Rollback when removing transceiver does end the track`);
+
+ // Same test as above but looking at direction and currentDirection.
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const pc1Transceiver = pc1.addTransceiver(kind);
+
+ // Start negotiation, causing a transceiver to be created.
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ const [pc2Transceiver] = pc2.getTransceivers();
+
+ // Rollback such that the transceiver is removed.
+ await pc2.setLocalDescription({type:'rollback'});
+ assert_equals(pc2.getTransceivers().length, 0);
+ // The removed transceiver is stopped.
+ assert_equals(pc2Transceiver.currentDirection, 'stopped',
+ 'currentDirection indicate stopped');
+ // A stopped transceiver is necessarily also stopping.
+ assert_equals(pc2Transceiver.direction, 'stopped',
+ 'direction indicate stopping');
+ // A stopped transceiver has no mid.
+ assert_equals(pc2Transceiver.mid, null, 'not associated');
+ }, `[${kind}] Rollback when removing transceiver makes it stopped`);
+
+ promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const constraints = {};
+ constraints[kind] = true;
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
+ const [track] = stream.getTracks();
+
+ pc1.addTrack(track);
+ pc2.addTrack(track);
+ const transceiver = pc2.getTransceivers()[0];
+
+ const ontrackEvent = new Promise(r => {
+ pc2.ontrack = e => r(e.track);
+ });
+
+ // Simulate glare: both peer connections set local offers.
+ await pc1.setLocalDescription();
+ await pc2.setLocalDescription();
+ // Set remote offer, which implicitly rolls back the local offer. Because
+ // `transceiver` is an addTrack-transceiver, it should get repurposed.
+ await pc2.setRemoteDescription(pc1.localDescription);
+ assert_equals(transceiver.receiver.track.readyState, 'live');
+ // Sanity check: the track should still be live when ontrack fires.
+ assert_equals((await ontrackEvent).readyState, 'live');
+ }, `[${kind}] Glare when transceiver is not removed does not end track`);
+});
+</script>