1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,489 @@ 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 +"use strict"; 1.9 + 1.10 +module.metadata = { 1.11 + "stability": "deprecated" 1.12 +}; 1.13 + 1.14 +const memory = require('./memory'); 1.15 +var timer = require("../timers"); 1.16 +var cfxArgs = require("@test/options"); 1.17 + 1.18 +exports.findAndRunTests = function findAndRunTests(options) { 1.19 + var TestFinder = require("./unit-test-finder").TestFinder; 1.20 + var finder = new TestFinder({ 1.21 + filter: options.filter, 1.22 + testInProcess: options.testInProcess, 1.23 + testOutOfProcess: options.testOutOfProcess 1.24 + }); 1.25 + var runner = new TestRunner({fs: options.fs}); 1.26 + finder.findTests( 1.27 + function (tests) { 1.28 + runner.startMany({tests: tests, 1.29 + stopOnError: options.stopOnError, 1.30 + onDone: options.onDone}); 1.31 + }); 1.32 +}; 1.33 + 1.34 +var TestRunner = exports.TestRunner = function TestRunner(options) { 1.35 + if (options) { 1.36 + this.fs = options.fs; 1.37 + } 1.38 + this.console = (options && "console" in options) ? options.console : console; 1.39 + memory.track(this); 1.40 + this.passed = 0; 1.41 + this.failed = 0; 1.42 + this.testRunSummary = []; 1.43 + this.expectFailNesting = 0; 1.44 +}; 1.45 + 1.46 +TestRunner.prototype = { 1.47 + toString: function toString() "[object TestRunner]", 1.48 + 1.49 + DEFAULT_PAUSE_TIMEOUT: 5*60000, 1.50 + PAUSE_DELAY: 500, 1.51 + 1.52 + _logTestFailed: function _logTestFailed(why) { 1.53 + if (!(why in this.test.errors)) 1.54 + this.test.errors[why] = 0; 1.55 + this.test.errors[why]++; 1.56 + }, 1.57 + 1.58 + pass: function pass(message) { 1.59 + if(!this.expectFailure) { 1.60 + if ("testMessage" in this.console) 1.61 + this.console.testMessage(true, true, this.test.name, message); 1.62 + else 1.63 + this.console.info("pass:", message); 1.64 + this.passed++; 1.65 + this.test.passed++; 1.66 + } 1.67 + else { 1.68 + this.expectFailure = false; 1.69 + this._logTestFailed("failure"); 1.70 + if ("testMessage" in this.console) { 1.71 + this.console.testMessage(true, false, this.test.name, message); 1.72 + } 1.73 + else { 1.74 + this.console.error("fail:", 'Failure Expected: ' + message) 1.75 + this.console.trace(); 1.76 + } 1.77 + this.failed++; 1.78 + this.test.failed++; 1.79 + } 1.80 + }, 1.81 + 1.82 + fail: function fail(message) { 1.83 + if(!this.expectFailure) { 1.84 + this._logTestFailed("failure"); 1.85 + if ("testMessage" in this.console) { 1.86 + this.console.testMessage(false, false, this.test.name, message); 1.87 + } 1.88 + else { 1.89 + this.console.error("fail:", message) 1.90 + this.console.trace(); 1.91 + } 1.92 + this.failed++; 1.93 + this.test.failed++; 1.94 + } 1.95 + else { 1.96 + this.expectFailure = false; 1.97 + if ("testMessage" in this.console) 1.98 + this.console.testMessage(false, true, this.test.name, message); 1.99 + else 1.100 + this.console.info("pass:", message); 1.101 + this.passed++; 1.102 + this.test.passed++; 1.103 + } 1.104 + }, 1.105 + 1.106 + expectFail: function(callback) { 1.107 + this.expectFailure = true; 1.108 + callback(); 1.109 + this.expectFailure = false; 1.110 + }, 1.111 + 1.112 + exception: function exception(e) { 1.113 + this._logTestFailed("exception"); 1.114 + if (cfxArgs.parseable) 1.115 + this.console.print("TEST-UNEXPECTED-FAIL | " + this.test.name + " | " + e + "\n"); 1.116 + this.console.exception(e); 1.117 + this.failed++; 1.118 + this.test.failed++; 1.119 + }, 1.120 + 1.121 + assertMatches: function assertMatches(string, regexp, message) { 1.122 + if (regexp.test(string)) { 1.123 + if (!message) 1.124 + message = uneval(string) + " matches " + uneval(regexp); 1.125 + this.pass(message); 1.126 + } else { 1.127 + var no = uneval(string) + " doesn't match " + uneval(regexp); 1.128 + if (!message) 1.129 + message = no; 1.130 + else 1.131 + message = message + " (" + no + ")"; 1.132 + this.fail(message); 1.133 + } 1.134 + }, 1.135 + 1.136 + assertRaises: function assertRaises(func, predicate, message) { 1.137 + try { 1.138 + func(); 1.139 + if (message) 1.140 + this.fail(message + " (no exception thrown)"); 1.141 + else 1.142 + this.fail("function failed to throw exception"); 1.143 + } catch (e) { 1.144 + var errorMessage; 1.145 + if (typeof(e) == "string") 1.146 + errorMessage = e; 1.147 + else 1.148 + errorMessage = e.message; 1.149 + if (typeof(predicate) == "string") 1.150 + this.assertEqual(errorMessage, predicate, message); 1.151 + else 1.152 + this.assertMatches(errorMessage, predicate, message); 1.153 + } 1.154 + }, 1.155 + 1.156 + assert: function assert(a, message) { 1.157 + if (!a) { 1.158 + if (!message) 1.159 + message = "assertion failed, value is " + a; 1.160 + this.fail(message); 1.161 + } else 1.162 + this.pass(message || "assertion successful"); 1.163 + }, 1.164 + 1.165 + assertNotEqual: function assertNotEqual(a, b, message) { 1.166 + if (a != b) { 1.167 + if (!message) 1.168 + message = "a != b != " + uneval(a); 1.169 + this.pass(message); 1.170 + } else { 1.171 + var equality = uneval(a) + " == " + uneval(b); 1.172 + if (!message) 1.173 + message = equality; 1.174 + else 1.175 + message += " (" + equality + ")"; 1.176 + this.fail(message); 1.177 + } 1.178 + }, 1.179 + 1.180 + assertEqual: function assertEqual(a, b, message) { 1.181 + if (a == b) { 1.182 + if (!message) 1.183 + message = "a == b == " + uneval(a); 1.184 + this.pass(message); 1.185 + } else { 1.186 + var inequality = uneval(a) + " != " + uneval(b); 1.187 + if (!message) 1.188 + message = inequality; 1.189 + else 1.190 + message += " (" + inequality + ")"; 1.191 + this.fail(message); 1.192 + } 1.193 + }, 1.194 + 1.195 + assertNotStrictEqual: function assertNotStrictEqual(a, b, message) { 1.196 + if (a !== b) { 1.197 + if (!message) 1.198 + message = "a !== b !== " + uneval(a); 1.199 + this.pass(message); 1.200 + } else { 1.201 + var equality = uneval(a) + " === " + uneval(b); 1.202 + if (!message) 1.203 + message = equality; 1.204 + else 1.205 + message += " (" + equality + ")"; 1.206 + this.fail(message); 1.207 + } 1.208 + }, 1.209 + 1.210 + assertStrictEqual: function assertStrictEqual(a, b, message) { 1.211 + if (a === b) { 1.212 + if (!message) 1.213 + message = "a === b === " + uneval(a); 1.214 + this.pass(message); 1.215 + } else { 1.216 + var inequality = uneval(a) + " !== " + uneval(b); 1.217 + if (!message) 1.218 + message = inequality; 1.219 + else 1.220 + message += " (" + inequality + ")"; 1.221 + this.fail(message); 1.222 + } 1.223 + }, 1.224 + 1.225 + assertFunction: function assertFunction(a, message) { 1.226 + this.assertStrictEqual('function', typeof a, message); 1.227 + }, 1.228 + 1.229 + assertUndefined: function(a, message) { 1.230 + this.assertStrictEqual('undefined', typeof a, message); 1.231 + }, 1.232 + 1.233 + assertNotUndefined: function(a, message) { 1.234 + this.assertNotStrictEqual('undefined', typeof a, message); 1.235 + }, 1.236 + 1.237 + assertNull: function(a, message) { 1.238 + this.assertStrictEqual(null, a, message); 1.239 + }, 1.240 + 1.241 + assertNotNull: function(a, message) { 1.242 + this.assertNotStrictEqual(null, a, message); 1.243 + }, 1.244 + 1.245 + assertObject: function(a, message) { 1.246 + this.assertStrictEqual('[object Object]', Object.prototype.toString.apply(a), message); 1.247 + }, 1.248 + 1.249 + assertString: function(a, message) { 1.250 + this.assertStrictEqual('[object String]', Object.prototype.toString.apply(a), message); 1.251 + }, 1.252 + 1.253 + assertArray: function(a, message) { 1.254 + this.assertStrictEqual('[object Array]', Object.prototype.toString.apply(a), message); 1.255 + }, 1.256 + 1.257 + assertNumber: function(a, message) { 1.258 + this.assertStrictEqual('[object Number]', Object.prototype.toString.apply(a), message); 1.259 + }, 1.260 + 1.261 + done: function done() { 1.262 + if (!this.isDone) { 1.263 + this.isDone = true; 1.264 + if(this.test.teardown) { 1.265 + this.test.teardown(this); 1.266 + } 1.267 + if (this.waitTimeout !== null) { 1.268 + timer.clearTimeout(this.waitTimeout); 1.269 + this.waitTimeout = null; 1.270 + } 1.271 + // Do not leave any callback set when calling to `waitUntil` 1.272 + this.waitUntilCallback = null; 1.273 + if (this.test.passed == 0 && this.test.failed == 0) { 1.274 + this._logTestFailed("empty test"); 1.275 + if ("testMessage" in this.console) { 1.276 + this.console.testMessage(false, false, this.test.name, "Empty test"); 1.277 + } 1.278 + else { 1.279 + this.console.error("fail:", "Empty test") 1.280 + } 1.281 + this.failed++; 1.282 + this.test.failed++; 1.283 + } 1.284 + 1.285 + this.testRunSummary.push({ 1.286 + name: this.test.name, 1.287 + passed: this.test.passed, 1.288 + failed: this.test.failed, 1.289 + errors: [error for (error in this.test.errors)].join(", ") 1.290 + }); 1.291 + 1.292 + if (this.onDone !== null) { 1.293 + var onDone = this.onDone; 1.294 + var self = this; 1.295 + this.onDone = null; 1.296 + timer.setTimeout(function() { onDone(self); }, 0); 1.297 + } 1.298 + } 1.299 + }, 1.300 + 1.301 + // Set of assertion functions to wait for an assertion to become true 1.302 + // These functions take the same arguments as the TestRunner.assert* methods. 1.303 + waitUntil: function waitUntil() { 1.304 + return this._waitUntil(this.assert, arguments); 1.305 + }, 1.306 + 1.307 + waitUntilNotEqual: function waitUntilNotEqual() { 1.308 + return this._waitUntil(this.assertNotEqual, arguments); 1.309 + }, 1.310 + 1.311 + waitUntilEqual: function waitUntilEqual() { 1.312 + return this._waitUntil(this.assertEqual, arguments); 1.313 + }, 1.314 + 1.315 + waitUntilMatches: function waitUntilMatches() { 1.316 + return this._waitUntil(this.assertMatches, arguments); 1.317 + }, 1.318 + 1.319 + /** 1.320 + * Internal function that waits for an assertion to become true. 1.321 + * @param {Function} assertionMethod 1.322 + * Reference to a TestRunner assertion method like test.assert, 1.323 + * test.assertEqual, ... 1.324 + * @param {Array} args 1.325 + * List of arguments to give to the previous assertion method. 1.326 + * All functions in this list are going to be called to retrieve current 1.327 + * assertion values. 1.328 + */ 1.329 + _waitUntil: function waitUntil(assertionMethod, args) { 1.330 + let count = 0; 1.331 + let maxCount = this.DEFAULT_PAUSE_TIMEOUT / this.PAUSE_DELAY; 1.332 + 1.333 + // We need to ensure that test is asynchronous 1.334 + if (!this.waitTimeout) 1.335 + this.waitUntilDone(this.DEFAULT_PAUSE_TIMEOUT); 1.336 + 1.337 + let callback = null; 1.338 + let finished = false; 1.339 + 1.340 + let test = this; 1.341 + 1.342 + // capture a traceback before we go async. 1.343 + let traceback = require("../console/traceback"); 1.344 + let stack = traceback.get(); 1.345 + stack.splice(-2, 2); 1.346 + let currentWaitStack = traceback.format(stack); 1.347 + let timeout = null; 1.348 + 1.349 + function loop(stopIt) { 1.350 + timeout = null; 1.351 + 1.352 + // Build a mockup object to fake TestRunner API and intercept calls to 1.353 + // pass and fail methods, in order to retrieve nice error messages 1.354 + // and assertion result 1.355 + let mock = { 1.356 + pass: function (msg) { 1.357 + test.pass(msg); 1.358 + test.waitUntilCallback = null; 1.359 + if (callback && !stopIt) 1.360 + callback(); 1.361 + finished = true; 1.362 + }, 1.363 + fail: function (msg) { 1.364 + // If we are called on test timeout, we stop the loop 1.365 + // and print which test keeps failing: 1.366 + if (stopIt) { 1.367 + test.console.error("test assertion never became true:\n", 1.368 + msg + "\n", 1.369 + currentWaitStack); 1.370 + if (timeout) 1.371 + timer.clearTimeout(timeout); 1.372 + return; 1.373 + } 1.374 + timeout = timer.setTimeout(loop, test.PAUSE_DELAY); 1.375 + } 1.376 + }; 1.377 + 1.378 + // Automatically call args closures in order to build arguments for 1.379 + // assertion function 1.380 + let appliedArgs = []; 1.381 + for (let i = 0, l = args.length; i < l; i++) { 1.382 + let a = args[i]; 1.383 + if (typeof a == "function") { 1.384 + try { 1.385 + a = a(); 1.386 + } 1.387 + catch(e) { 1.388 + test.fail("Exception when calling asynchronous assertion: " + e + 1.389 + "\n" + e.stack); 1.390 + finished = true; 1.391 + return; 1.392 + } 1.393 + } 1.394 + appliedArgs.push(a); 1.395 + } 1.396 + 1.397 + // Finally call assertion function with current assertion values 1.398 + assertionMethod.apply(mock, appliedArgs); 1.399 + } 1.400 + loop(); 1.401 + this.waitUntilCallback = loop; 1.402 + 1.403 + // Return an object with `then` method, to offer a way to execute 1.404 + // some code when the assertion passed or failed 1.405 + return { 1.406 + then: function (c) { 1.407 + callback = c; 1.408 + 1.409 + // In case of immediate positive result, we need to execute callback 1.410 + // immediately here: 1.411 + if (finished) 1.412 + callback(); 1.413 + } 1.414 + }; 1.415 + }, 1.416 + 1.417 + waitUntilDone: function waitUntilDone(ms) { 1.418 + if (ms === undefined) 1.419 + ms = this.DEFAULT_PAUSE_TIMEOUT; 1.420 + 1.421 + var self = this; 1.422 + 1.423 + function tiredOfWaiting() { 1.424 + self._logTestFailed("timed out"); 1.425 + if ("testMessage" in self.console) { 1.426 + self.console.testMessage(false, false, self.test.name, "Timed out"); 1.427 + } 1.428 + else { 1.429 + self.console.error("fail:", "Timed out") 1.430 + } 1.431 + if (self.waitUntilCallback) { 1.432 + self.waitUntilCallback(true); 1.433 + self.waitUntilCallback = null; 1.434 + } 1.435 + self.failed++; 1.436 + self.test.failed++; 1.437 + self.done(); 1.438 + } 1.439 + 1.440 + // We may already have registered a timeout callback 1.441 + if (this.waitTimeout) 1.442 + timer.clearTimeout(this.waitTimeout); 1.443 + 1.444 + this.waitTimeout = timer.setTimeout(tiredOfWaiting, ms); 1.445 + }, 1.446 + 1.447 + startMany: function startMany(options) { 1.448 + function runNextTest(self) { 1.449 + var test = options.tests.shift(); 1.450 + if (options.stopOnError && self.test && self.test.failed) { 1.451 + self.console.error("aborted: test failed and --stop-on-error was specified"); 1.452 + options.onDone(self); 1.453 + } else if (test) { 1.454 + self.start({test: test, onDone: runNextTest}); 1.455 + } else { 1.456 + options.onDone(self); 1.457 + } 1.458 + } 1.459 + runNextTest(this); 1.460 + }, 1.461 + 1.462 + start: function start(options) { 1.463 + this.test = options.test; 1.464 + this.test.passed = 0; 1.465 + this.test.failed = 0; 1.466 + this.test.errors = {}; 1.467 + 1.468 + this.isDone = false; 1.469 + this.onDone = function(self) { 1.470 + if (cfxArgs.parseable) 1.471 + self.console.print("TEST-END | " + self.test.name + "\n"); 1.472 + options.onDone(self); 1.473 + } 1.474 + this.waitTimeout = null; 1.475 + 1.476 + try { 1.477 + if (cfxArgs.parseable) 1.478 + this.console.print("TEST-START | " + this.test.name + "\n"); 1.479 + else 1.480 + this.console.info("executing '" + this.test.name + "'"); 1.481 + 1.482 + if(this.test.setup) { 1.483 + this.test.setup(this); 1.484 + } 1.485 + this.test.testFunction(this); 1.486 + } catch (e) { 1.487 + this.exception(e); 1.488 + } 1.489 + if (this.waitTimeout === null) 1.490 + this.done(); 1.491 + } 1.492 +};