875 lines
22 KiB
JavaScript
875 lines
22 KiB
JavaScript
/* -*- 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 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;
|
|
}
|
|
|
|
/******************************************************
|
|
* 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();
|
|
}
|
|
|
|
}
|