diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /services/sync/tps/extensions/mozmill/resource/modules/frame.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'services/sync/tps/extensions/mozmill/resource/modules/frame.js')
-rw-r--r-- | services/sync/tps/extensions/mozmill/resource/modules/frame.js | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/services/sync/tps/extensions/mozmill/resource/modules/frame.js b/services/sync/tps/extensions/mozmill/resource/modules/frame.js new file mode 100644 index 000000000..dae8276b6 --- /dev/null +++ b/services/sync/tps/extensions/mozmill/resource/modules/frame.js @@ -0,0 +1,788 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ + +var EXPORTED_SYMBOLS = ['Collector','Runner','events', 'runTestFile', 'log', + 'timers', 'persisted', 'shutdownApplication']; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +const TIMEOUT_SHUTDOWN_HTTPD = 15000; + +Cu.import("resource://gre/modules/Services.jsm"); + +Cu.import('resource://mozmill/stdlib/httpd.js'); + +var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker); +var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions); +var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors); +var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os); +var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings); +var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays); +var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs); +var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils); + +var securableModule = {}; +Cu.import('resource://mozmill/stdlib/securable-module.js', securableModule); + +var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); + +var httpd = null; +var persisted = {}; + +var assert = new assertions.Assert(); +var expect = new assertions.Expect(); + +var mozmill = undefined; +var mozelement = undefined; +var modules = undefined; + +var timers = []; + + +/** + * Shutdown or restart the application + * + * @param {boolean} [aFlags=undefined] + * Additional flags how to handle the shutdown or restart. The attributes + * eRestarti386 and eRestartx86_64 have not been documented yet. + * @see https://developer.mozilla.org/nsIAppStartup#Attributes + */ +function shutdownApplication(aFlags) { + var flags = Ci.nsIAppStartup.eForceQuit; + + if (aFlags) { + flags |= aFlags; + } + + // Send a request to shutdown the application. That will allow us and other + // components to finish up with any shutdown code. Please note that we don't + // care if other components or add-ons want to prevent this via cancelQuit, + // we really force the shutdown. + let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]. + createInstance(Components.interfaces.nsISupportsPRBool); + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); + + // Use a timer to trigger the application restart, which will allow us to + // send an ACK packet via jsbridge if the method has been called via Python. + var event = { + notify: function(timer) { + Services.startup.quit(flags); + } + } + + var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT); +} + +function stateChangeBase(possibilties, restrictions, target, cmeta, v) { + if (possibilties) { + if (!arrays.inArray(possibilties, v)) { + // TODO Error value not in this.poss + return; + } + } + + if (restrictions) { + for (var i in restrictions) { + var r = restrictions[i]; + if (!r(v)) { + // TODO error value did not pass restriction + return; + } + } + } + + // Fire jsbridge notification, logging notification, listener notifications + events[target] = v; + events.fireEvent(cmeta, target); +} + + +var events = { + appQuit : false, + currentModule : null, + currentState : null, + currentTest : null, + shutdownRequested : false, + userShutdown : null, + userShutdownTimer : null, + + listeners : {}, + globalListeners : [] +} + +events.setState = function (v) { + return stateChangeBase(['dependencies', 'setupModule', 'teardownModule', + 'test', 'setupTest', 'teardownTest', 'collection'], + null, 'currentState', 'setState', v); +} + +events.toggleUserShutdown = function (obj){ + if (!this.userShutdown) { + this.userShutdown = obj; + + var event = { + notify: function(timer) { + events.toggleUserShutdown(obj); + } + } + + this.userShutdownTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this.userShutdownTimer.initWithCallback(event, obj.timeout, Ci.nsITimer.TYPE_ONE_SHOT); + + } else { + this.userShutdownTimer.cancel(); + + // If the application is not going to shutdown, the user shutdown failed and + // we have to force a shutdown. + if (!events.appQuit) { + this.fail({'function':'events.toggleUserShutdown', + 'message':'Shutdown expected but none detected before timeout', + 'userShutdown': obj}); + + var flags = Ci.nsIAppStartup.eAttemptQuit; + if (events.isRestartShutdown()) { + flags |= Ci.nsIAppStartup.eRestart; + } + + shutdownApplication(flags); + } + } +} + +events.isUserShutdown = function () { + return this.userShutdown ? this.userShutdown["user"] : false; +} + +events.isRestartShutdown = function () { + return this.userShutdown.restart; +} + +events.startShutdown = function (obj) { + events.fireEvent('shutdown', obj); + + if (obj["user"]) { + events.toggleUserShutdown(obj); + } else { + shutdownApplication(obj.flags); + } +} + +events.setTest = function (test) { + test.__start__ = Date.now(); + test.__passes__ = []; + test.__fails__ = []; + + events.currentTest = test; + + var obj = {'filename': events.currentModule.__file__, + 'name': test.__name__} + events.fireEvent('setTest', obj); +} + +events.endTest = function (test) { + // use the current test unless specified + if (test === undefined) { + test = events.currentTest; + } + + // If no test is set it has already been reported. Beside that we don't want + // to report it a second time. + if (!test || test.status === 'done') + return; + + // report the end of a test + test.__end__ = Date.now(); + test.status = 'done'; + + var obj = {'filename': events.currentModule.__file__, + 'passed': test.__passes__.length, + 'failed': test.__fails__.length, + 'passes': test.__passes__, + 'fails' : test.__fails__, + 'name' : test.__name__, + 'time_start': test.__start__, + 'time_end': test.__end__} + + if (test.skipped) { + obj['skipped'] = true; + obj.skipped_reason = test.skipped_reason; + } + + if (test.meta) { + obj.meta = test.meta; + } + + // Report the test result only if the test is a true test or if it is failing + if (withs.startsWith(test.__name__, "test") || test.__fails__.length > 0) { + events.fireEvent('endTest', obj); + } +} + +events.setModule = function (aModule) { + aModule.__start__ = Date.now(); + aModule.__status__ = 'running'; + + var result = stateChangeBase(null, + [function (aModule) {return (aModule.__file__ != undefined)}], + 'currentModule', 'setModule', aModule); + + return result; +} + +events.endModule = function (aModule) { + // It should only reported once, so check if it already has been done + if (aModule.__status__ === 'done') + return; + + aModule.__end__ = Date.now(); + aModule.__status__ = 'done'; + + var obj = { + 'filename': aModule.__file__, + 'time_start': aModule.__start__, + 'time_end': aModule.__end__ + } + + events.fireEvent('endModule', obj); +} + +events.pass = function (obj) { + // a low level event, such as a keystroke, succeeds + if (events.currentTest) { + events.currentTest.__passes__.push(obj); + } + + for (var timer of timers) { + timer.actions.push( + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, + "obj": obj, + "result": "pass"} + ); + } + + events.fireEvent('pass', obj); +} + +events.fail = function (obj) { + var error = obj.exception; + + if (error) { + // Error objects aren't enumerable https://bugzilla.mozilla.org/show_bug.cgi?id=637207 + obj.exception = { + name: error.name, + message: error.message, + lineNumber: error.lineNumber, + fileName: error.fileName, + stack: error.stack + }; + } + + // a low level event, such as a keystroke, fails + if (events.currentTest) { + events.currentTest.__fails__.push(obj); + } + + for (var time of timers) { + timer.actions.push( + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, + "obj": obj, + "result": "fail"} + ); + } + + events.fireEvent('fail', obj); +} + +events.skip = function (reason) { + // this is used to report skips associated with setupModule and nothing else + events.currentTest.skipped = true; + events.currentTest.skipped_reason = reason; + + for (var timer of timers) { + timer.actions.push( + {"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__, + "obj": reason, + "result": "skip"} + ); + } + + events.fireEvent('skip', reason); +} + +events.fireEvent = function (name, obj) { + if (events.appQuit) { + // dump('* Event discarded: ' + name + ' ' + JSON.stringify(obj) + '\n'); + return; + } + + if (this.listeners[name]) { + for (var i in this.listeners[name]) { + this.listeners[name][i](obj); + } + } + + for (var listener of this.globalListeners) { + listener(name, obj); + } +} + +events.addListener = function (name, listener) { + if (this.listeners[name]) { + this.listeners[name].push(listener); + } else if (name == '') { + this.globalListeners.push(listener) + } else { + this.listeners[name] = [listener]; + } +} + +events.removeListener = function (listener) { + for (var listenerIndex in this.listeners) { + var e = this.listeners[listenerIndex]; + + for (var i in e){ + if (e[i] == listener) { + this.listeners[listenerIndex] = arrays.remove(e, i); + } + } + } + + for (var i in this.globalListeners) { + if (this.globalListeners[i] == listener) { + this.globalListeners = arrays.remove(this.globalListeners, i); + } + } +} + +events.persist = function () { + try { + events.fireEvent('persist', persisted); + } catch (e) { + events.fireEvent('error', "persist serialization failed.") + } +} + +events.firePythonCallback = function (obj) { + obj['test'] = events.currentModule.__file__; + events.fireEvent('firePythonCallback', obj); +} + +events.screenshot = function (obj) { + // Find the name of the test function + for (var attr in events.currentModule) { + if (events.currentModule[attr] == events.currentTest) { + var testName = attr; + break; + } + } + + obj['test_file'] = events.currentModule.__file__; + obj['test_name'] = testName; + events.fireEvent('screenshot', obj); +} + +var log = function (obj) { + events.fireEvent('log', obj); +} + +// Register the listeners +broker.addObject({'endTest': events.endTest, + 'fail': events.fail, + 'firePythonCallback': events.firePythonCallback, + 'log': log, + 'pass': events.pass, + 'persist': events.persist, + 'screenshot': events.screenshot, + 'shutdown': events.startShutdown, + }); + +try { + Cu.import('resource://jsbridge/modules/Events.jsm'); + + events.addListener('', function (name, obj) { + Events.fireEvent('mozmill.' + name, obj); + }); +} catch (e) { + Services.console.logStringMessage("Event module of JSBridge not available."); +} + + +/** + * Observer for notifications when the application is going to shutdown + */ +function AppQuitObserver() { + this.runner = null; + + Services.obs.addObserver(this, "quit-application-requested", false); +} + +AppQuitObserver.prototype = { + observe: function (aSubject, aTopic, aData) { + switch (aTopic) { + case "quit-application-requested": + Services.obs.removeObserver(this, "quit-application-requested"); + + // If we observe a quit notification make sure to send the + // results of the current test. In those cases we don't reach + // the equivalent code in runTestModule() + events.pass({'message': 'AppQuitObserver: ' + JSON.stringify(aData), + 'userShutdown': events.userShutdown}); + + if (this.runner) { + this.runner.end(); + } + + if (httpd) { + httpd.stop(); + } + + events.appQuit = true; + + break; + } + } +} + +var appQuitObserver = new AppQuitObserver(); + +/** + * The collector handles HTTPd.js and initilizing the module + */ +function Collector() { + this.test_modules_by_filename = {}; + this.testing = []; +} + +Collector.prototype.addHttpResource = function (aDirectory, aPath) { + var fp = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + fp.initWithPath(os.abspath(aDirectory, this.current_file)); + + return httpd.addHttpResource(fp, aPath); +} + +Collector.prototype.initTestModule = function (filename, testname) { + var test_module = this.loadFile(filename, this); + var has_restarted = !(testname == null); + test_module.__tests__ = []; + + for (var i in test_module) { + if (typeof(test_module[i]) == "function") { + test_module[i].__name__ = i; + + // Only run setupModule if we are a single test OR if we are the first + // test of a restart chain (don't run it prior to members in a restart + // chain) + if (i == "setupModule" && !has_restarted) { + test_module.__setupModule__ = test_module[i]; + } else if (i == "setupTest") { + test_module.__setupTest__ = test_module[i]; + } else if (i == "teardownTest") { + test_module.__teardownTest__ = test_module[i]; + } else if (i == "teardownModule") { + test_module.__teardownModule__ = test_module[i]; + } else if (withs.startsWith(i, "test")) { + if (testname && (i != testname)) { + continue; + } + + testname = null; + test_module.__tests__.push(test_module[i]); + } + } + } + + test_module.collector = this; + test_module.status = 'loaded'; + + this.test_modules_by_filename[filename] = test_module; + + return test_module; +} + +Collector.prototype.loadFile = function (path, collector) { + var moduleLoader = new securableModule.Loader({ + rootPaths: ["resource://mozmill/modules/"], + defaultPrincipal: "system", + globals : { Cc: Cc, + Ci: Ci, + Cu: Cu, + Cr: Components.results} + }); + + // load a test module from a file and add some candy + var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(path); + var uri = Services.io.newFileURI(file).spec; + + this.loadTestResources(); + + var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + var module = new Components.utils.Sandbox(systemPrincipal); + module.assert = assert; + module.Cc = Cc; + module.Ci = Ci; + module.Cr = Components.results; + module.Cu = Cu; + module.collector = collector; + module.driver = moduleLoader.require("driver"); + module.elementslib = mozelement; + module.errors = errors; + module.expect = expect; + module.findElement = mozelement; + module.log = log; + module.mozmill = mozmill; + module.persisted = persisted; + + module.require = function (mod) { + var loader = new securableModule.Loader({ + rootPaths: [Services.io.newFileURI(file.parent).spec, + "resource://mozmill/modules/"], + defaultPrincipal: "system", + globals : { assert: assert, + expect: expect, + mozmill: mozmill, + elementslib: mozelement, // This a quick hack to maintain backwards compatibility with 1.5.x + findElement: mozelement, + persisted: persisted, + Cc: Cc, + Ci: Ci, + Cu: Cu, + log: log } + }); + + if (modules != undefined) { + loader.modules = modules; + } + + var retval = loader.require(mod); + modules = loader.modules; + + return retval; + } + + if (collector != undefined) { + collector.current_file = file; + collector.current_path = path; + } + + try { + Services.scriptloader.loadSubScript(uri, module, "UTF-8"); + } catch (e) { + var obj = { + 'filename': path, + 'passed': 0, + 'failed': 1, + 'passes': [], + 'fails' : [{'exception' : { + message: e.message, + filename: e.filename, + lineNumber: e.lineNumber}}], + 'name' :'<TOP_LEVEL>' + }; + + events.fail({'exception': e}); + events.fireEvent('endTest', obj); + } + + module.__file__ = path; + module.__uri__ = uri; + + return module; +} + +Collector.prototype.loadTestResources = function () { + // load resources we want in our tests + if (mozmill === undefined) { + mozmill = {}; + Cu.import("resource://mozmill/driver/mozmill.js", mozmill); + } + if (mozelement === undefined) { + mozelement = {}; + Cu.import("resource://mozmill/driver/mozelement.js", mozelement); + } +} + + +/** + * + */ +function Httpd(aPort) { + this.http_port = aPort; + + while (true) { + try { + var srv = new HttpServer(); + srv.registerContentType("sjs", "sjs"); + srv.identity.setPrimary("http", "localhost", this.http_port); + srv.start(this.http_port); + + this._httpd = srv; + break; + } + catch (e) { + // Failure most likely due to port conflict + this.http_port++; + } + } +} + +Httpd.prototype.addHttpResource = function (aDir, aPath) { + var path = aPath ? ("/" + aPath + "/") : "/"; + + try { + this._httpd.registerDirectory(path, aDir); + return 'http://localhost:' + this.http_port + path; + } + catch (e) { + throw Error("Failure to register directory: " + aDir.path); + } +}; + +Httpd.prototype.stop = function () { + if (!this._httpd) { + return; + } + + var shutdown = false; + this._httpd.stop(function () { shutdown = true; }); + + assert.waitFor(function () { + return shutdown; + }, "Local HTTP server has been stopped", TIMEOUT_SHUTDOWN_HTTPD); + + this._httpd = null; +}; + +function startHTTPd() { + if (!httpd) { + // Ensure that we start the HTTP server only once during a session + httpd = new Httpd(43336); + } +} + + +function Runner() { + this.collector = new Collector(); + this.ended = false; + + var m = {}; Cu.import('resource://mozmill/driver/mozmill.js', m); + this.platform = m.platform; + + events.fireEvent('startRunner', true); +} + +Runner.prototype.end = function () { + if (!this.ended) { + this.ended = true; + + appQuitObserver.runner = null; + + events.endTest(); + events.endModule(events.currentModule); + events.fireEvent('endRunner', true); + events.persist(); + } +}; + +Runner.prototype.runTestFile = function (filename, name) { + var module = this.collector.initTestModule(filename, name); + this.runTestModule(module); +}; + +Runner.prototype.runTestModule = function (module) { + appQuitObserver.runner = this; + events.setModule(module); + + // If setupModule passes, run all the tests. Otherwise mark them as skipped. + if (this.execFunction(module.__setupModule__, module)) { + for (var test of module.__tests__) { + if (events.shutdownRequested) { + break; + } + + // If setupTest passes, run the test. Otherwise mark it as skipped. + if (this.execFunction(module.__setupTest__, module)) { + this.execFunction(test); + } else { + this.skipFunction(test, module.__setupTest__.__name__ + " failed"); + } + + this.execFunction(module.__teardownTest__, module); + } + + } else { + for (var test of module.__tests__) { + this.skipFunction(test, module.__setupModule__.__name__ + " failed"); + } + } + + this.execFunction(module.__teardownModule__, module); + events.endModule(module); +}; + +Runner.prototype.execFunction = function (func, arg) { + if (typeof func !== "function" || events.shutdownRequested) { + return true; + } + + var isTest = withs.startsWith(func.__name__, "test"); + + events.setState(isTest ? "test" : func.__name); + events.setTest(func); + + // skip excluded platforms + if (func.EXCLUDED_PLATFORMS != undefined) { + if (arrays.inArray(func.EXCLUDED_PLATFORMS, this.platform)) { + events.skip("Platform exclusion"); + events.endTest(func); + return false; + } + } + + // skip function if requested + if (func.__force_skip__ != undefined) { + events.skip(func.__force_skip__); + events.endTest(func); + return false; + } + + // execute the test function + try { + func(arg); + } catch (e) { + if (e instanceof errors.ApplicationQuitError) { + events.shutdownRequested = true; + } else { + events.fail({'exception': e, 'test': func}) + } + } + + // If a user shutdown has been requested and the function already returned, + // we can assume that a shutdown will not happen anymore. We should force a + // shutdown then, to prevent the next test from being executed. + if (events.isUserShutdown()) { + events.shutdownRequested = true; + events.toggleUserShutdown(events.userShutdown); + } + + events.endTest(func); + return events.currentTest.__fails__.length == 0; +}; + +function runTestFile(filename, name) { + var runner = new Runner(); + runner.runTestFile(filename, name); + runner.end(); + + return true; +} + +Runner.prototype.skipFunction = function (func, message) { + events.setTest(func); + events.skip(message); + events.endTest(func); +}; |