1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // NOTE: If you're adding new test harness functionality -- first, should you
7 // at all? Most stuff is better in specific tests, or in nested shell.js
8 // or browser.js. Second, supposing you should, please add it to this
9 // IIFE for better modularity/resilience against tests that must do
10 // particularly bizarre things that might break the harness.
15 /**********************************************************************
16 * CACHED PRIMORDIAL FUNCTIONALITY (before a test might overwrite it) *
17 **********************************************************************/
19 var undefined; // sigh
21 var Error = global.Error;
22 var Function = global.Function;
23 var Number = global.Number;
24 var RegExp = global.RegExp;
25 var String = global.String;
26 var Symbol = global.Symbol;
27 var TypeError = global.TypeError;
29 var ArrayIsArray = global.Array.isArray;
30 var MathAbs = global.Math.abs;
31 var ObjectCreate = global.Object.create;
32 var ObjectDefineProperty = global.Object.defineProperty;
33 var ReflectApply = global.Reflect.apply;
34 var RegExpPrototypeExec = global.RegExp.prototype.exec;
35 var StringPrototypeCharCodeAt = global.String.prototype.charCodeAt;
36 var StringPrototypeIndexOf = global.String.prototype.indexOf;
37 var StringPrototypeSubstring = global.String.prototype.substring;
39 var runningInBrowser = typeof global.window !== "undefined";
40 if (runningInBrowser) {
41 // Certain cached functionality only exists (and is only needed) when
42 // running in the browser. Segregate that caching here.
44 var SpecialPowersSetGCZeal =
45 global.SpecialPowers ? global.SpecialPowers.setGCZeal : undefined;
48 var evaluate = global.evaluate;
49 var options = global.options;
51 /****************************
52 * GENERAL HELPER FUNCTIONS *
53 ****************************/
55 // We *cannot* use Array.prototype.push for this, because that function sets
56 // the new trailing element, which could invoke a setter (left by a test) on
57 // Array.prototype or Object.prototype.
58 function ArrayPush(arr, val) {
59 assertEq(ArrayIsArray(arr), true,
60 "ArrayPush must only be used on actual arrays");
62 var desc = ObjectCreate(null);
64 desc.enumerable = true;
65 desc.configurable = true;
67 ObjectDefineProperty(arr, arr.length, desc);
70 function StringCharCodeAt(str, index) {
71 return ReflectApply(StringPrototypeCharCodeAt, str, [index]);
74 function StringSplit(str, delimiter) {
75 assertEq(typeof str === "string" && typeof delimiter === "string", true,
76 "StringSplit must be called with two string arguments");
77 assertEq(delimiter.length > 0, true,
78 "StringSplit doesn't support an empty delimiter string");
83 var i = ReflectApply(StringPrototypeIndexOf, str, [delimiter, last]);
85 if (last < str.length)
86 ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last]));
90 ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last, i]));
91 last = i + delimiter.length;
95 function shellOptionsClear() {
96 assertEq(runningInBrowser, false, "Only called when running in the shell.");
98 // Return early if no options are set.
99 var currentOptions = options ? options() : "";
100 if (currentOptions === "")
103 // Turn off current settings.
104 var optionNames = StringSplit(currentOptions, ",");
105 for (var i = 0; i < optionNames.length; i++) {
106 options(optionNames[i]);
110 /****************************
111 * TESTING FUNCTION EXPORTS *
112 ****************************/
114 function SameValue(v1, v2) {
115 // We could |return Object.is(v1, v2);|, but that's less portable.
116 if (v1 === 0 && v2 === 0)
117 return 1 / v1 === 1 / v2;
118 if (v1 !== v1 && v2 !== v2)
123 var assertEq = global.assertEq;
124 if (typeof assertEq !== "function") {
125 assertEq = function assertEq(actual, expected, message) {
126 if (!SameValue(actual, expected)) {
127 throw new TypeError(`Assertion failed: got "${actual}", expected "${expected}"` +
128 (message ? ": " + message : ""));
131 global.assertEq = assertEq;
134 function assertEqArray(actual, expected) {
135 var len = actual.length;
136 assertEq(len, expected.length, "mismatching array lengths");
141 assertEq(actual[i], expected[i], "mismatch at element " + i);
143 throw new Error(`Exception thrown at index ${i}: ${e}`);
146 global.assertEqArray = assertEqArray;
148 function assertThrows(f) {
149 if (arguments.length != 1) {
150 throw new Error("Too many arguments to assertThrows; maybe you meant assertThrowsInstanceOf?");
159 throw new Error(`Assertion failed: ${f} did not throw as expected`);
161 global.assertThrows = assertThrows;
163 function assertThrowsInstanceOf(f, ctor, msg) {
168 if (exc instanceof ctor)
170 fullmsg = `Assertion failed: expected exception ${ctor.name}, got ${exc}`;
173 if (fullmsg === undefined)
174 fullmsg = `Assertion failed: expected exception ${ctor.name}, no exception thrown`;
175 if (msg !== undefined)
176 fullmsg += " - " + msg;
178 throw new Error(fullmsg);
180 global.assertThrowsInstanceOf = assertThrowsInstanceOf;
182 /****************************
183 * UTILITY FUNCTION EXPORTS *
184 ****************************/
186 var dump = global.dump;
187 if (typeof global.dump === "function") {
188 // A presumptively-functional |dump| exists, so no need to do anything.
190 // We don't have |dump|. Try to simulate the desired effect another way.
191 if (runningInBrowser) {
192 // We can't actually print to the console: |global.print| invokes browser
193 // printing functionality here (it's overwritten just below), and
194 // |global.dump| isn't a function that'll dump to the console (presumably
195 // because the preference to enable |dump| wasn't set). Just make it a
197 dump = function() {};
199 // |print| prints to stdout: make |dump| do likewise.
206 if (runningInBrowser) {
207 // We're executing in a browser. Using |global.print| would invoke browser
208 // printing functionality: not what tests want! Instead, use a print
209 // function that syncs up with the test harness and console.
210 print = function print() {
211 var s = "TEST-INFO | ";
212 for (var i = 0; i < arguments.length; i++)
213 s += String(arguments[i]) + " ";
215 // Dump the string to the console for developers and the harness.
218 // AddPrintOutput doesn't require HTML special characters be escaped.
219 global.AddPrintOutput(s);
222 global.print = print;
224 // We're executing in a shell, and |global.print| is the desired function.
225 print = global.print;
228 var gczeal = global.gczeal;
229 if (typeof gczeal !== "function") {
230 if (typeof SpecialPowersSetGCZeal === "function") {
231 gczeal = function gczeal(z) {
232 SpecialPowersSetGCZeal(z);
235 gczeal = function() {}; // no-op if not available
238 global.gczeal = gczeal;
241 // Evaluates the given source code as global script code. browser.js provides
242 // a different implementation for this function.
243 var evaluateScript = global.evaluateScript;
244 if (typeof evaluate === "function" && typeof evaluateScript !== "function") {
245 evaluateScript = function evaluateScript(code) {
246 evaluate(String(code));
249 global.evaluateScript = evaluateScript;
252 function toPrinted(value) {
253 value = String(value);
255 var digits = "0123456789ABCDEF";
257 for (var i = 0; i < value.length; i++) {
258 var ch = StringCharCodeAt(value, i);
259 if (ch === 0x5C && i + 1 < value.length) {
260 var d = value[i + 1];
264 } else if (d === "r") {
270 } else if (ch === 0x0A) {
272 } else if (ch < 0x20 || ch > 0x7E) {
273 var a = digits[ch & 0xf];
275 var b = digits[ch & 0xf];
279 var c = digits[ch & 0xf];
281 var d = digits[ch & 0xf];
283 result += "\\u" + d + c + b + a;
285 result += "\\x" + b + a;
296 * An xorshift pseudo-random number generator see:
297 * https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
298 * This generator will always produce a value, n, where
301 function *XorShiftGenerator(seed, size) {
303 for (let i = 0; i < size; i++) {
310 global.XorShiftGenerator = XorShiftGenerator;
312 /*************************************************************************
313 * HARNESS-CENTRIC EXPORTS (we should generally work to eliminate these) *
314 *************************************************************************/
316 var PASSED = " PASSED! ";
317 var FAILED = " FAILED! ";
320 * Same as `new TestCase(description, expect, actual)`, except it doesn't
321 * return the newly created test case object.
323 function AddTestCase(description, expect, actual) {
324 new TestCase(description, expect, actual);
326 global.AddTestCase = AddTestCase;
328 var testCasesArray = [];
330 function TestCase(d, e, a, r) {
331 this.description = d;
334 this.passed = getTestCaseResult(e, a);
335 this.reason = typeof r !== 'undefined' ? String(r) : '';
337 ArrayPush(testCasesArray, this);
339 global.TestCase = TestCase;
341 TestCase.prototype = ObjectCreate(null);
342 TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; });
343 TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; });
344 TestCase.prototype.testDescription = (function TestCase_testDescription() { return this.description + ' ' + this.reason; });
346 function getTestCaseResult(expected, actual) {
347 if (typeof expected !== typeof actual)
349 if (typeof expected !== 'number')
350 // Note that many tests depend on the use of '==' here, not '==='.
351 return actual == expected;
353 // Distinguish NaN from other values. Using x !== x comparisons here
354 // works even if tests redefine isNaN.
355 if (actual !== actual)
356 return expected !== expected;
357 if (expected !== expected)
360 // Tolerate a certain degree of error.
361 if (actual !== expected)
362 return MathAbs(actual - expected) <= 1E-10;
364 // Here would be a good place to distinguish 0 and -0, if we wanted
365 // to. However, doing so would introduce a number of failures in
366 // areas where they don't seem important. For example, the WeekDay
367 // function in ECMA-262 returns -0 for Sundays before the epoch, but
368 // the Date functions in SpiderMonkey specified in terms of WeekDay
369 // often don't. This seems unimportant.
373 function reportTestCaseResult(description, expected, actual, output) {
374 var testcase = new TestCase(description, expected, actual, output);
376 // if running under reftest, let it handle result reporting.
377 if (!runningInBrowser) {
378 if (testcase.passed) {
379 print(PASSED + description);
381 reportFailure(description + " : " + output);
386 function getTestCases() {
387 return testCasesArray;
389 global.getTestCases = getTestCases;
392 * The test driver searches for such a phrase in the test output.
393 * If such phrase exists, it will set n as the expected exit code.
395 function expectExitCode(n) {
396 print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
398 global.expectExitCode = expectExitCode;
401 * Statuses current section of a test
403 function inSection(x) {
404 return "Section " + x + " of test - ";
406 global.inSection = inSection;
409 * Report a failure in the 'accepted' manner
411 function reportFailure(msg) {
413 var lines = StringSplit(msg, "\n");
415 for (var i = 0; i < lines.length; i++)
416 print(FAILED + " " + lines[i]);
418 global.reportFailure = reportFailure;
421 * Print a non-failure message.
423 function printStatus(msg) {
425 var lines = StringSplit(msg, "\n");
427 for (var i = 0; i < lines.length; i++)
428 print("STATUS: " + lines[i]);
430 global.printStatus = printStatus;
433 * Print a bugnumber message.
435 function printBugNumber(num) {
436 print('BUGNUMBER: ' + num);
438 global.printBugNumber = printBugNumber;
441 * Compare expected result to actual result, if they differ (in value and/or
442 * type) report a failure. If description is provided, include it in the
445 function reportCompare(expected, actual, description) {
446 var expected_t = typeof expected;
447 var actual_t = typeof actual;
450 if (typeof description === "undefined")
453 if (expected_t !== actual_t)
454 output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
456 if (expected != actual)
457 output += `Expected value '${toPrinted(expected)}', Actual value '${toPrinted(actual)}' `;
459 reportTestCaseResult(description, expected, actual, output);
461 global.reportCompare = reportCompare;
464 * Attempt to match a regular expression describing the result to
465 * the actual result, if they differ (in value and/or
466 * type) report a failure. If description is provided, include it in the
469 function reportMatch(expectedRegExp, actual, description) {
470 var expected_t = "string";
471 var actual_t = typeof actual;
474 if (typeof description === "undefined")
477 if (expected_t !== actual_t)
478 output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
480 var matches = ReflectApply(RegExpPrototypeExec, expectedRegExp, [actual]) !== null;
483 `Expected match to '${toPrinted(expectedRegExp)}', Actual value '${toPrinted(actual)}' `;
486 reportTestCaseResult(description, true, matches, output);
488 global.reportMatch = reportMatch;
490 function compareSource(expect, actual, summary) {
492 var expectP = String(expect);
493 var actualP = String(actual);
495 print('expect:\n' + expectP);
496 print('actual:\n' + actualP);
498 reportCompare(expectP, actualP, summary);
500 // actual must be compilable if expect is?
502 var expectCompile = 'No Error';
508 actualCompile = 'No Error';
510 actualCompile = ex1 + '';
512 reportCompare(expectCompile, actualCompile,
513 summary + ': compile actual');
517 global.compareSource = compareSource;
520 var testCases = getTestCases();
521 for (var i = 0; i < testCases.length; i++) {
522 var testCase = testCases[i];
523 testCase.reason += testCase.passed ? "" : "wrong value ";
525 // if running under reftest, let it handle result reporting.
526 if (!runningInBrowser) {
527 var message = `${testCase.description} = ${testCase.actual} expected: ${testCase.expect}`;
528 print((testCase.passed ? PASSED : FAILED) + message);
534 // This function uses the shell's print function. When running tests in the
535 // browser, browser.js overrides this function to write to the page.
536 function writeHeaderToLog(string) {
539 global.writeHeaderToLog = writeHeaderToLog;
541 /************************************
542 * PROMISE TESTING FUNCTION EXPORTS *
543 ************************************/
545 function getPromiseResult(promise) {
546 var result, error, caught = false;
547 promise.then(r => { result = r; },
548 e => { caught = true; error = e; });
554 global.getPromiseResult = getPromiseResult;
556 function assertEventuallyEq(promise, expected) {
557 assertEq(getPromiseResult(promise), expected);
559 global.assertEventuallyEq = assertEventuallyEq;
561 function assertEventuallyThrows(promise, expectedErrorType) {
562 assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
564 global.assertEventuallyThrows = assertEventuallyThrows;
566 function assertEventuallyDeepEq(promise, expected) {
567 assertDeepEq(getPromiseResult(promise), expected);
569 global.assertEventuallyDeepEq = assertEventuallyDeepEq;
571 /*******************************************
572 * RUN ONCE CODE TO SETUP ADDITIONAL STATE *
573 *******************************************/
575 // Clear all options before running any tests. browser.js performs this
576 // set-up as part of its jsTestDriverBrowserInit function.
577 if (!runningInBrowser) {
581 if (!runningInBrowser) {
582 // Set the minimum heap size for parallel marking to zero for testing
583 // purposes. We don't have access to gcparam in the browser.
584 gcparam('parallelMarkingThresholdMB', 0);