summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html')
-rw-r--r--testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html274
1 files changed, 274 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
new file mode 100644
index 0000000000..85ce8bc9f5
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
@@ -0,0 +1,274 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>RTCPeerConnection.prototype.getStats</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:
+ // webrtc-pc 20171130
+ // webrtc-stats 20171122
+
+ // The following helper function is called from RTCPeerConnection-helper.js
+ // getTrackFromUserMedia
+
+ // The following helper function is called from RTCPeerConnection-helper.js
+ // exchangeIceCandidates
+ // exchangeOfferAnswer
+
+ /*
+ 8.2. getStats
+ 1. Let selectorArg be the method's first argument.
+ 2. Let connection be the RTCPeerConnection object on which the method was invoked.
+ 3. If selectorArg is null, let selector be null.
+ 4. If selectorArg is a MediaStreamTrack let selector be an RTCRtpSender
+ or RTCRtpReceiver on connection which track member matches selectorArg.
+ If no such sender or receiver exists, or if more than one sender or
+ receiver fit this criteria, return a promise rejected with a newly
+ created InvalidAccessError.
+ 5. Let p be a new promise.
+ 6. Run the following steps in parallel:
+ 1. Gather the stats indicated by selector according to the stats selection algorithm.
+ 2. Resolve p with the resulting RTCStatsReport object, containing the gathered stats.
+ */
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ return pc.getStats();
+ }, 'getStats() with no argument should succeed');
+
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ return pc.getStats(null);
+ }, 'getStats(null) should succeed');
+
+ /*
+ 8.2. getStats
+ 4. If selectorArg is a MediaStreamTrack let selector be an RTCRtpSender
+ or RTCRtpReceiver on connection which track member matches selectorArg.
+ If no such sender or receiver exists, or if more than one sender or
+ receiver fit this criteria, return a promise rejected with a newly
+ created InvalidAccessError.
+ */
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ return getTrackFromUserMedia('audio')
+ .then(([track, mediaStream]) => {
+ return promise_rejects_dom(t, 'InvalidAccessError', pc.getStats(track));
+ });
+ }, 'getStats() with track not added to connection should reject with InvalidAccessError');
+
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ return getTrackFromUserMedia('audio')
+ .then(([track, mediaStream]) => {
+ pc.addTrack(track, mediaStream);
+ return pc.getStats(track);
+ });
+ }, 'getStats() with track added via addTrack should succeed');
+
+ 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();
+ pc.addTransceiver(track);
+
+ return pc.getStats(track);
+ }, 'getStats() with track added via addTransceiver should succeed');
+
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const transceiver1 = pc.addTransceiver('audio');
+
+ // Create another transceiver that resends what
+ // is being received, kind of like echo
+ const transceiver2 = pc.addTransceiver(transceiver1.receiver.track);
+ assert_equals(transceiver1.receiver.track, transceiver2.sender.track);
+
+ return promise_rejects_dom(t, 'InvalidAccessError', pc.getStats(transceiver1.receiver.track));
+ }, 'getStats() with track associated with both sender and receiver should reject with InvalidAccessError');
+
+ /*
+ 8.5. The stats selection algorithm
+ 2. If selector is null, gather stats for the whole connection, add them to result,
+ return result, and abort these steps.
+ */
+ promise_test(t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ return pc.getStats()
+ .then(statsReport => {
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'peer-connection'));
+ });
+ }, 'getStats() with no argument should return stats report containing peer-connection stats on an empty PC');
+
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+ const [sendtrack, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(sendtrack, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await Promise.all([
+ exchangeOfferAnswer(pc, pc2),
+ new Promise(r => pc2.ontrack = e => e.track.onunmute = r)
+ ]);
+ const statsReport = await pc.getStats();
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'peer-connection'));
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp'));
+ }, 'getStats() track with stream returns peer-connection and outbound-rtp stats');
+
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+ const [sendtrack, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(sendtrack);
+ exchangeIceCandidates(pc, pc2);
+ await Promise.all([
+ exchangeOfferAnswer(pc, pc2),
+ new Promise(r => pc2.ontrack = e => e.track.onunmute = r)
+ ]);
+ const statsReport = await pc.getStats();
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'peer-connection'));
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp'));
+ }, 'getStats() track without stream returns peer-connection and outbound-rtp stats');
+
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+ const [sendtrack, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(sendtrack, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await Promise.all([
+ exchangeOfferAnswer(pc, pc2),
+ new Promise(r => pc2.ontrack = e => e.track.onunmute = r)
+ ]);
+ const statsReport = await pc.getStats();
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp'));
+ }, 'getStats() audio contains outbound-rtp stats');
+
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+ const [sendtrack, mediaStream] = await getTrackFromUserMedia('video');
+ pc.addTrack(sendtrack, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await Promise.all([
+ exchangeOfferAnswer(pc, pc2),
+ new Promise(r => pc2.ontrack = e => e.track.onunmute = r)
+ ]);
+ const statsReport = await pc.getStats();
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp'));
+ }, 'getStats() video contains outbound-rtp stats');
+
+ /*
+ 8.5. The stats selection algorithm
+ 3. If selector is an RTCRtpSender, gather stats for and add the following objects
+ to result:
+ - All RTCOutboundRtpStreamStats objects corresponding to selector.
+ - All stats objects referenced directly or indirectly by the RTCOutboundRtpStreamStats
+ objects added.
+ */
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+
+ let [sendtrack, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(sendtrack, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await Promise.all([
+ exchangeOfferAnswer(pc, pc2),
+ new Promise(r => pc2.ontrack = e => e.track.onunmute = r)
+ ]);
+ const statsReport = await pc.getStats(sendtrack);
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'outbound-rtp'));
+ }, `getStats() on track associated with RTCRtpSender should return stats report containing outbound-rtp stats`);
+
+ /*
+ 8.5. The stats selection algorithm
+ 4. If selector is an RTCRtpReceiver, gather stats for and add the following objects
+ to result:
+ - All RTCInboundRtpStreamStats objects corresponding to selector.
+ - All stats objects referenced directly or indirectly by the RTCInboundRtpStreamStats
+ added.
+ */
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+
+ let [track, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(track, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await exchangeOfferAnswer(pc, pc2);
+ // Wait for unmute if the track is not already unmuted.
+ // According to spec, it should be muted when being created, but this
+ // is not what this test is testing, so allow it to be unmuted.
+ if (pc2.getReceivers()[0].track.muted) {
+ await new Promise(resolve => {
+ pc2.getReceivers()[0].track.addEventListener('unmute', resolve);
+ });
+ }
+ const statsReport = await pc2.getStats(pc2.getReceivers()[0].track);
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'inbound-rtp'));
+ }, `getStats() on track associated with RTCRtpReceiver should return stats report containing inbound-rtp stats`);
+
+ promise_test(async t => {
+ const pc = createPeerConnectionWithCleanup(t);
+ const pc2 = createPeerConnectionWithCleanup(t);
+
+ let [track, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTrack(track, mediaStream);
+ exchangeIceCandidates(pc, pc2);
+ await exchangeOfferAnswer(pc, pc2);
+ // Wait for unmute if the track is not already unmuted.
+ // According to spec, it should be muted when being created, but this
+ // is not what this test is testing, so allow it to be unmuted.
+ if (pc2.getReceivers()[0].track.muted) {
+ await new Promise(resolve => {
+ pc2.getReceivers()[0].track.addEventListener('unmute', resolve);
+ });
+ }
+ const statsReport = await pc2.getStats(pc2.getReceivers()[0].track);
+ assert_true(!![...statsReport.values()].find(({type}) => type === 'inbound-rtp'));
+ }, `getStats() audio contains inbound-rtp stats`);
+
+ promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const [track, mediaStream] = await getTrackFromUserMedia('audio');
+ pc.addTransceiver(track);
+ pc.addTransceiver(track);
+ await promise_rejects_dom(t, 'InvalidAccessError', pc.getStats(track));
+ }, `getStats(track) should not work if multiple senders have the same track`);
+
+ promise_test(async t => {
+ const kMinimumTimeElapsedBetweenGetStatsCallsMs = 500;
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const t0 = Math.floor(performance.now());
+ const t0Stats = [...(await pc.getStats()).values()].find(({type}) => type === 'peer-connection');
+ await new Promise(
+ r => t.step_timeout(r, kMinimumTimeElapsedBetweenGetStatsCallsMs));
+ const t1Stats = [...(await pc.getStats()).values()].find(({type}) => type === 'peer-connection');
+ const t1 = Math.ceil(performance.now());
+ const maximumTimeElapsedBetweenGetStatsCallsMs = t1 - t0;
+ const deltaTimestampMs = t1Stats.timestamp - t0Stats.timestamp;
+ // The delta must be at least the time we waited between calls.
+ assert_greater_than_equal(deltaTimestampMs,
+ kMinimumTimeElapsedBetweenGetStatsCallsMs);
+ // The delta must be at most the time elapsed before the first getStats()
+ // call and after the second getStats() call.
+ assert_less_than_equal(deltaTimestampMs,
+ maximumTimeElapsedBetweenGetStatsCallsMs);
+ }, `RTCStats.timestamp increases with time passing`);
+
+</script>