commit 4c90575bfc410a0868a1966bb2bd1920f3809dca
parent 3915f574e791c4e627094c5a855da91714faa1ec
Author: Nico Grunbaum <na-g@nostrum.com>
Date: Thu, 2 Oct 2025 18:59:52 +0000
Bug 1988565 - additional outbound-rtp stat lifetime WPTs;r=jib
Differential Revision: https://phabricator.services.mozilla.com/D264893
Diffstat:
1 file changed, 75 insertions(+), 4 deletions(-)
diff --git a/testing/web-platform/tests/webrtc/rtp-stats-lifetime.https.html b/testing/web-platform/tests/webrtc/rtp-stats-lifetime.https.html
@@ -19,12 +19,12 @@ async function hasStats(pc, type) {
return false;
}
-async function getInboundRtpPollUntilItExists(pc, kTimeoutMs = 10000) {
+async function getStatsTypePollUntilItExists(pc, type, kTimeoutMs = 10000) {
const t0 = performance.now();
while (performance.now() - t0 < kTimeoutMs) {
const report = await pc.getStats();
for (const stats of report.values()) {
- if (stats.type == 'inbound-rtp') {
+ if (stats.type == type) {
return stats;
}
}
@@ -118,7 +118,7 @@ promise_test(async t => {
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
await sender.replaceTrack(track);
- const inboundRtp = await getInboundRtpPollUntilItExists(pc2);
+ const inboundRtp = await getStatsTypePollUntilItExists(pc2, 'inbound-rtp');
assert_not_equals(
inboundRtp, null,
'inbound-rtp should be created in response to the sender having a track');
@@ -127,6 +127,77 @@ promise_test(async t => {
'inbound-rtp must only exist after packets have been received');
}, `RTCInboundRtpStreamStats are created by packet reception`);
+[{name: "pc", statsSource: pc => pc, kind: "audio"},
+ {name: "sender", statsSource: pc => pc.getSenders()[0], kind: "audio"},
+ {name: "pc", statsSource: pc => pc, kind: "video"},
+ {name: "sender", statsSource: pc => pc.getSenders()[0], kind: "video"}
+].forEach(({name, statsSource, kind}) => promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
+ pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
+
+ // Check statsSource for the outbound-rtp stats object.
+ const getOutboundRtpStats = async () =>
+ await getStatsTypePollUntilItExists(statsSource(pc1), 'outbound-rtp');
+
+ pc1.addTransceiver(kind);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ {
+ const stats = await getOutboundRtpStats();
+ assert_not_equals(
+ stats, null,
+ `outbound-rtp should be created in ${name}.getStats() in response to the pc.addTransceiver("${kind}")`);
+ }
+}, `RTCOutboundRtpStreamStats exist in ${name}.getStats() after created with a null track through pc.addTransceiver("${kind}")`));
+
+[{name: "pc", statsSource: pc => pc},
+ {name: "sender", statsSource: pc => pc.getSenders()[0]}
+].forEach(({name, statsSource}) => promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+ pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
+ pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
+
+ // Check statsSource for the outbound-rtp stats object.
+ const getOutboundRtpStats = async () =>
+ await getStatsTypePollUntilItExists(statsSource(pc1), 'outbound-rtp');
+
+ const stream = await getNoiseStream({video:true});
+
+ const {sender} = pc1.addTransceiver(stream.getTracks()[0]);
+ await pc1.setLocalDescription();
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription();
+ await pc1.setRemoteDescription(pc2.localDescription);
+
+ // Wait for packets to be received
+ {
+ const stats = await getOutboundRtpStats();
+ assert_not_equals(
+ stats, null,
+ `outbound-rtp should be created in ${name}.getStats() in response to the sender having a track`);
+ }
+
+ // Stop sending. This should not remove the outbound-rtp stats object.
+ await sender.replaceTrack(null);
+ // Check that the outbound-rtp stats object remains.
+ {
+ const stats = await getOutboundRtpStats();
+ assert_not_equals(
+ stats, null,
+ `outbound-rtp should remain in ${name}.getStats() after sender.replaceTrack(null)`);
+ }
+}, `RTCOutboundRtpStreamStats remain in ${name}.getStats() after sender.replaceTrack(null)`));
+
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
@@ -164,7 +235,7 @@ promise_test(async t => {
// But because of early media, we're still able to receive packets.
// - Whether or not we unmute the track in response to this is outside the
// scope of this test.
- const inboundRtp = await getInboundRtpPollUntilItExists(pc1);
+ const inboundRtp = await getStatsTypePollUntilItExists(pc1, 'inbound-rtp');
assert_not_equals(
inboundRtp, null,
'inbound-rtp should be created in the early media use case');