diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/modules/tests/xpcshell | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/modules/tests/xpcshell')
-rw-r--r-- | testing/modules/tests/xpcshell/test_assert.js | 491 | ||||
-rw-r--r-- | testing/modules/tests/xpcshell/test_mockRegistrar.js | 65 | ||||
-rw-r--r-- | testing/modules/tests/xpcshell/test_structuredlog.js | 147 | ||||
-rw-r--r-- | testing/modules/tests/xpcshell/xpcshell.toml | 8 |
4 files changed, 711 insertions, 0 deletions
diff --git a/testing/modules/tests/xpcshell/test_assert.js b/testing/modules/tests/xpcshell/test_assert.js new file mode 100644 index 0000000000..42b46f08f8 --- /dev/null +++ b/testing/modules/tests/xpcshell/test_assert.js @@ -0,0 +1,491 @@ +/* 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 + +var { Assert } = ChromeUtils.importESModule( + "resource://testing-common/Assert.sys.mjs" +); + +add_task(function test_basic_asserts() { + let assert = new 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(Assert.AssertionError.prototype, Error), + "Assert.AssertionError instanceof Error" + ); + + assert.throws( + makeBlock(assert.ok, false), + Assert.AssertionError, + "ok(false)" + ); + + assert.ok(true, "ok(true)"); + + assert.ok("test", "ok('test')"); + + assert.throws( + makeBlock(assert.equal, true, false), + 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), + Assert.AssertionError, + "notEqual" + ); + + assert.throws( + makeBlock(assert.strictEqual, 2, "2"), + Assert.AssertionError, + "strictEqual" + ); + + assert.throws( + makeBlock(assert.strictEqual, null, undefined), + 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)), + 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/), Assert.AssertionError); + assert.throws(makeBlock(assert.deepEqual, /a/g, /a/), Assert.AssertionError); + assert.throws(makeBlock(assert.deepEqual, /a/i, /a/), Assert.AssertionError); + assert.throws(makeBlock(assert.deepEqual, /a/m, /a/), Assert.AssertionError); + assert.throws( + makeBlock(assert.deepEqual, /a/gim, /a/im), + Assert.AssertionError + ); + + let re1 = /a/; + re1.lastIndex = 3; + assert.throws(makeBlock(assert.deepEqual, re1, /a/), Assert.AssertionError); + + // 7.4 + assert.deepEqual(4, "4", "deepEqual == check"); + assert.deepEqual(true, 1, "deepEqual == check"); + assert.throws( + makeBlock(assert.deepEqual, 4, "5"), + 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 }), + 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)), + 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), Assert.AssertionError); + + // String literal + object + assert.throws(makeBlock(assert.deepEqual, "a", {}), Assert.AssertionError); + + // Testing the throwing + function thrower(errorConstructor) { + throw new errorConstructor("test"); + } + makeBlock(thrower, Assert.AssertionError); + makeBlock(thrower, Assert.AssertionError); + + // the basic calls work + assert.throws( + makeBlock(thrower, Assert.AssertionError), + Assert.AssertionError, + "message" + ); + assert.throws( + makeBlock(thrower, Assert.AssertionError), + Assert.AssertionError + ); + assert.throws( + makeBlock(thrower, Assert.AssertionError), + 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), 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", + 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 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); + assert.throws( + () => assert.less(2, 2), + e => e == "AssertionError: 2 < 2" + ); + + assert.lessOrEqual(2, 2); + assert.throws( + () => assert.lessOrEqual(2, 1), + e => e == "AssertionError: 2 <= 1" + ); + + assert.throws( + () => assert.greater(NaN, 0), + e => e == "AssertionError: 'NaN' is not a number or date." + ); + + assert.throws( + () => assert.greater(0, NaN), + e => e == "AssertionError: 'NaN' is not a number or date." + ); + + let now = new Date(); + let firefoxReleaseDate = new Date("2004-11-09"); + assert.less(firefoxReleaseDate, now); + assert.throws( + () => assert.less(now, now), + e => e == `AssertionError: "${now.toJSON()}" < "${now.toJSON()}"` + ); + + assert.lessOrEqual(now, now); + assert.greaterOrEqual(now, now); + assert.throws( + () => assert.greaterOrEqual(firefoxReleaseDate, now), + e => + e == + `AssertionError: "${firefoxReleaseDate.toJSON()}" >= "${now.toJSON()}"` + ); + + // Invalid date: + assert.throws( + () => assert.greater(firefoxReleaseDate, new Date("invalid")), + e => e == "AssertionError: 'Invalid Date' is not a number or date." + ); + + /* ---- stringMatches ---- */ + assert.stringMatches("hello world", /llo\s/); + assert.stringMatches("hello world", "llo\\s"); + assert.throws( + () => assert.stringMatches("hello world", /foo/), + /^AssertionError: "hello world" matches "\/foo\/"/ + ); + assert.throws( + () => assert.stringMatches(5, /foo/), + /^AssertionError: Expected a string for lhs, but "5" isn't a string./ + ); + assert.throws( + () => assert.stringMatches("foo bar", "+"), + /^AssertionError: Expected a valid regular expression for rhs, but "\+" isn't one./ + ); + + /* ---- stringContains ---- */ + assert.stringContains("hello world", "llo"); + assert.throws( + () => assert.stringContains(5, "foo"), + /^AssertionError: Expected a string for both lhs and rhs, but either "5" or "foo" is not a string./ + ); +}); + +add_task(async function test_rejects() { + let assert = new 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/); + + // A non-rejection should also be an assertion failure: + try { + await assert.rejects(Promise.resolve(), /./, "ReSoLvEd"); + ok(false, "should have rejected"); + } catch (ex) { + deepEqual(ex.message, "Missing expected exception ReSoLvEd"); + } +}); diff --git a/testing/modules/tests/xpcshell/test_mockRegistrar.js b/testing/modules/tests/xpcshell/test_mockRegistrar.js new file mode 100644 index 0000000000..de4224a092 --- /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.importESModule( + "resource://testing-common/MockRegistrar.sys.mjs" +); + +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..5288bbcbb3 --- /dev/null +++ b/testing/modules/tests/xpcshell/test_structuredlog.js @@ -0,0 +1,147 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + const { StructuredLogger } = ChromeUtils.importESModule( + "resource://testing-common/StructuredLog.sys.mjs" + ); + + 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"); + }; + + let logger = new StructuredLogger("test_log", appendBuffer); + + // 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.toml b/testing/modules/tests/xpcshell/xpcshell.toml new file mode 100644 index 0000000000..bca32fa174 --- /dev/null +++ b/testing/modules/tests/xpcshell/xpcshell.toml @@ -0,0 +1,8 @@ +[DEFAULT] +head = "" + +["test_assert.js"] + +["test_mockRegistrar.js"] + +["test_structuredlog.js"] |