summaryrefslogtreecommitdiffstats
path: root/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/presentation/tests/xpcshell/test_tcp_control_channel.js')
-rw-r--r--dom/presentation/tests/xpcshell/test_tcp_control_channel.js398
1 files changed, 398 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 000000000..5f3df584d
--- /dev/null
+++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
@@ -0,0 +1,398 @@
+/* -*- 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.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() {
+ do_print('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, false);
+ }
+ this.tcpPort = aTcpPort;
+}
+
+TestDescription.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.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: function() {
+ 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: function(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: function(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: function(aAnswer) {
+ Assert.ok(false, 'get answer');
+ },
+ onIceCandidate: function(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: function() {
+ Assert.equal(this.status, 'created', '0. presenterControlChannel: opened');
+ this.status = 'opened';
+ },
+ notifyDisconnected: function(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: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+ },
+ onReconnectRequest: function(deviceInfo, url, presentationId, controlChannel) {
+ Assert.equal(url, 'http://example.com', 'expected url');
+ Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
+ yayFuncs.presenterControlChannelReconnect();
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlServerListener]),
+ };
+
+ let presenterDeviceInfo = {
+ id: 'presentatorID',
+ address: '127.0.0.1',
+ port: PRESENTER_CONTROL_CHANNEL_PORT,
+ certFingerprint: pcs.certFingerprint,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+ };
+
+ let controllerControlChannel = pcs.connect(presenterDeviceInfo);
+
+ controllerControlChannel.listener = {
+ status: 'created',
+ onOffer: function(offer) {
+ Assert.ok(false, 'get offer');
+ },
+ onAnswer: function(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: function(aCandidate) {
+ Assert.ok(false, 'get ICE candidate');
+ },
+ notifyConnected: function() {
+ 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: function(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: function() {
+ reconnectControllerControlChannel.reconnect('testPresentationId', 'http://example.com');
+ },
+ notifyReconnected: function() {
+ yayFuncs.controllerControlChannelReconnect();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+}
+
+function terminateRequest() {
+ let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
+ 'controllerControlChannelDisconnected',
+ 'presenterControlChannelDisconnected',
+ 'terminatedByController',
+ 'terminatedByReceiver']);
+ let controllerControlChannel;
+ let terminatePhase = 'controller';
+
+ pcs.listener = {
+ onTerminateRequest: function(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: function() {
+ Assert.ok(true, 'control channel notify connected');
+ yayFuncs.controllerControlChannelConnected();
+
+ terminatePhase = 'receiver';
+ controllerControlChannel.terminate('testPresentationId');
+ },
+ notifyDisconnected: function(aReason) {
+ Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify disconncted');
+ yayFuncs.controllerControlChannelDisconnected();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.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: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
+ };
+
+ let presenterDeviceInfo = {
+ id: 'presentatorID',
+ address: '127.0.0.1',
+ port: PRESENTER_CONTROL_CHANNEL_PORT,
+ certFingerprint: pcs.certFingerprint,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+ };
+
+ let presenterControlChannel = pcs.connect(presenterDeviceInfo);
+
+ presenterControlChannel.listener = {
+ notifyConnected: function() {
+ presenterControlChannel.terminate('testPresentationId');
+ },
+ notifyDisconnected: function(aReason) {
+ Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify disconnected');
+ yayFuncs.presenterControlChannelDisconnected();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+}
+
+function terminateRequestAbnormal() {
+ let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
+ 'controllerControlChannelDisconnected',
+ 'presenterControlChannelDisconnected']);
+ let controllerControlChannel;
+
+ pcs.listener = {
+ onTerminateRequest: function(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: function() {
+ Assert.ok(true, 'control channel notify connected');
+ yayFuncs.controllerControlChannelConnected();
+ },
+ notifyDisconnected: function(aReason) {
+ Assert.equal(aReason, Cr.NS_ERROR_FAILURE, 'controllerControlChannel notify disconncted with error');
+ yayFuncs.controllerControlChannelDisconnected();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
+ };
+
+ let presenterDeviceInfo = {
+ id: 'presentatorID',
+ address: '127.0.0.1',
+ port: PRESENTER_CONTROL_CHANNEL_PORT,
+ certFingerprint: pcs.certFingerprint,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+ };
+
+ let presenterControlChannel = pcs.connect(presenterDeviceInfo);
+
+ presenterControlChannel.listener = {
+ notifyConnected: function() {
+ presenterControlChannel.terminate('testPresentationId');
+ },
+ notifyDisconnected: function(aReason) {
+ Assert.equal(aReason, Cr.NS_ERROR_FAILURE, '4. presenterControlChannel notify disconnected with error');
+ yayFuncs.presenterControlChannelDisconnected();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+ };
+}
+
+function setOffline() {
+ pcs.listener = {
+ onServerReady: function(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: function() {
+ 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: function(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);
+
+ do_register_cleanup(() => {
+ Services.prefs.clearUserPref("dom.presentation.tcp_server.debug");
+ });
+
+ run_next_test();
+}