1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-system-events.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,278 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const events = require("sdk/system/events"); 1.9 +const self = require("sdk/self"); 1.10 +const { Cc, Ci, Cu } = require("chrome"); 1.11 +const { setTimeout } = require("sdk/timers"); 1.12 +const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader"); 1.13 +const nsIObserverService = Cc["@mozilla.org/observer-service;1"]. 1.14 + getService(Ci.nsIObserverService); 1.15 + 1.16 +let isConsoleEvent = (topic) => 1.17 + !!~["console-api-log-event", "console-storage-cache-event"].indexOf(topic) 1.18 + 1.19 +exports["test basic"] = function(assert) { 1.20 + let type = Date.now().toString(32); 1.21 + 1.22 + let timesCalled = 0; 1.23 + function handler({subject, data}) { timesCalled++; }; 1.24 + 1.25 + events.on(type, handler); 1.26 + events.emit(type, { data: "yo yo" }); 1.27 + 1.28 + assert.equal(timesCalled, 1, "event handler was called"); 1.29 + 1.30 + events.off(type, handler); 1.31 + events.emit(type, { data: "no way" }); 1.32 + 1.33 + assert.equal(timesCalled, 1, "removed handler is no longer called"); 1.34 + 1.35 + events.once(type, handler); 1.36 + events.emit(type, { data: "and we meet again" }); 1.37 + events.emit(type, { data: "it's always hard to say bye" }); 1.38 + 1.39 + assert.equal(timesCalled, 2, "handlers added via once are triggered once"); 1.40 +} 1.41 + 1.42 +exports["test simple argument passing"] = function (assert) { 1.43 + let type = Date.now().toString(32); 1.44 + 1.45 + let lastArg; 1.46 + function handler({data}) { lastArg = data; } 1.47 + events.on(type, handler); 1.48 + 1.49 + [true, false, 100, 0, 'a string', ''].forEach(arg => { 1.50 + events.emit(type, arg); 1.51 + assert.strictEqual(lastArg, arg + '', 1.52 + 'event emitted for ' + arg + ' has correct data value'); 1.53 + 1.54 + events.emit(type, { data: arg }); 1.55 + assert.strictEqual(lastArg, arg + '', 1.56 + 'event emitted for ' + arg + ' has correct data value when a property on an object'); 1.57 + }); 1.58 + 1.59 + [null, undefined, {}].forEach(arg => { 1.60 + events.emit(type, arg); 1.61 + assert.strictEqual(lastArg, null, 1.62 + 'emitting ' + arg + ' gets null data'); 1.63 + }); 1.64 + 1.65 + events.off(type, handler); 1.66 +}; 1.67 + 1.68 +exports["test error reporting"] = function(assert) { 1.69 + let { loader, messages } = LoaderWithHookedConsole2(module); 1.70 + 1.71 + let events = loader.require("sdk/system/events"); 1.72 + function brokenHandler(subject, data) { throw new Error("foo"); }; 1.73 + 1.74 + let lineNumber; 1.75 + try { brokenHandler() } catch (error) { lineNumber = error.lineNumber } 1.76 + 1.77 + let errorType = Date.now().toString(32); 1.78 + 1.79 + events.on(errorType, brokenHandler); 1.80 + events.emit(errorType, { data: "yo yo" }); 1.81 + 1.82 + assert.equal(messages.length, 2, "Got an exception"); 1.83 + assert.equal(messages[0], "console.error: " + self.name + ": \n", 1.84 + "error is logged"); 1.85 + let text = messages[1]; 1.86 + assert.ok(text.indexOf("Error: foo") >= 0, "error message is logged"); 1.87 + assert.ok(text.indexOf(module.uri) >= 0, "module uri is logged"); 1.88 + assert.ok(text.indexOf(lineNumber) >= 0, "error line is logged"); 1.89 + 1.90 + events.off(errorType, brokenHandler); 1.91 + 1.92 + loader.unload(); 1.93 +}; 1.94 + 1.95 +exports["test listeners are GC-ed"] = function(assert, done) { 1.96 + let receivedFromWeak = []; 1.97 + let receivedFromStrong = []; 1.98 + let loader = Loader(module); 1.99 + let events = loader.require('sdk/system/events'); 1.100 + 1.101 + let type = 'test-listeners-are-garbage-collected'; 1.102 + function handler(event) { receivedFromStrong.push(event); } 1.103 + function weakHandler(event) { receivedFromWeak.push(event); } 1.104 + 1.105 + events.on(type, handler, true); 1.106 + events.on(type, weakHandler); 1.107 + 1.108 + events.emit(type, { data: 1 }); 1.109 + assert.equal(receivedFromStrong.length, 1, "strong listener invoked"); 1.110 + assert.equal(receivedFromWeak.length, 1, "weak listener invoked"); 1.111 + 1.112 + handler = weakHandler = null; 1.113 + 1.114 + Cu.schedulePreciseGC(function() { 1.115 + events.emit(type, { data: 2 }); 1.116 + 1.117 + assert.equal(receivedFromWeak.length, 1, "weak listener was GC-ed"); 1.118 + assert.equal(receivedFromStrong.length, 2, "strong listener was invoked"); 1.119 + 1.120 + loader.unload(); 1.121 + done(); 1.122 + }); 1.123 +}; 1.124 + 1.125 +exports["test alive listeners are removed on unload"] = function(assert) { 1.126 + let receivedFromWeak = []; 1.127 + let receivedFromStrong = []; 1.128 + let loader = Loader(module); 1.129 + let events = loader.require('sdk/system/events'); 1.130 + 1.131 + let type = 'test-alive-listeners-are-removed'; 1.132 + const handler = (event) => receivedFromStrong.push(event); 1.133 + const weakHandler = (event) => receivedFromWeak.push(event); 1.134 + 1.135 + events.on(type, handler, true); 1.136 + events.on(type, weakHandler); 1.137 + 1.138 + events.emit(type, { data: 1 }); 1.139 + assert.equal(receivedFromStrong.length, 1, "strong listener invoked"); 1.140 + assert.equal(receivedFromWeak.length, 1, "weak listener invoked"); 1.141 + 1.142 + loader.unload(); 1.143 + events.emit(type, { data: 2 }); 1.144 + 1.145 + assert.equal(receivedFromWeak.length, 1, "weak listener was removed"); 1.146 + assert.equal(receivedFromStrong.length, 1, "strong listener was removed"); 1.147 +}; 1.148 + 1.149 +exports["test handle nsIObserverService notifications"] = function(assert) { 1.150 + let ios = Cc['@mozilla.org/network/io-service;1'] 1.151 + .getService(Ci.nsIIOService); 1.152 + 1.153 + let uri = ios.newURI("http://www.foo.com", null, null); 1.154 + 1.155 + let type = Date.now().toString(32); 1.156 + let timesCalled = 0; 1.157 + let lastSubject = null; 1.158 + let lastData = null; 1.159 + let lastType = null; 1.160 + 1.161 + function handler({ subject, data, type }) { 1.162 + // Ignores internal console events 1.163 + if (isConsoleEvent(type)) 1.164 + return; 1.165 + timesCalled++; 1.166 + lastSubject = subject; 1.167 + lastData = data; 1.168 + lastType = type; 1.169 + }; 1.170 + 1.171 + events.on(type, handler); 1.172 + nsIObserverService.notifyObservers(uri, type, "some data"); 1.173 + 1.174 + assert.equal(timesCalled, 1, "notification invokes handler"); 1.175 + assert.equal(lastType, type, "event.type is notification topic"); 1.176 + assert.equal(lastSubject, uri, "event.subject is notification subject"); 1.177 + assert.equal(lastData, "some data", "event.data is notification data"); 1.178 + 1.179 + function customSubject() {} 1.180 + function customData() {} 1.181 + 1.182 + events.emit(type, { data: customData, subject: customSubject }); 1.183 + 1.184 + assert.equal(timesCalled, 2, "notification invokes handler"); 1.185 + assert.equal(lastType, type, "event.type is notification topic"); 1.186 + assert.equal(lastSubject, customSubject, 1.187 + "event.subject is wrapped & unwrapped"); 1.188 + assert.equal(lastData, customData, "event.data is wrapped & unwrapped"); 1.189 + 1.190 + events.off(type, handler); 1.191 + 1.192 + nsIObserverService.notifyObservers(null, type, "some data"); 1.193 + 1.194 + assert.equal(timesCalled, 2, "event handler is removed"); 1.195 + 1.196 + events.on("*", handler); 1.197 + 1.198 + nsIObserverService.notifyObservers(null, type, "more data"); 1.199 + 1.200 + assert.equal(timesCalled, 3, "notification invokes * handler"); 1.201 + assert.equal(lastType, type, "event.type is notification topic"); 1.202 + assert.equal(lastSubject, null, 1.203 + "event.subject is notification subject"); 1.204 + assert.equal(lastData, "more data", "event.data is notification data"); 1.205 + 1.206 + events.off("*", handler); 1.207 + 1.208 + nsIObserverService.notifyObservers(null, type, "last data"); 1.209 + 1.210 + assert.equal(timesCalled, 3, "* event handler is removed"); 1.211 +}; 1.212 + 1.213 +exports["test emit to nsIObserverService observers"] = function(assert) { 1.214 + let ios = Cc['@mozilla.org/network/io-service;1'] 1.215 + .getService(Ci.nsIIOService); 1.216 + 1.217 + let uri = ios.newURI("http://www.foo.com", null, null); 1.218 + let timesCalled = 0; 1.219 + let lastSubject = null; 1.220 + let lastData = null; 1.221 + let lastTopic = null; 1.222 + 1.223 + var topic = Date.now().toString(32) 1.224 + let nsIObserver = { 1.225 + QueryInterface: function() { 1.226 + return nsIObserver; 1.227 + }, 1.228 + observe: function(subject, topic, data) { 1.229 + // Ignores internal console events 1.230 + if (isConsoleEvent(topic)) 1.231 + return; 1.232 + timesCalled = timesCalled + 1; 1.233 + lastSubject = subject; 1.234 + lastData = data; 1.235 + lastTopic = topic; 1.236 + } 1.237 + }; 1.238 + 1.239 + nsIObserverService.addObserver(nsIObserver, topic, false); 1.240 + 1.241 + events.emit(topic, { subject: uri, data: "some data" }); 1.242 + 1.243 + assert.equal(timesCalled, 1, "emit notifies observers"); 1.244 + assert.equal(lastTopic, topic, "event type is notification topic"); 1.245 + assert.equal(lastSubject.wrappedJSObject.object, uri, 1.246 + "event.subject is notification subject"); 1.247 + assert.equal(lastData, "some data", "event.data is notification data"); 1.248 + function customSubject() {} 1.249 + function customData() {} 1.250 + events.emit(topic, { subject: customSubject, data: customData }); 1.251 + 1.252 + assert.equal(timesCalled, 2, "emit notifies observers"); 1.253 + assert.equal(lastTopic, topic, "event.type is notification"); 1.254 + assert.equal(lastSubject.wrappedJSObject.object, customSubject, 1.255 + "event.subject is notification subject"); 1.256 + assert.equal(lastData, customData, "event.data is notification data"); 1.257 + 1.258 + nsIObserverService.removeObserver(nsIObserver, topic); 1.259 + 1.260 + events.emit(topic, { data: "more data" }); 1.261 + 1.262 + assert.equal(timesCalled, 2, "removed observers no longer invoked"); 1.263 + 1.264 + nsIObserverService.addObserver(nsIObserver, "*", false); 1.265 + 1.266 + events.emit(topic, { data: "data again" }); 1.267 + 1.268 + assert.equal(timesCalled, 3, "emit notifies * observers"); 1.269 + 1.270 + assert.equal(lastTopic, topic, "event.type is notification"); 1.271 + assert.equal(lastSubject, null, 1.272 + "event.subject is notification subject"); 1.273 + assert.equal(lastData, "data again", "event.data is notification data"); 1.274 + 1.275 + nsIObserverService.removeObserver(nsIObserver, "*"); 1.276 + 1.277 + events.emit(topic, { data: "last data" }); 1.278 + assert.equal(timesCalled, 3, "removed observers no longer invoked"); 1.279 +} 1.280 + 1.281 +require("test").run(exports);