blob: ff081403393d93b20abdb7748dd01df1394b9053 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/*
* Return true if both of these return true:
* - LENIENT_PRED applied to CODE
* - STRICT_PRED applied to CODE with a use strict directive added to the front
*
* Run STRICT_PRED first, for testing code that affects the global environment
* in loose mode, but fails in strict mode.
*/
function testLenientAndStrict(code, lenient_pred, strict_pred) {
return (strict_pred("'use strict'; " + code) &&
lenient_pred(code));
}
/*
* completesNormally(CODE) returns true if evaluating CODE (as eval
* code) completes normally (rather than throwing an exception).
*/
function completesNormally(code) {
try {
eval(code);
return true;
} catch (exception) {
return false;
}
}
/*
* returns(VALUE)(CODE) returns true if evaluating CODE (as eval code)
* completes normally (rather than throwing an exception), yielding a value
* strictly equal to VALUE.
*/
function returns(value) {
return function(code) {
shouldBe(code, JSON.stringify(value));
return true;
}
}
/*
* returnsCopyOf(VALUE)(CODE) returns true if evaluating CODE (as eval code)
* completes normally (rather than throwing an exception), yielding a value
* that is deepEqual to VALUE.
*/
function returnsCopyOf(value) {
return function(code) {
try {
return deepEqual(eval(code), value);
} catch (exception) {
return false;
}
}
}
/*
* raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as
* eval code) throws an exception object that is an instance of EXCEPTION,
* and returns false if it throws any other error or evaluates
* successfully. For example: raises(TypeError)("0()") == true.
*/
function raisesException(exception) {
return function (code) {
shouldThrowType(code, exception);
return true;
};
};
/*
* parsesSuccessfully(CODE) returns true if CODE parses as function
* code without an error.
*/
function parsesSuccessfully(code) {
shouldBeTrue("!!Function("+JSON.stringify(code)+")");
return true;
};
/*
* parseRaisesException(EXCEPTION)(CODE) returns true if parsing CODE
* as function code raises EXCEPTION.
*/
function parseRaisesException(exception) {
return function (code) {
shouldThrowType("Function("+JSON.stringify(code)+")", exception);
return true;
};
};
/*
* Return the result of applying uneval to VAL, and replacing all runs
* of whitespace with a single horizontal space (poor man's
* tokenization).
*/
function clean_uneval(val) {
return uneval(val).replace(/\s+/g, ' ');
}
/*
* Return true if A is equal to B, where equality on arrays and objects
* means that they have the same set of enumerable properties, the values
* of each property are deep_equal, and their 'length' properties are
* equal. Equality on other types is ==.
*/
function deepEqual(a, b) {
if (typeof a != typeof b)
return false;
if (typeof a == 'object') {
var props = {};
// For every property of a, does b have that property with an equal value?
for (var prop in a) {
if (!deepEqual(a[prop], b[prop]))
return false;
props[prop] = true;
}
// Are all of b's properties present on a?
for (var prop in b)
if (!props[prop])
return false;
// length isn't enumerable, but we want to check it, too.
return a.length == b.length;
}
if (a === b) {
// Distinguish 0 from -0, even though they are ===.
return a !== 0 || 1/a === 1/b;
}
// Treat NaNs as equal, even though NaN !== NaN.
// NaNs are the only non-reflexive values, i.e., if a !== a, then a is a NaN.
// isNaN is broken: it converts its argument to number, so isNaN("foo") => true
return a !== a && b !== b;
}