summaryrefslogtreecommitdiffstats
path: root/js/src/tests/shell.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/shell.js
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/shell.js')
-rw-r--r--js/src/tests/shell.js588
1 files changed, 588 insertions, 0 deletions
diff --git a/js/src/tests/shell.js b/js/src/tests/shell.js
new file mode 100644
index 0000000000..4fd0942b8d
--- /dev/null
+++ b/js/src/tests/shell.js
@@ -0,0 +1,588 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+// NOTE: If you're adding new test harness functionality -- first, should you
+// at all? Most stuff is better in specific tests, or in nested shell.js
+// or browser.js. Second, supposing you should, please add it to this
+// IIFE for better modularity/resilience against tests that must do
+// particularly bizarre things that might break the harness.
+
+(function(global) {
+ "use strict";
+
+ /**********************************************************************
+ * CACHED PRIMORDIAL FUNCTIONALITY (before a test might overwrite it) *
+ **********************************************************************/
+
+ var undefined; // sigh
+
+ var Error = global.Error;
+ var Function = global.Function;
+ var Number = global.Number;
+ var RegExp = global.RegExp;
+ var String = global.String;
+ var Symbol = global.Symbol;
+ var TypeError = global.TypeError;
+
+ var ArrayIsArray = global.Array.isArray;
+ var MathAbs = global.Math.abs;
+ var ObjectCreate = global.Object.create;
+ var ObjectDefineProperty = global.Object.defineProperty;
+ var ReflectApply = global.Reflect.apply;
+ var RegExpPrototypeExec = global.RegExp.prototype.exec;
+ var StringPrototypeCharCodeAt = global.String.prototype.charCodeAt;
+ var StringPrototypeIndexOf = global.String.prototype.indexOf;
+ var StringPrototypeSubstring = global.String.prototype.substring;
+
+ var runningInBrowser = typeof global.window !== "undefined";
+ if (runningInBrowser) {
+ // Certain cached functionality only exists (and is only needed) when
+ // running in the browser. Segregate that caching here.
+
+ var SpecialPowersSetGCZeal =
+ global.SpecialPowers ? global.SpecialPowers.setGCZeal : undefined;
+ }
+
+ var evaluate = global.evaluate;
+ var options = global.options;
+
+ /****************************
+ * GENERAL HELPER FUNCTIONS *
+ ****************************/
+
+ // We *cannot* use Array.prototype.push for this, because that function sets
+ // the new trailing element, which could invoke a setter (left by a test) on
+ // Array.prototype or Object.prototype.
+ function ArrayPush(arr, val) {
+ assertEq(ArrayIsArray(arr), true,
+ "ArrayPush must only be used on actual arrays");
+
+ var desc = ObjectCreate(null);
+ desc.value = val;
+ desc.enumerable = true;
+ desc.configurable = true;
+ desc.writable = true;
+ ObjectDefineProperty(arr, arr.length, desc);
+ }
+
+ function StringCharCodeAt(str, index) {
+ return ReflectApply(StringPrototypeCharCodeAt, str, [index]);
+ }
+
+ function StringSplit(str, delimiter) {
+ assertEq(typeof str === "string" && typeof delimiter === "string", true,
+ "StringSplit must be called with two string arguments");
+ assertEq(delimiter.length > 0, true,
+ "StringSplit doesn't support an empty delimiter string");
+
+ var parts = [];
+ var last = 0;
+ while (true) {
+ var i = ReflectApply(StringPrototypeIndexOf, str, [delimiter, last]);
+ if (i < 0) {
+ if (last < str.length)
+ ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last]));
+ return parts;
+ }
+
+ ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last, i]));
+ last = i + delimiter.length;
+ }
+ }
+
+ function shellOptionsClear() {
+ assertEq(runningInBrowser, false, "Only called when running in the shell.");
+
+ // Return early if no options are set.
+ var currentOptions = options ? options() : "";
+ if (currentOptions === "")
+ return;
+
+ // Turn off current settings.
+ var optionNames = StringSplit(currentOptions, ",");
+ for (var i = 0; i < optionNames.length; i++) {
+ options(optionNames[i]);
+ }
+ }
+
+ /****************************
+ * TESTING FUNCTION EXPORTS *
+ ****************************/
+
+ function SameValue(v1, v2) {
+ // We could |return Object.is(v1, v2);|, but that's less portable.
+ if (v1 === 0 && v2 === 0)
+ return 1 / v1 === 1 / v2;
+ if (v1 !== v1 && v2 !== v2)
+ return true;
+ return v1 === v2;
+ }
+
+ var assertEq = global.assertEq;
+ if (typeof assertEq !== "function") {
+ assertEq = function assertEq(actual, expected, message) {
+ if (!SameValue(actual, expected)) {
+ throw new TypeError(`Assertion failed: got "${actual}", expected "${expected}"` +
+ (message ? ": " + message : ""));
+ }
+ };
+ global.assertEq = assertEq;
+ }
+
+ function assertEqArray(actual, expected) {
+ var len = actual.length;
+ assertEq(len, expected.length, "mismatching array lengths");
+
+ var i = 0;
+ try {
+ for (; i < len; i++)
+ assertEq(actual[i], expected[i], "mismatch at element " + i);
+ } catch (e) {
+ throw new Error(`Exception thrown at index ${i}: ${e}`);
+ }
+ }
+ global.assertEqArray = assertEqArray;
+
+ function assertThrows(f) {
+ if (arguments.length != 1) {
+ throw new Error("Too many arguments to assertThrows; maybe you meant assertThrowsInstanceOf?");
+ }
+ var ok = false;
+ try {
+ f();
+ } catch (exc) {
+ ok = true;
+ }
+ if (!ok)
+ throw new Error(`Assertion failed: ${f} did not throw as expected`);
+ }
+ global.assertThrows = assertThrows;
+
+ function assertThrowsInstanceOf(f, ctor, msg) {
+ var fullmsg;
+ try {
+ f();
+ } catch (exc) {
+ if (exc instanceof ctor)
+ return;
+ fullmsg = `Assertion failed: expected exception ${ctor.name}, got ${exc}`;
+ }
+
+ if (fullmsg === undefined)
+ fullmsg = `Assertion failed: expected exception ${ctor.name}, no exception thrown`;
+ if (msg !== undefined)
+ fullmsg += " - " + msg;
+
+ throw new Error(fullmsg);
+ }
+ global.assertThrowsInstanceOf = assertThrowsInstanceOf;
+
+ /****************************
+ * UTILITY FUNCTION EXPORTS *
+ ****************************/
+
+ var dump = global.dump;
+ if (typeof global.dump === "function") {
+ // A presumptively-functional |dump| exists, so no need to do anything.
+ } else {
+ // We don't have |dump|. Try to simulate the desired effect another way.
+ if (runningInBrowser) {
+ // We can't actually print to the console: |global.print| invokes browser
+ // printing functionality here (it's overwritten just below), and
+ // |global.dump| isn't a function that'll dump to the console (presumably
+ // because the preference to enable |dump| wasn't set). Just make it a
+ // no-op.
+ dump = function() {};
+ } else {
+ // |print| prints to stdout: make |dump| do likewise.
+ dump = global.print;
+ }
+ global.dump = dump;
+ }
+
+ var print;
+ if (runningInBrowser) {
+ // We're executing in a browser. Using |global.print| would invoke browser
+ // printing functionality: not what tests want! Instead, use a print
+ // function that syncs up with the test harness and console.
+ print = function print() {
+ var s = "TEST-INFO | ";
+ for (var i = 0; i < arguments.length; i++)
+ s += String(arguments[i]) + " ";
+
+ // Dump the string to the console for developers and the harness.
+ dump(s + "\n");
+
+ // AddPrintOutput doesn't require HTML special characters be escaped.
+ global.AddPrintOutput(s);
+ };
+
+ global.print = print;
+ } else {
+ // We're executing in a shell, and |global.print| is the desired function.
+ print = global.print;
+ }
+
+ var gczeal = global.gczeal;
+ if (typeof gczeal !== "function") {
+ if (typeof SpecialPowersSetGCZeal === "function") {
+ gczeal = function gczeal(z) {
+ SpecialPowersSetGCZeal(z);
+ };
+ } else {
+ gczeal = function() {}; // no-op if not available
+ }
+
+ global.gczeal = gczeal;
+ }
+
+ // Evaluates the given source code as global script code. browser.js provides
+ // a different implementation for this function.
+ var evaluateScript = global.evaluateScript;
+ if (typeof evaluate === "function" && typeof evaluateScript !== "function") {
+ evaluateScript = function evaluateScript(code) {
+ evaluate(String(code));
+ };
+
+ global.evaluateScript = evaluateScript;
+ }
+
+ function toPrinted(value) {
+ value = String(value);
+
+ var digits = "0123456789ABCDEF";
+ var result = "";
+ for (var i = 0; i < value.length; i++) {
+ var ch = StringCharCodeAt(value, i);
+ if (ch === 0x5C && i + 1 < value.length) {
+ var d = value[i + 1];
+ if (d === "n") {
+ result += "NL";
+ i++;
+ } else if (d === "r") {
+ result += "CR";
+ i++;
+ } else {
+ result += "\\";
+ }
+ } else if (ch === 0x0A) {
+ result += "NL";
+ } else if (ch < 0x20 || ch > 0x7E) {
+ var a = digits[ch & 0xf];
+ ch >>= 4;
+ var b = digits[ch & 0xf];
+ ch >>= 4;
+
+ if (ch) {
+ var c = digits[ch & 0xf];
+ ch >>= 4;
+ var d = digits[ch & 0xf];
+
+ result += "\\u" + d + c + b + a;
+ } else {
+ result += "\\x" + b + a;
+ }
+ } else {
+ result += value[i];
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * An xorshift pseudo-random number generator see:
+ * https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
+ * This generator will always produce a value, n, where
+ * 0 <= n <= 255
+ */
+ function *XorShiftGenerator(seed, size) {
+ let x = seed;
+ for (let i = 0; i < size; i++) {
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ yield x % 256;
+ }
+ }
+ global.XorShiftGenerator = XorShiftGenerator;
+
+ /*************************************************************************
+ * HARNESS-CENTRIC EXPORTS (we should generally work to eliminate these) *
+ *************************************************************************/
+
+ var PASSED = " PASSED! ";
+ var FAILED = " FAILED! ";
+
+ /*
+ * Same as `new TestCase(description, expect, actual)`, except it doesn't
+ * return the newly created test case object.
+ */
+ function AddTestCase(description, expect, actual) {
+ new TestCase(description, expect, actual);
+ }
+ global.AddTestCase = AddTestCase;
+
+ var testCasesArray = [];
+
+ function TestCase(d, e, a, r) {
+ this.description = d;
+ this.expect = e;
+ this.actual = a;
+ this.passed = getTestCaseResult(e, a);
+ this.reason = typeof r !== 'undefined' ? String(r) : '';
+
+ ArrayPush(testCasesArray, this);
+ }
+ global.TestCase = TestCase;
+
+ TestCase.prototype = ObjectCreate(null);
+ TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; });
+ TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; });
+ TestCase.prototype.testDescription = (function TestCase_testDescription() { return this.description + ' ' + this.reason; });
+
+ function getTestCaseResult(expected, actual) {
+ if (typeof expected !== typeof actual)
+ return false;
+ if (typeof expected !== 'number')
+ // Note that many tests depend on the use of '==' here, not '==='.
+ return actual == expected;
+
+ // Distinguish NaN from other values. Using x !== x comparisons here
+ // works even if tests redefine isNaN.
+ if (actual !== actual)
+ return expected !== expected;
+ if (expected !== expected)
+ return false;
+
+ // Tolerate a certain degree of error.
+ if (actual !== expected)
+ return MathAbs(actual - expected) <= 1E-10;
+
+ // Here would be a good place to distinguish 0 and -0, if we wanted
+ // to. However, doing so would introduce a number of failures in
+ // areas where they don't seem important. For example, the WeekDay
+ // function in ECMA-262 returns -0 for Sundays before the epoch, but
+ // the Date functions in SpiderMonkey specified in terms of WeekDay
+ // often don't. This seems unimportant.
+ return true;
+ }
+
+ function reportTestCaseResult(description, expected, actual, output) {
+ var testcase = new TestCase(description, expected, actual, output);
+
+ // if running under reftest, let it handle result reporting.
+ if (!runningInBrowser) {
+ if (testcase.passed) {
+ print(PASSED + description);
+ } else {
+ reportFailure(description + " : " + output);
+ }
+ }
+ }
+
+ function getTestCases() {
+ return testCasesArray;
+ }
+ global.getTestCases = getTestCases;
+
+ /*
+ * The test driver searches for such a phrase in the test output.
+ * If such phrase exists, it will set n as the expected exit code.
+ */
+ function expectExitCode(n) {
+ print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
+ }
+ global.expectExitCode = expectExitCode;
+
+ /*
+ * Statuses current section of a test
+ */
+ function inSection(x) {
+ return "Section " + x + " of test - ";
+ }
+ global.inSection = inSection;
+
+ /*
+ * Report a failure in the 'accepted' manner
+ */
+ function reportFailure(msg) {
+ msg = String(msg);
+ var lines = StringSplit(msg, "\n");
+
+ for (var i = 0; i < lines.length; i++)
+ print(FAILED + " " + lines[i]);
+ }
+ global.reportFailure = reportFailure;
+
+ /*
+ * Print a non-failure message.
+ */
+ function printStatus(msg) {
+ msg = String(msg);
+ var lines = StringSplit(msg, "\n");
+
+ for (var i = 0; i < lines.length; i++)
+ print("STATUS: " + lines[i]);
+ }
+ global.printStatus = printStatus;
+
+ /*
+ * Print a bugnumber message.
+ */
+ function printBugNumber(num) {
+ print('BUGNUMBER: ' + num);
+ }
+ global.printBugNumber = printBugNumber;
+
+ /*
+ * Compare expected result to actual result, if they differ (in value and/or
+ * type) report a failure. If description is provided, include it in the
+ * failure report.
+ */
+ function reportCompare(expected, actual, description) {
+ var expected_t = typeof expected;
+ var actual_t = typeof actual;
+ var output = "";
+
+ if (typeof description === "undefined")
+ description = "";
+
+ if (expected_t !== actual_t)
+ output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
+
+ if (expected != actual)
+ output += `Expected value '${toPrinted(expected)}', Actual value '${toPrinted(actual)}' `;
+
+ reportTestCaseResult(description, expected, actual, output);
+ }
+ global.reportCompare = reportCompare;
+
+ /*
+ * Attempt to match a regular expression describing the result to
+ * the actual result, if they differ (in value and/or
+ * type) report a failure. If description is provided, include it in the
+ * failure report.
+ */
+ function reportMatch(expectedRegExp, actual, description) {
+ var expected_t = "string";
+ var actual_t = typeof actual;
+ var output = "";
+
+ if (typeof description === "undefined")
+ description = "";
+
+ if (expected_t !== actual_t)
+ output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
+
+ var matches = ReflectApply(RegExpPrototypeExec, expectedRegExp, [actual]) !== null;
+ if (!matches) {
+ output +=
+ `Expected match to '${toPrinted(expectedRegExp)}', Actual value '${toPrinted(actual)}' `;
+ }
+
+ reportTestCaseResult(description, true, matches, output);
+ }
+ global.reportMatch = reportMatch;
+
+ function compareSource(expect, actual, summary) {
+ // compare source
+ var expectP = String(expect);
+ var actualP = String(actual);
+
+ print('expect:\n' + expectP);
+ print('actual:\n' + actualP);
+
+ reportCompare(expectP, actualP, summary);
+
+ // actual must be compilable if expect is?
+ try {
+ var expectCompile = 'No Error';
+ var actualCompile;
+
+ Function(expect);
+ try {
+ Function(actual);
+ actualCompile = 'No Error';
+ } catch(ex1) {
+ actualCompile = ex1 + '';
+ }
+ reportCompare(expectCompile, actualCompile,
+ summary + ': compile actual');
+ } catch(ex) {
+ }
+ }
+ global.compareSource = compareSource;
+
+ function test() {
+ var testCases = getTestCases();
+ for (var i = 0; i < testCases.length; i++) {
+ var testCase = testCases[i];
+ testCase.reason += testCase.passed ? "" : "wrong value ";
+
+ // if running under reftest, let it handle result reporting.
+ if (!runningInBrowser) {
+ var message = `${testCase.description} = ${testCase.actual} expected: ${testCase.expect}`;
+ print((testCase.passed ? PASSED : FAILED) + message);
+ }
+ }
+ }
+ global.test = test;
+
+ // This function uses the shell's print function. When running tests in the
+ // browser, browser.js overrides this function to write to the page.
+ function writeHeaderToLog(string) {
+ print(string);
+ }
+ global.writeHeaderToLog = writeHeaderToLog;
+
+ /************************************
+ * PROMISE TESTING FUNCTION EXPORTS *
+ ************************************/
+
+ function getPromiseResult(promise) {
+ var result, error, caught = false;
+ promise.then(r => { result = r; },
+ e => { caught = true; error = e; });
+ drainJobQueue();
+ if (caught)
+ throw error;
+ return result;
+ }
+ global.getPromiseResult = getPromiseResult;
+
+ function assertEventuallyEq(promise, expected) {
+ assertEq(getPromiseResult(promise), expected);
+ }
+ global.assertEventuallyEq = assertEventuallyEq;
+
+ function assertEventuallyThrows(promise, expectedErrorType) {
+ assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
+ };
+ global.assertEventuallyThrows = assertEventuallyThrows;
+
+ function assertEventuallyDeepEq(promise, expected) {
+ assertDeepEq(getPromiseResult(promise), expected);
+ };
+ global.assertEventuallyDeepEq = assertEventuallyDeepEq;
+
+ /*******************************************
+ * RUN ONCE CODE TO SETUP ADDITIONAL STATE *
+ *******************************************/
+
+ // Clear all options before running any tests. browser.js performs this
+ // set-up as part of its jsTestDriverBrowserInit function.
+ if (!runningInBrowser) {
+ shellOptionsClear();
+ }
+
+ if (!runningInBrowser) {
+ // Set the minimum heap size for parallel marking to zero for testing
+ // purposes. We don't have access to gcparam in the browser.
+ gcparam('parallelMarkingThresholdKB', 0);
+ }
+})(this);
+
+var DESCRIPTION;