summaryrefslogtreecommitdiffstats
path: root/testing/modules/Assert.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'testing/modules/Assert.jsm')
-rw-r--r--testing/modules/Assert.jsm473
1 files changed, 473 insertions, 0 deletions
diff --git a/testing/modules/Assert.jsm b/testing/modules/Assert.jsm
new file mode 100644
index 000000000..db2747aa6
--- /dev/null
+++ b/testing/modules/Assert.jsm
@@ -0,0 +1,473 @@
+/* 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/. */
+
+// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
+// When you see a javadoc comment that contains a number, it's a reference to a
+// specific section of the CommonJS spec.
+//
+// Originally from narwhal.js (http://narwhaljs.org)
+// Copyright (c) 2009 Thomas Robinson <280north.com>
+// MIT license: http://opensource.org/licenses/MIT
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "Assert"
+];
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/ObjectUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+/**
+ * 1. The assert module provides functions that throw AssertionError's when
+ * particular conditions are not met.
+ *
+ * To use the module you'll need to instantiate it first, which allows consumers
+ * to override certain behavior on the newly obtained instance. For examples,
+ * see the javadoc comments for the `report` member function.
+ */
+var Assert = this.Assert = function(reporterFunc) {
+ if (reporterFunc)
+ this.setReporter(reporterFunc);
+};
+
+function instanceOf(object, type) {
+ return Object.prototype.toString.call(object) == "[object " + type + "]";
+}
+
+function replacer(key, value) {
+ if (value === undefined) {
+ return "" + value;
+ }
+ if (typeof value === "number" && (isNaN(value) || !isFinite(value))) {
+ return value.toString();
+ }
+ if (typeof value === "function" || instanceOf(value, "RegExp")) {
+ return value.toString();
+ }
+ return value;
+}
+
+const kTruncateLength = 128;
+
+function truncate(text, newLength = kTruncateLength) {
+ if (typeof text == "string") {
+ return text.length < newLength ? text : text.slice(0, newLength);
+ } else {
+ return text;
+ }
+}
+
+function getMessage(error, prefix = "") {
+ let actual, expected;
+ // Wrap calls to JSON.stringify in try...catch blocks, as they may throw. If
+ // so, fall back to toString().
+ try {
+ actual = JSON.stringify(error.actual, replacer);
+ } catch (ex) {
+ actual = Object.prototype.toString.call(error.actual);
+ }
+ try {
+ expected = JSON.stringify(error.expected, replacer);
+ } catch (ex) {
+ expected = Object.prototype.toString.call(error.expected);
+ }
+ let message = prefix;
+ if (error.operator) {
+ message += (prefix ? " - " : "") + truncate(actual) + " " + error.operator +
+ " " + truncate(expected);
+ }
+ return message;
+}
+
+/**
+ * 2. The AssertionError is defined in assert.
+ *
+ * Example:
+ * new assert.AssertionError({
+ * message: message,
+ * actual: actual,
+ * expected: expected,
+ * operator: operator
+ * });
+ *
+ * At present only the four keys mentioned above are used and
+ * understood by the spec. Implementations or sub modules can pass
+ * other keys to the AssertionError's constructor - they will be
+ * ignored.
+ */
+Assert.AssertionError = function(options) {
+ this.name = "AssertionError";
+ this.actual = options.actual;
+ this.expected = options.expected;
+ this.operator = options.operator;
+ this.message = getMessage(this, options.message);
+ // The part of the stack that comes from this module is not interesting.
+ let stack = Components.stack;
+ do {
+ stack = stack.asyncCaller || stack.caller;
+ } while(stack && stack.filename && stack.filename.includes("Assert.jsm"))
+ this.stack = stack;
+};
+
+// assert.AssertionError instanceof Error
+Assert.AssertionError.prototype = Object.create(Error.prototype, {
+ constructor: {
+ value: Assert.AssertionError,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+});
+
+var proto = Assert.prototype;
+
+proto._reporter = null;
+/**
+ * Set a custom assertion report handler function. Arguments passed in to this
+ * function are:
+ * err (AssertionError|null) An error object when the assertion failed or null
+ * when it passed
+ * message (string) Message describing the assertion
+ * stack (stack) Stack trace of the assertion function
+ *
+ * Example:
+ * ```js
+ * Assert.setReporter(function customReporter(err, message, stack) {
+ * if (err) {
+ * do_report_result(false, err.message, err.stack);
+ * } else {
+ * do_report_result(true, message, stack);
+ * }
+ * });
+ * ```
+ *
+ * @param reporterFunc
+ * (function) Report handler function
+ */
+proto.setReporter = function(reporterFunc) {
+ this._reporter = reporterFunc;
+};
+
+/**
+ * 3. All of the following functions must throw an AssertionError when a
+ * corresponding condition is not met, with a message that may be undefined if
+ * not provided. All assertion methods provide both the actual and expected
+ * values to the assertion error for display purposes.
+ *
+ * This report method only throws errors on assertion failures, as per spec,
+ * but consumers of this module (think: xpcshell-test, mochitest) may want to
+ * override this default implementation.
+ *
+ * Example:
+ * ```js
+ * // The following will report an assertion failure.
+ * this.report(1 != 2, 1, 2, "testing JS number math!", "==");
+ * ```
+ *
+ * @param failed
+ * (boolean) Indicates if the assertion failed or not
+ * @param actual
+ * (mixed) The result of evaluating the assertion
+ * @param expected (optional)
+ * (mixed) Expected result from the test author
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ * @param operator (optional)
+ * (string) Operation qualifier used by the assertion method (ex: '==')
+ */
+proto.report = function(failed, actual, expected, message, operator) {
+ let err = new Assert.AssertionError({
+ message: message,
+ actual: actual,
+ expected: expected,
+ operator: operator
+ });
+ if (!this._reporter) {
+ // If no custom reporter is set, throw the error.
+ if (failed) {
+ throw err;
+ }
+ } else {
+ this._reporter(failed ? err : null, err.message, err.stack);
+ }
+};
+
+/**
+ * 4. Pure assertion tests whether a value is truthy, as determined by !!guard.
+ * assert.ok(guard, message_opt);
+ * This statement is equivalent to assert.equal(true, !!guard, message_opt);.
+ * To test strictly for the value true, use assert.strictEqual(true, guard,
+ * message_opt);.
+ *
+ * @param value
+ * (mixed) Test subject to be evaluated as truthy
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.ok = function(value, message) {
+ this.report(!value, value, true, message, "==");
+};
+
+/**
+ * 5. The equality assertion tests shallow, coercive equality with ==.
+ * assert.equal(actual, expected, message_opt);
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as equivalent to `expected`
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.equal = function equal(actual, expected, message) {
+ this.report(actual != expected, actual, expected, message, "==");
+};
+
+/**
+ * 6. The non-equality assertion tests for whether two objects are not equal
+ * with != assert.notEqual(actual, expected, message_opt);
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as NOT equivalent to `expected`
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.notEqual = function notEqual(actual, expected, message) {
+ this.report(actual == expected, actual, expected, message, "!=");
+};
+
+/**
+ * 7. The equivalence assertion tests a deep equality relation.
+ * assert.deepEqual(actual, expected, message_opt);
+ *
+ * We check using the most exact approximation of equality between two objects
+ * to keep the chance of false positives to a minimum.
+ * `JSON.stringify` is not designed to be used for this purpose; objects may
+ * have ambiguous `toJSON()` implementations that would influence the test.
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as equivalent to `expected`, including nested properties
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.deepEqual = function deepEqual(actual, expected, message) {
+ this.report(!ObjectUtils.deepEqual(actual, expected), actual, expected, message, "deepEqual");
+};
+
+/**
+ * 8. The non-equivalence assertion tests for any deep inequality.
+ * assert.notDeepEqual(actual, expected, message_opt);
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as NOT equivalent to `expected`, including nested properties
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.notDeepEqual = function notDeepEqual(actual, expected, message) {
+ this.report(ObjectUtils.deepEqual(actual, expected), actual, expected, message, "notDeepEqual");
+};
+
+/**
+ * 9. The strict equality assertion tests strict equality, as determined by ===.
+ * assert.strictEqual(actual, expected, message_opt);
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as strictly equivalent to `expected`
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.strictEqual = function strictEqual(actual, expected, message) {
+ this.report(actual !== expected, actual, expected, message, "===");
+};
+
+/**
+ * 10. The strict non-equality assertion tests for strict inequality, as
+ * determined by !==. assert.notStrictEqual(actual, expected, message_opt);
+ *
+ * @param actual
+ * (mixed) Test subject to be evaluated as NOT strictly equivalent to `expected`
+ * @param expected
+ * (mixed) Test reference to evaluate against `actual`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.notStrictEqual = function notStrictEqual(actual, expected, message) {
+ this.report(actual === expected, actual, expected, message, "!==");
+};
+
+function expectedException(actual, expected) {
+ if (!actual || !expected) {
+ return false;
+ }
+
+ if (instanceOf(expected, "RegExp")) {
+ return expected.test(actual);
+ } else if (actual instanceof expected) {
+ return true;
+ } else if (expected.call({}, actual) === true) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * 11. Expected to throw an error:
+ * assert.throws(block, Error_opt, message_opt);
+ *
+ * @param block
+ * (function) Function block to evaluate and catch eventual thrown errors
+ * @param expected (optional)
+ * (mixed) Test reference to evaluate against the thrown result from `block`
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.throws = function(block, expected, message) {
+ let actual;
+
+ if (typeof expected === "string") {
+ message = expected;
+ expected = null;
+ }
+
+ try {
+ block();
+ } catch (e) {
+ actual = e;
+ }
+
+ message = (expected && expected.name ? " (" + expected.name + ")." : ".") +
+ (message ? " " + message : ".");
+
+ if (!actual) {
+ this.report(true, actual, expected, "Missing expected exception" + message);
+ }
+
+ if ((actual && expected && !expectedException(actual, expected))) {
+ throw actual;
+ }
+
+ this.report(false, expected, expected, message);
+};
+
+/**
+ * A promise that is expected to reject:
+ * assert.rejects(promise, expected, message);
+ *
+ * @param promise
+ * (promise) A promise that is expected to reject
+ * @param expected (optional)
+ * (mixed) Test reference to evaluate against the rejection result
+ * @param message (optional)
+ * (string) Short explanation of the expected result
+ */
+proto.rejects = function(promise, expected, message) {
+ return new Promise((resolve, reject) => {
+ if (typeof expected === "string") {
+ message = expected;
+ expected = null;
+ }
+ return promise.then(
+ () => this.report(true, null, expected, "Missing expected exception " + message),
+ err => {
+ if (expected && !expectedException(err, expected)) {
+ reject(err);
+ return;
+ }
+ this.report(false, err, expected, message);
+ resolve();
+ }
+ ).then(null, reject);
+ });
+};
+
+function compareNumbers(expression, lhs, rhs, message, operator) {
+ let lhsIsNumber = typeof lhs == "number";
+ let rhsIsNumber = typeof rhs == "number";
+
+ if (lhsIsNumber && rhsIsNumber) {
+ this.report(expression, lhs, rhs, message, operator);
+ return;
+ }
+
+ let errorMessage;
+ if (!lhsIsNumber && !rhsIsNumber) {
+ errorMessage = "Neither '" + lhs + "' nor '" + rhs + "' are numbers";
+ } else {
+ errorMessage = "'" + (lhsIsNumber ? rhs : lhs) + "' is not a number";
+ }
+ this.report(true, lhs, rhs, errorMessage);
+}
+
+/**
+ * The lhs must be greater than the rhs.
+ * assert.greater(lhs, rhs, message_opt);
+ *
+ * @param lhs
+ * (number) The left-hand side value
+ * @param rhs
+ * (number) The right-hand side value
+ * @param message (optional)
+ * (string) Short explanation of the comparison result
+ */
+proto.greater = function greater(lhs, rhs, message) {
+ compareNumbers.call(this, lhs <= rhs, lhs, rhs, message, ">");
+};
+
+/**
+ * The lhs must be greater than or equal to the rhs.
+ * assert.greaterOrEqual(lhs, rhs, message_opt);
+ *
+ * @param lhs
+ * (number) The left-hand side value
+ * @param rhs
+ * (number) The right-hand side value
+ * @param message (optional)
+ * (string) Short explanation of the comparison result
+ */
+proto.greaterOrEqual = function greaterOrEqual(lhs, rhs, message) {
+ compareNumbers.call(this, lhs < rhs, lhs, rhs, message, ">=");
+};
+
+/**
+ * The lhs must be less than the rhs.
+ * assert.less(lhs, rhs, message_opt);
+ *
+ * @param lhs
+ * (number) The left-hand side value
+ * @param rhs
+ * (number) The right-hand side value
+ * @param message (optional)
+ * (string) Short explanation of the comparison result
+ */
+proto.less = function less(lhs, rhs, message) {
+ compareNumbers.call(this, lhs >= rhs, lhs, rhs, message, "<");
+};
+
+/**
+ * The lhs must be less than or equal to the rhs.
+ * assert.lessOrEqual(lhs, rhs, message_opt);
+ *
+ * @param lhs
+ * (number) The left-hand side value
+ * @param rhs
+ * (number) The right-hand side value
+ * @param message (optional)
+ * (string) Short explanation of the comparison result
+ */
+proto.lessOrEqual = function lessOrEqual(lhs, rhs, message) {
+ compareNumbers.call(this, lhs > rhs, lhs, rhs, message, "<=");
+};
+