blob: f115c70413738041a68bf1bc66be9c6791c95fcb [file] [log] [blame]
// This test module provides infrastructure for generating and running tests on a binary
// operator.
//
// It works by generating test functions to exercise the specified operator using
// from permutations of operand value pairs. For each test, it computes the expected
// result by exercising the test function once (using the LLINT) at test generation time.
// The test runner later compares the result produced by the function (as it tiers up)
// against the expected result.
//
// The generated tests can exercise the operator using the following operand types.
// variable * variable
// constant * variable
// variable * constant
//
// If all goes well, this test module will terminate silently. If not, it will print
// errors.
//============================================================================
// Debugging options:
var verbose = false;
var abortOnFirstFail = false;
var testFilterStr = undefined; // Define a filter string to filter tests to run.
var verboseTestGeneration = false;
//============================================================================
// Test generation:
function stringifyIfNeeded(x) {
if (typeof x == "string")
return '"' + x + '"';
if (typeof x == "object")
return 'objWithVal:' + x;
return x;
}
// operandTypes are "VarVar", "VarConst", and "ConstVar".
var funcIndex = 0;
function generateBinaryTests(tests, opName, op, operandTypes, leftValues, rightValues) {
var funcNamePrefix = opName + operandTypes;
for (var i = 0; i < leftValues.length; i++) {
for (var j = 0; j < rightValues.length; j++) {
var test = { };
xStr = leftValues[i];
yStr = rightValues[j];
test.x = eval(xStr);
test.y = eval(yStr);
var funcName = funcNamePrefix + funcIndex++;
if (operandTypes == "VarVar") {
test.signature = funcName + "(x, y) { return x " + op + " y }";
test.name = test.signature + " with x:" + xStr + ", y:" + yStr;
} else if (operandTypes == "VarConst") {
test.signature = funcName + "(x, _) { return x " + op + " " + yStr + " }";
test.name = test.signature + " with x:" + xStr;
} else if (operandTypes == "ConstVar") {
test.signature = funcName + "(_, y) { return " + xStr + " " + op + " y }";
test.name = test.signature + " with y:" + yStr;
}
test.func = eval("(function " + test.signature + ")");
noInline(test.func);
test.expectedResult = test.func(test.x, test.y);
test.name += ", expected:" + stringifyIfNeeded(test.expectedResult);
tests.push(test);
if (verboseTestGeneration)
print("Generated " + test.name);
}
}
}
//============================================================================
// Test running and reporting:
var errorReport = "";
function isIdentical(x, y) {
if (typeof x == "undefined" && typeof y == "undefined")
return true;
if (typeof x != typeof y)
return false;
if (x == y) {
if (x)
return true;
// Distinguish between 0 and negative 0.
if (1 / x == 1 / y)
return true;
} else if (Number.isNaN(x) && Number.isNaN(y))
return true;
return false;
}
function runTest(test) {
if (testFilterStr && !test.name.includes(testFilterStr))
return;
var firstFailed = -1;
try {
if (verbose)
print(test.name);
for (var i = 0; i < 10000; i++) {
var result = test.func(test.x, test.y);
if (isIdentical(result, test.expectedResult))
continue;
if (firstFailed < 0) {
errorReport += "FAILED: " + test.name + " started failing on iteration " + i
+ ": actual " + stringifyIfNeeded(result) + "\n";
if (abortOnFirstFail)
throw errorReport;
firstFailed = i;
}
}
} catch(e) {
if (abortOnFirstFail)
throw e; // Negate the catch by re-throwing.
errorReport += "FAILED: Unexpected exception: " + e + "\n";
}
}
function run() {
if (verbose)
print("Start testing");
for (var test of tests)
runTest(test);
if (errorReport !== "")
throw "Found failures:\n" + errorReport;
if (verbose)
print("Done testing");
}