services/sync/tps/extensions/mozmill/resource/modules/frame.js

changeset 0
6474c204b198
     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 +};

mercurial