diff options
Diffstat (limited to 'dom/presentation/tests/xpcshell/test_tcp_control_channel.js')
-rw-r--r-- | dom/presentation/tests/xpcshell/test_tcp_control_channel.js | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js new file mode 100644 index 0000000000..ee0b3aa074 --- /dev/null +++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js @@ -0,0 +1,523 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +var pcs; + +// Call |run_next_test| if all functions in |names| are called +function makeJointSuccess(names) { + let funcs = {}, + successCount = 0; + names.forEach(function(name) { + funcs[name] = function() { + info("got expected: " + name); + if (++successCount === names.length) { + run_next_test(); + } + }; + }); + return funcs; +} + +function TestDescription(aType, aTcpAddress, aTcpPort) { + this.type = aType; + this.tcpAddress = Cc["@mozilla.org/array;1"].createInstance( + Ci.nsIMutableArray + ); + for (let address of aTcpAddress) { + let wrapper = Cc["@mozilla.org/supports-cstring;1"].createInstance( + Ci.nsISupportsCString + ); + wrapper.data = address; + this.tcpAddress.appendElement(wrapper); + } + this.tcpPort = aTcpPort; +} + +TestDescription.prototype = { + QueryInterface: ChromeUtils.generateQI(["nsIPresentationChannelDescription"]), +}; + +const CONTROLLER_CONTROL_CHANNEL_PORT = 36777; +const PRESENTER_CONTROL_CHANNEL_PORT = 36888; + +var CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_OK; +var candidate; + +// presenter's presentation channel description +const OFFER_ADDRESS = "192.168.123.123"; +const OFFER_PORT = 123; + +// controller's presentation channel description +const ANSWER_ADDRESS = "192.168.321.321"; +const ANSWER_PORT = 321; + +function loopOfferAnser() { + pcs = Cc["@mozilla.org/presentation/control-service;1"].createInstance( + Ci.nsIPresentationControlService + ); + pcs.id = "controllerID"; + pcs.listener = { + onServerReady() { + testPresentationServer(); + }, + }; + + // First run with TLS enabled. + pcs.startServer(true, PRESENTER_CONTROL_CHANNEL_PORT); +} + +function testPresentationServer() { + let yayFuncs = makeJointSuccess([ + "controllerControlChannelClose", + "presenterControlChannelClose", + "controllerControlChannelReconnect", + "presenterControlChannelReconnect", + ]); + let presenterControlChannel; + + pcs.listener = { + onSessionRequest(deviceInfo, url, presentationId, controlChannel) { + presenterControlChannel = controlChannel; + Assert.equal(deviceInfo.id, pcs.id, "expected device id"); + Assert.equal(deviceInfo.address, "127.0.0.1", "expected device address"); + Assert.equal(url, "http://example.com", "expected url"); + Assert.equal( + presentationId, + "testPresentationId", + "expected presentation id" + ); + + presenterControlChannel.listener = { + status: "created", + onOffer(aOffer) { + Assert.equal( + this.status, + "opened", + "1. presenterControlChannel: get offer, send answer" + ); + this.status = "onOffer"; + + let offer = aOffer.QueryInterface( + Ci.nsIPresentationChannelDescription + ); + Assert.strictEqual( + offer.tcpAddress.queryElementAt(0, Ci.nsISupportsCString).data, + OFFER_ADDRESS, + "expected offer address array" + ); + Assert.equal(offer.tcpPort, OFFER_PORT, "expected offer port"); + try { + let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; + let answer = new TestDescription( + tcpType, + [ANSWER_ADDRESS], + ANSWER_PORT + ); + presenterControlChannel.sendAnswer(answer); + } catch (e) { + Assert.ok(false, "sending answer fails" + e); + } + }, + onAnswer(aAnswer) { + Assert.ok(false, "get answer"); + }, + onIceCandidate(aCandidate) { + Assert.ok( + true, + "3. presenterControlChannel: get ice candidate, close channel" + ); + let recvCandidate = JSON.parse(aCandidate); + for (let key in recvCandidate) { + if (typeof recvCandidate[key] !== "function") { + Assert.equal( + recvCandidate[key], + candidate[key], + "key " + key + " should match." + ); + } + } + presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); + }, + notifyConnected() { + Assert.equal( + this.status, + "created", + "0. presenterControlChannel: opened" + ); + this.status = "opened"; + }, + notifyDisconnected(aReason) { + Assert.equal( + this.status, + "onOffer", + "4. presenterControlChannel: closed" + ); + Assert.equal( + aReason, + CLOSE_CONTROL_CHANNEL_REASON, + "presenterControlChannel notify closed" + ); + this.status = "closed"; + yayFuncs.controllerControlChannelClose(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; + }, + onReconnectRequest(deviceInfo, url, presentationId, controlChannel) { + Assert.equal(url, "http://example.com", "expected url"); + Assert.equal( + presentationId, + "testPresentationId", + "expected presentation id" + ); + yayFuncs.presenterControlChannelReconnect(); + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlServerListener", + ]), + }; + + let presenterDeviceInfo = { + id: "presentatorID", + address: "127.0.0.1", + port: PRESENTER_CONTROL_CHANNEL_PORT, + certFingerprint: pcs.certFingerprint, + QueryInterface: ChromeUtils.generateQI(["nsITCPDeviceInfo"]), + }; + + let controllerControlChannel = pcs.connect(presenterDeviceInfo); + + controllerControlChannel.listener = { + status: "created", + onOffer(offer) { + Assert.ok(false, "get offer"); + }, + onAnswer(aAnswer) { + Assert.equal( + this.status, + "opened", + "2. controllerControlChannel: get answer, send ICE candidate" + ); + + let answer = aAnswer.QueryInterface(Ci.nsIPresentationChannelDescription); + Assert.strictEqual( + answer.tcpAddress.queryElementAt(0, Ci.nsISupportsCString).data, + ANSWER_ADDRESS, + "expected answer address array" + ); + Assert.equal(answer.tcpPort, ANSWER_PORT, "expected answer port"); + candidate = { + candidate: "1 1 UDP 1 127.0.0.1 34567 type host", + sdpMid: "helloworld", + sdpMLineIndex: 1, + }; + controllerControlChannel.sendIceCandidate(JSON.stringify(candidate)); + }, + onIceCandidate(aCandidate) { + Assert.ok(false, "get ICE candidate"); + }, + notifyConnected() { + Assert.equal( + this.status, + "created", + "0. controllerControlChannel: opened, send offer" + ); + controllerControlChannel.launch( + "testPresentationId", + "http://example.com" + ); + this.status = "opened"; + try { + let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; + let offer = new TestDescription(tcpType, [OFFER_ADDRESS], OFFER_PORT); + controllerControlChannel.sendOffer(offer); + } catch (e) { + Assert.ok(false, "sending offer fails:" + e); + } + }, + notifyDisconnected(aReason) { + this.status = "closed"; + Assert.equal( + aReason, + CLOSE_CONTROL_CHANNEL_REASON, + "4. controllerControlChannel notify closed" + ); + yayFuncs.presenterControlChannelClose(); + + let reconnectControllerControlChannel = pcs.connect(presenterDeviceInfo); + reconnectControllerControlChannel.listener = { + notifyConnected() { + reconnectControllerControlChannel.reconnect( + "testPresentationId", + "http://example.com" + ); + }, + notifyReconnected() { + yayFuncs.controllerControlChannelReconnect(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; +} + +function terminateRequest() { + let yayFuncs = makeJointSuccess([ + "controllerControlChannelConnected", + "controllerControlChannelDisconnected", + "presenterControlChannelDisconnected", + "terminatedByController", + "terminatedByReceiver", + ]); + let controllerControlChannel; + let terminatePhase = "controller"; + + pcs.listener = { + onTerminateRequest( + deviceInfo, + presentationId, + controlChannel, + isFromReceiver + ) { + Assert.equal(deviceInfo.address, "127.0.0.1", "expected device address"); + Assert.equal( + presentationId, + "testPresentationId", + "expected presentation id" + ); + controlChannel.terminate(presentationId); // Reply terminate ack. + + if (terminatePhase === "controller") { + controllerControlChannel = controlChannel; + Assert.equal(deviceInfo.id, pcs.id, "expected controller device id"); + Assert.equal(isFromReceiver, false, "expected request from controller"); + yayFuncs.terminatedByController(); + + controllerControlChannel.listener = { + notifyConnected() { + Assert.ok(true, "control channel notify connected"); + yayFuncs.controllerControlChannelConnected(); + + terminatePhase = "receiver"; + controllerControlChannel.terminate("testPresentationId"); + }, + notifyDisconnected(aReason) { + Assert.equal( + aReason, + CLOSE_CONTROL_CHANNEL_REASON, + "controllerControlChannel notify disconncted" + ); + yayFuncs.controllerControlChannelDisconnected(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; + } else { + Assert.equal( + deviceInfo.id, + presenterDeviceInfo.id, + "expected presenter device id" + ); + Assert.equal(isFromReceiver, true, "expected request from receiver"); + yayFuncs.terminatedByReceiver(); + presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); + } + }, + QueryInterface: ChromeUtils.generateQI([ + "nsITCPPresentationServerListener", + ]), + }; + + let presenterDeviceInfo = { + id: "presentatorID", + address: "127.0.0.1", + port: PRESENTER_CONTROL_CHANNEL_PORT, + certFingerprint: pcs.certFingerprint, + QueryInterface: ChromeUtils.generateQI(["nsITCPDeviceInfo"]), + }; + + let presenterControlChannel = pcs.connect(presenterDeviceInfo); + + presenterControlChannel.listener = { + notifyConnected() { + presenterControlChannel.terminate("testPresentationId"); + }, + notifyDisconnected(aReason) { + Assert.equal( + aReason, + CLOSE_CONTROL_CHANNEL_REASON, + "4. presenterControlChannel notify disconnected" + ); + yayFuncs.presenterControlChannelDisconnected(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; +} + +function terminateRequestAbnormal() { + let yayFuncs = makeJointSuccess([ + "controllerControlChannelConnected", + "controllerControlChannelDisconnected", + "presenterControlChannelDisconnected", + ]); + let controllerControlChannel; + + pcs.listener = { + onTerminateRequest( + deviceInfo, + presentationId, + controlChannel, + isFromReceiver + ) { + Assert.equal(deviceInfo.id, pcs.id, "expected controller device id"); + Assert.equal(deviceInfo.address, "127.0.0.1", "expected device address"); + Assert.equal( + presentationId, + "testPresentationId", + "expected presentation id" + ); + Assert.equal(isFromReceiver, false, "expected request from controller"); + controlChannel.terminate("unmatched-presentationId"); // Reply abnormal terminate ack. + + controllerControlChannel = controlChannel; + + controllerControlChannel.listener = { + notifyConnected() { + Assert.ok(true, "control channel notify connected"); + yayFuncs.controllerControlChannelConnected(); + }, + notifyDisconnected(aReason) { + Assert.equal( + aReason, + Cr.NS_ERROR_FAILURE, + "controllerControlChannel notify disconncted with error" + ); + yayFuncs.controllerControlChannelDisconnected(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; + }, + QueryInterface: ChromeUtils.generateQI([ + "nsITCPPresentationServerListener", + ]), + }; + + let presenterDeviceInfo = { + id: "presentatorID", + address: "127.0.0.1", + port: PRESENTER_CONTROL_CHANNEL_PORT, + certFingerprint: pcs.certFingerprint, + QueryInterface: ChromeUtils.generateQI(["nsITCPDeviceInfo"]), + }; + + let presenterControlChannel = pcs.connect(presenterDeviceInfo); + + presenterControlChannel.listener = { + notifyConnected() { + presenterControlChannel.terminate("testPresentationId"); + }, + notifyDisconnected(aReason) { + Assert.equal( + aReason, + Cr.NS_ERROR_FAILURE, + "4. presenterControlChannel notify disconnected with error" + ); + yayFuncs.presenterControlChannelDisconnected(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIPresentationControlChannelListener", + ]), + }; +} + +function setOffline() { + pcs.listener = { + onServerReady(aPort, aCertFingerprint) { + Assert.notEqual( + aPort, + 0, + "TCPPresentationServer port changed and the port should be valid" + ); + pcs.close(); + run_next_test(); + }, + }; + + // Let the server socket restart automatically. + Services.io.offline = true; + Services.io.offline = false; +} + +function oneMoreLoop() { + try { + pcs.listener = { + onServerReady() { + testPresentationServer(); + }, + }; + + // Second run with TLS disabled. + pcs.startServer(false, PRESENTER_CONTROL_CHANNEL_PORT); + } catch (e) { + Assert.ok(false, "TCP presentation init fail:" + e); + run_next_test(); + } +} + +function shutdown() { + pcs.listener = { + onServerReady(aPort, aCertFingerprint) { + Assert.ok(false, "TCPPresentationServer port changed"); + }, + }; + pcs.close(); + Assert.equal(pcs.port, 0, "TCPPresentationServer closed"); + run_next_test(); +} + +// Test manually close control channel with NS_ERROR_FAILURE +function changeCloseReason() { + CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_ERROR_FAILURE; + run_next_test(); +} + +add_test(loopOfferAnser); +add_test(terminateRequest); +add_test(terminateRequestAbnormal); +add_test(setOffline); +add_test(changeCloseReason); +add_test(oneMoreLoop); +add_test(shutdown); + +function run_test() { + // Need profile dir to store the key / cert + do_get_profile(); + // Ensure PSM is initialized + Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); + + Services.prefs.setBoolPref("dom.presentation.tcp_server.debug", true); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("dom.presentation.tcp_server.debug"); + }); + + run_next_test(); +} |