Bug 1393306 - Test deprecation warning in 57 for removal of stat.isRemote in 58.
MozReview-Commit-ID: IL8WsU22QCi
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -330,16 +330,18 @@ skip-if = (android_version == '18') # an
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_threeUnbundledConnections.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_selftest.html]
# Bug 1227781: Crash with bogus TURN server.
[test_peerConnection_bug1227781.html]
[test_peerConnection_stats.html]
skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator, Bug 1373858)
+[test_peerConnection_stats_isRemoteWarning57.html]
+skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_sender_and_receiver_stats.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_trackless_sender_stats.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_verifyDescriptions.html]
skip-if = (android_version == '18')
[test_fingerprinting_resistance.html]
[test_getUserMedia_nonDefaultRate.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_stats_isRemoteWarning57.html
@@ -0,0 +1,152 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+
+createHTML({title: "Test 57 stat.isRemote deprecate warning", bug: "1393306"});
+
+// In 58, remote stats will move from "outbound-rtp"/"inbound-rtp" to
+// "remote-outbound-rtp"/"remote-inbound-rtp" and isRemote is gone (Bug 1380555).
+//
+// See https://blog.mozilla.org/webrtc/getstats-firefox-58/ for more.
+//
+// Test the deprecation warnings added to 57 to detect safe vs breaking access
+// of remote stats. In short, sites that rely on remoteId will be fine:
+//
+// stats.get([...stats.values()].find(s => s.type == "outbound-rtp" &&
+// !s.isRemote).remoteId).packetsReceived;
+//
+// but sites that check isRemote for true, will break in 58:
+//
+// [...stats.values()].find(s => s.type == "inbound-rtp" &&
+// s.isRemote).packetsReceived; // will break in 58
+//
+// Test exercises carefully placed mouse-traps to warn on the latter case only.
+
+function hasRtcps(stats) {
+ return [...stats.values()].find(s => s.type == "inbound-rtp" && s.isRemote) &&
+ [...stats.values()].find(s => s.type == "outbound-rtp" && s.isRemote);
+}
+
+async function testVideoCall(testCallback) {
+ let pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
+
+ pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
+ pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
+
+ pc1.onnegotiationneeded = async e => {
+ await pc1.setLocalDescription(await pc1.createOffer());
+ await pc2.setRemoteDescription(pc1.localDescription);
+ await pc2.setLocalDescription(await pc2.createAnswer());
+ await pc1.setRemoteDescription(pc2.localDescription);
+ };
+
+ let stream = await navigator.mediaDevices.getUserMedia({video: true});
+ pc1.addTrack(stream.getTracks()[0], stream);
+ pc2.addTrack(stream.getTracks()[0], stream);
+
+ while (!hasRtcps(await pc1.getStats())) {
+ await wait(100);
+ }
+
+ await testCallback(pc1, pc2);
+ pc1.close();
+ pc2.close();
+ stream.getTracks().forEach(track => track.stop());
+}
+
+function warningIsTurnedOn(stats) {
+ switch([...stats.values()][0].toString()) {
+ case "[object RTCStatsDeprecationWarningEntry]": {
+ return true;
+ }
+ case "[object Object]": {
+ return false;
+ }
+ default: {
+ ok(false, "Something went wrong!");
+ }
+ }
+}
+
+// Example reading of stats, not meant to be exhaustive.
+let mb = bytes => (bytes/1024000).toFixed(2);
+let dumpHeader = o => `${o.type} ${o.mediaType}\
+ ${new Date(o.timestamp).toTimeString()}<br>SSRC: ${o.ssrc} `;
+let dumpOutbound = o => dumpHeader(o) + `Sent: ${o.packetsSent} \
+packets (${mb(o.bytesSent)} MB)<br>Dropped frames: ${o.droppedFrames}<br>`;
+let dumpInbound = o => dumpHeader(o) + `Received: ${o.packetsReceived} \
+packets (${mb(o.bytesReceived)} MB) Lost: ${o.packetsLost}
+Discarded packets: ${o.discardedPackets} Jitter: ${o.jitter}<br>`;
+
+runNetworkTest(async options => {
+ await testVideoCall(async (pc1, pc2) => {
+ // Test typical read pattern avoiding remote stats. Must not warn.
+ {
+ let stats = await pc1.getStats();
+ for (let stat of stats.values()) {
+ switch (stat.type) {
+ case "outbound-rtp": {
+ if (stat.isRemote) continue;
+ info("Local: " + dumpOutbound(stat));
+ break;
+ }
+ case "inbound-rtp": {
+ if (stat.isRemote) continue;
+ info("Local: " + dumpInbound(stat));
+ break;
+ }
+ }
+ }
+
+ ok(warningIsTurnedOn(await pc1.getStats()),
+ "Accessing local stats must not trigger or shut down warning.");
+ }
+
+ // Reading remote stats directly must warn.
+ //
+ // NOTE: Tried and failed to detect this warning. It doesn't show up in
+ // "console-api-log-event" for some reason, so we're merely testing for
+ // warning OR warning being turned off, to exercise code. Actual warning
+ // must be observed with manual testing).
+
+ hasRtcps(await pc1.getStats()).ssrc;
+ ok(!warningIsTurnedOn(await pc1.getStats()),
+ "Access of remote stats trips warning (or turns it off, can't tell).");
+
+ // Use pc2 for remaining tests.
+ pc1 = pc2;
+
+ // Test getting remote stats through remoteId. Must turn off warning.
+ {
+ let stats = await pc1.getStats();
+ let local = [...stats.values()].find(s => s.type == "outbound-rtp" &&
+ !s.isRemote);
+
+ ok(warningIsTurnedOn(await pc1.getStats()), "Warning on still.");
+
+ let remote = stats.get(local.remoteId);
+
+ ok(!warningIsTurnedOn(await pc1.getStats()),
+ "Warning must be off (shouldn't trigger, but can't tell).");
+ }
+ });
+
+ await testVideoCall(async (pc1, pc2) => {
+ ok(warningIsTurnedOn(await pc1.getStats()), "Warning is on initially.");
+ // Check that we stop warning if we see JSON.stringify().
+ JSON.stringify([...(await pc1.getStats()).values()]);
+ ok(!warningIsTurnedOn(await pc1.getStats()),
+ "Must give up warning in the face of JSON.stringify.");
+ });
+ finish();
+});
+
+</script>
+</pre>
+</body>
+</html>