summaryrefslogtreecommitdiffstats
path: root/testing/modules/tests/xpcshell
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /testing/modules/tests/xpcshell
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/modules/tests/xpcshell')
-rw-r--r--testing/modules/tests/xpcshell/.eslintrc.js5
-rw-r--r--testing/modules/tests/xpcshell/test_assert.js451
-rw-r--r--testing/modules/tests/xpcshell/test_mockRegistrar.js65
-rw-r--r--testing/modules/tests/xpcshell/test_structuredlog.js153
-rw-r--r--testing/modules/tests/xpcshell/xpcshell.ini6
5 files changed, 680 insertions, 0 deletions
diff --git a/testing/modules/tests/xpcshell/.eslintrc.js b/testing/modules/tests/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..69e89d0054
--- /dev/null
+++ b/testing/modules/tests/xpcshell/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/xpcshell-test"],
+};
diff --git a/testing/modules/tests/xpcshell/test_assert.js b/testing/modules/tests/xpcshell/test_assert.js
new file mode 100644
index 0000000000..14fd38e883
--- /dev/null
+++ b/testing/modules/tests/xpcshell/test_assert.js
@@ -0,0 +1,451 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test cases borrowed and adapted from:
+// https://github.com/joyent/node/blob/6101eb184db77d0b11eb96e48744e57ecce4b73d/test/simple/test-assert.js
+// MIT license: http://opensource.org/licenses/MIT
+
+function run_test() {
+ let ns = {};
+ ChromeUtils.import("resource://testing-common/Assert.jsm", ns);
+ let assert = new ns.Assert();
+
+ function makeBlock(f, ...args) {
+ return function() {
+ return f.apply(assert, args);
+ };
+ }
+
+ function protoCtrChain(o) {
+ let result = [];
+ while ((o = o.__proto__)) {
+ result.push(o.constructor);
+ }
+ return result.join();
+ }
+
+ function indirectInstanceOf(obj, cls) {
+ if (obj instanceof cls) {
+ return true;
+ }
+ let clsChain = protoCtrChain(cls.prototype);
+ let objChain = protoCtrChain(obj);
+ return objChain.slice(-clsChain.length) === clsChain;
+ }
+
+ assert.ok(
+ indirectInstanceOf(ns.Assert.AssertionError.prototype, Error),
+ "Assert.AssertionError instanceof Error"
+ );
+
+ assert.throws(
+ makeBlock(assert.ok, false),
+ ns.Assert.AssertionError,
+ "ok(false)"
+ );
+
+ assert.ok(true, "ok(true)");
+
+ assert.ok("test", "ok('test')");
+
+ assert.throws(
+ makeBlock(assert.equal, true, false),
+ ns.Assert.AssertionError,
+ "equal"
+ );
+
+ assert.equal(null, null, "equal");
+
+ assert.equal(undefined, undefined, "equal");
+
+ assert.equal(null, undefined, "equal");
+
+ assert.equal(true, true, "equal");
+
+ assert.notEqual(true, false, "notEqual");
+
+ assert.throws(
+ makeBlock(assert.notEqual, true, true),
+ ns.Assert.AssertionError,
+ "notEqual"
+ );
+
+ assert.throws(
+ makeBlock(assert.strictEqual, 2, "2"),
+ ns.Assert.AssertionError,
+ "strictEqual"
+ );
+
+ assert.throws(
+ makeBlock(assert.strictEqual, null, undefined),
+ ns.Assert.AssertionError,
+ "strictEqual"
+ );
+
+ assert.notStrictEqual(2, "2", "notStrictEqual");
+
+ // deepEquals joy!
+ // 7.2
+ assert.deepEqual(
+ new Date(2000, 3, 14),
+ new Date(2000, 3, 14),
+ "deepEqual date"
+ );
+ assert.deepEqual(new Date(NaN), new Date(NaN), "deepEqual invalid dates");
+
+ assert.throws(
+ makeBlock(assert.deepEqual, new Date(), new Date(2000, 3, 14)),
+ ns.Assert.AssertionError,
+ "deepEqual date"
+ );
+
+ // 7.3
+ assert.deepEqual(/a/, /a/);
+ assert.deepEqual(/a/g, /a/g);
+ assert.deepEqual(/a/i, /a/i);
+ assert.deepEqual(/a/m, /a/m);
+ assert.deepEqual(/a/gim, /a/gim);
+ assert.throws(
+ makeBlock(assert.deepEqual, /ab/, /a/),
+ ns.Assert.AssertionError
+ );
+ assert.throws(
+ makeBlock(assert.deepEqual, /a/g, /a/),
+ ns.Assert.AssertionError
+ );
+ assert.throws(
+ makeBlock(assert.deepEqual, /a/i, /a/),
+ ns.Assert.AssertionError
+ );
+ assert.throws(
+ makeBlock(assert.deepEqual, /a/m, /a/),
+ ns.Assert.AssertionError
+ );
+ assert.throws(
+ makeBlock(assert.deepEqual, /a/gim, /a/im),
+ ns.Assert.AssertionError
+ );
+
+ let re1 = /a/;
+ re1.lastIndex = 3;
+ assert.throws(
+ makeBlock(assert.deepEqual, re1, /a/),
+ ns.Assert.AssertionError
+ );
+
+ // 7.4
+ assert.deepEqual(4, "4", "deepEqual == check");
+ assert.deepEqual(true, 1, "deepEqual == check");
+ assert.throws(
+ makeBlock(assert.deepEqual, 4, "5"),
+ ns.Assert.AssertionError,
+ "deepEqual == check"
+ );
+
+ // 7.5
+ // having the same number of owned properties && the same set of keys
+ assert.deepEqual({ a: 4 }, { a: 4 });
+ assert.deepEqual({ a: 4, b: "2" }, { a: 4, b: "2" });
+ assert.deepEqual([4], ["4"]);
+ assert.throws(
+ makeBlock(assert.deepEqual, { a: 4 }, { a: 4, b: true }),
+ ns.Assert.AssertionError
+ );
+ assert.deepEqual(["a"], { 0: "a" });
+
+ let a1 = [1, 2, 3];
+ let a2 = [1, 2, 3];
+ a1.a = "test";
+ a1.b = true;
+ a2.b = true;
+ a2.a = "test";
+ assert.throws(
+ makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)),
+ ns.Assert.AssertionError
+ );
+ assert.deepEqual(a1, a2);
+
+ let nbRoot = {
+ toString() {
+ return this.first + " " + this.last;
+ },
+ };
+
+ function nameBuilder(first, last) {
+ this.first = first;
+ this.last = last;
+ return this;
+ }
+ nameBuilder.prototype = nbRoot;
+
+ function nameBuilder2(first, last) {
+ this.first = first;
+ this.last = last;
+ return this;
+ }
+ nameBuilder2.prototype = nbRoot;
+
+ let nb1 = new nameBuilder("Ryan", "Dahl");
+ let nb2 = new nameBuilder2("Ryan", "Dahl");
+
+ assert.deepEqual(nb1, nb2);
+
+ nameBuilder2.prototype = Object;
+ nb2 = new nameBuilder2("Ryan", "Dahl");
+ assert.throws(
+ makeBlock(assert.deepEqual, nb1, nb2),
+ ns.Assert.AssertionError
+ );
+
+ // String literal + object
+ assert.throws(makeBlock(assert.deepEqual, "a", {}), ns.Assert.AssertionError);
+
+ // Testing the throwing
+ function thrower(errorConstructor) {
+ throw new errorConstructor("test");
+ }
+ makeBlock(thrower, ns.Assert.AssertionError);
+ makeBlock(thrower, ns.Assert.AssertionError);
+
+ // the basic calls work
+ assert.throws(
+ makeBlock(thrower, ns.Assert.AssertionError),
+ ns.Assert.AssertionError,
+ "message"
+ );
+ assert.throws(
+ makeBlock(thrower, ns.Assert.AssertionError),
+ ns.Assert.AssertionError
+ );
+ assert.throws(
+ makeBlock(thrower, ns.Assert.AssertionError),
+ ns.Assert.AssertionError
+ );
+
+ // if not passing an error, catch all.
+ assert.throws(makeBlock(thrower, TypeError), TypeError);
+
+ // when passing a type, only catch errors of the appropriate type
+ let threw = false;
+ try {
+ assert.throws(makeBlock(thrower, TypeError), ns.Assert.AssertionError);
+ } catch (e) {
+ threw = true;
+ assert.ok(e instanceof TypeError, "type");
+ }
+ assert.equal(
+ true,
+ threw,
+ "Assert.throws with an explicit error is eating extra errors",
+ ns.Assert.AssertionError
+ );
+ threw = false;
+
+ function ifError(err) {
+ if (err) {
+ throw err;
+ }
+ }
+ assert.throws(function() {
+ ifError(new Error("test error"));
+ }, /test error/);
+
+ // make sure that validating using constructor really works
+ threw = false;
+ try {
+ assert.throws(function() {
+ throw new Error({});
+ }, Array);
+ } catch (e) {
+ threw = true;
+ }
+ assert.ok(threw, "wrong constructor validation");
+
+ // use a RegExp to validate error message
+ assert.throws(makeBlock(thrower, TypeError), /test/);
+
+ // use a fn to validate error object
+ assert.throws(makeBlock(thrower, TypeError), function(err) {
+ if (err instanceof TypeError && /test/.test(err)) {
+ return true;
+ }
+ return false;
+ });
+ // do the same with an arrow function
+ assert.throws(makeBlock(thrower, TypeError), err => {
+ if (err instanceof TypeError && /test/.test(err)) {
+ return true;
+ }
+ return false;
+ });
+
+ function testAssertionMessage(actual, expected) {
+ try {
+ assert.equal(actual, "");
+ } catch (e) {
+ assert.equal(
+ e.toString(),
+ ["AssertionError:", expected, "==", '""'].join(" ")
+ );
+ }
+ }
+ testAssertionMessage(undefined, '"undefined"');
+ testAssertionMessage(null, "null");
+ testAssertionMessage(true, "true");
+ testAssertionMessage(false, "false");
+ testAssertionMessage(0, "0");
+ testAssertionMessage(100, "100");
+ testAssertionMessage(NaN, '"NaN"');
+ testAssertionMessage(Infinity, '"Infinity"');
+ testAssertionMessage(-Infinity, '"-Infinity"');
+ testAssertionMessage("", '""');
+ testAssertionMessage("foo", '"foo"');
+ testAssertionMessage([], "[]");
+ testAssertionMessage([1, 2, 3], "[1,2,3]");
+ testAssertionMessage(/a/, '"/a/"');
+ testAssertionMessage(/abc/gim, '"/abc/gim"');
+ testAssertionMessage(function f() {}, '"function f() {}"');
+ testAssertionMessage({}, "{}");
+ testAssertionMessage({ a: undefined, b: null }, '{"a":"undefined","b":null}');
+ testAssertionMessage(
+ { a: NaN, b: Infinity, c: -Infinity },
+ '{"a":"NaN","b":"Infinity","c":"-Infinity"}'
+ );
+
+ // https://github.com/joyent/node/issues/2893
+ try {
+ assert.throws(function() {
+ ifError(null);
+ });
+ } catch (e) {
+ threw = true;
+ assert.equal(
+ e.message,
+ "Error: The 'expected' argument was not supplied to Assert.throws() - false == true"
+ );
+ }
+ assert.ok(threw);
+
+ // https://github.com/joyent/node/issues/5292
+ try {
+ assert.equal(1, 2);
+ } catch (e) {
+ assert.equal(e.toString().split("\n")[0], "AssertionError: 1 == 2");
+ }
+
+ try {
+ assert.equal(1, 2, "oh no");
+ } catch (e) {
+ assert.equal(e.toString().split("\n")[0], "AssertionError: oh no - 1 == 2");
+ }
+
+ // Need to JSON.stringify so that their length is > 128 characters.
+ let longArray0 = Array.from(Array(50), (v, i) => i);
+ let longArray1 = longArray0.concat([51]);
+ try {
+ assert.deepEqual(longArray0, longArray1);
+ } catch (e) {
+ let message = e.toString();
+ // Just check that they're both entirely present in the message
+ assert.ok(message.includes(JSON.stringify(longArray0)));
+ assert.ok(message.includes(JSON.stringify(longArray1)));
+ }
+
+ // Test XPCShell-test integration:
+ ok(true, "OK, this went well");
+ deepEqual(/a/g, /a/g, "deep equal should work on RegExp");
+ deepEqual(/a/gim, /a/gim, "deep equal should work on RegExp");
+ deepEqual(
+ { a: 4, b: "1" },
+ { b: "1", a: 4 },
+ "deep equal should work on regular Object"
+ );
+ deepEqual(a1, a2, "deep equal should work on Array with Object properties");
+
+ // Test robustness of reporting:
+ equal(
+ new ns.Assert.AssertionError({
+ actual: {
+ toJSON() {
+ throw new Error("bam!");
+ },
+ },
+ expected: "foo",
+ operator: "=",
+ }).message,
+ '[object Object] = "foo"'
+ );
+
+ let message;
+ assert.greater(3, 2);
+ try {
+ assert.greater(2, 2);
+ } catch (e) {
+ message = e.toString().split("\n")[0];
+ }
+ assert.equal(message, "AssertionError: 2 > 2");
+
+ assert.greaterOrEqual(2, 2);
+ try {
+ assert.greaterOrEqual(1, 2);
+ } catch (e) {
+ message = e.toString().split("\n")[0];
+ }
+ assert.equal(message, "AssertionError: 1 >= 2");
+
+ assert.less(1, 2);
+ try {
+ assert.less(2, 2);
+ } catch (e) {
+ message = e.toString().split("\n")[0];
+ }
+ assert.equal(message, "AssertionError: 2 < 2");
+
+ assert.lessOrEqual(2, 2);
+ try {
+ assert.lessOrEqual(2, 1);
+ } catch (e) {
+ message = e.toString().split("\n")[0];
+ }
+ assert.equal(message, "AssertionError: 2 <= 1");
+
+ run_next_test();
+}
+
+add_task(async function test_rejects() {
+ let ns = {};
+ ChromeUtils.import("resource://testing-common/Assert.jsm", ns);
+ let assert = new ns.Assert();
+
+ // A helper function to test failures.
+ async function checkRejectsFails(err, expected) {
+ try {
+ await assert.rejects(Promise.reject(err), expected);
+ ok(false, "should have thrown");
+ } catch (ex) {
+ deepEqual(ex, err, "Assert.rejects threw the original unexpected error");
+ }
+ }
+
+ // A "throwable" error that's not an actual Error().
+ let SomeErrorLikeThing = function() {};
+
+ // The actual tests...
+
+ // An explicit error object:
+ // An instance to check against.
+ await assert.rejects(Promise.reject(new Error("oh no")), Error, "rejected");
+ // A regex to match against the message.
+ await assert.rejects(Promise.reject(new Error("oh no")), /oh no/, "rejected");
+
+ // Failure cases:
+ // An instance to check against that doesn't match.
+ await checkRejectsFails(new Error("something else"), SomeErrorLikeThing);
+ // A regex that doesn't match.
+ await checkRejectsFails(new Error("something else"), /oh no/);
+
+ // Check simple string messages.
+ await assert.rejects(Promise.reject("oh no"), /oh no/, "rejected");
+ // Wrong message.
+ await checkRejectsFails("something else", /oh no/);
+});
diff --git a/testing/modules/tests/xpcshell/test_mockRegistrar.js b/testing/modules/tests/xpcshell/test_mockRegistrar.js
new file mode 100644
index 0000000000..7082857033
--- /dev/null
+++ b/testing/modules/tests/xpcshell/test_mockRegistrar.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+
+function platformInfo(injectedValue) {
+ this.platformVersion = injectedValue;
+}
+
+platformInfo.prototype = {
+ platformVersion: "some version",
+ platformBuildID: "some id",
+ QueryInterface: ChromeUtils.generateQI(["nsIPlatformInfo"]),
+};
+
+add_test(function test_register() {
+ let localPlatformInfo = {
+ platformVersion: "local version",
+ platformBuildID: "local id",
+ QueryInterface: ChromeUtils.generateQI(["nsIPlatformInfo"]),
+ };
+
+ MockRegistrar.register("@mozilla.org/xre/app-info;1", localPlatformInfo);
+ Assert.equal(
+ Cc["@mozilla.org/xre/app-info;1"].createInstance(Ci.nsIPlatformInfo)
+ .platformVersion,
+ "local version"
+ );
+ run_next_test();
+});
+
+add_test(function test_register_with_arguments() {
+ MockRegistrar.register("@mozilla.org/xre/app-info;1", platformInfo, [
+ "override",
+ ]);
+ Assert.equal(
+ Cc["@mozilla.org/xre/app-info;1"].createInstance(Ci.nsIPlatformInfo)
+ .platformVersion,
+ "override"
+ );
+ run_next_test();
+});
+
+add_test(function test_register_twice() {
+ MockRegistrar.register("@mozilla.org/xre/app-info;1", platformInfo, [
+ "override",
+ ]);
+ Assert.equal(
+ Cc["@mozilla.org/xre/app-info;1"].createInstance(Ci.nsIPlatformInfo)
+ .platformVersion,
+ "override"
+ );
+
+ MockRegistrar.register("@mozilla.org/xre/app-info;1", platformInfo, [
+ "override again",
+ ]);
+ Assert.equal(
+ Cc["@mozilla.org/xre/app-info;1"].createInstance(Ci.nsIPlatformInfo)
+ .platformVersion,
+ "override again"
+ );
+ run_next_test();
+});
diff --git a/testing/modules/tests/xpcshell/test_structuredlog.js b/testing/modules/tests/xpcshell/test_structuredlog.js
new file mode 100644
index 0000000000..79f4162ccb
--- /dev/null
+++ b/testing/modules/tests/xpcshell/test_structuredlog.js
@@ -0,0 +1,153 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ const { StructuredLogger } = ChromeUtils.import(
+ "resource://testing-common/StructuredLog.jsm"
+ );
+
+ let testBuffer = [];
+
+ let appendBuffer = function(msg) {
+ testBuffer.push(JSON.stringify(msg));
+ };
+
+ let assertLastMsg = function(refData) {
+ // Check all fields in refData agree with those in the
+ // last message logged, and pop that message.
+ let lastMsg = JSON.parse(testBuffer.pop());
+ for (let field in refData) {
+ deepEqual(lastMsg[field], refData[field]);
+ }
+ // The logger should always set the source to the logger name.
+ equal(lastMsg.source, "test_log");
+ // The source_file field is always set by the mutator function.
+ equal(lastMsg.source_file, "test_structuredlog.js");
+ };
+
+ let addFileName = function(data) {
+ data.source_file = "test_structuredlog.js";
+ };
+
+ let logger = new StructuredLogger("test_log", appendBuffer, [addFileName]);
+
+ // Test unstructured logging
+ logger.info("Test message");
+ assertLastMsg({
+ action: "log",
+ message: "Test message",
+ level: "INFO",
+ });
+
+ logger.info("Test message", { foo: "bar" });
+ assertLastMsg({
+ action: "log",
+ message: "Test message",
+ level: "INFO",
+ extra: { foo: "bar" },
+ });
+
+ // Test end / start actions
+ logger.testStart("aTest");
+ assertLastMsg({
+ test: "aTest",
+ action: "test_start",
+ });
+
+ logger.testEnd("aTest", "OK");
+ assertLastMsg({
+ test: "aTest",
+ action: "test_end",
+ status: "OK",
+ });
+
+ // A failed test populates the "expected" field.
+ logger.testStart("aTest");
+ logger.testEnd("aTest", "FAIL", "PASS");
+ assertLastMsg({
+ action: "test_end",
+ test: "aTest",
+ status: "FAIL",
+ expected: "PASS",
+ });
+
+ // A failed test populates the "expected" field.
+ logger.testStart("aTest");
+ logger.testEnd("aTest", "FAIL", "PASS", null, "Many\nlines\nof\nstack\n");
+ assertLastMsg({
+ action: "test_end",
+ test: "aTest",
+ status: "FAIL",
+ expected: "PASS",
+ stack: "Many\nlines\nof\nstack\n",
+ });
+
+ // Skipped tests don't log failures
+ logger.testStart("aTest");
+ logger.testEnd("aTest", "SKIP", "PASS");
+ ok(!JSON.parse(testBuffer[testBuffer.length - 1]).hasOwnProperty("expected"));
+ assertLastMsg({
+ action: "test_end",
+ test: "aTest",
+ status: "SKIP",
+ });
+
+ logger.testStatus("aTest", "foo", "PASS", "PASS", "Passed test");
+ ok(!JSON.parse(testBuffer[testBuffer.length - 1]).hasOwnProperty("expected"));
+ assertLastMsg({
+ action: "test_status",
+ test: "aTest",
+ subtest: "foo",
+ status: "PASS",
+ message: "Passed test",
+ });
+
+ logger.testStatus("aTest", "bar", "FAIL");
+ assertLastMsg({
+ action: "test_status",
+ test: "aTest",
+ subtest: "bar",
+ status: "FAIL",
+ expected: "PASS",
+ });
+
+ logger.testStatus(
+ "aTest",
+ "bar",
+ "FAIL",
+ "PASS",
+ null,
+ "Many\nlines\nof\nstack\n"
+ );
+ assertLastMsg({
+ action: "test_status",
+ test: "aTest",
+ subtest: "bar",
+ status: "FAIL",
+ expected: "PASS",
+ stack: "Many\nlines\nof\nstack\n",
+ });
+
+ // Skipped tests don't log failures
+ logger.testStatus("aTest", "baz", "SKIP");
+ ok(!JSON.parse(testBuffer[testBuffer.length - 1]).hasOwnProperty("expected"));
+ assertLastMsg({
+ action: "test_status",
+ test: "aTest",
+ subtest: "baz",
+ status: "SKIP",
+ });
+
+ // Suite start and end messages.
+ var ids = { aManifest: ["aTest"] };
+ logger.suiteStart(ids);
+ assertLastMsg({
+ action: "suite_start",
+ tests: { aManifest: ["aTest"] },
+ });
+
+ logger.suiteEnd();
+ assertLastMsg({
+ action: "suite_end",
+ });
+}
diff --git a/testing/modules/tests/xpcshell/xpcshell.ini b/testing/modules/tests/xpcshell/xpcshell.ini
new file mode 100644
index 0000000000..9401dc3abf
--- /dev/null
+++ b/testing/modules/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+head =
+
+[test_assert.js]
+[test_mockRegistrar.js]
+[test_structuredlog.js]