diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-mandatory-getStats.https.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-mandatory-getStats.https.html | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-mandatory-getStats.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-mandatory-getStats.https.html new file mode 100644 index 0000000000..8247ba2a40 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-mandatory-getStats.https.html @@ -0,0 +1,277 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>Mandatory-to-implement stats compliance (a subset of webrtc-stats)</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="RTCPeerConnection-helper.js"></script> +<script src="dictionary-helper.js"></script> +<script src="RTCStats-helper.js"></script> +<script> +'use strict'; + +// From https://w3c.github.io/webrtc-pc/#mandatory-to-implement-stats + +const mandatory = { + RTCRtpStreamStats: [ + "ssrc", + "kind", + "transportId", + "codecId", + ], + RTCReceivedRtpStreamStats: [ + "packetsReceived", + "packetsLost", + "jitter", + "framesDropped" + ], + RTCInboundRtpStreamStats: [ + "trackIdentifier", + "remoteId", + "framesDecoded", + "nackCount", + "framesReceived", + "bytesReceived", + "totalAudioEnergy", + "totalSamplesDuration", + "packetsDiscarded" + ], + RTCRemoteInboundRtpStreamStats: [ + "localId", + "roundTripTime", + ], + RTCSentRtpStreamStats: [ + "packetsSent", + "bytesSent" + ], + RTCOutboundRtpStreamStats: [ + "remoteId", + "framesEncoded", + "nackCount", + "framesSent" + ], + RTCRemoteOutboundRtpStreamStats: [ + "localId", + "remoteTimestamp", + ], + RTCPeerConnectionStats: [ + "dataChannelsOpened", + "dataChannelsClosed", + ], + RTCDataChannelStats: [ + "label", + "protocol", + "dataChannelIdentifier", + "state", + "messagesSent", + "bytesSent", + "messagesReceived", + "bytesReceived", + ], + RTCMediaSourceStats: [ + "trackIdentifier", + "kind" + ], + RTCAudioSourceStats: [ + "totalAudioEnergy", + "totalSamplesDuration" + ], + RTCVideoSourceStats: [ + "width", + "height", + "framesPerSecond" + ], + RTCCodecStats: [ + "payloadType", + /* codecType is part of MTI but is not systematically set + per https://www.w3.org/TR/webrtc-stats/#dom-rtccodecstats-codectype + If the dictionary member is not present, it means that + this media format can be both encoded and decoded. */ + // "codecType", + "mimeType", + "clockRate", + "channels", + "sdpFmtpLine", + ], + RTCTransportStats: [ + "bytesSent", + "bytesReceived", + "selectedCandidatePairId", + "localCertificateId", + "remoteCertificateId", + ], + RTCIceCandidatePairStats: [ + "transportId", + "localCandidateId", + "remoteCandidateId", + "state", + "nominated", + "bytesSent", + "bytesReceived", + "totalRoundTripTime", + "currentRoundTripTime" + ], + RTCIceCandidateStats: [ + "address", + "port", + "protocol", + "candidateType", + "url", + ], + RTCCertificateStats: [ + "fingerprint", + "fingerprintAlgorithm", + "base64Certificate", + /* issuerCertificateId is part of MTI but is not systematically set + per https://www.w3.org/TR/webrtc-stats/#dom-rtccertificatestats-issuercertificateid + If the current certificate is at the end of the chain + (i.e. a self-signed certificate), this will not be set. */ + // "issuerCertificateId", + ], +}; + +// From https://w3c.github.io/webrtc-stats/webrtc-stats.html#rtcstatstype-str* + +const dictionaryNames = { + "codec": "RTCCodecStats", + "inbound-rtp": "RTCInboundRtpStreamStats", + "outbound-rtp": "RTCOutboundRtpStreamStats", + "remote-inbound-rtp": "RTCRemoteInboundRtpStreamStats", + "remote-outbound-rtp": "RTCRemoteOutboundRtpStreamStats", + "csrc": "RTCRtpContributingSourceStats", + "peer-connection": "RTCPeerConnectionStats", + "data-channel": "RTCDataChannelStats", + "media-source": { + audio: "RTCAudioSourceStats", + video: "RTCVideoSourceStats" + }, + "track": { + video: "RTCSenderVideoTrackAttachmentStats", + audio: "RTCSenderAudioTrackAttachmentStats" + }, + "sender": { + audio: "RTCAudioSenderStats", + video: "RTCVideoSenderStats" + }, + "receiver": { + audio: "RTCAudioReceiverStats", + video: "RTCVideoReceiverStats", + }, + "transport": "RTCTransportStats", + "candidate-pair": "RTCIceCandidatePairStats", + "local-candidate": "RTCIceCandidateStats", + "remote-candidate": "RTCIceCandidateStats", + "certificate": "RTCCertificateStats", +}; + +// From https://w3c.github.io/webrtc-stats/webrtc-stats.html (webidl) + +const parents = { + RTCVideoSourceStats: "RTCMediaSourceStats", + RTCAudioSourceStats: "RTCMediaSourceStats", + RTCReceivedRtpStreamStats: "RTCRtpStreamStats", + RTCInboundRtpStreamStats: "RTCReceivedRtpStreamStats", + RTCRemoteInboundRtpStreamStats: "RTCReceivedRtpStreamStats", + RTCSentRtpStreamStats: "RTCRtpStreamStats", + RTCOutboundRtpStreamStats: "RTCSentRtpStreamStats", + RTCRemoteOutboundRtpStreamStats : "RTCSentRtpStreamStats", +}; + +const remaining = JSON.parse(JSON.stringify(mandatory)); +for (const dictName in remaining) { + remaining[dictName] = new Set(remaining[dictName]); +} + +async function getAllStats(t, pc) { + // Try to obtain as many stats as possible, waiting up to 20 seconds for + // roundTripTime of RTCRemoteInboundRtpStreamStats and + // remoteTimestamp of RTCRemoteOutboundRtpStreamStats which can take + // several RTCP messages to calculate. + let stats; + let remoteInboundFound = false; + let remoteOutboundFound = false; + for (let i = 0; i < 20; i++) { + stats = await pc.getStats(); + const values = [...stats.values()]; + const [remoteInboundAudio, remoteInboundVideo] = ["audio", "video"].map( + kind => values.find(s => + s.type == "remote-inbound-rtp" && s.kind == kind)); + if (remoteInboundAudio && "roundTripTime" in remoteInboundAudio && + remoteInboundVideo && "roundTripTime" in remoteInboundVideo) { + remoteInboundFound = true; + } + const [remoteOutboundAudio, remoteOutboundVideo] = ["audio", "video"].map( + kind => values.find(s => + s.type == "remote-outbound-rtp" && s.kind == kind)); + if (remoteOutboundAudio && "remoteTimestamp" in remoteOutboundAudio && + remoteOutboundVideo && "remoteTimestamp" in remoteOutboundVideo) { + remoteOutboundFound = true; + } + if (remoteInboundFound && remoteOutboundFound) { + return stats; + } + await new Promise(r => t.step_timeout(r, 1000)); + } + return stats; +} + +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const dc1 = pc1.createDataChannel("dummy", {negotiated: true, id: 0}); + const dc2 = pc2.createDataChannel("dummy", {negotiated: true, id: 0}); + + const stream = await getNoiseStream({video: true, audio:true}); + for (const track of stream.getTracks()) { + pc1.addTrack(track, stream); + pc2.addTrack(track, stream); + t.add_cleanup(() => track.stop()); + } + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + const stats = await getAllStats(t, pc1); + + // The focus of this test is not API correctness, but rather to provide an + // accessible metric of implementation progress by dictionary member. We count + // whether we've seen each dictionary's mandatory members in getStats(). + + test(t => { + for (const stat of stats.values()) { + let dictName = dictionaryNames[stat.type]; + if (!dictName) continue; + if (typeof dictName == "object") { + dictName = dictName[stat.kind]; + } + + assert_equals(typeof dictName, "string", "Test error. String."); + if (dictName && mandatory[dictName]) { + do { + const memberNames = mandatory[dictName]; + const remainingNames = remaining[dictName]; + assert_true(memberNames.length > 0, "Test error. Parent not found."); + for (const memberName of memberNames) { + if (memberName in stat) { + assert_not_equals(stat[memberName], undefined, "Not undefined"); + remainingNames.delete(memberName); + } + } + dictName = parents[dictName]; + } while (dictName); + } + } + }, "Validating stats"); + + for (const dictName in mandatory) { + for (const memberName of mandatory[dictName]) { + test(t => { + assert_true(!remaining[dictName].has(memberName), + `Is ${memberName} present`); + }, `${dictName}'s ${memberName}`); + } + } +}, 'getStats succeeds'); + +</script> |