summaryrefslogtreecommitdiff
path: root/harness
diff options
context:
space:
mode:
authorJosh Wolfe <jwolfe@igalia.com>2017-08-28 10:37:38 -0700
committerRick Waldron <waldron.rick@gmail.com>2017-09-08 10:15:19 -0400
commit29938e9525978c3e3601be3b30991bf1ddf5b832 (patch)
tree198c6dbd382e0df7882bf8a9e588604243b2be2e /harness
parent7f88a6d7f94f44a8413458dd53cef37cd46e8f50 (diff)
downloadqtdeclarative-testsuites-29938e9525978c3e3601be3b30991bf1ddf5b832.tar.gz
type coercion harness utilities
Diffstat (limited to 'harness')
-rw-r--r--harness/typeCoercion.js254
1 files changed, 254 insertions, 0 deletions
diff --git a/harness/typeCoercion.js b/harness/typeCoercion.js
new file mode 100644
index 000000000..1fd42ec1b
--- /dev/null
+++ b/harness/typeCoercion.js
@@ -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;
+}