diff options
Diffstat (limited to 'dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js')
-rw-r--r-- | dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js | 1318 |
1 files changed, 1318 insertions, 0 deletions
diff --git a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js new file mode 100644 index 000000000..137a5609a --- /dev/null +++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js @@ -0,0 +1,1318 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* global Services, do_register_cleanup, do_test_pending */ + +"use strict"; + +const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const INFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1"; +const PROVIDER_CONTRACT_ID = "@mozilla.org/presentation-device/multicastdns-provider;1"; +const SD_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1"; +const UUID_CONTRACT_ID = "@mozilla.org/uuid-generator;1"; +const SERVER_CONTRACT_ID = "@mozilla.org/presentation/control-service;1"; + +const PREF_DISCOVERY = "dom.presentation.discovery.enabled"; +const PREF_DISCOVERABLE = "dom.presentation.discoverable"; +const PREF_DEVICENAME= "dom.presentation.device.name"; + +const LATEST_VERSION = 1; +const SERVICE_TYPE = "_presentation-ctrl._tcp"; +const versionAttr = Cc["@mozilla.org/hash-property-bag;1"] + .createInstance(Ci.nsIWritablePropertyBag2); +versionAttr.setPropertyAsUint32("version", LATEST_VERSION); + +var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + +function sleep(aMs) { + let deferred = Promise.defer(); + + let timer = Cc["@mozilla.org/timer;1"] + .createInstance(Ci.nsITimer); + + timer.initWithCallback({ + notify: function () { + deferred.resolve(); + }, + }, aMs, timer.TYPE_ONE_SHOT); + + return deferred.promise; +} + +function MockFactory(aClass) { + this._cls = aClass; +} +MockFactory.prototype = { + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + switch(typeof(this._cls)) { + case "function": + return new this._cls().QueryInterface(aIID); + case "object": + return this._cls.QueryInterface(aIID); + default: + return null; + } + }, + lockFactory: function(aLock) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) +}; + +function ContractHook(aContractID, aClass) { + this._contractID = aContractID; + this.classID = Cc[UUID_CONTRACT_ID].getService(Ci.nsIUUIDGenerator).generateUUID(); + this._newFactory = new MockFactory(aClass); + + if (!this.hookedMap.has(this._contractID)) { + this.hookedMap.set(this._contractID, []); + } + + this.init(); +} + +ContractHook.prototype = { + hookedMap: new Map(), // remember only the most original factory. + + init: function() { + this.reset(); + + let oldContract = this.unregister(); + this.hookedMap.get(this._contractID).push(oldContract); + registrar.registerFactory(this.classID, + "", + this._contractID, + this._newFactory); + + do_register_cleanup(() => { this.cleanup.apply(this); }); + }, + + reset: function() {}, + + cleanup: function() { + this.reset(); + + this.unregister(); + let prevContract = this.hookedMap.get(this._contractID).pop(); + + if (prevContract.factory) { + registrar.registerFactory(prevContract.classID, + "", + this._contractID, + prevContract.factory); + } + }, + + unregister: function() { + var classID, factory; + + try { + classID = registrar.contractIDToCID(this._contractID); + factory = Cm.getClassObject(Cc[this._contractID], Ci.nsIFactory); + } catch (ex) { + classID = ""; + factory = null; + } + + if (factory) { + registrar.unregisterFactory(classID, factory); + } + + return { classID: classID, factory: factory }; + } +}; + +function MockDNSServiceInfo() {} +MockDNSServiceInfo.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceInfo]), + + set host(aHost) { + this._host = aHost; + }, + + get host() { + return this._host; + }, + + set address(aAddress) { + this._address = aAddress; + }, + + get address() { + return this._address; + }, + + set port(aPort) { + this._port = aPort; + }, + + get port() { + return this._port; + }, + + set serviceName(aServiceName) { + this._serviceName = aServiceName; + }, + + get serviceName() { + return this._serviceName; + }, + + set serviceType(aServiceType) { + this._serviceType = aServiceType; + }, + + get serviceType() { + return this._serviceType; + }, + + set domainName(aDomainName) { + this._domainName = aDomainName; + }, + + get domainName() { + return this._domainName; + }, + + set attributes(aAttributes) { + this._attributes = aAttributes; + }, + + get attributes() { + return this._attributes; + } +}; + +function TestPresentationDeviceListener() { + this.devices = {}; +} +TestPresentationDeviceListener.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + + addDevice: function(device) { this.devices[device.id] = device; }, + removeDevice: function(device) { delete this.devices[device.id]; }, + updateDevice: function(device) { this.devices[device.id] = device; }, + onSessionRequest: function(device, url, presentationId, controlChannel) {}, + + count: function() { + var size = 0, key; + for (key in this.devices) { + if (this.devices.hasOwnProperty(key)) { + ++size; + } + } + return size; + } +}; + +function createDevice(host, port, serviceName, serviceType, domainName, attributes) { + let device = new MockDNSServiceInfo(); + device.host = host || ""; + device.port = port || 0; + device.address = host || ""; + device.serviceName = serviceName || ""; + device.serviceType = serviceType || ""; + device.domainName = domainName || ""; + device.attributes = attributes || versionAttr; + return device; +} + +function registerService() { + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let deferred = Promise.defer(); + + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) {}, + registerService: function(serviceInfo, listener) { + deferred.resolve(); + this.serviceRegistered++; + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() { + this.serviceUnregistered++; + }.bind(this) + }; + }, + resolveService: function(serviceInfo, listener) {}, + serviceRegistered: 0, + serviceUnregistered: 0 + }; + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + + Assert.equal(mockObj.serviceRegistered, 0); + Assert.equal(mockObj.serviceUnregistered, 0); + + // Register + provider.listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + }; + + deferred.promise.then(function() { + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 0); + + // Unregister + provider.listener = null; + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 1); + + run_next_test(); + }); +} + +function noRegisterService() { + Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); + + let deferred = Promise.defer(); + + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) {}, + registerService: function(serviceInfo, listener) { + deferred.resolve(); + Assert.ok(false, "should not register service if not discoverable"); + }, + resolveService: function(serviceInfo, listener) {}, + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + + // Try register + provider.listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + }; + + let race = Promise.race([ + deferred.promise, + sleep(1000), + ]); + + race.then(() => { + provider.listener = null; + + run_next_test(); + }); +} + +function registerServiceDynamically() { + Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); + + let deferred = Promise.defer(); + + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) {}, + registerService: function(serviceInfo, listener) { + deferred.resolve(); + this.serviceRegistered++; + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() { + this.serviceUnregistered++; + }.bind(this) + }; + }, + resolveService: function(serviceInfo, listener) {}, + serviceRegistered: 0, + serviceUnregistered: 0 + }; + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + + Assert.equal(mockObj.serviceRegistered, 0); + Assert.equal(mockObj.serviceRegistered, 0); + + // Try Register + provider.listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + }; + + Assert.equal(mockObj.serviceRegistered, 0); + Assert.equal(mockObj.serviceUnregistered, 0); + + // Enable registration + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + deferred.promise.then(function() { + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 0); + + // Disable registration + Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 1); + + // Try unregister + provider.listener = null; + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 1); + + run_next_test(); + }); +} + +function addDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = new TestPresentationDeviceListener(); + Assert.equal(listener.count(), 0); + + // Start discovery + provider.listener = listener; + Assert.equal(listener.count(), 1); + + // Force discovery again + provider.forceDiscovery(); + Assert.equal(listener.count(), 1); + + provider.listener = null; + Assert.equal(listener.count(), 1); + + run_next_test(); +} + +function filterDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) { + let tests = [ + { requestedUrl: "app://fling-player.gaiamobile.org/index.html", supported: true }, + { requestedUrl: "app://notification-receiver.gaiamobile.org/index.html", supported: true }, + { requestedUrl: "http://example.com", supported: true }, + { requestedUrl: "https://example.com", supported: true }, + { requestedUrl: "ftp://example.com", supported: false }, + { requestedUrl: "app://unknown-app-id", supported: false }, + { requestedUrl: "unknowSchem://example.com", supported: false }, + ]; + + for (let test of tests) { + Assert.equal(device.isRequestedUrlSupported(test.requestedUrl), test.supported); + } + + provider.listener = null; + run_next_test(); + }, + updateDevice: function() {}, + removeDevice: function() {}, + onSessionRequest: function() {}, + }; + + provider.listener = listener; +} + +function handleSessionRequest() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); + + const testUrl = "http://example.com"; + const testPresentationId = "test-presentation-id"; + const testDeviceName = "test-device-name"; + + Services.prefs.setCharPref(PREF_DEVICENAME, testDeviceName); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + connect: function(deviceInfo) { + this.request = { + deviceInfo: deviceInfo, + }; + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + }; + }, + id: "", + version: LATEST_VERSION, + isCompatibleServer: function(version) { + return this.version === version; + } + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) { + this.device = device; + }, + }; + + provider.listener = listener; + + let controlChannel = listener.device.establishControlChannel(); + + Assert.equal(mockServerObj.request.deviceInfo.id, mockDevice.host); + Assert.equal(mockServerObj.request.deviceInfo.address, mockDevice.host); + Assert.equal(mockServerObj.request.deviceInfo.port, mockDevice.port); + Assert.equal(mockServerObj.id, testDeviceName); + + provider.listener = null; + + run_next_test(); +} + +function handleOnSessionRequest() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + startServer: function() {}, + sessionRequest: function() {}, + close: function() {}, + id: '', + version: LATEST_VERSION, + port: 0, + listener: null, + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + onSessionRequest: function(device, url, presentationId, controlChannel) { + Assert.ok(true, "receive onSessionRequest event"); + this.request = { + deviceId: device.id, + url: url, + presentationId: presentationId, + }; + } + }; + + provider.listener = listener; + + const deviceInfo = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), + id: mockDevice.host, + address: mockDevice.host, + port: 54321, + }; + + const testUrl = "http://example.com"; + const testPresentationId = "test-presentation-id"; + const testControlChannel = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + }; + provider.QueryInterface(Ci.nsIPresentationControlServerListener) + .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel); + + Assert.equal(listener.request.deviceId, deviceInfo.id); + Assert.equal(listener.request.url, testUrl); + Assert.equal(listener.request.presentationId, testPresentationId); + + provider.listener = null; + + run_next_test(); +} + +function handleOnSessionRequestFromUnknownDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) {}, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) {} + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + startServer: function() {}, + sessionRequest: function() {}, + close: function() {}, + id: '', + version: LATEST_VERSION, + port: 0, + listener: null, + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) { + Assert.ok(false, "shouldn't create any new device"); + }, + removeDevice: function(device) { + Assert.ok(false, "shouldn't remote any device"); + }, + updateDevice: function(device) { + Assert.ok(false, "shouldn't update any device"); + }, + onSessionRequest: function(device, url, presentationId, controlChannel) { + Assert.ok(true, "receive onSessionRequest event"); + this.request = { + deviceId: device.id, + url: url, + presentationId: presentationId, + }; + } + }; + + provider.listener = listener; + + const deviceInfo = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), + id: "unknown-device.local", + address: "unknown-device.local", + port: 12345, + }; + + const testUrl = "http://example.com"; + const testPresentationId = "test-presentation-id"; + const testControlChannel = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + }; + provider.QueryInterface(Ci.nsIPresentationControlServerListener) + .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel); + + Assert.equal(listener.request.deviceId, deviceInfo.id); + Assert.equal(listener.request.url, testUrl); + Assert.equal(listener.request.presentationId, testPresentationId); + + provider.listener = null; + + run_next_test(); +} + +function noAddDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + + let mockDevice = createDevice("device.local", 12345, "service.name", SERVICE_TYPE); + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + Assert.ok(false, "shouldn't perform any device discovery"); + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + } + }; + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + }; + provider.listener = listener; + provider.forceDiscovery(); + provider.listener = null; + + run_next_test(); +} + +function ignoreIncompatibleDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + + let deferred = Promise.defer(); + + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) { + deferred.resolve(); + listener.onServiceRegistered(createDevice("", + 54321, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + startServer: function() { + Services.tm.currentThread.dispatch(() => { + this.listener.onServerReady(this.port, this.certFingerprint); + }, Ci.nsIThread.DISPATCH_NORMAL); + }, + sessionRequest: function() {}, + close: function() {}, + id: '', + version: LATEST_VERSION, + isCompatibleServer: function(version) { + return false; + }, + port: 54321, + certFingerprint: 'mock-cert-fingerprint', + listener: null, + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = new TestPresentationDeviceListener(); + + // Register service + provider.listener = listener; + + deferred.promise.then(function() { + Assert.equal(mockServerObj.id, mockDevice.host); + + // Start discovery + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + Assert.equal(listener.count(), 0); + + provider.listener = null; + + run_next_test(); + }); +} + +function ignoreSelfDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + + let deferred = Promise.defer(); + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) { + deferred.resolve(); + listener.onServiceRegistered(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + startServer: function() { + Services.tm.currentThread.dispatch(() => { + this.listener.onServerReady(this.port, this.certFingerprint); + }, Ci.nsIThread.DISPATCH_NORMAL); + }, + sessionRequest: function() {}, + close: function() {}, + id: '', + version: LATEST_VERSION, + isCompatibleServer: function(version) { + return this.version === version; + }, + port: 54321, + certFingerprint: 'mock-cert-fingerprint', + listener: null, + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = new TestPresentationDeviceListener(); + + // Register service + provider.listener = listener; + deferred.promise.then(() => { + Assert.equal(mockServerObj.id, mockDevice.host); + + // Start discovery + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + Assert.equal(listener.count(), 0); + + provider.listener = null; + + run_next_test(); + }); +} + +function addDeviceDynamically() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = new TestPresentationDeviceListener(); + provider.listener = listener; + Assert.equal(listener.count(), 0); + + // Enable discovery + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + Assert.equal(listener.count(), 1); + + // Try discovery again + provider.forceDiscovery(); + Assert.equal(listener.count(), 1); + + // Try discovery once more + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + provider.forceDiscovery(); + Assert.equal(listener.count(), 1); + + provider.listener = null; + + run_next_test(); +} + +function updateDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE); + let mockDevice2 = createDevice("A.local", 23456, "N2", SERVICE_TYPE); + + let mockObj = { + discovered: false, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + + if (!this.discovered) { + listener.onServiceFound(mockDevice1); + } else { + listener.onServiceFound(mockDevice2); + } + this.discovered = true; + + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() { + listener.onDiscoveryStopped(serviceType); + } + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceType, SERVICE_TYPE); + if (serviceInfo.serviceName == "N1") { + listener.onServiceResolved(mockDevice1); + } else if (serviceInfo.serviceName == "N2") { + listener.onServiceResolved(mockDevice2); + } else { + Assert.ok(false); + } + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + + addDevice: function(device) { + Assert.ok(!this.isDeviceAdded); + Assert.equal(device.id, mockDevice1.host); + Assert.equal(device.name, mockDevice1.serviceName); + this.isDeviceAdded = true; + }, + removeDevice: function(device) { Assert.ok(false); }, + updateDevice: function(device) { + Assert.ok(!this.isDeviceUpdated); + Assert.equal(device.id, mockDevice2.host); + Assert.equal(device.name, mockDevice2.serviceName); + this.isDeviceUpdated = true; + }, + + isDeviceAdded: false, + isDeviceUpdated: false + }; + Assert.equal(listener.isDeviceAdded, false); + Assert.equal(listener.isDeviceUpdated, false); + + // Start discovery + provider.listener = listener; // discover: N1 + + Assert.equal(listener.isDeviceAdded, true); + Assert.equal(listener.isDeviceUpdated, false); + + // temporarily disable to stop discovery and re-enable + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + provider.forceDiscovery(); // discover: N2 + + Assert.equal(listener.isDeviceAdded, true); + Assert.equal(listener.isDeviceUpdated, true); + + provider.listener = null; + + run_next_test(); +} + +function diffDiscovery() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE); + let mockDevice2 = createDevice("B.local", 23456, "N2", SERVICE_TYPE); + let mockDevice3 = createDevice("C.local", 45678, "N3", SERVICE_TYPE); + + let mockObj = { + discovered: false, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + + if (!this.discovered) { + listener.onServiceFound(mockDevice1); + listener.onServiceFound(mockDevice2); + } else { + listener.onServiceFound(mockDevice1); + listener.onServiceFound(mockDevice3); + } + this.discovered = true; + + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() { + listener.onDiscoveryStopped(serviceType); + } + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceType, SERVICE_TYPE); + if (serviceInfo.serviceName == "N1") { + listener.onServiceResolved(mockDevice1); + } else if (serviceInfo.serviceName == "N2") { + listener.onServiceResolved(mockDevice2); + } else if (serviceInfo.serviceName == "N3") { + listener.onServiceResolved(mockDevice3); + } else { + Assert.ok(false); + } + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = new TestPresentationDeviceListener(); + Assert.equal(listener.count(), 0); + + // Start discovery + provider.listener = listener; // discover: N1, N2 + Assert.equal(listener.count(), 2); + Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); + Assert.equal(listener.devices['B.local'].name, mockDevice2.serviceName); + Assert.ok(!listener.devices['C.local']); + + // temporarily disable to stop discovery and re-enable + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + provider.forceDiscovery(); // discover: N1, N3, going to remove: N2 + Assert.equal(listener.count(), 3); + Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); + Assert.equal(listener.devices['B.local'].name, mockDevice2.serviceName); + Assert.equal(listener.devices['C.local'].name, mockDevice3.serviceName); + + // temporarily disable to stop discovery and re-enable + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + provider.forceDiscovery(); // discover: N1, N3, remove: N2 + Assert.equal(listener.count(), 2); + Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); + Assert.ok(!listener.devices['B.local']); + Assert.equal(listener.devices['C.local'].name, mockDevice3.serviceName); + + provider.listener = null; + + run_next_test(); +} + +function serverClosed() { + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) { + this.serviceRegistered++; + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() { + this.serviceUnregistered++; + }.bind(this) + }; + }, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + }, + serviceRegistered: 0, + serviceUnregistered: 0 + }; + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + + Assert.equal(mockObj.serviceRegistered, 0); + Assert.equal(mockObj.serviceUnregistered, 0); + + // Register + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) { this.devices.push(device); }, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + devices: [] + }; + Assert.equal(listener.devices.length, 0); + + provider.listener = listener; + Assert.equal(mockObj.serviceRegistered, 1); + Assert.equal(mockObj.serviceUnregistered, 0); + Assert.equal(listener.devices.length, 1); + + let serverListener = provider.QueryInterface(Ci.nsIPresentationControlServerListener); + let randomPort = 9527; + serverListener.onServerReady(randomPort, ''); + + Assert.equal(mockObj.serviceRegistered, 2); + Assert.equal(mockObj.serviceUnregistered, 1); + Assert.equal(listener.devices.length, 1); + + // Unregister + provider.listener = null; + Assert.equal(mockObj.serviceRegistered, 2); + Assert.equal(mockObj.serviceUnregistered, 2); + Assert.equal(listener.devices.length, 1); + + run_next_test(); +} + +function serverRetry() { + Services.prefs.setBoolPref(PREF_DISCOVERY, false); + Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); + + let isRetrying = false; + + let mockSDObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) {}, + registerService: function(serviceInfo, listener) { + Assert.ok(isRetrying, "register service after retrying startServer"); + provider.listener = null; + run_next_test(); + }, + resolveService: function(serviceInfo, listener) {} + }; + + let mockServerObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), + startServer: function(encrypted, port) { + if (!isRetrying) { + isRetrying = true; + Services.tm.currentThread.dispatch(() => { + this.listener.onServerStopped(Cr.NS_ERROR_FAILURE); + }, Ci.nsIThread.DISPATCH_NORMAL); + } else { + this.port = 54321; + Services.tm.currentThread.dispatch(() => { + this.listener.onServerReady(this.port, this.certFingerprint); + }, Ci.nsIThread.DISPATCH_NORMAL); + } + }, + sessionRequest: function() {}, + close: function() {}, + id: '', + version: LATEST_VERSION, + port: 0, + certFingerprint: 'mock-cert-fingerprint', + listener: null, + }; + + let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); + let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) {}, + removeDevice: function(device) {}, + updateDevice: function(device) {}, + onSessionRequest: function(device, url, presentationId, controlChannel) {} + }; + + provider.listener = listener; +} + +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); + + let infoHook = new ContractHook(INFO_CONTRACT_ID, MockDNSServiceInfo); + + do_register_cleanup(() => { + Services.prefs.clearUserPref(PREF_DISCOVERY); + Services.prefs.clearUserPref(PREF_DISCOVERABLE); + }); + + add_test(registerService); + add_test(noRegisterService); + add_test(registerServiceDynamically); + add_test(addDevice); + add_test(filterDevice); + add_test(handleSessionRequest); + add_test(handleOnSessionRequest); + add_test(handleOnSessionRequestFromUnknownDevice); + add_test(noAddDevice); + add_test(ignoreIncompatibleDevice); + add_test(ignoreSelfDevice); + add_test(addDeviceDynamically); + add_test(updateDevice); + add_test(diffDiscovery); + add_test(serverClosed); + add_test(serverRetry); + + run_next_test(); +} |