summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCPeerConnection-perfect-negotiation-helper.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testing/web-platform/tests/webrtc/RTCPeerConnection-perfect-negotiation-helper.js153
1 files changed, 153 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-perfect-negotiation-helper.js b/testing/web-platform/tests/webrtc/RTCPeerConnection-perfect-negotiation-helper.js
new file mode 100644
index 0000000000..ed647bbe78
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-perfect-negotiation-helper.js
@@ -0,0 +1,153 @@
+'use strict'
+
+function peer(other, polite, fail = null) {
+ const send = (tgt, msg) => tgt.postMessage(JSON.parse(JSON.stringify(msg)),
+ "*");
+ if (!fail) fail = e => send(window.parent, {error: `${e.name}: ${e.message}`});
+ const pc = new RTCPeerConnection();
+
+ if (!window.assert_equals) {
+ window.assert_equals = (a, b, msg) => a === b ||
+ fail(new Error(`${msg} expected ${b} but got ${a}`));
+ }
+
+ const commands = {
+ async addTransceiver() {
+ const transceiver = pc.addTransceiver("video");
+ await new Promise(r => pc.addEventListener("negotiated", r, {once: true}));
+ if (!transceiver.currentDirection) {
+ // Might have just missed the negotiation train. Catch next one.
+ await new Promise(r => pc.addEventListener("negotiated", r, {once: true}));
+ }
+ assert_equals(transceiver.currentDirection, "sendonly", "have direction");
+ return pc.getTransceivers().length;
+ },
+ async simpleConnect() {
+ const p = commands.addTransceiver();
+ await new Promise(r => pc.oniceconnectionstatechange =
+ () => pc.iceConnectionState == "connected" && r());
+ return await p;
+ },
+ async getNumTransceivers() {
+ return pc.getTransceivers().length;
+ },
+ };
+
+ try {
+ pc.addEventListener("icecandidate", ({candidate}) => send(other,
+ {candidate}));
+ let makingOffer = false, ignoreIceCandidateFailures = false;
+ let srdAnswerPending = false;
+ pc.addEventListener("negotiationneeded", async () => {
+ try {
+ assert_equals(pc.signalingState, "stable", "negotiationneeded always fires in stable state");
+ assert_equals(makingOffer, false, "negotiationneeded not already in progress");
+ makingOffer = true;
+ await pc.setLocalDescription();
+ assert_equals(pc.signalingState, "have-local-offer", "negotiationneeded not racing with onmessage");
+ assert_equals(pc.localDescription.type, "offer", "negotiationneeded SLD worked");
+ send(other, {description: pc.localDescription});
+ } catch (e) {
+ fail(e);
+ } finally {
+ makingOffer = false;
+ }
+ });
+ window.onmessage = async ({data: {description, candidate, run}}) => {
+ try {
+ if (description) {
+ // If we have a setRemoteDescription() answer operation pending, then
+ // we will be "stable" by the time the next setRemoteDescription() is
+ // executed, so we count this being stable when deciding whether to
+ // ignore the offer.
+ let isStable =
+ pc.signalingState == "stable" ||
+ (pc.signalingState == "have-local-offer" && srdAnswerPending);
+ const ignoreOffer = description.type == "offer" && !polite &&
+ (makingOffer || !isStable);
+ if (ignoreOffer) {
+ ignoreIceCandidateFailures = true;
+ return;
+ }
+ if (description.type == "answer")
+ srdAnswerPending = true;
+ await pc.setRemoteDescription(description);
+ ignoreIceCandidateFailures = false;
+ srdAnswerPending = false;
+ if (description.type == "offer") {
+ assert_equals(pc.signalingState, "have-remote-offer", "Remote offer");
+ assert_equals(pc.remoteDescription.type, "offer", "SRD worked");
+ await pc.setLocalDescription();
+ assert_equals(pc.signalingState, "stable", "onmessage not racing with negotiationneeded");
+ assert_equals(pc.localDescription.type, "answer", "onmessage SLD worked");
+ send(other, {description: pc.localDescription});
+ } else {
+ assert_equals(pc.remoteDescription.type, "answer", "Answer was set");
+ assert_equals(pc.signalingState, "stable", "answered");
+ pc.dispatchEvent(new Event("negotiated"));
+ }
+ } else if (candidate) {
+ try {
+ await pc.addIceCandidate(candidate);
+ } catch (e) {
+ if (!ignoreIceCandidateFailures) throw e;
+ }
+ } else if (run) {
+ send(window.parent, {[run.id]: await commands[run.cmd]() || 0});
+ }
+ } catch (e) {
+ fail(e);
+ }
+ };
+ } catch (e) {
+ fail(e);
+ }
+ return pc;
+}
+
+async function setupPeerIframe(t, polite) {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ iframe.srcdoc =
+ `<html\><script\>(${peer.toString()})(window.parent, ${polite});</script\></html\>`;
+ document.documentElement.appendChild(iframe);
+
+ const failCatcher = t.step_func(({data}) =>
+ ("error" in data) && assert_unreached(`Error in iframe: ${data.error}`));
+ window.addEventListener("message", failCatcher);
+ t.add_cleanup(() => window.removeEventListener("message", failCatcher));
+ await new Promise(r => iframe.onload = r);
+ return iframe;
+}
+
+function setupPeerTopLevel(t, other, polite) {
+ const pc = peer(other, polite, t.step_func(e => { throw e; }));
+ t.add_cleanup(() => { pc.close(); window.onmessage = null; });
+}
+
+let counter = 0;
+async function run(target, cmd) {
+ const id = `result${counter++}`;
+ target.postMessage({run: {cmd, id}}, "*");
+ return new Promise(r => window.addEventListener("message",
+ function listen({data}) {
+ if (!(id in data)) return;
+ window.removeEventListener("message", listen);
+ r(data[id]);
+ }));
+}
+
+let iframe;
+async function setupAB(t, politeA, politeB) {
+ iframe = await setupPeerIframe(t, politeB);
+ return setupPeerTopLevel(t, iframe.contentWindow, politeA);
+}
+const runA = cmd => run(window, cmd);
+const runB = cmd => run(iframe.contentWindow, cmd);
+const runBoth = (cmdA, cmdB = cmdA) => Promise.all([runA(cmdA), runB(cmdB)]);
+
+async function promise_test_both_roles(f, name) {
+ promise_test(async t => f(t, await setupAB(t, true, false)), name);
+ promise_test(async t => f(t, await setupAB(t, false, true)),
+ `${name} with roles reversed`);
+}