Commit 3a75ada6 authored by Henrik Boström's avatar Henrik Boström Committed by Commit Bot

[PeerConnection] Improve getStats WPT test coverage.

This CL adds two tests that were discovered missing while reviewing the
test coverage, both of which we PASS:
- getStats(tracks) throws if multiple senders have the same track.
  (Actually it turns out such a test already existed, the old one is
  removed because it was asserting unnecessarily many things.)
- RTCStats.timestamp should increase with time elapsed.

This CL also makes exsting tests more behavior driven. For example, a
test that advertises itself to test that "report containing
peer-connection stats and outbound-track-stats" should not fail because
we have not implemented RTCCodecStats.sdpFmtpLine - this is a different
behavior. As such, tests that are designed to test the existence of a
stats object are updated only to verify that.
  What the old tests were verifying - that the ENTIRE REPORT is valid
and complete - are moved and tested in a separate test that asserts
everything. These tests we still fail because of sdpFmtpLine.

Lastly, the outbound-rtp tests are updated to perform an O/A exchange.
If the senders have not been negotiated to send, there doesn't exist
an encoder or RTP packetizer and as such we shouldn't expect to see
outbound-rtp objects yet.

Bug: chromium:1148286
Change-Id: I50d476f1af41cfae80c77486481e3de06a1d8f5d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2534911Reviewed-by: default avatarPhilipp Hancke <philipp.hancke@googlemail.com>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827758}
parent 4715999c
...@@ -5905,7 +5905,6 @@ crbug.com/1093198 external/wpt/webrtc/RTCPeerConnection-addIceCandidate-timing [ ...@@ -5905,7 +5905,6 @@ crbug.com/1093198 external/wpt/webrtc/RTCPeerConnection-addIceCandidate-timing [
crbug.com/1093003 [ Mac ] external/wpt/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html [ Pass Failure ] crbug.com/1093003 [ Mac ] external/wpt/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html [ Pass Failure ]
# Sheriff 2020-06-10 # Sheriff 2020-06-10
crbug.com/1093429 [ Mac ] external/wpt/webrtc/RTCPeerConnection-getStats.https.html [ Failure ]
crbug.com/1093709 external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html [ Pass Failure ] crbug.com/1093709 external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html [ Pass Failure ]
# Sheriff 2020-06-11 # Sheriff 2020-06-11
......
...@@ -4,13 +4,18 @@ PASS getStats(null) should succeed ...@@ -4,13 +4,18 @@ PASS getStats(null) should succeed
PASS getStats() with track not added to connection should reject with InvalidAccessError PASS getStats() with track not added to connection should reject with InvalidAccessError
PASS getStats() with track added via addTrack should succeed PASS getStats() with track added via addTrack should succeed
PASS getStats() with track added via addTransceiver should succeed PASS getStats() with track added via addTransceiver should succeed
PASS getStats() with track associated with more than one sender should reject with InvalidAccessError
PASS getStats() with track associated with both sender and receiver should reject with InvalidAccessError PASS getStats() with track associated with both sender and receiver should reject with InvalidAccessError
PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false PASS getStats() track with stream returns peer-connection and outbound-rtp stats
FAIL getStats() with no argument should return stats for no-stream tracks assert_equals: Expect dictionary.totalAudioEnergy to be number expected "number" but got "undefined" PASS getStats() track without stream returns peer-connection and outbound-rtp stats
FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.senderId to be string expected "string" but got "undefined" PASS getStats() audio outbound-rtp contains all mandatory stats
FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined" PASS getStats() video outbound-rtp contains all mandatory stats
FAIL getStats() audio and video validate all mandatory stats assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined"
PASS getStats() on track associated with RTCRtpSender should return stats report containing outbound-rtp stats
PASS getStats() on track associated with RTCRtpReceiver should return stats report containing inbound-rtp stats
PASS getStats() inbound-rtp contains all mandatory stats
FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined" Reached unreachable code FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined" Reached unreachable code
PASS getStats(track) should not work if multiple senders have the same track
PASS RTCStats.timestamp increases with time passing
Harness: the test ran to completion. Harness: the test ran to completion.
...@@ -90,23 +90,6 @@ ...@@ -90,23 +90,6 @@
return pc.getStats(track); return pc.getStats(track);
}, 'getStats() with track added via addTransceiver should succeed'); }, 'getStats() with track added via addTransceiver should succeed');
promise_test(t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
return getTrackFromUserMedia('audio')
.then(([track, mediaStream]) => {
// addTransceiver allows adding same track multiple times
const transceiver1 = pc.addTransceiver(track);
const transceiver2 = pc.addTransceiver(track);
assert_not_equals(transceiver1, transceiver2);
assert_not_equals(transceiver1.sender, transceiver2.sender);
assert_equals(transceiver1.sender.track, transceiver2.sender.track);
return promise_rejects_dom(t, 'InvalidAccessError', pc.getStats(track));
});
}, `getStats() with track associated with more than one sender should reject with InvalidAccessError`);
promise_test(t => { promise_test(t => {
const pc = new RTCPeerConnection(); const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close()); t.add_cleanup(() => pc.close());
...@@ -135,35 +118,69 @@ ...@@ -135,35 +118,69 @@
}); });
}, 'getStats() with no argument should return stats report containing peer-connection stats on an empty PC'); }, 'getStats() with no argument should return stats report containing peer-connection stats on an empty PC');
promise_test(t => { promise_test(async t => {
const pc = new RTCPeerConnection(); const pc = createPeerConnectionWithCleanup(t);
t.add_cleanup(() => pc.close()); const pc2 = createPeerConnectionWithCleanup(t);
return getTrackFromUserMedia('audio') const [track, mediaStream] = await getTrackFromUserMedia('audio');
.then(([track, mediaStream]) => { pc.addTrack(track, mediaStream);
pc.addTrack(track, mediaStream); exchangeIceCandidates(pc, pc2);
return pc.getStats(); await exchangeOfferAnswer(pc, pc2);
}) await listenToConnected(pc);
.then(statsReport => { const statsReport = await pc.getStats();
// validateStatsReport(statsReport); getRequiredStats(statsReport, 'peer-connection');
// assert_stats_report_has_stats(statsReport, ['peer-connection']); getRequiredStats(statsReport, 'outbound-rtp');
assert_stats_report_has_stats(statsReport, ['outbound-rtp']); }, 'getStats() track with stream returns peer-connection and outbound-rtp stats');
});
}, 'getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats');
promise_test(t => { promise_test(async t => {
const pc = new RTCPeerConnection(); const pc = createPeerConnectionWithCleanup(t);
t.add_cleanup(() => pc.close()); const pc2 = createPeerConnectionWithCleanup(t);
return getTrackFromUserMedia('audio') const [track, mediaStream] = await getTrackFromUserMedia('audio');
.then(([track, mediaStream]) => { pc.addTrack(track);
pc.addTrack(track); exchangeIceCandidates(pc, pc2);
return pc.getStats(); await exchangeOfferAnswer(pc, pc2);
}) await listenToConnected(pc);
.then(statsReport => { const statsReport = await pc.getStats();
validateStatsReport(statsReport); getRequiredStats(statsReport, 'peer-connection');
assert_stats_report_has_stats(statsReport, ['peer-connection']); getRequiredStats(statsReport, 'outbound-rtp');
assert_stats_report_has_stats(statsReport, ['outbound-rtp']); }, 'getStats() track without stream returns peer-connection and outbound-rtp stats');
});
}, 'getStats() with no argument should return stats for no-stream tracks'); promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track, mediaStream] = await getTrackFromUserMedia('audio');
pc.addTrack(track, mediaStream);
exchangeIceCandidates(pc, pc2);
await exchangeOfferAnswer(pc, pc2);
await listenToConnected(pc);
const statsReport = await pc.getStats();
assert_stats_report_has_stats(statsReport, ['outbound-rtp']);
}, 'getStats() audio outbound-rtp contains all mandatory stats');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track, mediaStream] = await getTrackFromUserMedia('video');
pc.addTrack(track, mediaStream);
exchangeIceCandidates(pc, pc2);
await exchangeOfferAnswer(pc, pc2);
await listenToConnected(pc);
const statsReport = await pc.getStats();
assert_stats_report_has_stats(statsReport, ['outbound-rtp']);
}, 'getStats() video outbound-rtp contains all mandatory stats');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [audioTrack, audioStream] = await getTrackFromUserMedia('audio');
pc.addTrack(audioTrack, audioStream);
const [videoTrack, videoStream] = await getTrackFromUserMedia('video');
pc.addTrack(videoTrack, videoStream);
exchangeIceCandidates(pc, pc2);
await exchangeOfferAnswer(pc, pc2);
await listenToConnected(pc);
const statsReport = await pc.getStats();
validateStatsReport(statsReport);
}, 'getStats() audio and video validate all mandatory stats');
/* /*
8.5. The stats selection algorithm 8.5. The stats selection algorithm
...@@ -181,12 +198,10 @@ ...@@ -181,12 +198,10 @@
pc.addTrack(track, mediaStream); pc.addTrack(track, mediaStream);
exchangeIceCandidates(pc, pc2); exchangeIceCandidates(pc, pc2);
await exchangeOfferAnswer(pc, pc2); await exchangeOfferAnswer(pc, pc2);
await listenToIceConnected(pc); await listenToConnected(pc);
const stats = await pc.getStats(track); const stats = await pc.getStats(track);
validateStatsReport(stats); getRequiredStats(stats, 'outbound-rtp');
assert_stats_report_has_stats(stats, ['outbound-rtp']); }, `getStats() on track associated with RTCRtpSender should return stats report containing outbound-rtp stats`);
}, `getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats`);
/* /*
8.5. The stats selection algorithm 8.5. The stats selection algorithm
...@@ -213,9 +228,28 @@ ...@@ -213,9 +228,28 @@
}); });
} }
const stats = await pc2.getStats(pc2.getReceivers()[0].track); const stats = await pc2.getStats(pc2.getReceivers()[0].track);
validateStatsReport(stats); getRequiredStats(stats, 'inbound-rtp');
assert_stats_report_has_stats(stats, ['inbound-rtp']); }, `getStats() on track associated with RTCRtpReceiver should return stats report containing inbound-rtp stats`);
}, `getStats() on track associated with RtpReceiver 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 stats = await pc2.getStats(pc2.getReceivers()[0].track);
getRequiredStats(stats, 'inbound-rtp');
}, `getStats() inbound-rtp contains all mandatory stats`);
/* /*
8.6 Mandatory To Implement Stats 8.6 Mandatory To Implement Stats
...@@ -339,4 +373,34 @@ ...@@ -339,4 +373,34 @@
}, `getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats`); }, `getStats() with connected peer connections having tracks and data channel should return all mandatory to implement 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 = performance.now();
const t0Stats = getRequiredStats(await pc.getStats(), 'peer-connection');
await new Promise(
r => t.step_timeout(r, kMinimumTimeElapsedBetweenGetStatsCallsMs));
const t1Stats = getRequiredStats(await pc.getStats(), 'peer-connection');
const t1 = 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> </script>
This is a testharness.js-based test.
PASS getStats() with no argument should succeed
PASS getStats(null) should succeed
PASS getStats() with track not added to connection should reject with InvalidAccessError
PASS getStats() with track added via addTrack should succeed
PASS getStats() with track added via addTransceiver should succeed
PASS getStats() with track associated with more than one sender should reject with InvalidAccessError
PASS getStats() with track associated with both sender and receiver should reject with InvalidAccessError
PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
FAIL getStats() with no argument should return stats for no-stream tracks assert_equals: Expect dictionary.totalAudioEnergy to be number expected "number" but got "undefined"
FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.codecType to be string expected "string" but got "undefined"
FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.address to be string expected "string" but got "undefined"
FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.codecType to be string expected "string" but got "undefined" Reached unreachable code
Harness: the test ran to completion.
...@@ -4,13 +4,18 @@ PASS getStats(null) should succeed ...@@ -4,13 +4,18 @@ PASS getStats(null) should succeed
PASS getStats() with track not added to connection should reject with InvalidAccessError PASS getStats() with track not added to connection should reject with InvalidAccessError
PASS getStats() with track added via addTrack should succeed PASS getStats() with track added via addTrack should succeed
FAIL getStats() with track added via addTransceiver should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL getStats() with track added via addTransceiver should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL getStats() with track associated with more than one sender should reject with InvalidAccessError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL getStats() with track associated with both sender and receiver should reject with InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. FAIL getStats() with track associated with both sender and receiver should reject with InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false PASS getStats() track with stream returns peer-connection and outbound-rtp stats
FAIL getStats() with no argument should return stats for no-stream tracks assert_equals: Expect dictionary.totalAudioEnergy to be number expected "number" but got "undefined" PASS getStats() track without stream returns peer-connection and outbound-rtp stats
FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.senderId to be string expected "string" but got "undefined" PASS getStats() audio outbound-rtp contains all mandatory stats
FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined" PASS getStats() video outbound-rtp contains all mandatory stats
FAIL getStats() audio and video validate all mandatory stats assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined"
PASS getStats() on track associated with RTCRtpSender should return stats report containing outbound-rtp stats
PASS getStats() on track associated with RTCRtpReceiver should return stats report containing inbound-rtp stats
PASS getStats() inbound-rtp contains all mandatory stats
FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined" Reached unreachable code FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined" Reached unreachable code
FAIL getStats(track) should not work if multiple senders have the same track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
PASS RTCStats.timestamp increases with time passing
Harness: the test ran to completion. Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment