addon-sdk/source/lib/sdk/test/harness.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/test/harness.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,627 @@
     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 +"use strict";
     1.8 +
     1.9 +module.metadata = {
    1.10 +  "stability": "experimental"
    1.11 +};
    1.12 +
    1.13 +const { Cc, Ci, Cu } = require("chrome");
    1.14 +const { Loader } = require('./loader');
    1.15 +const { serializeStack, parseStack  } = require("toolkit/loader");
    1.16 +const { setTimeout } = require('../timers');
    1.17 +const { PlainTextConsole } = require("../console/plain-text");
    1.18 +const { when: unload } = require("../system/unload");
    1.19 +const { format, fromException }  = require("../console/traceback");
    1.20 +const system = require("../system");
    1.21 +const memory = require('../deprecated/memory');
    1.22 +const { gc: gcPromise } = require('./memory');
    1.23 +const { defer } = require('../core/promise');
    1.24 +
    1.25 +// Trick manifest builder to make it think we need these modules ?
    1.26 +const unit = require("../deprecated/unit-test");
    1.27 +const test = require("../../test");
    1.28 +const url = require("../url");
    1.29 +
    1.30 +function emptyPromise() {
    1.31 +  let { promise, resolve } = defer();
    1.32 +  resolve();
    1.33 +  return promise;
    1.34 +}
    1.35 +
    1.36 +var cService = Cc['@mozilla.org/consoleservice;1'].getService()
    1.37 +               .QueryInterface(Ci.nsIConsoleService);
    1.38 +
    1.39 +// The console used to log messages
    1.40 +var testConsole;
    1.41 +
    1.42 +// Cuddlefish loader in which we load and execute tests.
    1.43 +var loader;
    1.44 +
    1.45 +// Function to call when we're done running tests.
    1.46 +var onDone;
    1.47 +
    1.48 +// Function to print text to a console, w/o CR at the end.
    1.49 +var print;
    1.50 +
    1.51 +// How many more times to run all tests.
    1.52 +var iterationsLeft;
    1.53 +
    1.54 +// Whether to report memory profiling information.
    1.55 +var profileMemory;
    1.56 +
    1.57 +// Whether we should stop as soon as a test reports a failure.
    1.58 +var stopOnError;
    1.59 +
    1.60 +// Function to call to retrieve a list of tests to execute
    1.61 +var findAndRunTests;
    1.62 +
    1.63 +// Combined information from all test runs.
    1.64 +var results = {
    1.65 +  passed: 0,
    1.66 +  failed: 0,
    1.67 +  testRuns: []
    1.68 +};
    1.69 +
    1.70 +// A list of the compartments and windows loaded after startup
    1.71 +var startLeaks;
    1.72 +
    1.73 +// JSON serialization of last memory usage stats; we keep it stringified
    1.74 +// so we don't actually change the memory usage stats (in terms of objects)
    1.75 +// of the JSRuntime we're profiling.
    1.76 +var lastMemoryUsage;
    1.77 +
    1.78 +function analyzeRawProfilingData(data) {
    1.79 +  var graph = data.graph;
    1.80 +  var shapes = {};
    1.81 +
    1.82 +  // Convert keys in the graph from strings to ints.
    1.83 +  // TODO: Can we get rid of this ridiculousness?
    1.84 +  var newGraph = {};
    1.85 +  for (id in graph) {
    1.86 +    newGraph[parseInt(id)] = graph[id];
    1.87 +  }
    1.88 +  graph = newGraph;
    1.89 +
    1.90 +  var modules = 0;
    1.91 +  var moduleIds = [];
    1.92 +  var moduleObjs = {UNKNOWN: 0};
    1.93 +  for (let name in data.namedObjects) {
    1.94 +    moduleObjs[name] = 0;
    1.95 +    moduleIds[data.namedObjects[name]] = name;
    1.96 +    modules++;
    1.97 +  }
    1.98 +
    1.99 +  var count = 0;
   1.100 +  for (id in graph) {
   1.101 +    var parent = graph[id].parent;
   1.102 +    while (parent) {
   1.103 +      if (parent in moduleIds) {
   1.104 +        var name = moduleIds[parent];
   1.105 +        moduleObjs[name]++;
   1.106 +        break;
   1.107 +      }
   1.108 +      if (!(parent in graph)) {
   1.109 +        moduleObjs.UNKNOWN++;
   1.110 +        break;
   1.111 +      }
   1.112 +      parent = graph[parent].parent;
   1.113 +    }
   1.114 +    count++;
   1.115 +  }
   1.116 +
   1.117 +  print("\nobject count is " + count + " in " + modules + " modules" +
   1.118 +        " (" + data.totalObjectCount + " across entire JS runtime)\n");
   1.119 +  if (lastMemoryUsage) {
   1.120 +    var last = JSON.parse(lastMemoryUsage);
   1.121 +    var diff = {
   1.122 +      moduleObjs: dictDiff(last.moduleObjs, moduleObjs),
   1.123 +      totalObjectClasses: dictDiff(last.totalObjectClasses,
   1.124 +                                   data.totalObjectClasses)
   1.125 +    };
   1.126 +
   1.127 +    for (let name in diff.moduleObjs)
   1.128 +      print("  " + diff.moduleObjs[name] + " in " + name + "\n");
   1.129 +    for (let name in diff.totalObjectClasses)
   1.130 +      print("  " + diff.totalObjectClasses[name] + " instances of " +
   1.131 +            name + "\n");
   1.132 +  }
   1.133 +  lastMemoryUsage = JSON.stringify(
   1.134 +    {moduleObjs: moduleObjs,
   1.135 +     totalObjectClasses: data.totalObjectClasses}
   1.136 +  );
   1.137 +}
   1.138 +
   1.139 +function dictDiff(last, curr) {
   1.140 +  var diff = {};
   1.141 +
   1.142 +  for (let name in last) {
   1.143 +    var result = (curr[name] || 0) - last[name];
   1.144 +    if (result)
   1.145 +      diff[name] = (result > 0 ? "+" : "") + result;
   1.146 +  }
   1.147 +  for (let name in curr) {
   1.148 +    var result = curr[name] - (last[name] || 0);
   1.149 +    if (result)
   1.150 +      diff[name] = (result > 0 ? "+" : "") + result;
   1.151 +  }
   1.152 +  return diff;
   1.153 +}
   1.154 +
   1.155 +function reportMemoryUsage() {
   1.156 +  if (!profileMemory) {
   1.157 +    return emptyPromise();
   1.158 +  }
   1.159 +
   1.160 +  return gcPromise().then((function () {
   1.161 +    var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
   1.162 +              .getService(Ci.nsIMemoryReporterManager);
   1.163 +    let count = 0;
   1.164 +    function logReporter(process, path, kind, units, amount, description) {
   1.165 +      print(((++count == 1) ? "\n" : "") + description + ": " + amount + "\n");
   1.166 +    }
   1.167 +    mgr.getReportsForThisProcess(logReporter, null);
   1.168 +
   1.169 +    var weakrefs = [info.weakref.get()
   1.170 +                    for each (info in memory.getObjects())];
   1.171 +    weakrefs = [weakref for each (weakref in weakrefs) if (weakref)];
   1.172 +    print("Tracked memory objects in testing sandbox: " + weakrefs.length + "\n");
   1.173 +  }));
   1.174 +}
   1.175 +
   1.176 +var gWeakrefInfo;
   1.177 +
   1.178 +function checkMemory() {
   1.179 +  return gcPromise().then(_ => {
   1.180 +    let leaks = getPotentialLeaks();
   1.181 +
   1.182 +    let compartmentURLs = Object.keys(leaks.compartments).filter(function(url) {
   1.183 +      return !(url in startLeaks.compartments);
   1.184 +    });
   1.185 +
   1.186 +    let windowURLs = Object.keys(leaks.windows).filter(function(url) {
   1.187 +      return !(url in startLeaks.windows);
   1.188 +    });
   1.189 +
   1.190 +    for (let url of compartmentURLs)
   1.191 +      console.warn("LEAKED", leaks.compartments[url]);
   1.192 +
   1.193 +    for (let url of windowURLs)
   1.194 +      console.warn("LEAKED", leaks.windows[url]);
   1.195 +  }).then(showResults);
   1.196 +}
   1.197 +
   1.198 +function showResults() {
   1.199 +  let { promise, resolve } = defer();
   1.200 +
   1.201 +  if (gWeakrefInfo) {
   1.202 +    gWeakrefInfo.forEach(
   1.203 +      function(info) {
   1.204 +        var ref = info.weakref.get();
   1.205 +        if (ref !== null) {
   1.206 +          var data = ref.__url__ ? ref.__url__ : ref;
   1.207 +          var warning = data == "[object Object]"
   1.208 +            ? "[object " + data.constructor.name + "(" +
   1.209 +              [p for (p in data)].join(", ") + ")]"
   1.210 +            : data;
   1.211 +          console.warn("LEAK", warning, info.bin);
   1.212 +        }
   1.213 +      }
   1.214 +    );
   1.215 +  }
   1.216 +
   1.217 +  onDone(results);
   1.218 +
   1.219 +  resolve();
   1.220 +  return promise;
   1.221 +}
   1.222 +
   1.223 +function cleanup() {
   1.224 +  let coverObject = {};
   1.225 +  try {
   1.226 +    for (let name in loader.modules)
   1.227 +      memory.track(loader.modules[name],
   1.228 +                           "module global scope: " + name);
   1.229 +      memory.track(loader, "Cuddlefish Loader");
   1.230 +
   1.231 +    if (profileMemory) {
   1.232 +      gWeakrefInfo = [{ weakref: info.weakref, bin: info.bin }
   1.233 +                      for each (info in memory.getObjects())];
   1.234 +    }
   1.235 +
   1.236 +    loader.unload();
   1.237 +
   1.238 +    if (loader.globals.console.errorsLogged && !results.failed) {
   1.239 +      results.failed++;
   1.240 +      console.error("warnings and/or errors were logged.");
   1.241 +    }
   1.242 +
   1.243 +    if (consoleListener.errorsLogged && !results.failed) {
   1.244 +      console.warn(consoleListener.errorsLogged + " " +
   1.245 +                   "warnings or errors were logged to the " +
   1.246 +                   "platform's nsIConsoleService, which could " +
   1.247 +                   "be of no consequence; however, they could also " +
   1.248 +                   "be indicative of aberrant behavior.");
   1.249 +    }
   1.250 +
   1.251 +    // read the code coverage object, if it exists, from CoverJS-moz
   1.252 +    if (typeof loader.globals.global == "object") {
   1.253 +      coverObject = loader.globals.global['__$coverObject'] || {};
   1.254 +    }
   1.255 +
   1.256 +    consoleListener.errorsLogged = 0;
   1.257 +    loader = null;
   1.258 +
   1.259 +    memory.gc();
   1.260 +  }
   1.261 +  catch (e) {
   1.262 +    results.failed++;
   1.263 +    console.error("unload.send() threw an exception.");
   1.264 +    console.exception(e);
   1.265 +  };
   1.266 +
   1.267 +  setTimeout(require('@test/options').checkMemory ? checkMemory : showResults, 1);
   1.268 +
   1.269 +  // dump the coverobject
   1.270 +  if (Object.keys(coverObject).length){
   1.271 +    const self = require('sdk/self');
   1.272 +    const {pathFor} = require("sdk/system");
   1.273 +    let file = require('sdk/io/file');
   1.274 +    const {env} = require('sdk/system/environment');
   1.275 +    console.log("CWD:", env.PWD);
   1.276 +    let out = file.join(env.PWD,'coverstats-'+self.id+'.json');
   1.277 +    console.log('coverstats:', out);
   1.278 +    let outfh = file.open(out,'w');
   1.279 +    outfh.write(JSON.stringify(coverObject,null,2));
   1.280 +    outfh.flush();
   1.281 +    outfh.close();
   1.282 +  }
   1.283 +}
   1.284 +
   1.285 +function getPotentialLeaks() {
   1.286 +  memory.gc();
   1.287 +
   1.288 +  // Things we can assume are part of the platform and so aren't leaks
   1.289 +  let WHITELIST_BASE_URLS = [
   1.290 +    "chrome://",
   1.291 +    "resource:///",
   1.292 +    "resource://app/",
   1.293 +    "resource://gre/",
   1.294 +    "resource://gre-resources/",
   1.295 +    "resource://pdf.js/",
   1.296 +    "resource://pdf.js.components/",
   1.297 +    "resource://services-common/",
   1.298 +    "resource://services-crypto/",
   1.299 +    "resource://services-sync/"
   1.300 +  ];
   1.301 +
   1.302 +  let ioService = Cc["@mozilla.org/network/io-service;1"].
   1.303 +                 getService(Ci.nsIIOService);
   1.304 +  let uri = ioService.newURI("chrome://global/content/", "UTF-8", null);
   1.305 +  let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
   1.306 +                  getService(Ci.nsIChromeRegistry);
   1.307 +  uri = chromeReg.convertChromeURL(uri);
   1.308 +  let spec = uri.spec;
   1.309 +  let pos = spec.indexOf("!/");
   1.310 +  WHITELIST_BASE_URLS.push(spec.substring(0, pos + 2));
   1.311 +
   1.312 +  let zoneRegExp = new RegExp("^explicit/js-non-window/zones/zone[^/]+/compartment\\((.+)\\)");
   1.313 +  let compartmentRegexp = new RegExp("^explicit/js-non-window/compartments/non-window-global/compartment\\((.+)\\)/");
   1.314 +  let compartmentDetails = new RegExp("^([^,]+)(?:, (.+?))?(?: \\(from: (.*)\\))?$");
   1.315 +  let windowRegexp = new RegExp("^explicit/window-objects/top\\((.*)\\)/active");
   1.316 +  let windowDetails = new RegExp("^(.*), id=.*$");
   1.317 +
   1.318 +  function isPossibleLeak(item) {
   1.319 +    if (!item.location)
   1.320 +      return false;
   1.321 +
   1.322 +    for (let whitelist of WHITELIST_BASE_URLS) {
   1.323 +      if (item.location.substring(0, whitelist.length) == whitelist)
   1.324 +        return false;
   1.325 +    }
   1.326 +
   1.327 +    return true;
   1.328 +  }
   1.329 +
   1.330 +  let compartments = {};
   1.331 +  let windows = {};
   1.332 +  function logReporter(process, path, kind, units, amount, description) {
   1.333 +    let matches;
   1.334 +
   1.335 +    if ((matches = compartmentRegexp.exec(path)) || (matches = zoneRegExp.exec(path))) {
   1.336 +      if (matches[1] in compartments)
   1.337 +        return;
   1.338 +
   1.339 +      let details = compartmentDetails.exec(matches[1]);
   1.340 +      if (!details) {
   1.341 +        console.error("Unable to parse compartment detail " + matches[1]);
   1.342 +        return;
   1.343 +      }
   1.344 +
   1.345 +      let item = {
   1.346 +        path: matches[1],
   1.347 +        principal: details[1],
   1.348 +        location: details[2] ? details[2].replace("\\", "/", "g") : undefined,
   1.349 +        source: details[3] ? details[3].split(" -> ").reverse() : undefined,
   1.350 +        toString: function() this.location
   1.351 +      };
   1.352 +
   1.353 +      if (!isPossibleLeak(item))
   1.354 +        return;
   1.355 +
   1.356 +      compartments[matches[1]] = item;
   1.357 +      return;
   1.358 +    }
   1.359 +
   1.360 +    if (matches = windowRegexp.exec(path)) {
   1.361 +      if (matches[1] in windows)
   1.362 +        return;
   1.363 +
   1.364 +      let details = windowDetails.exec(matches[1]);
   1.365 +      if (!details) {
   1.366 +        console.error("Unable to parse window detail " + matches[1]);
   1.367 +        return;
   1.368 +      }
   1.369 +
   1.370 +      let item = {
   1.371 +        path: matches[1],
   1.372 +        location: details[1].replace("\\", "/", "g"),
   1.373 +        source: [details[1].replace("\\", "/", "g")],
   1.374 +        toString: function() this.location
   1.375 +      };
   1.376 +
   1.377 +      if (!isPossibleLeak(item))
   1.378 +        return;
   1.379 +
   1.380 +      windows[matches[1]] = item;
   1.381 +    }
   1.382 +  }
   1.383 +
   1.384 +  Cc["@mozilla.org/memory-reporter-manager;1"]
   1.385 +    .getService(Ci.nsIMemoryReporterManager)
   1.386 +    .getReportsForThisProcess(logReporter, null);
   1.387 +
   1.388 +  return { compartments: compartments, windows: windows };
   1.389 +}
   1.390 +
   1.391 +function nextIteration(tests) {
   1.392 +  if (tests) {
   1.393 +    results.passed += tests.passed;
   1.394 +    results.failed += tests.failed;
   1.395 +
   1.396 +    reportMemoryUsage().then(_ => {
   1.397 +      let testRun = [];
   1.398 +      for each (let test in tests.testRunSummary) {
   1.399 +        let testCopy = {};
   1.400 +        for (let info in test) {
   1.401 +          testCopy[info] = test[info];
   1.402 +        }
   1.403 +        testRun.push(testCopy);
   1.404 +      }
   1.405 +
   1.406 +      results.testRuns.push(testRun);
   1.407 +      iterationsLeft--;
   1.408 +
   1.409 +      checkForEnd();
   1.410 +    })
   1.411 +  }
   1.412 +  else {
   1.413 +    checkForEnd();
   1.414 +  }
   1.415 +}
   1.416 +
   1.417 +function checkForEnd() {
   1.418 +  if (iterationsLeft && (!stopOnError || results.failed == 0)) {
   1.419 +    // Pass the loader which has a hooked console that doesn't dispatch
   1.420 +    // errors to the JS console and avoid firing false alarm in our
   1.421 +    // console listener
   1.422 +    findAndRunTests(loader, nextIteration);
   1.423 +  }
   1.424 +  else {
   1.425 +    setTimeout(cleanup, 0);
   1.426 +  }
   1.427 +}
   1.428 +
   1.429 +var POINTLESS_ERRORS = [
   1.430 +  'Invalid chrome URI:',
   1.431 +  'OpenGL LayerManager Initialized Succesfully.',
   1.432 +  '[JavaScript Error: "TelemetryStopwatch:',
   1.433 +  'reference to undefined property',
   1.434 +  '[JavaScript Error: "The character encoding of the HTML document was ' +
   1.435 +    'not declared.',
   1.436 +  '[Javascript Warning: "Error: Failed to preserve wrapper of wrapped ' +
   1.437 +    'native weak map key',
   1.438 +  '[JavaScript Warning: "Duplicate resource declaration for',
   1.439 +  'file: "chrome://browser/content/',
   1.440 +  'file: "chrome://global/content/',
   1.441 +  '[JavaScript Warning: "The character encoding of a framed document was ' +
   1.442 +    'not declared.'
   1.443 +];
   1.444 +
   1.445 +var consoleListener = {
   1.446 +  errorsLogged: 0,
   1.447 +  observe: function(object) {
   1.448 +    if (!(object instanceof Ci.nsIScriptError))
   1.449 +      return;
   1.450 +    this.errorsLogged++;
   1.451 +    var message = object.QueryInterface(Ci.nsIConsoleMessage).message;
   1.452 +    var pointless = [err for each (err in POINTLESS_ERRORS)
   1.453 +                         if (message.indexOf(err) >= 0)];
   1.454 +    if (pointless.length == 0 && message)
   1.455 +      testConsole.log(message);
   1.456 +  }
   1.457 +};
   1.458 +
   1.459 +function TestRunnerConsole(base, options) {
   1.460 +  this.__proto__ = {
   1.461 +    errorsLogged: 0,
   1.462 +    warn: function warn() {
   1.463 +      this.errorsLogged++;
   1.464 +      base.warn.apply(base, arguments);
   1.465 +    },
   1.466 +    error: function error() {
   1.467 +      this.errorsLogged++;
   1.468 +      base.error.apply(base, arguments);
   1.469 +    },
   1.470 +    info: function info(first) {
   1.471 +      if (options.verbose)
   1.472 +        base.info.apply(base, arguments);
   1.473 +      else
   1.474 +        if (first == "pass:")
   1.475 +          print(".");
   1.476 +    },
   1.477 +    __proto__: base
   1.478 +  };
   1.479 +}
   1.480 +
   1.481 +function stringify(arg) {
   1.482 +  try {
   1.483 +    return String(arg);
   1.484 +  }
   1.485 +  catch(ex) {
   1.486 +    return "<toString() error>";
   1.487 +  }
   1.488 +}
   1.489 +
   1.490 +function stringifyArgs(args) {
   1.491 +  return Array.map(args, stringify).join(" ");
   1.492 +}
   1.493 +
   1.494 +function TestRunnerTinderboxConsole(base, options) {
   1.495 +  this.base = base;
   1.496 +  this.print = options.print;
   1.497 +  this.verbose = options.verbose;
   1.498 +  this.errorsLogged = 0;
   1.499 +
   1.500 +  // Binding all the public methods to an instance so that they can be used
   1.501 +  // as callback / listener functions straightaway.
   1.502 +  this.log = this.log.bind(this);
   1.503 +  this.info = this.info.bind(this);
   1.504 +  this.warn = this.warn.bind(this);
   1.505 +  this.error = this.error.bind(this);
   1.506 +  this.debug = this.debug.bind(this);
   1.507 +  this.exception = this.exception.bind(this);
   1.508 +  this.trace = this.trace.bind(this);
   1.509 +};
   1.510 +
   1.511 +TestRunnerTinderboxConsole.prototype = {
   1.512 +  testMessage: function testMessage(pass, expected, test, message) {
   1.513 +    let type = "TEST-";
   1.514 +    if (expected) {
   1.515 +      if (pass)
   1.516 +        type += "PASS";
   1.517 +      else
   1.518 +        type += "KNOWN-FAIL";
   1.519 +    }
   1.520 +    else {
   1.521 +      this.errorsLogged++;
   1.522 +      if (pass)
   1.523 +        type += "UNEXPECTED-PASS";
   1.524 +      else
   1.525 +        type += "UNEXPECTED-FAIL";
   1.526 +    }
   1.527 +
   1.528 +    this.print(type + " | " + test + " | " + message + "\n");
   1.529 +    if (!expected)
   1.530 +      this.trace();
   1.531 +  },
   1.532 +
   1.533 +  log: function log() {
   1.534 +    this.print("TEST-INFO | " + stringifyArgs(arguments) + "\n");
   1.535 +  },
   1.536 +
   1.537 +  info: function info(first) {
   1.538 +    this.print("TEST-INFO | " + stringifyArgs(arguments) + "\n");
   1.539 +  },
   1.540 +
   1.541 +  warn: function warn() {
   1.542 +    this.errorsLogged++;
   1.543 +    this.print("TEST-UNEXPECTED-FAIL | " + stringifyArgs(arguments) + "\n");
   1.544 +  },
   1.545 +
   1.546 +  error: function error() {
   1.547 +    this.errorsLogged++;
   1.548 +    this.print("TEST-UNEXPECTED-FAIL | " + stringifyArgs(arguments) + "\n");
   1.549 +    this.base.error.apply(this.base, arguments);
   1.550 +  },
   1.551 +
   1.552 +  debug: function debug() {
   1.553 +    this.print("TEST-INFO | " + stringifyArgs(arguments) + "\n");
   1.554 +  },
   1.555 +
   1.556 +  exception: function exception(e) {
   1.557 +    this.print("An exception occurred.\n" +
   1.558 +               require("../console/traceback").format(e) + "\n" + e + "\n");
   1.559 +  },
   1.560 +
   1.561 +  trace: function trace() {
   1.562 +    var traceback = require("../console/traceback");
   1.563 +    var stack = traceback.get();
   1.564 +    stack.splice(-1, 1);
   1.565 +    this.print("TEST-INFO | " + stringify(traceback.format(stack)) + "\n");
   1.566 +  }
   1.567 +};
   1.568 +
   1.569 +var runTests = exports.runTests = function runTests(options) {
   1.570 +  iterationsLeft = options.iterations;
   1.571 +  profileMemory = options.profileMemory;
   1.572 +  stopOnError = options.stopOnError;
   1.573 +  onDone = options.onDone;
   1.574 +  print = options.print;
   1.575 +  findAndRunTests = options.findAndRunTests;
   1.576 +
   1.577 +  try {
   1.578 +    cService.registerListener(consoleListener);
   1.579 +    print("Running tests on " + system.name + " " + system.version +
   1.580 +          "/Gecko " + system.platformVersion + " (" +
   1.581 +          system.id + ") under " +
   1.582 +          system.platform + "/" + system.architecture + ".\n");
   1.583 +
   1.584 +    if (options.parseable)
   1.585 +      testConsole = new TestRunnerTinderboxConsole(new PlainTextConsole(), options);
   1.586 +    else
   1.587 +      testConsole = new TestRunnerConsole(new PlainTextConsole(), options);
   1.588 +
   1.589 +    loader = Loader(module, {
   1.590 +      console: testConsole,
   1.591 +      global: {} // useful for storing things like coverage testing.
   1.592 +    });
   1.593 +
   1.594 +    // Load these before getting initial leak stats as they will still be in
   1.595 +    // memory when we check later
   1.596 +    require("../deprecated/unit-test");
   1.597 +    require("../deprecated/unit-test-finder");
   1.598 +    startLeaks = getPotentialLeaks();
   1.599 +
   1.600 +    nextIteration();
   1.601 +  } catch (e) {
   1.602 +    let frames = fromException(e).reverse().reduce(function(frames, frame) {
   1.603 +      if (frame.fileName.split("/").pop() === "unit-test-finder.js")
   1.604 +        frames.done = true
   1.605 +      if (!frames.done) frames.push(frame)
   1.606 +
   1.607 +      return frames
   1.608 +    }, [])
   1.609 +
   1.610 +    let prototype = typeof(e) === "object" ? e.constructor.prototype :
   1.611 +                    Error.prototype;
   1.612 +    let stack = serializeStack(frames.reverse());
   1.613 +
   1.614 +    let error = Object.create(prototype, {
   1.615 +      message: { value: e.message, writable: true, configurable: true },
   1.616 +      fileName: { value: e.fileName, writable: true, configurable: true },
   1.617 +      lineNumber: { value: e.lineNumber, writable: true, configurable: true },
   1.618 +      stack: { value: stack, writable: true, configurable: true },
   1.619 +      toString: { value: function() String(e), writable: true, configurable: true },
   1.620 +    });
   1.621 +
   1.622 +    print("Error: " + error + " \n " + format(error));
   1.623 +    onDone({passed: 0, failed: 1});
   1.624 +  }
   1.625 +};
   1.626 +
   1.627 +unload(function() {
   1.628 +  cService.unregisterListener(consoleListener);
   1.629 +});
   1.630 +

mercurial