/* 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/. */ "use strict"; const {utils: Cu} = Components; Cu.import("chrome://marionette/content/error.js"); this.EXPORTED_SYMBOLS = ["simpletest"]; this.simpletest = {}; /** * The simpletest harness, exposed in the script evaluation sandbox. */ simpletest.Harness = class { constructor(window, context, contentLogger, timeout, testName) { this.window = window; this.tests = []; this.logger = contentLogger; this.context = context; this.timeout = timeout; this.testName = testName; this.TEST_UNEXPECTED_FAIL = "TEST-UNEXPECTED-FAIL"; this.TEST_UNEXPECTED_PASS = "TEST-UNEXPECTED-PASS"; this.TEST_PASS = "TEST-PASS"; this.TEST_KNOWN_FAIL = "TEST-KNOWN-FAIL"; } get exports() { return new Map([ ["ok", this.ok.bind(this)], ["is", this.is.bind(this)], ["isnot", this.isnot.bind(this)], ["todo", this.todo.bind(this)], ["log", this.log.bind(this)], ["getLogs", this.getLogs.bind(this)], ["generate_results", this.generate_results.bind(this)], ["waitFor", this.waitFor.bind(this)], ["TEST_PASS", this.TEST_PASS], ["TEST_KNOWN_FAIL", this.TEST_KNOWN_FAIL], ["TEST_UNEXPECTED_FAIL", this.TEST_UNEXPECTED_FAIL], ["TEST_UNEXPECTED_PASS", this.TEST_UNEXPECTED_PASS], ]); } addTest(condition, name, passString, failString, diag, state) { let test = { result: !!condition, name: name, diag: diag, state: state }; this.logResult( test, typeof passString == "undefined" ? this.TEST_PASS : passString, typeof failString == "undefined" ? this.TEST_UNEXPECTED_FAIL : failString); this.tests.push(test); } ok(condition, name, passString, failString) { let diag = `${this.repr(condition)} was ${!!condition}, expected true`; this.addTest(condition, name, passString, failString, diag); } is(a, b, name, passString, failString) { let pass = (a == b); let diag = pass ? this.repr(a) + " should equal " + this.repr(b) : "got " + this.repr(a) + ", expected " + this.repr(b); this.addTest(pass, name, passString, failString, diag); } isnot(a, b, name, passString, failString) { let pass = (a != b); let diag = pass ? this.repr(a) + " should not equal " + this.repr(b) : "didn't expect " + this.repr(a) + ", but got it"; this.addTest(pass, name, passString, failString, diag); } todo(condition, name, passString, failString) { let diag = this.repr(condition) + " was expected false"; this.addTest(!condition, name, typeof(passString) == "undefined" ? this.TEST_KNOWN_FAIL : passString, typeof(failString) == "undefined" ? this.TEST_UNEXPECTED_FAIL : failString, diag, "todo"); } log(msg, level) { dump("MARIONETTE LOG: " + (level ? level : "INFO") + ": " + msg + "\n"); if (this.logger) { this.logger.log(msg, level); } } // TODO(ato): Suspect this isn't used anywhere getLogs() { if (this.logger) { return this.logger.get(); } } generate_results() { let passed = 0; let failures = []; let expectedFailures = []; let unexpectedSuccesses = []; for (let i in this.tests) { let isTodo = (this.tests[i].state == "todo"); if(this.tests[i].result) { if (isTodo) { expectedFailures.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); } else { passed++; } } else { if (isTodo) { unexpectedSuccesses.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); } else { failures.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); } } } // Reset state in case this object is reused for more tests. this.tests = []; return { passed: passed, failures: failures, expectedFailures: expectedFailures, unexpectedSuccesses: unexpectedSuccesses, }; } logToFile(file) { //TODO } logResult(test, passString, failString) { //TODO: dump to file let resultString = test.result ? passString : failString; let diagnostic = test.name + (test.diag ? " - " + test.diag : ""); let msg = resultString + " | " + this.testName + " | " + diagnostic; dump("MARIONETTE TEST RESULT:" + msg + "\n"); } repr(o) { if (typeof o == "undefined") { return "undefined"; } else if (o === null) { return "null"; } try { if (typeof o.__repr__ == "function") { return o.__repr__(); } else if (typeof o.repr == "function" && o.repr !== arguments.callee) { return o.repr(); } } catch (e) {} try { if (typeof o.NAME === "string" && (o.toString === Function.prototype.toString || o.toString === Object.prototype.toString)) { return o.NAME; } } catch (e) {} let ostring; try { ostring = (o + ""); } catch (e) { return "[" + typeof(o) + "]"; } if (typeof o == "function") { o = ostring.replace(/^\s+/, ""); let idx = o.indexOf("{"); if (idx != -1) { o = o.substr(0, idx) + "{...}"; } } return ostring; } waitFor(callback, test, timeout) { if (test()) { callback(); return; } let now = new Date(); let deadline = (timeout instanceof Date) ? timeout : new Date(now.valueOf() + (typeof timeout == "undefined" ? this.timeout : timeout)); if (deadline <= now) { dump("waitFor timeout: " + test.toString() + "\n"); // the script will timeout here, so no need to raise a separate // timeout exception return; } this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, deadline); } };