diff options
Diffstat (limited to '')
-rw-r--r-- | testing/web-platform/tests/webrtc/RollbackEvents.https.html | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RollbackEvents.https.html b/testing/web-platform/tests/webrtc/RollbackEvents.https.html new file mode 100644 index 0000000000..25c83842c9 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RollbackEvents.https.html @@ -0,0 +1,231 @@ +<!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) => { + // Make sure "ontrack" fires if a prevuously rolled back track is added back. + promise_test(async t => { + const constraints = {}; + constraints[kind] = true; + const stream = await navigator.mediaDevices.getUserMedia(constraints); + const [track] = stream.getTracks(); + t.add_cleanup(() => track.stop()); + + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + pc1.addTrack(track, stream); + pc2.addTrack(track, stream); + const [pc1Transceiver] = pc1.getTransceivers(); + const [pc2Transceiver] = pc2.getTransceivers(); + + let remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2); + + // Apply remote offer, but don't complete the entire exchange. + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + // The addTrack-transceiver gets associated, no need for a second + // transceiver. + assert_equals(pc2.getTransceivers().length, 1); + const remoteStream = await remoteStreamViaOnTrackPromise; + assert_equals(remoteStream.id, stream.id); + + const onRemoveTrackPromise = new Promise(r => { + remoteStream.onremovetrack = () => { r(); }; + }); + + // Cause track removal due to rollback. + await pc2.setRemoteDescription({type:'rollback'}); + // The track was removed. + await onRemoveTrackPromise; + + // Sanity check that ontrack still fires if we add it back again by applying + // the same remote offer. + remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2); + await pc2.setRemoteDescription(pc1.localDescription); + const revivedRemoteStream = await remoteStreamViaOnTrackPromise; + // This test only expects IDs to be the same. The same stream object should + // also be used, but this should be covered by separate tests. + // TODO(https://crbug.com/1321738): Add MediaStream identity tests. + assert_equals(remoteStream.id, revivedRemoteStream.id); + // No cheating, the same transciever should be used as before. + assert_equals(pc2.getTransceivers().length, 1); + }, `[${kind}] Track with stream: removal due to disassociation in rollback and then add it back again`); + + // This is the same test as above, but this time without any remote streams. + // This test could fail if [[FiredDirection]] was not reset in a rollback but + // the above version of the test might still pass due to the track being + // re-added to its stream. + promise_test(async t => { + const constraints = {}; + constraints[kind] = true; + const stream = await navigator.mediaDevices.getUserMedia(constraints); + const [track] = stream.getTracks(); + t.add_cleanup(() => track.stop()); + + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + pc1.addTrack(track); + pc2.addTrack(track); + const [pc1Transceiver] = pc1.getTransceivers(); + const [pc2Transceiver] = pc2.getTransceivers(); + + let remoteTrackPromise = getTrackViaOnTrackPromise(pc2); + + // Apply remote offer, but don't complete the entire exchange. + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + // The addTrack-transceiver gets associated, no need for a second + // transceiver. + assert_equals(pc2.getTransceivers().length, 1); + const remoteTrack = await remoteTrackPromise; + assert_not_equals(remoteTrack, null); + + // Cause track removal due to rollback. + await pc2.setRemoteDescription({type:'rollback'}); + // There's nothing equivalent to stream.onremovetrack when you don't have a + // stream, but the track should become muted (if it isn't already). + if (!remoteTrack.muted) { + await new Promise(r => remoteTrack.onmute = () => { r(); }); + } + assert_equals(remoteTrack.muted, true); + + // Sanity check that ontrack still fires if we add it back again by applying + // the same remote offer. + remoteTrackPromise = getTrackViaOnTrackPromise(pc2); + await pc2.setRemoteDescription(pc1.localDescription); + const revivedRemoteTrack = await remoteTrackPromise; + // We can be sure the same track is used, because the same transceiver is + // used (and transciever.receiver.track has same lifetime as transceiver). + assert_equals(pc2.getTransceivers().length, 1); + assert_equals(remoteTrack, revivedRemoteTrack); + }, `[${kind}] Track without stream: removal due to disassociation in rollback and then add it back`); + + // Make sure "ontrack" can fire in a rollback (undo making it inactive). + promise_test(async t => { + const constraints = {}; + constraints[kind] = true; + const stream = await navigator.mediaDevices.getUserMedia(constraints); + const [track] = stream.getTracks(); + t.add_cleanup(() => track.stop()); + + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + pc1.addTrack(track, stream); + const [pc1Transceiver] = pc1.getTransceivers(); + + let remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2); + + // 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_equals(pc2Transceiver.direction, 'recvonly'); + assert_equals(pc2Transceiver.currentDirection, 'recvonly'); + + const remoteStream = await remoteStreamViaOnTrackPromise; + assert_equals(remoteStream.id, stream.id); + const onRemoveTrackPromise = new Promise(r => { + remoteStream.onremovetrack = () => { r(); }; + }); + + // Cause track removal. + pc1Transceiver.direction = 'inactive'; + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + // The track was removed. + await onRemoveTrackPromise; + + // Rolling back the offer revives the track, causing ontrack to fire again. + remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2); + await pc2.setRemoteDescription({type:'rollback'}); + const revivedRemoteStream = await remoteStreamViaOnTrackPromise; + // This test only expects IDs to be the same. The same stream object should + // also be used, but this should be covered by separate tests. + // TODO(https://crbug.com/1321738): Add MediaStream identity tests. + assert_equals(remoteStream.id, revivedRemoteStream.id); + }, `[${kind}] Track with stream: removal due to direction changing and then add back using rollback`); + + // Same test as above but without remote streams. + promise_test(async t => { + const constraints = {}; + constraints[kind] = true; + const stream = await navigator.mediaDevices.getUserMedia(constraints); + const [track] = stream.getTracks(); + t.add_cleanup(() => track.stop()); + + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + pc1.addTrack(track); + const [pc1Transceiver] = pc1.getTransceivers(); + + let remoteTrackPromise = getTrackViaOnTrackPromise(pc2); + + // 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_equals(pc2Transceiver.direction, 'recvonly'); + assert_equals(pc2Transceiver.currentDirection, 'recvonly'); + + const remoteTrack = await remoteTrackPromise; + + // Cause track removal. + pc1Transceiver.direction = 'inactive'; + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + // There's nothing equivalent to stream.onremovetrack when you don't have a + // stream, but the track should become muted (if it isn't already). + if (!remoteTrack.muted) { + await new Promise(r => remoteTrack.onmute = () => { r(); }); + } + assert_equals(remoteTrack.muted, true); + + // Rolling back the offer revives the track, causing ontrack to fire again. + remoteTrackPromise = getTrackViaOnTrackPromise(pc2); + await pc2.setRemoteDescription({type:'rollback'}); + const revivedRemoteTrack = await remoteTrackPromise; + // We can be sure the same track is used, because the same transceiver is + // used (and transciever.receiver.track has same lifetime as transceiver). + assert_equals(pc2.getTransceivers().length, 1); + assert_equals(remoteTrack, revivedRemoteTrack); + }, `[${kind}] Track without stream: removal due to direction changing and then add back using rollback`); +}); + +function getTrackViaOnTrackPromise(pc) { + return new Promise(r => { + pc.ontrack = e => { + pc.ontrack = null; + r(e.track); + }; + }); +} + +function getRemoteStreamViaOnTrackPromise(pc) { + return new Promise(r => { + pc.ontrack = e => { + pc.ontrack = null; + r(e.streams[0]); + }; + }); +} + +</script> |