1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tps/extensions/mozmill/resource/modules/frame.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,785 @@ 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 +var EXPORTED_SYMBOLS = ['Collector','Runner','events', 'runTestFile', 'log', 1.9 + 'timers', 'persisted', 'shutdownApplication']; 1.10 + 1.11 +const Cc = Components.classes; 1.12 +const Ci = Components.interfaces; 1.13 +const Cu = Components.utils; 1.14 + 1.15 +const TIMEOUT_SHUTDOWN_HTTPD = 15000; 1.16 + 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +Cu.import('resource://mozmill/stdlib/httpd.js'); 1.20 + 1.21 +var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker); 1.22 +var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions); 1.23 +var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors); 1.24 +var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os); 1.25 +var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings); 1.26 +var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays); 1.27 +var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs); 1.28 +var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils); 1.29 + 1.30 +var securableModule = {}; 1.31 +Cu.import('resource://mozmill/stdlib/securable-module.js', securableModule); 1.32 + 1.33 +var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); 1.34 + 1.35 +var httpd = null; 1.36 +var persisted = {}; 1.37 + 1.38 +var assert = new assertions.Assert(); 1.39 + 1.40 +var mozmill = undefined; 1.41 +var mozelement = undefined; 1.42 +var modules = undefined; 1.43 + 1.44 +var timers = []; 1.45 + 1.46 + 1.47 +/** 1.48 + * Shutdown or restart the application 1.49 + * 1.50 + * @param {boolean} [aFlags=undefined] 1.51 + * Additional flags how to handle the shutdown or restart. The attributes 1.52 + * eRestarti386 and eRestartx86_64 have not been documented yet. 1.53 + * @see https://developer.mozilla.org/nsIAppStartup#Attributes 1.54 + */ 1.55 +function shutdownApplication(aFlags) { 1.56 + var flags = Ci.nsIAppStartup.eForceQuit; 1.57 + 1.58 + if (aFlags) { 1.59 + flags |= aFlags; 1.60 + } 1.61 + 1.62 + // Send a request to shutdown the application. That will allow us and other 1.63 + // components to finish up with any shutdown code. Please note that we don't 1.64 + // care if other components or add-ons want to prevent this via cancelQuit, 1.65 + // we really force the shutdown. 1.66 + let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]. 1.67 + createInstance(Components.interfaces.nsISupportsPRBool); 1.68 + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); 1.69 + 1.70 + // Use a timer to trigger the application restart, which will allow us to 1.71 + // send an ACK packet via jsbridge if the method has been called via Python. 1.72 + var event = { 1.73 + notify: function(timer) { 1.74 + Services.startup.quit(flags); 1.75 + } 1.76 + } 1.77 + 1.78 + var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.79 + timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT); 1.80 +} 1.81 + 1.82 +function stateChangeBase(possibilties, restrictions, target, cmeta, v) { 1.83 + if (possibilties) { 1.84 + if (!arrays.inArray(possibilties, v)) { 1.85 + // TODO Error value not in this.poss 1.86 + return; 1.87 + } 1.88 + } 1.89 + 1.90 + if (restrictions) { 1.91 + for (var i in restrictions) { 1.92 + var r = restrictions[i]; 1.93 + if (!r(v)) { 1.94 + // TODO error value did not pass restriction 1.95 + return; 1.96 + } 1.97 + } 1.98 + } 1.99 + 1.100 + // Fire jsbridge notification, logging notification, listener notifications 1.101 + events[target] = v; 1.102 + events.fireEvent(cmeta, target); 1.103 +} 1.104 + 1.105 + 1.106 +var events = { 1.107 + appQuit : false, 1.108 + currentModule : null, 1.109 + currentState : null, 1.110 + currentTest : null, 1.111 + shutdownRequested : false, 1.112 + userShutdown : null, 1.113 + userShutdownTimer : null, 1.114 + 1.115 + listeners : {}, 1.116 + globalListeners : [] 1.117 +} 1.118 + 1.119 +events.setState = function (v) { 1.120 + return stateChangeBase(['dependencies', 'setupModule', 'teardownModule', 1.121 + 'test', 'setupTest', 'teardownTest', 'collection'], 1.122 + null, 'currentState', 'setState', v); 1.123 +} 1.124 + 1.125 +events.toggleUserShutdown = function (obj){ 1.126 + if (!this.userShutdown) { 1.127 + this.userShutdown = obj; 1.128 + 1.129 + var event = { 1.130 + notify: function(timer) { 1.131 + events.toggleUserShutdown(obj); 1.132 + } 1.133 + } 1.134 + 1.135 + this.userShutdownTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.136 + this.userShutdownTimer.initWithCallback(event, obj.timeout, Ci.nsITimer.TYPE_ONE_SHOT); 1.137 + 1.138 + } else { 1.139 + this.userShutdownTimer.cancel(); 1.140 + 1.141 + // If the application is not going to shutdown, the user shutdown failed and 1.142 + // we have to force a shutdown. 1.143 + if (!events.appQuit) { 1.144 + this.fail({'function':'events.toggleUserShutdown', 1.145 + 'message':'Shutdown expected but none detected before timeout', 1.146 + 'userShutdown': obj}); 1.147 + 1.148 + var flags = Ci.nsIAppStartup.eAttemptQuit; 1.149 + if (events.isRestartShutdown()) { 1.150 + flags |= Ci.nsIAppStartup.eRestart; 1.151 + } 1.152 + 1.153 + shutdownApplication(flags); 1.154 + } 1.155 + } 1.156 +} 1.157 + 1.158 +events.isUserShutdown = function () { 1.159 + return this.userShutdown ? this.userShutdown["user"] : false; 1.160 +} 1.161 + 1.162 +events.isRestartShutdown = function () { 1.163 + return this.userShutdown.restart; 1.164 +} 1.165 + 1.166 +events.startShutdown = function (obj) { 1.167 + events.fireEvent('shutdown', obj); 1.168 + 1.169 + if (obj["user"]) { 1.170 + events.toggleUserShutdown(obj); 1.171 + } else { 1.172 + shutdownApplication(obj.flags); 1.173 + } 1.174 +} 1.175 + 1.176 +events.setTest = function (test) { 1.177 + test.__start__ = Date.now(); 1.178 + test.__passes__ = []; 1.179 + test.__fails__ = []; 1.180 + 1.181 + events.currentTest = test; 1.182 + 1.183 + var obj = {'filename': events.currentModule.__file__, 1.184 + 'name': test.__name__} 1.185 + events.fireEvent('setTest', obj); 1.186 +} 1.187 + 1.188 +events.endTest = function (test) { 1.189 + // use the current test unless specified 1.190 + if (test === undefined) { 1.191 + test = events.currentTest; 1.192 + } 1.193 + 1.194 + // If no test is set it has already been reported. Beside that we don't want 1.195 + // to report it a second time. 1.196 + if (!test || test.status === 'done') 1.197 + return; 1.198 + 1.199 + // report the end of a test 1.200 + test.__end__ = Date.now(); 1.201 + test.status = 'done'; 1.202 + 1.203 + var obj = {'filename': events.currentModule.__file__, 1.204 + 'passed': test.__passes__.length, 1.205 + 'failed': test.__fails__.length, 1.206 + 'passes': test.__passes__, 1.207 + 'fails' : test.__fails__, 1.208 + 'name' : test.__name__, 1.209 + 'time_start': test.__start__, 1.210 + 'time_end': test.__end__} 1.211 + 1.212 + if (test.skipped) { 1.213 + obj['skipped'] = true; 1.214 + obj.skipped_reason = test.skipped_reason; 1.215 + } 1.216 + 1.217 + if (test.meta) { 1.218 + obj.meta = test.meta; 1.219 + } 1.220 + 1.221 + // Report the test result only if the test is a true test or if it is failing 1.222 + if (withs.startsWith(test.__name__, "test") || test.__fails__.length > 0) { 1.223 + events.fireEvent('endTest', obj); 1.224 + } 1.225 +} 1.226 + 1.227 +events.setModule = function (aModule) { 1.228 + aModule.__start__ = Date.now(); 1.229 + aModule.__status__ = 'running'; 1.230 + 1.231 + var result = stateChangeBase(null, 1.232 + [function (aModule) {return (aModule.__file__ != undefined)}], 1.233 + 'currentModule', 'setModule', aModule); 1.234 + 1.235 + return result; 1.236 +} 1.237 + 1.238 +events.endModule = function (aModule) { 1.239 + // It should only reported once, so check if it already has been done 1.240 + if (aModule.__status__ === 'done') 1.241 + return; 1.242 + 1.243 + aModule.__end__ = Date.now(); 1.244 + aModule.__status__ = 'done'; 1.245 + 1.246 + var obj = { 1.247 + 'filename': aModule.__file__, 1.248 + 'time_start': aModule.__start__, 1.249 + 'time_end': aModule.__end__ 1.250 + } 1.251 + 1.252 + events.fireEvent('endModule', obj); 1.253 +} 1.254 + 1.255 +events.pass = function (obj) { 1.256 + // a low level event, such as a keystroke, succeeds 1.257 + if (events.currentTest) { 1.258 + events.currentTest.__passes__.push(obj); 1.259 + } 1.260 + 1.261 + for each (var timer in timers) { 1.262 + timer.actions.push( 1.263 + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, 1.264 + "obj": obj, 1.265 + "result": "pass"} 1.266 + ); 1.267 + } 1.268 + 1.269 + events.fireEvent('pass', obj); 1.270 +} 1.271 + 1.272 +events.fail = function (obj) { 1.273 + var error = obj.exception; 1.274 + 1.275 + if (error) { 1.276 + // Error objects aren't enumerable https://bugzilla.mozilla.org/show_bug.cgi?id=637207 1.277 + obj.exception = { 1.278 + name: error.name, 1.279 + message: error.message, 1.280 + lineNumber: error.lineNumber, 1.281 + fileName: error.fileName, 1.282 + stack: error.stack 1.283 + }; 1.284 + } 1.285 + 1.286 + // a low level event, such as a keystroke, fails 1.287 + if (events.currentTest) { 1.288 + events.currentTest.__fails__.push(obj); 1.289 + } 1.290 + 1.291 + for each (var time in timers) { 1.292 + timer.actions.push( 1.293 + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, 1.294 + "obj": obj, 1.295 + "result": "fail"} 1.296 + ); 1.297 + } 1.298 + 1.299 + events.fireEvent('fail', obj); 1.300 +} 1.301 + 1.302 +events.skip = function (reason) { 1.303 + // this is used to report skips associated with setupModule and nothing else 1.304 + events.currentTest.skipped = true; 1.305 + events.currentTest.skipped_reason = reason; 1.306 + 1.307 + for (var timer of timers) { 1.308 + timer.actions.push( 1.309 + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, 1.310 + "obj": reason, 1.311 + "result": "skip"} 1.312 + ); 1.313 + } 1.314 + 1.315 + events.fireEvent('skip', reason); 1.316 +} 1.317 + 1.318 +events.fireEvent = function (name, obj) { 1.319 + if (events.appQuit) { 1.320 + // dump('* Event discarded: ' + name + ' ' + JSON.stringify(obj) + '\n'); 1.321 + return; 1.322 + } 1.323 + 1.324 + if (this.listeners[name]) { 1.325 + for (var i in this.listeners[name]) { 1.326 + this.listeners[name][i](obj); 1.327 + } 1.328 + } 1.329 + 1.330 + for each(var listener in this.globalListeners) { 1.331 + listener(name, obj); 1.332 + } 1.333 +} 1.334 + 1.335 +events.addListener = function (name, listener) { 1.336 + if (this.listeners[name]) { 1.337 + this.listeners[name].push(listener); 1.338 + } else if (name == '') { 1.339 + this.globalListeners.push(listener) 1.340 + } else { 1.341 + this.listeners[name] = [listener]; 1.342 + } 1.343 +} 1.344 + 1.345 +events.removeListener = function (listener) { 1.346 + for (var listenerIndex in this.listeners) { 1.347 + var e = this.listeners[listenerIndex]; 1.348 + 1.349 + for (var i in e){ 1.350 + if (e[i] == listener) { 1.351 + this.listeners[listenerIndex] = arrays.remove(e, i); 1.352 + } 1.353 + } 1.354 + } 1.355 + 1.356 + for (var i in this.globalListeners) { 1.357 + if (this.globalListeners[i] == listener) { 1.358 + this.globalListeners = arrays.remove(this.globalListeners, i); 1.359 + } 1.360 + } 1.361 +} 1.362 + 1.363 +events.persist = function () { 1.364 + try { 1.365 + events.fireEvent('persist', persisted); 1.366 + } catch (e) { 1.367 + events.fireEvent('error', "persist serialization failed.") 1.368 + } 1.369 +} 1.370 + 1.371 +events.firePythonCallback = function (obj) { 1.372 + obj['test'] = events.currentModule.__file__; 1.373 + events.fireEvent('firePythonCallback', obj); 1.374 +} 1.375 + 1.376 +events.screenshot = function (obj) { 1.377 + // Find the name of the test function 1.378 + for (var attr in events.currentModule) { 1.379 + if (events.currentModule[attr] == events.currentTest) { 1.380 + var testName = attr; 1.381 + break; 1.382 + } 1.383 + } 1.384 + 1.385 + obj['test_file'] = events.currentModule.__file__; 1.386 + obj['test_name'] = testName; 1.387 + events.fireEvent('screenshot', obj); 1.388 +} 1.389 + 1.390 +var log = function (obj) { 1.391 + events.fireEvent('log', obj); 1.392 +} 1.393 + 1.394 +// Register the listeners 1.395 +broker.addObject({'endTest': events.endTest, 1.396 + 'fail': events.fail, 1.397 + 'firePythonCallback': events.firePythonCallback, 1.398 + 'log': log, 1.399 + 'pass': events.pass, 1.400 + 'persist': events.persist, 1.401 + 'screenshot': events.screenshot, 1.402 + 'shutdown': events.startShutdown, 1.403 + }); 1.404 + 1.405 +try { 1.406 + Cu.import('resource://jsbridge/modules/Events.jsm'); 1.407 + 1.408 + events.addListener('', function (name, obj) { 1.409 + Events.fireEvent('mozmill.' + name, obj); 1.410 + }); 1.411 +} catch (e) { 1.412 + Services.console.logStringMessage("Event module of JSBridge not available."); 1.413 +} 1.414 + 1.415 + 1.416 +/** 1.417 + * Observer for notifications when the application is going to shutdown 1.418 + */ 1.419 +function AppQuitObserver() { 1.420 + this.runner = null; 1.421 + 1.422 + Services.obs.addObserver(this, "quit-application-requested", false); 1.423 +} 1.424 + 1.425 +AppQuitObserver.prototype = { 1.426 + observe: function (aSubject, aTopic, aData) { 1.427 + switch (aTopic) { 1.428 + case "quit-application-requested": 1.429 + Services.obs.removeObserver(this, "quit-application-requested"); 1.430 + 1.431 + // If we observe a quit notification make sure to send the 1.432 + // results of the current test. In those cases we don't reach 1.433 + // the equivalent code in runTestModule() 1.434 + events.pass({'message': 'AppQuitObserver: ' + JSON.stringify(aData), 1.435 + 'userShutdown': events.userShutdown}); 1.436 + 1.437 + if (this.runner) { 1.438 + this.runner.end(); 1.439 + } 1.440 + 1.441 + if (httpd) { 1.442 + httpd.stop(); 1.443 + } 1.444 + 1.445 + events.appQuit = true; 1.446 + 1.447 + break; 1.448 + } 1.449 + } 1.450 +} 1.451 + 1.452 +var appQuitObserver = new AppQuitObserver(); 1.453 + 1.454 +/** 1.455 + * The collector handles HTTPd.js and initilizing the module 1.456 + */ 1.457 +function Collector() { 1.458 + this.test_modules_by_filename = {}; 1.459 + this.testing = []; 1.460 +} 1.461 + 1.462 +Collector.prototype.addHttpResource = function (aDirectory, aPath) { 1.463 + var fp = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); 1.464 + fp.initWithPath(os.abspath(aDirectory, this.current_file)); 1.465 + 1.466 + return httpd.addHttpResource(fp, aPath); 1.467 +} 1.468 + 1.469 +Collector.prototype.initTestModule = function (filename, testname) { 1.470 + var test_module = this.loadFile(filename, this); 1.471 + var has_restarted = !(testname == null); 1.472 + test_module.__tests__ = []; 1.473 + 1.474 + for (var i in test_module) { 1.475 + if (typeof(test_module[i]) == "function") { 1.476 + test_module[i].__name__ = i; 1.477 + 1.478 + // Only run setupModule if we are a single test OR if we are the first 1.479 + // test of a restart chain (don't run it prior to members in a restart 1.480 + // chain) 1.481 + if (i == "setupModule" && !has_restarted) { 1.482 + test_module.__setupModule__ = test_module[i]; 1.483 + } else if (i == "setupTest") { 1.484 + test_module.__setupTest__ = test_module[i]; 1.485 + } else if (i == "teardownTest") { 1.486 + test_module.__teardownTest__ = test_module[i]; 1.487 + } else if (i == "teardownModule") { 1.488 + test_module.__teardownModule__ = test_module[i]; 1.489 + } else if (withs.startsWith(i, "test")) { 1.490 + if (testname && (i != testname)) { 1.491 + continue; 1.492 + } 1.493 + 1.494 + testname = null; 1.495 + test_module.__tests__.push(test_module[i]); 1.496 + } 1.497 + } 1.498 + } 1.499 + 1.500 + test_module.collector = this; 1.501 + test_module.status = 'loaded'; 1.502 + 1.503 + this.test_modules_by_filename[filename] = test_module; 1.504 + 1.505 + return test_module; 1.506 +} 1.507 + 1.508 +Collector.prototype.loadFile = function (path, collector) { 1.509 + var moduleLoader = new securableModule.Loader({ 1.510 + rootPaths: ["resource://mozmill/modules/"], 1.511 + defaultPrincipal: "system", 1.512 + globals : { Cc: Cc, 1.513 + Ci: Ci, 1.514 + Cu: Cu, 1.515 + Cr: Components.results} 1.516 + }); 1.517 + 1.518 + // load a test module from a file and add some candy 1.519 + var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); 1.520 + file.initWithPath(path); 1.521 + var uri = Services.io.newFileURI(file).spec; 1.522 + 1.523 + this.loadTestResources(); 1.524 + 1.525 + var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); 1.526 + var module = new Components.utils.Sandbox(systemPrincipal); 1.527 + module.assert = new assertions.Assert(); 1.528 + module.Cc = Cc; 1.529 + module.Ci = Ci; 1.530 + module.Cr = Components.results; 1.531 + module.Cu = Cu; 1.532 + module.collector = collector; 1.533 + module.driver = moduleLoader.require("driver"); 1.534 + module.elementslib = mozelement; 1.535 + module.errors = errors; 1.536 + module.expect = new assertions.Expect(); 1.537 + module.findElement = mozelement; 1.538 + module.log = log; 1.539 + module.mozmill = mozmill; 1.540 + module.persisted = persisted; 1.541 + 1.542 + module.require = function (mod) { 1.543 + var loader = new securableModule.Loader({ 1.544 + rootPaths: [Services.io.newFileURI(file.parent).spec, 1.545 + "resource://mozmill/modules/"], 1.546 + defaultPrincipal: "system", 1.547 + globals : { mozmill: mozmill, 1.548 + elementslib: mozelement, // This a quick hack to maintain backwards compatibility with 1.5.x 1.549 + findElement: mozelement, 1.550 + persisted: persisted, 1.551 + Cc: Cc, 1.552 + Ci: Ci, 1.553 + Cu: Cu, 1.554 + log: log } 1.555 + }); 1.556 + 1.557 + if (modules != undefined) { 1.558 + loader.modules = modules; 1.559 + } 1.560 + 1.561 + var retval = loader.require(mod); 1.562 + modules = loader.modules; 1.563 + 1.564 + return retval; 1.565 + } 1.566 + 1.567 + if (collector != undefined) { 1.568 + collector.current_file = file; 1.569 + collector.current_path = path; 1.570 + } 1.571 + 1.572 + try { 1.573 + Services.scriptloader.loadSubScript(uri, module, "UTF-8"); 1.574 + } catch (e) { 1.575 + var obj = { 1.576 + 'filename': path, 1.577 + 'passed': 0, 1.578 + 'failed': 1, 1.579 + 'passes': [], 1.580 + 'fails' : [{'exception' : { 1.581 + message: e.message, 1.582 + filename: e.filename, 1.583 + lineNumber: e.lineNumber}}], 1.584 + 'name' :'<TOP_LEVEL>' 1.585 + }; 1.586 + 1.587 + events.fail({'exception': e}); 1.588 + events.fireEvent('endTest', obj); 1.589 + } 1.590 + 1.591 + module.__file__ = path; 1.592 + module.__uri__ = uri; 1.593 + 1.594 + return module; 1.595 +} 1.596 + 1.597 +Collector.prototype.loadTestResources = function () { 1.598 + // load resources we want in our tests 1.599 + if (mozmill === undefined) { 1.600 + mozmill = {}; 1.601 + Cu.import("resource://mozmill/driver/mozmill.js", mozmill); 1.602 + } 1.603 + if (mozelement === undefined) { 1.604 + mozelement = {}; 1.605 + Cu.import("resource://mozmill/driver/mozelement.js", mozelement); 1.606 + } 1.607 +} 1.608 + 1.609 + 1.610 +/** 1.611 + * 1.612 + */ 1.613 +function Httpd(aPort) { 1.614 + this.http_port = aPort; 1.615 + 1.616 + while (true) { 1.617 + try { 1.618 + var srv = new HttpServer(); 1.619 + srv.registerContentType("sjs", "sjs"); 1.620 + srv.identity.setPrimary("http", "localhost", this.http_port); 1.621 + srv.start(this.http_port); 1.622 + 1.623 + this._httpd = srv; 1.624 + break; 1.625 + } 1.626 + catch (e) { 1.627 + // Failure most likely due to port conflict 1.628 + this.http_port++; 1.629 + } 1.630 + } 1.631 +} 1.632 + 1.633 +Httpd.prototype.addHttpResource = function (aDir, aPath) { 1.634 + var path = aPath ? ("/" + aPath + "/") : "/"; 1.635 + 1.636 + try { 1.637 + this._httpd.registerDirectory(path, aDir); 1.638 + return 'http://localhost:' + this.http_port + path; 1.639 + } 1.640 + catch (e) { 1.641 + throw Error("Failure to register directory: " + aDir.path); 1.642 + } 1.643 +}; 1.644 + 1.645 +Httpd.prototype.stop = function () { 1.646 + if (!this._httpd) { 1.647 + return; 1.648 + } 1.649 + 1.650 + var shutdown = false; 1.651 + this._httpd.stop(function () { shutdown = true; }); 1.652 + 1.653 + assert.waitFor(function () { 1.654 + return shutdown; 1.655 + }, "Local HTTP server has been stopped", TIMEOUT_SHUTDOWN_HTTPD); 1.656 + 1.657 + this._httpd = null; 1.658 +}; 1.659 + 1.660 +function startHTTPd() { 1.661 + if (!httpd) { 1.662 + // Ensure that we start the HTTP server only once during a session 1.663 + httpd = new Httpd(43336); 1.664 + } 1.665 +} 1.666 + 1.667 + 1.668 +function Runner() { 1.669 + this.collector = new Collector(); 1.670 + this.ended = false; 1.671 + 1.672 + var m = {}; Cu.import('resource://mozmill/driver/mozmill.js', m); 1.673 + this.platform = m.platform; 1.674 + 1.675 + events.fireEvent('startRunner', true); 1.676 +} 1.677 + 1.678 +Runner.prototype.end = function () { 1.679 + if (!this.ended) { 1.680 + this.ended = true; 1.681 + 1.682 + appQuitObserver.runner = null; 1.683 + 1.684 + events.endTest(); 1.685 + events.endModule(events.currentModule); 1.686 + events.fireEvent('endRunner', true); 1.687 + events.persist(); 1.688 + } 1.689 +}; 1.690 + 1.691 +Runner.prototype.runTestFile = function (filename, name) { 1.692 + var module = this.collector.initTestModule(filename, name); 1.693 + this.runTestModule(module); 1.694 +}; 1.695 + 1.696 +Runner.prototype.runTestModule = function (module) { 1.697 + appQuitObserver.runner = this; 1.698 + events.setModule(module); 1.699 + 1.700 + // If setupModule passes, run all the tests. Otherwise mark them as skipped. 1.701 + if (this.execFunction(module.__setupModule__, module)) { 1.702 + for (var test of module.__tests__) { 1.703 + if (events.shutdownRequested) { 1.704 + break; 1.705 + } 1.706 + 1.707 + // If setupTest passes, run the test. Otherwise mark it as skipped. 1.708 + if (this.execFunction(module.__setupTest__, module)) { 1.709 + this.execFunction(test); 1.710 + } else { 1.711 + this.skipFunction(test, module.__setupTest__.__name__ + " failed"); 1.712 + } 1.713 + 1.714 + this.execFunction(module.__teardownTest__, module); 1.715 + } 1.716 + 1.717 + } else { 1.718 + for (var test of module.__tests__) { 1.719 + this.skipFunction(test, module.__setupModule__.__name__ + " failed"); 1.720 + } 1.721 + } 1.722 + 1.723 + this.execFunction(module.__teardownModule__, module); 1.724 + events.endModule(module); 1.725 +}; 1.726 + 1.727 +Runner.prototype.execFunction = function (func, arg) { 1.728 + if (typeof func !== "function" || events.shutdownRequested) { 1.729 + return true; 1.730 + } 1.731 + 1.732 + var isTest = withs.startsWith(func.__name__, "test"); 1.733 + 1.734 + events.setState(isTest ? "test" : func.__name); 1.735 + events.setTest(func); 1.736 + 1.737 + // skip excluded platforms 1.738 + if (func.EXCLUDED_PLATFORMS != undefined) { 1.739 + if (arrays.inArray(func.EXCLUDED_PLATFORMS, this.platform)) { 1.740 + events.skip("Platform exclusion"); 1.741 + events.endTest(func); 1.742 + return false; 1.743 + } 1.744 + } 1.745 + 1.746 + // skip function if requested 1.747 + if (func.__force_skip__ != undefined) { 1.748 + events.skip(func.__force_skip__); 1.749 + events.endTest(func); 1.750 + return false; 1.751 + } 1.752 + 1.753 + // execute the test function 1.754 + try { 1.755 + func(arg); 1.756 + } catch (e) { 1.757 + if (e instanceof errors.ApplicationQuitError) { 1.758 + events.shutdownRequested = true; 1.759 + } else { 1.760 + events.fail({'exception': e, 'test': func}) 1.761 + } 1.762 + } 1.763 + 1.764 + // If a user shutdown has been requested and the function already returned, 1.765 + // we can assume that a shutdown will not happen anymore. We should force a 1.766 + // shutdown then, to prevent the next test from being executed. 1.767 + if (events.isUserShutdown()) { 1.768 + events.shutdownRequested = true; 1.769 + events.toggleUserShutdown(events.userShutdown); 1.770 + } 1.771 + 1.772 + events.endTest(func); 1.773 + return events.currentTest.__fails__.length == 0; 1.774 +}; 1.775 + 1.776 +function runTestFile(filename, name) { 1.777 + var runner = new Runner(); 1.778 + runner.runTestFile(filename, name); 1.779 + runner.end(); 1.780 + 1.781 + return true; 1.782 +} 1.783 + 1.784 +Runner.prototype.skipFunction = function (func, message) { 1.785 + events.setTest(func); 1.786 + events.skip(message); 1.787 + events.endTest(func); 1.788 +};