Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="sdpUtils.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="iceTestUtils.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1401592",
title: "Test that glean is recording stats as expected",
visible: true
});
const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
async function gleanResetTestValues() {
return SpecialPowers.spawnChrome([], async () => {
await Services.fog.testFlushAllChildren();
Services.fog.testResetFOG();
}
)
};
async function gleanFlushChildren() {
return SpecialPowers.spawnChrome([], async () => {
await Services.fog.testFlushAllChildren();
}
)
};
async function getAllWarningRates() {
return {
warnNoGetparameters:
await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue(),
warnLengthChanged:
await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue(),
warnNoTransactionid:
await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue(),
};
}
const tests = [
// Keep these tests first; we do not want previous test-cases that might
// still be doing DTLS handshake stuff while their PCs are closing.
async function checkDtlsHandshakeSuccess() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
let client_successes = await GleanTest.webrtcdtls.clientHandshakeResult.SUCCESS.testGetValue() || 0;
let server_successes = await GleanTest.webrtcdtls.serverHandshakeResult.SUCCESS.testGetValue() || 0;
let cipher_count = await GleanTest.webrtcdtls.cipher["0xc02b"].testGetValue() || 0;
let srtp_cipher_count = await GleanTest.webrtcdtls.srtpCipher["0x0007"].testGetValue() || 0;
is(client_successes, 0);
is(server_successes, 0);
is(cipher_count, 0);
is(srtp_cipher_count, 0);
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
pc1.addTrack(stream.getTracks()[0]);
await connect(pc1, pc2, 32000, "DTLS connected", true, true);
// This telemetry happens on STS/socket process
await gleanFlushChildren();
client_successes = await GleanTest.webrtcdtls.clientHandshakeResult.SUCCESS.testGetValue() || 0;
server_successes = await GleanTest.webrtcdtls.serverHandshakeResult.SUCCESS.testGetValue() || 0;
cipher_count = await GleanTest.webrtcdtls.cipher["0xc02b"].testGetValue() || 0;
srtp_cipher_count = await GleanTest.webrtcdtls.srtpCipher["0x0007"].testGetValue() || 0;
is(client_successes, 1);
is(server_successes, 1);
is(cipher_count, 2);
is(srtp_cipher_count, 2);
},
async function checkDtlsHandshakeFailure() {
// We don't have many failures we can induce here, but messing up the
// fingerprint is one way.
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
await gleanResetTestValues();
let client_failures = await GleanTest.webrtcdtls.clientHandshakeResult.SSL_ERROR_BAD_CERTIFICATE.testGetValue() || 0;
let server_failures = await GleanTest.webrtcdtls.serverHandshakeResult.SSL_ERROR_BAD_CERT_ALERT.testGetValue() || 0;
is(client_failures, 0);
is(server_failures, 0);
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
offerer.addTrack(stream.getTracks()[0]);
trickleIce(offerer, answerer);
trickleIce(answerer, offerer);
await offerer.setLocalDescription();
let badSdp = offerer.localDescription.sdp;
// Tweak the last digit in the fingerprint sent to the answerer. Answerer
// (which will be the DTLS client) will get an SSL_ERROR_BAD_CERTIFICATE
// error, and the offerer (which will be the DTLS server) will get an
// SSL_ERROR_BAD_CERT_ALERT.
const lastDigit = badSdp.match(/a=fingerprint:.*([0-9A-F])$/m)[1];
const newLastDigit = lastDigit == '0' ? '1' : '0';
badSdp = badSdp.replace(/(a=fingerprint:.*)[0-9A-F]$/m, "$1" + newLastDigit);
info(badSdp);
await answerer.setRemoteDescription({sdp: badSdp, type: "offer"});
await answerer.setLocalDescription();
await offerer.setRemoteDescription(answerer.localDescription);
const throwOnTimeout = async () => {
await wait(32000);
throw new Error(
`ICE did not complete within ${timeout} ms`);
};
const connectionPromises = [connectionStateReached(offerer, "failed"),
connectionStateReached(answerer, "failed")];
await Promise.race([
Promise.all(connectionPromises),
throwOnTimeout()
]);
// This telemetry happens on STS/socket process
await gleanFlushChildren();
client_failures = await GleanTest.webrtcdtls.clientHandshakeResult.SSL_ERROR_BAD_CERTIFICATE.testGetValue() || 0;
server_failures = await GleanTest.webrtcdtls.serverHandshakeResult.SSL_ERROR_BAD_CERT_ALERT.testGetValue() || 0;
is(client_failures, 1);
is(server_failures, 1);
},
async function checkDtlsVersion1_3() {
// Make 1.3 the default
await withPrefs([["media.peerconnection.dtls.version.max", 772]],
async () => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 0);
is(count1_2, 0);
is(count1_3, 0);
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
pc1.addTrack(stream.getTracks()[0]);
await connect(pc1, pc2, 32000, "DTLS connected", true, true);
// This telemetry happens on STS/socket process
await gleanFlushChildren();
count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 0);
is(count1_2, 0);
is(count1_3, 2);
});
},
async function checkDtlsVersion1_2() {
// Make 1.2 the default
await withPrefs([["media.peerconnection.dtls.version.max", 771]],
async () => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 0);
is(count1_2, 0);
is(count1_3, 0);
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
pc1.addTrack(stream.getTracks()[0]);
await connect(pc1, pc2, 32000, "DTLS connected", true, true);
// This telemetry happens on STS/socket process
await gleanFlushChildren();
count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 0);
is(count1_2, 2);
is(count1_3, 0);
});
},
async function checkDtlsVersion1_0() {
// Make 1.0 the default
await withPrefs([["media.peerconnection.dtls.version.max", 770],
["media.peerconnection.dtls.version.min", 770]],
async () => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 0);
is(count1_2, 0);
is(count1_3, 0);
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
pc1.addTrack(stream.getTracks()[0]);
await connect(pc1, pc2, 32000, "DTLS connected", true, true);
// This telemetry happens on STS/socket process
await gleanFlushChildren();
count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0;
count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0;
count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0;
is(count1_0, 2);
is(count1_2, 0);
is(count1_3, 0);
});
},
async function checkRTCRtpSenderCount() {
const pc = new RTCPeerConnection();
const oldCount = await GleanTest.rtcrtpsender.count.testGetValue() ?? 0;
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
const countDiff = await GleanTest.rtcrtpsender.count.testGetValue() - oldCount;
is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender");
},
async function checkRTCRtpSenderSetParametersCompatCount() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const oldCount = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() ?? 0;
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
const countDiff = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() - oldCount;
is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender that uses the setParameters compat mode");
},
async function checkSendEncodings() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded the use of sendEncodings");
},
async function checkAddTransceiverNoSendEncodings() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const { sender } = pc.addTransceiver('video');
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings");
},
async function checkAddTrack() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc.addTrack(stream.getTracks()[0]);
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings");
},
async function checkGoodSetParametersCompatMode() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
const oldWarningRates = await getAllWarningRates();
await sender.setParameters(sender.getParameters());
const newWarningRates = await getAllWarningRates();
isDeeply(oldWarningRates, newWarningRates);
},
async function checkBadSetParametersNoGetParametersWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning in setParameters due to lack of a getParameters call");
oldRate = newRate;
// Glean should only record the warning once per sender!
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning in setParameters due to lack of a getParameters call");
},
async function checkBadSetParametersLengthChangedWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
let params = sender.getParameters();
params.encodings.pop();
await sender.setParameters(params);
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to a length change in encodings");
oldRate = newRate;
// Glean should only record the warning once per sender!
params = sender.getParameters();
params.encodings.pop();
await sender.setParameters(params);
newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to a length change in encodings");
},
async function checkBadSetParametersRidChangedWarning() {
// This pref does not let rid change errors slide anymore
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
let params = sender.getParameters();
params.encodings[1].rid = "foo";
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a rid change in encodings");
// Glean should only record the error once per sender!
params = sender.getParameters();
params.encodings[1].rid = "bar";
oldRate = newRate;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a rid change in encodings");
},
async function checkBadSetParametersNoTransactionIdWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to missing transactionId in setParameters");
oldRate = newRate;
// Glean should only record the warning once per sender!
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to missing transactionId in setParameters");
},
async function checkBadSetParametersLengthChangedError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
let params = sender.getParameters();
params.encodings.pop();
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a length change in encodings");
// Glean should only record the error once per sender!
params = sender.getParameters();
params.encodings.pop();
oldRate = newRate;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a length change in encodings");
},
async function checkBadSetParametersRidChangedError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
let params = sender.getParameters();
params.encodings[1].rid = "foo";
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a rid change in encodings");
// Glean should only record the error once per sender!
params = sender.getParameters();
params.encodings[1].rid = "bar";
oldRate = newRate;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a rid change in encodings");
},
async function checkBadSetParametersNoGetParametersError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
try {
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error in setParameters due to lack of a getParameters call");
// Glean should only record the error once per sender!
oldRate = newRate;
try {
await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] });
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error in setParameters due to lack of a getParameters call");
},
async function checkBadSetParametersStaleTransactionIdError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
let params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to stale transactionId in setParameters");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to stale transactionId in setParameters");
},
async function checkBadSetParametersNoEncodingsError() {
// If we do not allow the old setParameters, this will fail the length check
// instead.
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
let params = sender.getParameters();
params.encodings = [];
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded an error due to empty encodings in setParameters");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
params.encodings = [];
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded an error due empty encodings in setParameters");
},
async function checkBadSetParametersOtherError() {
const pc = new RTCPeerConnection();
const { sender } = pc.addTransceiver('video', {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
let params = sender.getParameters();
params.encodings[0].scaleResolutionDownBy = 0.5;
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to some other failure");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
params.encodings[0].scaleResolutionDownBy = 0.5;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to some other failure");
},
async function checkUlpfecNegotiated() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
let offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription(offer);
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Validate logging shows ulpfec negotiated and preferred video VP8
let ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0;
ok(ulpfecNotNegotiated == 0, "checkUlpfecNegotiated glean should not count not_negotiated");
let ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0;
ok(ulpfecNegotiated == 2, "checkUlpfecNegotiated glean should show ulpfec negotiated");
let preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0;
ok(preferredVideoCodec == 2, "checkUlpfecNegotiated glean should show preferred video codec VP8");
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i == 0 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i == 0 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`);
ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`);
ok(e.codecs.includes("ulpfec"), `No ulpfec codec present for i=${i}`);
is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkNoUlpfecNegotiated() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
await pc1.setLocalDescription();
const offer = pc1.localDescription;
const payloadType = sdputils.findCodecId(offer.sdp, "ulpfec");
const sdp = sdputils.removeCodec(offer.sdp, payloadType);
await pc2.setRemoteDescription({type: 'offer', sdp});
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Validate logging shows ulpfec not negotiated and preferred video VP8
const ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0;
is(ulpfecNotNegotiated, 2, "checkNoUlpfecNegotiated glean should count not_negotiated");
const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0;
is(ulpfecNegotiated, 0, "checkNoUlpfecNegotiated glean should not show ulpfec negotiated " + ulpfecNegotiated);
const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0;
is(preferredVideoCodec, 2, "checkNoUlpfecNegotiated glean should show preferred video codec VP8");
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i == 0 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i == 0 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`);
ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`);
ok(!e.codecs.includes("ulpfec"), `No ulpfec codec present for i=${i}`);
is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkFlexfecOffered() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
await pc1.setLocalDescription();
const offer = pc1.localDescription;
const sdp = offer.sdp.replaceAll('VP8','flexfec');
await pc2.setRemoteDescription({type: 'offer', sdp});
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Validate logging shows flexfec counted once ulpfec negotiated twice
// and preferred video VP9 since no VP8 was offered.
const flexfecOffered = await GleanTest.codecStats.otherFecSignaled.flexfec.testGetValue() || 0;
is(flexfecOffered, 1, "checkFlexfecOffered glean should count flexfec being offered" + flexfecOffered);
const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0;
is(ulpfecNegotiated, 2, "checkUlpfecNegotiated glean should show ulpfec negotiated");
const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP9.testGetValue() || 0;
is(preferredVideoCodec, 2, "checkFlexfecOffered glean should show preferred video codec VP9");
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i == 0 ? "VP9" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i == 0 ? undefined : "VP9", `Expected preferred send video codec for i=${i}`);
ok(!e.codecs.includes("VP8"), `No VP8 codec present for i=${i}`);
ok(!e.codecs.includes("flexfec"), `No flexfec codec present for i=${i}`);
ok(e.codecs.includes("VP9"), `VP9 codec present for i=${i}`);
ok(e.codecs.includes("ulpfec"), `ulpfec codec present for i=${i}`);
is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkPreferredVideoCodec() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
await pc1.setLocalDescription();
const offer = pc1.localDescription;
// Set a video codec that does not exist to simulate receiving codecs we
// dont support and verify it gets logged.
const sdp = offer.sdp.replaceAll('VP8','AVADA1');
await pc2.setRemoteDescription({type: 'offer', sdp});
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// We should show AVADA1 as the preferred codec from the offer and the
// answer should prefer VP9 since VP8 was removed.
const preferredVideoCodecAVADA1 = await GleanTest.codecStats.videoPreferredCodec.AVADA1.testGetValue() || 0;
is(preferredVideoCodecAVADA1, 1, "checkPreferredVideoCodec glean should show preferred video codec AVADA1" + preferredVideoCodecAVADA1);
const preferredVideoCodecVP9 = await GleanTest.codecStats.videoPreferredCodec.VP9.testGetValue() || 0;
is(preferredVideoCodecVP9, 1, "checkPreferredVideoCodec glean should show preferred video codec VP9" + preferredVideoCodecVP9);
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i == 0 ? "AVADA1" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i == 0 ? undefined : "VP9", `Expected preferred send video codec for i=${i}`);
ok(!e.codecs.includes("VP8"), `No VP8 codec present for i=${i}`);
ok(!e.codecs.includes("AVADA1"), `No AVADA1 codec present for i=${i}`);
ok(e.codecs.includes("VP9"), `VP9 codec present for i=${i}`);
is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkPreferredAudioCodec() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
await pc1.setLocalDescription();
const offer = pc1.localDescription;
// Set an audio codec that does not exist to simulate receiving codecs we
// dont support and verify it gets logged.
const sdp = offer.sdp.replaceAll('opus','FAKECodec');
await pc2.setRemoteDescription({type: 'offer', sdp});
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// We should show CN as the preferred codec from the offer and the answer
// should prefer G722 since opus was removed.
const preferredAudioCodecFAKECodec = await GleanTest.codecStats.audioPreferredCodec.FAKECodec.testGetValue() || 0;
is(preferredAudioCodecFAKECodec, 1, "checkPreferredAudioCodec Glean should show preferred audio codec FAKECodec " + preferredAudioCodecFAKECodec);
const preferredAudioCodecG722 = await GleanTest.codecStats.audioPreferredCodec.G722.testGetValue() || 0;
is(preferredAudioCodecG722, 1, "checkPreferredAudioCodec Glean should show preferred audio codec G722 " + preferredAudioCodecG722);
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, i == 0 ? "1" : "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, i == 0 ? "0" : "1", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 2, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < audioMsections.length; ++i) {
const e = audioMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected audio rtcp-mux for i=${i}`);
is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected audio direction for i=${i}`);
is(e.preferred_recv_codec, i == 0 ? "FAKECodec" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i == 0 ? undefined : "G722", `Expected preferred send video codec for i=${i}`);
ok(!e.codecs.includes("opus"), `No opus present for i=${i}`);
ok(!e.codecs.includes("FAKECodec"), `No FAKECodec present for i=${i}`);
ok(e.codecs.includes("G722"), `VP9 codec present for i=${i}`);
}
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 0, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
pc1.close();
pc2.close();
},
async function checkLoggingMultipleTransceivers() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc1.addTrack(stream.getTracks()[0]);
pc1.addTransceiver(stream.getTracks()[0]);
pc1.addTransceiver(stream.getTracks()[0]);
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Renegotiate
const newOffer = await pc1.createOffer();
await pc1.setLocalDescription(newOffer);
await pc2.setRemoteDescription(newOffer);
const newAnswer = await pc2.createAnswer();
await pc2.setLocalDescription(newAnswer);
await pc1.setRemoteDescription(newAnswer);
// Validate logging shows for each transciever but is not duplicated with the renegotiation
const ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0;
is(ulpfecNotNegotiated, 0, "checkLoggingMultipleTransceivers glean should not count not_negotiated");
const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0;
is(ulpfecNegotiated, 6, "checkLoggingMultipleTransceivers glean should show ulpfec negotiated " + ulpfecNegotiated);
const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0;
is(preferredVideoCodec, 6, "checkLoggingMultipleTransceivers glean should show preferred video codec VP8 " + preferredVideoCodec);
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 4, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i % 2 == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, i > 1 ? "2" : "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i % 2 == 0 ? "3" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i % 2 == 1 ? "3" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 12, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i % 6 < 3 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, `${Math.floor(i / 6) + 1}`, `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i % 6 < 3 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i % 6 < 3 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i % 6 < 3 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`);
ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`);
is(e.num_send_simulcast_layers, i % 6 < 3 ? undefined : "1", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkSimulcastSendEncodings() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const { sender } = pc1.addTransceiver(stream.getTracks()[0], {
sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }]
});
const offer = await pc1.createOffer();
const mungedOffer = ridToMid(offer);
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription({type: 'offer', sdp: mungedOffer});
const answer = await pc2.createAnswer();
const mungedAnswer = midToRid(answer);
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription({type: 'answer', sdp: mungedAnswer});
const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0;
is(preferredVideoCodec, 4, "checkSimulcastSendEncodings glean should show preferred video codec VP8");
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "3" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 4, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i < 3 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i < 3 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i < 3 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i < 3 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`);
ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`);
is(e.num_send_simulcast_layers, i < 3 ? undefined : "3", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkSimulcastJsep() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
pc2.addTransceiver('video', { direction: "recvonly" });
pc2.addTransceiver('video', { direction: "recvonly" });
const offer = await pc2.createOffer();
const mungedOffer = midToRid(offer);
await pc2.setLocalDescription(offer);
await pc1.setRemoteDescription({type: 'offer', sdp: mungedOffer});
pc1.addTrack(stream.getTracks()[0], stream);
const answer = await pc1.createAnswer();
const mungedAnswer = ridToMid(answer);
await pc2.setRemoteDescription({type: 'answer', sdp: mungedAnswer});
await pc1.setLocalDescription(answer);
const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0;
is(preferredVideoCodec, 3, "checkSimulcastJsep glean should show preferred video codec VP8");
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "2" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || [];
is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1
const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || [];
is(videoMsections.length, 3, "Expected number of video m-sections"); // pc2 finished negotiation before pc1
for (let i = 0; i < videoMsections.length; ++i) {
const e = videoMsections[i].extra;
is(e.pc_id, SpecialPowers.wrap(i < 2 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`);
is(e.direction, i < 2 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`);
is(e.preferred_recv_codec, i < 2 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`);
is(e.preferred_send_codec, i < 2 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`);
ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`);
is(e.num_send_simulcast_layers, i < 2 ? undefined : "2", `Expected number of simulcast layers for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkConfigurationNoBundle() {
const pc1 = new RTCPeerConnection({bundlePolicy: "max-compat"});
const pc2 = new RTCPeerConnection({iceTransportPolicy: "relay"});
await gleanResetTestValues();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender1 = pc1.addTrack(stream.getTracks()[0]);
const sender2 = pc1.addTrack(sender1.track.clone());
await pc1.setLocalDescription();
const offer = pc1.localDescription;
const mungedOffer = offer.sdp.replace("BUNDLE", "BUNGLE");
await pc2.setRemoteDescription({ type: "offer", sdp: mungedOffer });
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, i == 0 ? "balanced" : "max-compat", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, i == 0 ? "relay" : "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "2", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, i == 0 ? "2" : "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, i == 1 ? "2" : "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`);
}
pc1.close();
pc2.close();
},
async function checkDataChannel() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
await gleanResetTestValues();
pc1.createDataChannel('test');
await pc1.setLocalDescription();
const offer = pc1.localDescription;
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
// Validate negotiation event logging
const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || [];
is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1
for (let i = 0; i < sdps.length; ++i) {
const e = sdps[i].extra;
is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`);
is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`);
is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`);
is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`);
is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`);
is(e.num_transports, "1", `Expected number of transports for i=${i}`);
is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`);
is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`);
is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`);
is(e.num_msections_video_recvonly, "0", `Expected number of video recvonly m-sections for i=${i}`);
is(e.num_msections_video_sendonly, "0", `Expected number of video sendonly m-sections for i=${i}`);
is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`);
is(e.num_msections_data, "1", `Expected number of data m-sections for i=${i}`);
}
pc1.close();
pc2.close();
},
];
runNetworkTest(async () => {
for (const test of tests) {
info(`Running test: ${test.name}`);
await test();
info(`Done running test: ${test.name}`);
}
});
</script>
</pre>
</body>
</html>