blob: fd08a16b11ec3dfabaad5af70b8f38255226c5a9 [file] [log] [blame]
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>Support for all stats defined in WebRTC Stats</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="../webrtc/RTCPeerConnection-helper.js"></script>
<script src="../webrtc/dictionary-helper.js"></script>
<script src="../webrtc/RTCStats-helper.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script>
'use strict';
// inspired from similar test for MTI stats in ../webrtc/RTCPeerConnection-mandatory-getStats.https.html
// 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"
},
"sender": {
audio: "RTCAudioSenderStats",
video: "RTCVideoSenderStats"
},
"receiver": {
audio: "RTCAudioReceiverStats",
video: "RTCVideoReceiverStats",
},
"transport": "RTCTransportStats",
"candidate-pair": "RTCIceCandidatePairStats",
"local-candidate": "RTCIceCandidateStats",
"remote-candidate": "RTCIceCandidateStats",
"certificate": "RTCCertificateStats",
};
async function getAllStats(t, pc) {
// Try to obtain as many stats as possible, waiting up to 20 seconds for
// roundTripTime which can take several RTCP messages to calculate.
let stats;
for (let i = 0; i < 20; i++) {
stats = await pc.getStats();
const values = [...stats.values()];
const [audio, video] = ["audio", "video"].map(kind =>
values.find(s => s.type == "remote-inbound-rtp" && s.kind == kind));
if (audio && "roundTripTime" in audio &&
video && "roundTripTime" in video) {
return stats;
}
await new Promise(r => t.step_timeout(r, 1000));
}
return stats;
}
promise_test(async t => {
// load the IDL to know which members to be looking for
const idl = await fetch("/interfaces/webrtc-stats.idl").then(r => r.text());
// for RTCStats definition
const webrtcIdl = await fetch("/interfaces/webrtc.idl").then(r => r.text());
const astArray = WebIDL2.parse(idl + webrtcIdl);
let all = {};
for (let type in dictionaryNames) {
// TODO: make use of audio/video distinction
let dictionaries = dictionaryNames[type].audio ? Object.values(dictionaryNames[type]) : [dictionaryNames[type]];
all[type] = [];
let i = 0;
// Recursively collect members from inherited dictionaries
while (i < dictionaries.length) {
const dictName = dictionaries[i];
const dict = astArray.find(i => i.name === dictName && i.type === "dictionary");
if (dict && dict.members) {
all[type] = all[type].concat(dict.members.map(m => m.name));
if (dict.inheritance) {
dictionaries.push(dict.inheritance);
}
}
i++;
}
// Unique-ify
all[type] = [...new Set(all[type])];
}
const remaining = JSON.parse(JSON.stringify(all));
for (const type in remaining) {
remaining[type] = new Set(remaining[type]);
}
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 members in getStats().
test(t => {
for (const stat of stats.values()) {
if (all[stat.type]) {
const memberNames = all[stat.type];
const remainingNames = remaining[stat.type];
assert_true(memberNames.length > 0, "Test error. No member found.");
for (const memberName of memberNames) {
if (memberName in stat) {
assert_not_equals(stat[memberName], undefined, "Not undefined");
remainingNames.delete(memberName);
}
}
}
}
}, "Validating stats");
for (const type in all) {
for (const memberName of all[type]) {
test(t => {
assert_true(!remaining[type].has(memberName),
`Is ${memberName} present`);
}, `${type}'s ${memberName}`);
}
}
}, 'getStats succeeds');
</script>