type coercion harness utilities

This commit is contained in:
Josh Wolfe 2017-08-28 10:37:38 -07:00 committed by Rick Waldron
parent 7f88a6d7f9
commit 29938e9525
2 changed files with 284 additions and 0 deletions

254
harness/typeCoercion.js Normal file
View File

@ -0,0 +1,254 @@
// Copyright (C) 2017 Josh Wolfe. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Functions to help generate test cases for testing type coercion abstract
operations like ToNumber.
---*/
function getValuesCoercibleToIntegerZero() {
var result = [];
var primitiveValues = [
// ToNumber
null,
false,
0,
"0",
// ToInteger: NaN -> +0
undefined,
NaN,
"",
"foo",
"true",
// ToInteger: floor(abs(number))
0.9,
-0,
-0.9,
"0.9",
"-0",
"-0.9",
];
// ToPrimitive
primitiveValues.forEach(function(zero) {
result.push(zero);
result = result.concat(getPrimitiveWrappers(zero, "number"));
});
// Non-primitive values that coerce to 0:
// toString() returns a string that parses to NaN.
result = result.concat([
{},
[],
]);
return result;
}
function getValuesCoercibleToIntegerOne() {
var result = [];
var primitiveValues = [
// ToNumber
true,
1,
"1",
// ToInteger: floor(abs(number))
1.9,
"1.9",
];
// ToPrimitive
primitiveValues.forEach(function(value) {
result.push(value);
result = result.concat(getPrimitiveWrappers(value, "number"));
});
// Non-primitive values that coerce to 1:
// toString() returns a string that parses to 1.
result = result.concat([
[1],
["1"],
]);
return result;
}
function getValuesCoercibleToIntegerFromInteger(nominalInteger) {
assert(Number.isInteger(nominalInteger));
var result = [];
var primitiveValues = [ nominalInteger ];
// ToInteger: floor(abs(number))
if (nominalInteger >= 0) {
primitiveValues.push(nominalInteger + 0.9);
}
if (nominalInteger <= 0) {
primitiveValues.push(nominalInteger - 0.9);
}
// ToNumber: String -> Number
primitiveValues = primitiveValues.concat(primitiveValues.map(function(number) { return number.toString(); }));
// ToPrimitive
primitiveValues.forEach(function(value) {
result.push(value);
result = result.concat(getPrimitiveWrappers(value, "number"));
});
// Non-primitive values that coerce to the nominal integer:
// toString() returns a string that parsers to a primitive value.
result = result.concat(primitiveValues.map(function(number) { return [number]; }));
return result;
}
function getPrimitiveWrappers(primitiveValue, hint) {
assert(hint === "number" || hint === "string");
var result = [];
if (primitiveValue != null) {
// null and undefined result in {} rather than a proper wrapper,
// so skip this case for those values.
result.push(Object(primitiveValue));
}
result = result.concat(getValuesCoercibleToPrimitiveWithMethod(hint, function() {
return primitiveValue;
}));
return result;
}
function getValuesCoercibleToPrimitiveWithMethod(hint, method) {
var methodNames;
if (hint === "number") {
methodNames = ["valueOf", "toString"];
} else {
methodNames = ["toString", "valueOf"];
}
return [
// precedence order
{
[Symbol.toPrimitive]: method,
[methodNames[0]]: function() { throw new Test262Error(); },
[methodNames[1]]: function() { throw new Test262Error(); },
}, {
[methodNames[0]]: method,
[methodNames[1]]: function() { throw new Test262Error(); },
}, {
[methodNames[1]]: method,
},
// GetMethod: if func is undefined or null, return undefined.
{
[Symbol.toPrimitive]: undefined,
[methodNames[0]]: method,
[methodNames[1]]: method,
}, {
[Symbol.toPrimitive]: null,
[methodNames[0]]: method,
[methodNames[1]]: method,
},
// if methodNames[0] is not callable, fallback to methodNames[1]
{
[methodNames[0]]: null,
[methodNames[1]]: method,
}, {
[methodNames[0]]: 1,
[methodNames[1]]: method,
}, {
[methodNames[0]]: {},
[methodNames[1]]: method,
},
// if methodNames[0] returns an object, fallback to methodNames[1]
{
[methodNames[0]]: function() { return {}; },
[methodNames[1]]: method,
}, {
[methodNames[0]]: function() { return Object(1); },
[methodNames[1]]: method,
},
];
}
function getValuesNotCoercibleToInteger() {
// ToInteger only throws from ToNumber.
return getValuesNotCoercibleToNumber();
}
function getValuesNotCoercibleToNumber() {
var result = [];
// ToNumber: Symbol -> TypeError
var primitiveValues = [
Symbol("1"),
];
if (typeof BigInt !== "undefined") {
// ToNumber: BigInt -> TypeError
primitiveValues.push(BigInt(0));
}
primitiveValues.forEach(function(value) {
result.push({error:TypeError, value:value});
getPrimitiveWrappers(value, "number").forEach(function(value) {
result.push({error:TypeError, value:value});
});
});
// ToPrimitive
result = result.concat(getValuesNotCoercibleToPrimitive("number"));
return result;
}
function getValuesNotCoercibleToPrimitive(hint) {
function MyError() {}
var result = [];
var methodNames;
if (hint === "number") {
methodNames = ["valueOf", "toString"];
} else {
methodNames = ["toString", "valueOf"];
}
// ToPrimitive: input[@@toPrimitive] is not callable (and non-null)
result.push({error:TypeError, value:{[Symbol.toPrimitive]: 1}});
result.push({error:TypeError, value:{[Symbol.toPrimitive]: {}}});
// ToPrimitive: input[@@toPrimitive] returns object
result.push({error:TypeError, value:{[Symbol.toPrimitive]: function() { return Object(1); }}});
result.push({error:TypeError, value:{[Symbol.toPrimitive]: function() { return {}; }}});
// ToPrimitive: input[@@toPrimitive] throws
result.push({error:MyError, value:{[Symbol.toPrimitive]: function() { throw new MyError(); }}});
// OrdinaryToPrimitive: method throws
result = result.concat(getValuesCoercibleToPrimitiveWithMethod(hint, function() {
throw new MyError();
}).map(function(value) {
return {error:MyError, value:value};
}));
// OrdinaryToPrimitive: both methods are unsuitable
var unsuitableMethods = [
// not callable:
null,
1,
{},
// returns object:
function() { return Object(1); },
function() { return {}; },
];
unsuitableMethods.forEach(function(method) {
result.push({error:TypeError, value:{valueOf:method, toString:method}});
});
return result;
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2017 Josh Wolfe. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-string.prototype.indexof
description: String.prototype.indexOf type coercion for position parameter
info: >
String.prototype.indexOf ( searchString [ , position ] )
4. Let pos be ? ToInteger(position).
includes: [typeCoercion.js]
---*/
getValuesCoercibleToIntegerZero().forEach(function(zero) {
assert.sameValue("aaaa".indexOf("aa", zero), 0, "with value " + zero);
});
getValuesCoercibleToIntegerOne().forEach(function(one) {
assert.sameValue("aaaa".indexOf("aa", one), 1, "with value " + one);
});
getValuesCoercibleToIntegerFromInteger(2).forEach(function(two) {
assert.sameValue("aaaa".indexOf("aa", two), 2, "with value " + two);
});
getValuesNotCoercibleToInteger().forEach(function(pair) {
var error = pair.error;
var value = pair.value;
assert.throws(error, function() { "".indexOf("", value); });
});