summaryrefslogtreecommitdiffstats
path: root/js/src/tests/shell.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/shell.js')
-rw-r--r--js/src/tests/shell.js889
1 files changed, 889 insertions, 0 deletions
diff --git a/js/src/tests/shell.js b/js/src/tests/shell.js
new file mode 100644
index 000000000..36dbc79da
--- /dev/null
+++ b/js/src/tests/shell.js
@@ -0,0 +1,889 @@
+/* -*- 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) {
+ /**********************************************************************
+ * CACHED PRIMORDIAL FUNCTIONALITY (before a test might overwrite it) *
+ **********************************************************************/
+
+ var undefined; // sigh
+
+ var Error = global.Error;
+ var Number = global.Number;
+ var String = global.String;
+ var TypeError = global.TypeError;
+
+ var ArrayIsArray = global.Array.isArray;
+ var ObjectCreate = global.Object.create;
+ var ObjectDefineProperty = global.Object.defineProperty;
+ var ReflectApply = global.Reflect.apply;
+ var StringPrototypeEndsWith = global.String.prototype.endsWith;
+
+ 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 runningInShell = typeof window === "undefined";
+
+ /****************************
+ * GENERAL HELPER FUNCTIONS *
+ ****************************/
+
+ // We could use Array.prototype.pop, but we don't so it's clear exactly what
+ // dependencies this function has on test-modifiable behavior (i.e. none).
+ function ArrayPop(arr) {
+ assertEq(ArrayIsArray(arr), true,
+ "ArrayPop must only be used on actual arrays");
+
+ var len = arr.length;
+ if (len === 0)
+ return undefined;
+
+ var v = arr[len - 1];
+ arr.length--;
+ return v;
+ }
+
+ // 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 StringEndsWith(str, needle) {
+ return ReflectApply(StringPrototypeEndsWith, str, [needle]);
+ }
+
+ /****************************
+ * 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) {
+ 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 quit = global.quit;
+ if (typeof quit !== "function") {
+ // XXX There's something very strange about quit() in browser runs being a
+ // function that doesn't quit at all (!). We should get rid of quit()
+ // as an integral part of tests in favor of something else.
+ quit = function quit() {};
+ global.quit = quit;
+ }
+
+ 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;
+ }
+
+ /******************************************************
+ * TEST METADATA EXPORTS (these are of dubious value) *
+ ******************************************************/
+
+ global.SECTION = "";
+ global.VERSION = "";
+ global.BUGNUMBER = "";
+
+ /*************************************************************************
+ * HARNESS-CENTRIC EXPORTS (we should generally work to eliminate these) *
+ *************************************************************************/
+
+ var PASSED = " PASSED! ";
+ global.PASSED = PASSED;
+
+ var FAILED = " FAILED! ";
+ global.FAILED = FAILED;
+
+ /** Set up test environment. */
+ function startTest() {
+ if (global.BUGNUMBER)
+ global.print("BUGNUMBER: " + global.BUGNUMBER);
+ }
+ global.startTest = startTest;
+
+ var callStack = [];
+
+ /**
+ * Puts funcName at the top of the call stack. This stack is used to show
+ * a function-reported-from field when reporting failures.
+ */
+ function enterFunc(funcName) {
+ assertEq(typeof funcName, "string",
+ "enterFunc must be given a string funcName");
+
+ if (!StringEndsWith(funcName, "()"))
+ funcName += "()";
+
+ ArrayPush(callStack, funcName);
+ }
+ global.enterFunc = enterFunc;
+
+ /**
+ * Pops the top funcName off the call stack. funcName, if provided, is used
+ * to check push-pop balance.
+ */
+ function exitFunc(funcName) {
+ assertEq(typeof funcName === "string" || typeof funcName === "undefined",
+ true,
+ "exitFunc must be given no arguments or a string");
+
+ var lastFunc = ArrayPop(callStack);
+ assertEq(typeof lastFunc, "string", "exitFunc called too many times");
+
+ if (funcName) {
+ if (!StringEndsWith(funcName, "()"))
+ funcName += "()";
+
+ if (lastFunc !== funcName) {
+ // XXX Eliminate this dependency on global.reportCompare's identity.
+ global.reportCompare(funcName, lastFunc,
+ "Test driver failure wrong exit function ");
+ }
+ }
+ }
+ global.exitFunc = exitFunc;
+
+ /** Peeks at the top of the call stack. */
+ function currentFunc() {
+ if (callStack.length == 0)
+ return "top level script";
+
+ return callStack[callStack.length - 1];
+ }
+ global.currentFunc = currentFunc;
+
+ // XXX This function is *only* used in harness functions and really shouldn't
+ // be exported.
+ var writeFormattedResult =
+ function writeFormattedResult(expect, actual, string, passed) {
+ print((passed ? PASSED : FAILED) + string + ' expected: ' + expect);
+ };
+ global.writeFormattedResult = writeFormattedResult;
+
+ /*****************************************************
+ * RHINO-SPECIFIC EXPORTS (are these used any more?) *
+ *****************************************************/
+
+ function inRhino() {
+ return typeof global.defineClass === "function";
+ }
+ global.inRhino = inRhino;
+
+ function GetContext() {
+ return global.Packages.com.netscape.javascript.Context.getCurrentContext();
+ }
+ global.GetContext = GetContext;
+
+ function OptLevel(i) {
+ i = Number(i);
+ var cx = GetContext();
+ cx.setOptimizationLevel(i);
+ }
+ global.OptLevel = OptLevel;
+})(this);
+
+(function(global) {
+ 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;
+ }
+
+ 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;
+})(this);
+
+var STATUS = "STATUS: ";
+
+var gDelayTestDriverEnd = false;
+
+var gTestcases = new Array();
+var gTc = gTestcases.length;
+var summary = '';
+var description = '';
+var expected = '';
+var actual = '';
+var msg = '';
+
+/*
+ * constant strings
+ */
+var GLOBAL = this + '';
+
+var DESCRIPTION;
+var EXPECTED;
+
+/*
+ * Signals to results.py that the current test case should be considered to
+ * have passed if it doesn't throw an exception.
+ *
+ * When the test suite is run in the browser, this function gets overridden by
+ * the same-named function in browser.js.
+ */
+function testPassesUnlessItThrows() {
+ print(PASSED);
+}
+
+/*
+ * wrapper for test case constructor that doesn't require the SECTION
+ * argument.
+ */
+
+function AddTestCase( description, expect, actual ) {
+ new TestCase( SECTION, description, expect, actual );
+}
+
+function TestCase(n, d, e, a)
+{
+ this.name = n;
+ this.description = d;
+ this.expect = e;
+ this.actual = a;
+ this.passed = getTestCaseResult(e, a);
+ this.reason = '';
+ this.bugnumber = typeof(BUGNUMER) != 'undefined' ? BUGNUMBER : '';
+ this.type = (typeof window == 'undefined' ? 'shell' : 'browser');
+ ({}).constructor.defineProperty(
+ gTestcases,
+ gTc++,
+ {
+ value: this,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ }
+ );
+}
+
+gFailureExpected = false;
+
+TestCase.prototype.dump = function () {
+ // let reftest handle error reporting, otherwise
+ // output a summary line.
+ if (typeof document != "object" ||
+ !document.location.href.match(/jsreftest.html/))
+ {
+ dump('\njstest: ' + this.path + ' ' +
+ 'bug: ' + this.bugnumber + ' ' +
+ 'result: ' + (this.passed ? 'PASSED':'FAILED') + ' ' +
+ 'type: ' + this.type + ' ' +
+ 'description: ' + toPrinted(this.description) + ' ' +
+// 'expected: ' + toPrinted(this.expect) + ' ' +
+// 'actual: ' + toPrinted(this.actual) + ' ' +
+ 'reason: ' + toPrinted(this.reason) + '\n');
+ }
+};
+
+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 getTestCases()
+{
+ return gTestcases;
+}
+
+/*
+ * 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 + ' ---');
+}
+
+/*
+ * Statuses current section of a test
+ */
+function inSection(x)
+{
+ return "Section " + x + " of test - ";
+}
+
+/*
+ * Report a failure in the 'accepted' manner
+ */
+function reportFailure (msg)
+{
+ var lines = msg.split ("\n");
+ var l;
+ var funcName = currentFunc();
+ var prefix = (funcName) ? "[reported from " + funcName + "] ": "";
+
+ for (var i=0; i<lines.length; i++)
+ print (FAILED + prefix + lines[i]);
+}
+
+/*
+ * Print a non-failure message.
+ */
+function printStatus (msg)
+{
+/* js1_6 had...
+ msg = String(msg);
+ msg = msg.toString();
+*/
+ msg = msg.toString();
+ var lines = msg.split ("\n");
+ var l;
+
+ for (var i=0; i<lines.length; i++)
+ print (STATUS + lines[i]);
+}
+
+/*
+ * Print a bugnumber message.
+ */
+function printBugNumber (num)
+{
+ BUGNUMBER = num;
+ print ('BUGNUMBER: ' + num);
+}
+
+function toPrinted(value)
+{
+ value = String(value);
+ value = value.replace(/\\n/g, 'NL')
+ .replace(/\n/g, 'NL')
+ .replace(/\\r/g, 'CR')
+ .replace(/[^\x20-\x7E]+/g, escapeString);
+ return value;
+}
+
+function escapeString (str)
+{
+ var a, b, c, d;
+ var len = str.length;
+ var result = "";
+ var digits = ["0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "A", "B", "C", "D", "E", "F"];
+
+ for (var i=0; i<len; i++)
+ {
+ var ch = str.charCodeAt(i);
+
+ a = digits[ch & 0xf];
+ ch >>= 4;
+ b = digits[ch & 0xf];
+ ch >>= 4;
+
+ if (ch)
+ {
+ c = digits[ch & 0xf];
+ ch >>= 4;
+ d = digits[ch & 0xf];
+
+ result += "\\u" + d + c + b + a;
+ }
+ else
+ {
+ result += "\\x" + b + a;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * 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) + "' ";
+ }
+
+ var testcase = new TestCase("unknown-test-name", description, expected, actual);
+ testcase.reason = output;
+
+ // if running under reftest, let it handle result reporting.
+ if (typeof document != "object" ||
+ !document.location.href.match(/jsreftest.html/)) {
+ if (testcase.passed)
+ {
+ print(PASSED + description);
+ }
+ else
+ {
+ reportFailure (description + " : " + output);
+ }
+ }
+ return testcase.passed;
+}
+
+/*
+ * 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 = expectedRegExp.test(actual);
+ if (!matches) {
+ output += "Expected match to '" + toPrinted(expectedRegExp) +
+ "', Actual value '" + toPrinted(actual) + "' ";
+ }
+
+ var testcase = new TestCase("unknown-test-name", description, true, matches);
+ testcase.reason = output;
+
+ // if running under reftest, let it handle result reporting.
+ if (typeof document != "object" ||
+ !document.location.href.match(/jsreftest.html/)) {
+ if (testcase.passed)
+ {
+ print(PASSED + description);
+ }
+ else
+ {
+ reportFailure (description + " : " + output);
+ }
+ }
+ return testcase.passed;
+}
+
+/*
+ * 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;
+ }
+}
+
+function compareSource(expect, actual, summary)
+{
+ // compare source
+ var expectP = expect.
+ replace(/([(){},.:\[\]])/mg, ' $1 ').
+ replace(/(\w+)/mg, ' $1 ').
+ replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
+ replace(/\s+/mg, ' ').
+ replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
+
+ var actualP = actual.
+ replace(/([(){},.:\[\]])/mg, ' $1 ').
+ replace(/(\w+)/mg, ' $1 ').
+ replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>').
+ replace(/\s+/mg, ' ').
+ replace(/new (\w+)\s*\(\s*\)/mg, 'new $1');
+
+ 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;
+
+ eval(expect);
+ try
+ {
+ eval(actual);
+ actualCompile = 'No Error';
+ }
+ catch(ex1)
+ {
+ actualCompile = ex1 + '';
+ }
+ reportCompare(expectCompile, actualCompile,
+ summary + ': compile actual');
+ }
+ catch(ex)
+ {
+ }
+}
+
+function optionsInit() {
+
+ // record initial values to support resetting
+ // options to their initial values
+ options.initvalues = {};
+
+ // record values in a stack to support pushing
+ // and popping options
+ options.stackvalues = [];
+
+ var optionNames = options().split(',');
+
+ for (var i = 0; i < optionNames.length; i++)
+ {
+ var optionName = optionNames[i];
+ if (optionName)
+ {
+ options.initvalues[optionName] = '';
+ }
+ }
+}
+
+function optionsClear() {
+
+ // turn off current settings
+ // except jit.
+ var optionNames = options().split(',');
+ for (var i = 0; i < optionNames.length; i++)
+ {
+ var optionName = optionNames[i];
+ if (optionName &&
+ optionName != "methodjit" &&
+ optionName != "methodjit_always" &&
+ optionName != "ion")
+ {
+ options(optionName);
+ }
+ }
+}
+
+function optionsPush()
+{
+ var optionsframe = {};
+
+ options.stackvalues.push(optionsframe);
+
+ var optionNames = options().split(',');
+
+ for (var i = 0; i < optionNames.length; i++)
+ {
+ var optionName = optionNames[i];
+ if (optionName)
+ {
+ optionsframe[optionName] = '';
+ }
+ }
+
+ optionsClear();
+}
+
+function optionsPop()
+{
+ var optionsframe = options.stackvalues.pop();
+
+ optionsClear();
+
+ for (optionName in optionsframe)
+ {
+ options(optionName);
+ }
+
+}
+
+function optionsReset() {
+
+ try
+ {
+ optionsClear();
+
+ // turn on initial settings
+ for (var optionName in options.initvalues)
+ {
+ if (!options.hasOwnProperty(optionName))
+ continue;
+ options(optionName);
+ }
+ }
+ catch(ex)
+ {
+ print('optionsReset: caught ' + ex);
+ }
+
+}
+
+if (typeof options == 'function')
+{
+ optionsInit();
+ optionsClear();
+}
+
+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 Math.abs(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 test() {
+ for ( gTc=0; gTc < gTestcases.length; gTc++ ) {
+ // temporary hack to work around some unknown issue in 1.7
+ try
+ {
+ gTestcases[gTc].passed = writeTestCaseResult(
+ gTestcases[gTc].expect,
+ gTestcases[gTc].actual,
+ gTestcases[gTc].description +" = "+ gTestcases[gTc].actual );
+ gTestcases[gTc].reason += ( gTestcases[gTc].passed ) ? "" : "wrong value ";
+ }
+ catch(e)
+ {
+ print('test(): empty testcase for gTc = ' + gTc + ' ' + e);
+ }
+ }
+ return ( gTestcases );
+}
+
+/*
+ * Begin printing functions. These functions use the shell's
+ * print function. When running tests in the browser, browser.js
+ * overrides these functions to write to the page.
+ */
+
+function writeTestCaseResult( expect, actual, string ) {
+ var passed = getTestCaseResult( expect, actual );
+ // if running under reftest, let it handle result reporting.
+ if (typeof document != "object" ||
+ !document.location.href.match(/jsreftest.html/)) {
+ writeFormattedResult( expect, actual, string, passed );
+ }
+ return passed;
+}
+
+function writeHeaderToLog( string ) {
+ print( string );
+}
+/* end of print functions */
+
+
+function jsTestDriverEnd()
+{
+ // gDelayTestDriverEnd is used to
+ // delay collection of the test result and
+ // signal to Spider so that tests can continue
+ // to run after page load has fired. They are
+ // responsible for setting gDelayTestDriverEnd = true
+ // then when completed, setting gDelayTestDriverEnd = false
+ // then calling jsTestDriverEnd()
+
+ if (gDelayTestDriverEnd)
+ {
+ return;
+ }
+
+ try
+ {
+ optionsReset();
+ }
+ catch(ex)
+ {
+ dump('jsTestDriverEnd ' + ex);
+ }
+
+ for (var i = 0; i < gTestcases.length; i++)
+ {
+ gTestcases[i].dump();
+ }
+
+}