diff --git a/docs/components/expression-metadata.js b/docs/components/expression-metadata.js index 0084ad6c257..40928cf52b7 100644 --- a/docs/components/expression-metadata.js +++ b/docs/components/expression-metadata.js @@ -5,99 +5,21 @@ import CompoundExpression from '../../src/style-spec/expression/compound_express // registers compound expressions import '../../src/style-spec/expression/definitions/index'; +const comparisonSignatures = [{ + type: 'boolean', + parameters: ['value', 'value'] +}, { + type: 'boolean', + parameters: ['value', 'value', 'collator'] +}]; + const types = { - '==': [{ - type: 'boolean', - parameters: ['string', 'string'] - }, { - type: 'boolean', - parameters: ['string', 'string', 'collator'] - }, { - type: 'boolean', - parameters: ['number', 'number'] - }, { - type: 'boolean', - parameters: ['boolean', 'boolean'] - }, { - type: 'boolean', - parameters: ['null', 'null'] - }, { - type: 'boolean', - parameters: ['string', 'value'] - }, { - type: 'boolean', - parameters: ['string', 'value', 'collator'] - }, { - type: 'boolean', - parameters: ['number', 'value'] - }, { - type: 'boolean', - parameters: ['boolean', 'value'] - }, { - type: 'boolean', - parameters: ['null', 'value'] - }, { - type: 'boolean', - parameters: ['value', 'string'] - }, { - type: 'boolean', - parameters: ['value', 'string', 'collator'] - }, { - type: 'boolean', - parameters: ['value', 'number'] - }, { - type: 'boolean', - parameters: ['value', 'boolean'] - }, { - type: 'boolean', - parameters: ['value', 'null'] - }], - '!=': [{ - type: 'boolean', - parameters: ['string', 'string'] - }, { - type: 'boolean', - parameters: ['string', 'string', 'collator'] - }, { - type: 'boolean', - parameters: ['number', 'number'] - }, { - type: 'boolean', - parameters: ['boolean', 'boolean'] - }, { - type: 'boolean', - parameters: ['null', 'null'] - }, { - type: 'boolean', - parameters: ['string', 'value'] - }, { - type: 'boolean', - parameters: ['string', 'value', 'collator'] - }, { - type: 'boolean', - parameters: ['number', 'value'] - }, { - type: 'boolean', - parameters: ['boolean', 'value'] - }, { - type: 'boolean', - parameters: ['null', 'value'] - }, { - type: 'boolean', - parameters: ['value', 'string'] - }, { - type: 'boolean', - parameters: ['value', 'string', 'collator'] - }, { - type: 'boolean', - parameters: ['value', 'number'] - }, { - type: 'boolean', - parameters: ['value', 'boolean'] - }, { - type: 'boolean', - parameters: ['value', 'null'] - }], + '==': comparisonSignatures, + '!=': comparisonSignatures, + '<': comparisonSignatures, + '<=': comparisonSignatures, + '>': comparisonSignatures, + '>=': comparisonSignatures, string: [{ type: 'string', parameters: ['value'] diff --git a/src/style-spec/expression/definitions/comparison.js b/src/style-spec/expression/definitions/comparison.js new file mode 100644 index 00000000000..d643d6c2cc5 --- /dev/null +++ b/src/style-spec/expression/definitions/comparison.js @@ -0,0 +1,185 @@ +// @flow + +import { toString, ValueType, BooleanType, CollatorType } from '../types'; +import Assertion from './assertion'; +import { typeOf } from '../values'; +import RuntimeError from '../runtime_error'; + +import type { Expression } from '../expression'; +import type EvaluationContext from '../evaluation_context'; +import type ParsingContext from '../parsing_context'; +import type { Type } from '../types'; + +type ComparisonOperator = '==' | '!=' | '<' | '>' | '<=' | '>=' ; + +function isComparableType(op: ComparisonOperator, type: Type) { + if (op === '==' || op === '!=') { + // equality operator + return type.kind === 'boolean' || + type.kind === 'string' || + type.kind === 'number' || + type.kind === 'null' || + type.kind === 'value'; + } else { + // ordering operator + return type.kind === 'string' || + type.kind === 'number' || + type.kind === 'value'; + } +} + + +function eq(ctx, a, b) { return a === b; } +function neq(ctx, a, b) { return a !== b; } +function lt(ctx, a, b) { return a < b; } +function gt(ctx, a, b) { return a > b; } +function lteq(ctx, a, b) { return a <= b; } +function gteq(ctx, a, b) { return a >= b; } + +function eqCollate(ctx, a, b, c) { return c.compare(a, b) === 0; } +function neqCollate(ctx, a, b, c) { return !eqCollate(ctx, a, b, c); } +function ltCollate(ctx, a, b, c) { return c.compare(a, b) < 0; } +function gtCollate(ctx, a, b, c) { return c.compare(a, b) > 0; } +function lteqCollate(ctx, a, b, c) { return c.compare(a, b) <= 0; } +function gteqCollate(ctx, a, b, c) { return c.compare(a, b) >= 0; } + +/** + * Special form for comparison operators, implementing the signatures: + * - (T, T, ?Collator) => boolean + * - (T, value, ?Collator) => boolean + * - (value, T, ?Collator) => boolean + * + * For inequalities, T must be either value, string, or number. For ==/!=, it + * can also be boolean or null. + * + * Equality semantics are equivalent to Javascript's strict equality (===/!==) + * -- i.e., when the arguments' types don't match, == evaluates to false, != to + * true. + * + * When types don't match in an ordering comparison, a runtime error is thrown. + * + * @private + */ +function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollator) { + const isOrderComparison = op !== '==' && op !== '!='; + + return class Comparison implements Expression { + type: Type; + lhs: Expression; + rhs: Expression; + collator: ?Expression; + hasUntypedArgument: boolean; + + constructor(lhs: Expression, rhs: Expression, collator: ?Expression) { + this.type = BooleanType; + this.lhs = lhs; + this.rhs = rhs; + this.collator = collator; + this.hasUntypedArgument = lhs.type.kind === 'value' || rhs.type.kind === 'value'; + } + + static parse(args: Array, context: ParsingContext): ?Expression { + if (args.length !== 3 && args.length !== 4) + return context.error(`Expected two or three arguments.`); + + const op: ComparisonOperator = (args[0]: any); + + let lhs = context.parse(args[1], 1, ValueType); + if (!lhs) return null; + if (!isComparableType(op, lhs.type)) { + return context.concat(1).error(`"${op}" comparisons are not supported for type '${toString(lhs.type)}'.`); + } + let rhs = context.parse(args[2], 2, ValueType); + if (!rhs) return null; + if (!isComparableType(op, rhs.type)) { + return context.concat(2).error(`"${op}" comparisons are not supported for type '${toString(rhs.type)}'.`); + } + + if ( + lhs.type.kind !== rhs.type.kind && + lhs.type.kind !== 'value' && + rhs.type.kind !== 'value' + ) { + return context.error(`Cannot compare types '${toString(lhs.type)}' and '${toString(rhs.type)}'.`); + } + + if (isOrderComparison) { + // typing rules specific to less/greater than operators + if (lhs.type.kind === 'value' && rhs.type.kind !== 'value') { + // (value, T) + lhs = new Assertion(rhs.type, [lhs]); + } else if (lhs.type.kind !== 'value' && rhs.type.kind === 'value') { + // (T, value) + rhs = new Assertion(lhs.type, [rhs]); + } + } + + let collator = null; + if (args.length === 4) { + if ( + lhs.type.kind !== 'string' && + rhs.type.kind !== 'string' && + lhs.type.kind !== 'value' && + rhs.type.kind !== 'value' + ) { + return context.error(`Cannot use collator to compare non-string types.`); + } + collator = context.parse(args[3], 3, CollatorType); + if (!collator) return null; + } + + return new Comparison(lhs, rhs, collator); + } + + evaluate(ctx: EvaluationContext) { + const lhs = this.lhs.evaluate(ctx); + const rhs = this.rhs.evaluate(ctx); + + if (isOrderComparison && this.hasUntypedArgument) { + const lt = typeOf(lhs); + const rt = typeOf(rhs); + // check that type is string or number, and equal + if (lt.kind !== rt.kind || !(lt.kind === 'string' || lt.kind === 'number')) { + throw new RuntimeError(`Expected arguments for "${op}" to be (string, string) or (number, number), but found (${lt.kind}, ${rt.kind}) instead.`); + } + } + + if (this.collator && !isOrderComparison && this.hasUntypedArgument) { + const lt = typeOf(lhs); + const rt = typeOf(rhs); + if (lt.kind !== 'string' || rt.kind !== 'string') { + return compareBasic(ctx, lhs, rhs); + } + } + + return this.collator ? + compareWithCollator(ctx, lhs, rhs, this.collator.evaluate(ctx)) : + compareBasic(ctx, lhs, rhs); + } + + eachChild(fn: (Expression) => void) { + fn(this.lhs); + fn(this.rhs); + if (this.collator) { + fn(this.collator); + } + } + + possibleOutputs() { + return [true, false]; + } + + serialize() { + const serialized = [op]; + this.eachChild(child => { serialized.push(child.serialize()); }); + return serialized; + } + }; +} + +export const Equals = makeComparison('==', eq, eqCollate); +export const NotEquals = makeComparison('!=', neq, neqCollate); +export const LessThan = makeComparison('<', lt, ltCollate); +export const GreaterThan = makeComparison('>', gt, gtCollate); +export const LessThanOrEqual = makeComparison('<=', lteq, lteqCollate); +export const GreaterThanOrEqual = makeComparison('>=', gteq, gteqCollate); diff --git a/src/style-spec/expression/definitions/equals.js b/src/style-spec/expression/definitions/equals.js deleted file mode 100644 index 374d6f916c2..00000000000 --- a/src/style-spec/expression/definitions/equals.js +++ /dev/null @@ -1,103 +0,0 @@ -// @flow - -import { toString, ValueType, BooleanType, CollatorType } from '../types'; - -import type { Expression } from '../expression'; -import type EvaluationContext from '../evaluation_context'; -import type ParsingContext from '../parsing_context'; -import type { Type } from '../types'; - -function isComparableType(type: Type) { - return type.kind === 'string' || - type.kind === 'number' || - type.kind === 'boolean' || - type.kind === 'null'; -} - -/** - * Special form for ==, !=, implementing the following signatures: - * - (T1: Comparable, T2: Comparable) => boolean { T1 == T2 } - * - (Comparable, value) => boolean - * - (value, Comparable) => boolean - * - * Where Comparable = string | number | boolean | null. - * - * Evaluation semantics for the value cases are equivalent to Javascript's - * strict equality (===/!==) -- i.e., when the value argument's type doesn't - * match that of the Comparable argument, == evaluates to false, != to true. - * - * @private - */ -function makeComparison(op: string, negate: boolean) { - return class Comparison implements Expression { - type: Type; - lhs: Expression; - rhs: Expression; - collator: Expression | null; - - constructor(lhs: Expression, rhs: Expression, collator: Expression | null) { - this.type = BooleanType; - this.lhs = lhs; - this.rhs = rhs; - this.collator = collator; - } - - static parse(args: Array, context: ParsingContext): ?Expression { - if (args.length !== 3 && args.length !== 4) - return context.error(`Expected two or three arguments.`); - - const lhs = context.parse(args[1], 1, ValueType); - if (!lhs) return null; - const rhs = context.parse(args[2], 2, ValueType); - if (!rhs) return null; - - if (!isComparableType(lhs.type) && !isComparableType(rhs.type)) { - return context.error(`Expected at least one argument to be a string, number, boolean, or null, but found (${toString(lhs.type)}, ${toString(rhs.type)}) instead.`); - } - - if (lhs.type.kind !== rhs.type.kind && lhs.type.kind !== 'value' && rhs.type.kind !== 'value') { - return context.error(`Cannot compare ${toString(lhs.type)} and ${toString(rhs.type)}.`); - } - - let collator = null; - if (args.length === 4) { - if (lhs.type.kind !== 'string' && rhs.type.kind !== 'string') { - return context.error(`Cannot use collator to compare non-string types.`); - } - collator = context.parse(args[3], 3, CollatorType); - if (!collator) return null; - } - - return new Comparison(lhs, rhs, collator); - } - - evaluate(ctx: EvaluationContext) { - const equal = this.collator ? - this.collator.evaluate(ctx).compare(this.lhs.evaluate(ctx), this.rhs.evaluate(ctx)) === 0 : - this.lhs.evaluate(ctx) === this.rhs.evaluate(ctx); - - return negate ? !equal : equal; - } - - eachChild(fn: (Expression) => void) { - fn(this.lhs); - fn(this.rhs); - if (this.collator) { - fn(this.collator); - } - } - - possibleOutputs() { - return [true, false]; - } - - serialize() { - const serialized = [op]; - this.eachChild(child => { serialized.push(child.serialize()); }); - return serialized; - } - }; -} - -export const Equals = makeComparison('==', false); -export const NotEquals = makeComparison('!=', true); diff --git a/src/style-spec/expression/definitions/index.js b/src/style-spec/expression/definitions/index.js index d357072c475..b0221082afc 100644 --- a/src/style-spec/expression/definitions/index.js +++ b/src/style-spec/expression/definitions/index.js @@ -17,7 +17,14 @@ import Case from './case'; import Step from './step'; import Interpolate from './interpolate'; import Coalesce from './coalesce'; -import { Equals, NotEquals } from './equals'; +import { + Equals, + NotEquals, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual +} from './comparison'; import { CollatorExpression } from './collator'; import Length from './length'; @@ -29,6 +36,10 @@ const expressions: ExpressionRegistry = { // special forms '==': Equals, '!=': NotEquals, + '>': GreaterThan, + '<': LessThan, + '>=': GreaterThanOrEqual, + '<=': LessThanOrEqual, 'array': ArrayAssertion, 'at': At, 'boolean': Assertion, @@ -68,16 +79,6 @@ function get(key, obj) { return typeof v === 'undefined' ? null : v; } -function lt(ctx, [a, b]) { return a.evaluate(ctx) < b.evaluate(ctx); } -function gt(ctx, [a, b]) { return a.evaluate(ctx) > b.evaluate(ctx); } -function lteq(ctx, [a, b]) { return a.evaluate(ctx) <= b.evaluate(ctx); } -function gteq(ctx, [a, b]) { return a.evaluate(ctx) >= b.evaluate(ctx); } - -function ltCollate(ctx, [a, b, c]) { return c.evaluate(ctx).compare(a.evaluate(ctx), b.evaluate(ctx)) < 0; } -function gtCollate(ctx, [a, b, c]) { return c.evaluate(ctx).compare(a.evaluate(ctx), b.evaluate(ctx)) > 0; } -function lteqCollate(ctx, [a, b, c]) { return c.evaluate(ctx).compare(a.evaluate(ctx), b.evaluate(ctx)) <= 0; } -function gteqCollate(ctx, [a, b, c]) { return c.evaluate(ctx).compare(a.evaluate(ctx), b.evaluate(ctx)) >= 0; } - function binarySearch(v, a, i, j) { while (i <= j) { const m = (i + j) >> 1; @@ -473,38 +474,6 @@ CompoundExpression.register(expressions, { // assumes v is a array literal with values sorted in ascending order and of a single type (ctx, [k, v]) => binarySearch(ctx.properties()[(k: any).value], (v: any).value, 0, (v: any).value.length - 1) ], - '>': { - type: BooleanType, - overloads: [ - [[NumberType, NumberType], gt], - [[StringType, StringType], gt], - [[StringType, StringType, CollatorType], gtCollate] - ] - }, - '<': { - type: BooleanType, - overloads: [ - [[NumberType, NumberType], lt], - [[StringType, StringType], lt], - [[StringType, StringType, CollatorType], ltCollate] - ] - }, - '>=': { - type: BooleanType, - overloads: [ - [[NumberType, NumberType], gteq], - [[StringType, StringType], gteq], - [[StringType, StringType, CollatorType], gteqCollate] - ] - }, - '<=': { - type: BooleanType, - overloads: [ - [[NumberType, NumberType], lteq], - [[StringType, StringType], lteq], - [[StringType, StringType, CollatorType], lteqCollate] - ] - }, 'all': { type: BooleanType, overloads: [ diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 31073ac0e26..fd4c24b768c 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -2963,7 +2963,7 @@ } }, "==": { - "doc": "Returns `true` if the input values are equal, `false` otherwise. Equality is strictly typed: values of different types are always considered not equal. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the input values are equal, `false` otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -2975,7 +2975,7 @@ } }, "!=": { - "doc": "Returns `true` if the input values are not equal, `false` otherwise. Equality is strictly typed: values of different types are always considered not equal. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the input values are not equal, `false` otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -2987,7 +2987,7 @@ } }, ">": { - "doc": "Returns `true` if the first input is strictly greater than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the first input is strictly greater than the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -2999,7 +2999,7 @@ } }, "<": { - "doc": "Returns `true` if the first input is strictly less than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the first input is strictly less than the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -3011,7 +3011,7 @@ } }, ">=": { - "doc": "Returns `true` if the first input is greater than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the first input is greater than or equal to the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -3023,7 +3023,7 @@ } }, "<=": { - "doc": "Returns `true` if the first input is less than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "doc": "Returns `true` if the first input is less than or equal to the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", "sdk-support": { "basic functionality": { diff --git a/test/integration/expression-tests/array/implicit-2/test.json b/test/integration/expression-tests/array/implicit-2/test.json index e8e753f5c84..8c6fe029c57 100644 --- a/test/integration/expression-tests/array/implicit-2/test.json +++ b/test/integration/expression-tests/array/implicit-2/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["get", "array"], "inputs": [ diff --git a/test/integration/expression-tests/array/implicit-3/test.json b/test/integration/expression-tests/array/implicit-3/test.json index 2461d871701..2b095e411b8 100644 --- a/test/integration/expression-tests/array/implicit-3/test.json +++ b/test/integration/expression-tests/array/implicit-3/test.json @@ -4,9 +4,7 @@ "value": "number", "length": 2, "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["get", "array"], "inputs": [ diff --git a/test/integration/expression-tests/at/infer-array-type/test.json b/test/integration/expression-tests/at/infer-array-type/test.json index 40214772e52..e3e6c2ac1cc 100644 --- a/test/integration/expression-tests/at/infer-array-type/test.json +++ b/test/integration/expression-tests/at/infer-array-type/test.json @@ -2,9 +2,7 @@ "propertySpec": { "type": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["at", 1, ["literal", [1, 2, 3]]], "inputs": [], diff --git a/test/integration/expression-tests/case/basic/test.json b/test/integration/expression-tests/case/basic/test.json index 2e4c8472982..510e4eb391a 100644 --- a/test/integration/expression-tests/case/basic/test.json +++ b/test/integration/expression-tests/case/basic/test.json @@ -2,9 +2,7 @@ "propertySpec": { "type": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["case", ["get", "x"], "x", ["get", "y"], "y", "otherwise"], "inputs": [ diff --git a/test/integration/expression-tests/case/infer-array-type/test.json b/test/integration/expression-tests/case/infer-array-type/test.json index 046ff1defb3..9c5e3ec25f8 100644 --- a/test/integration/expression-tests/case/infer-array-type/test.json +++ b/test/integration/expression-tests/case/infer-array-type/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": [ "case", diff --git a/test/integration/expression-tests/ceil/basic/test.json b/test/integration/expression-tests/ceil/basic/test.json index 3b29833ac71..8fbee7c586f 100644 --- a/test/integration/expression-tests/ceil/basic/test.json +++ b/test/integration/expression-tests/ceil/basic/test.json @@ -17,7 +17,7 @@ "isZoomConstant": true, "type": "number" }, - "outputs": [ -2, -2, -2, -2, 3, 3, 3, 2 ], + "outputs": [-2, -2, -2, -2, 3, 3, 3, 2], "serialized": ["ceil", ["number", ["get", "x"]]] } } diff --git a/test/integration/expression-tests/coalesce/infer-array-type/test.json b/test/integration/expression-tests/coalesce/infer-array-type/test.json index 1270a45ae38..8661cffed26 100644 --- a/test/integration/expression-tests/coalesce/infer-array-type/test.json +++ b/test/integration/expression-tests/coalesce/infer-array-type/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": [ "coalesce", diff --git a/test/integration/expression-tests/collator/accent-equals-de/test.json b/test/integration/expression-tests/collator/accent-equals-de/test.json index 0dd5c163db0..e69ebb8943b 100644 --- a/test/integration/expression-tests/collator/accent-equals-de/test.json +++ b/test/integration/expression-tests/collator/accent-equals-de/test.json @@ -43,7 +43,11 @@ "resolved-locale", [ "collator", - {"case-sensitive": true, "diacritic-sensitive": false, "locale": "de"} + { + "case-sensitive": true, + "diacritic-sensitive": false, + "locale": "de" + } ] ], "de" diff --git a/test/integration/expression-tests/collator/comparison-number-error/test.json b/test/integration/expression-tests/collator/comparison-number-error/test.json index cbc72182cbb..921caa41d19 100644 --- a/test/integration/expression-tests/collator/comparison-number-error/test.json +++ b/test/integration/expression-tests/collator/comparison-number-error/test.json @@ -9,7 +9,7 @@ "compiled": { "result": "error", "errors": [ - {"key": "[1]", "error": "Expected string but found number instead."} + {"key": "", "error": "Cannot use collator to compare non-string types."} ] } } diff --git a/test/integration/expression-tests/constant-folding/evaluation-error/test.json b/test/integration/expression-tests/constant-folding/evaluation-error/test.json index c64ad651ccb..1cde4dc3584 100644 --- a/test/integration/expression-tests/constant-folding/evaluation-error/test.json +++ b/test/integration/expression-tests/constant-folding/evaluation-error/test.json @@ -2,9 +2,7 @@ "propertySpec": { "type": "color", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["step", ["get", "x"], "black", 0, "invalid", 10, "blue"], "inputs": [ diff --git a/test/integration/expression-tests/equal/array/test.json b/test/integration/expression-tests/equal/array/test.json index 40b9285dd25..fe28d07984b 100644 --- a/test/integration/expression-tests/equal/array/test.json +++ b/test/integration/expression-tests/equal/array/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected at least one argument to be a string, number, boolean, or null, but found (value, array) instead." + "key": "[2]", + "error": "\"==\" comparisons are not supported for type 'array'." } ] } diff --git a/test/integration/expression-tests/equal/collator-value/test.json b/test/integration/expression-tests/equal/collator-value/test.json new file mode 100644 index 00000000000..4d0db1e8dc0 --- /dev/null +++ b/test/integration/expression-tests/equal/collator-value/test.json @@ -0,0 +1,31 @@ +{ + "expression": [ + "==", + ["get", "x"], + ["get", "y"], + ["collator", {"case-sensitive": false, "diacritic-sensitive": false}] + ], + "inputs": [ + [{}, {"properties": {"x": "a", "y": "A"}}], + [{}, {"properties": {"x": "1", "y": "2"}}], + [{}, {"properties": {"x": "1", "y": null}}], + [{}, {"properties": {"x": null, "y": null}}], + [{}, {"properties": {"x": 1, "y": "1"}}], + [{}, {"properties": {"x": 1, "y": 1}}] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [true, false, false, true, false, true], + "serialized": [ + "==", + ["get", "x"], + ["get", "y"], + ["collator", {"case-sensitive": false, "diacritic-sensitive": false}] + ] + } +} diff --git a/test/integration/expression-tests/equal/color/test.json b/test/integration/expression-tests/equal/color/test.json index 37b3339adb1..6e04c74104c 100644 --- a/test/integration/expression-tests/equal/color/test.json +++ b/test/integration/expression-tests/equal/color/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected at least one argument to be a string, number, boolean, or null, but found (value, color) instead." + "key": "[2]", + "error": "\"==\" comparisons are not supported for type 'color'." } ] } diff --git a/test/integration/expression-tests/equal/mismatch/test.json b/test/integration/expression-tests/equal/mismatch/test.json index 02a5ce690bb..a1b44024485 100644 --- a/test/integration/expression-tests/equal/mismatch/test.json +++ b/test/integration/expression-tests/equal/mismatch/test.json @@ -3,7 +3,9 @@ "expected": { "compiled": { "result": "error", - "errors": [{"key": "", "error": "Cannot compare string and number."}] + "errors": [ + {"key": "", "error": "Cannot compare types 'string' and 'number'."} + ] } } } diff --git a/test/integration/expression-tests/equal/object/test.json b/test/integration/expression-tests/equal/object/test.json index f3eb3d67df6..97a51854e82 100644 --- a/test/integration/expression-tests/equal/object/test.json +++ b/test/integration/expression-tests/equal/object/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected at least one argument to be a string, number, boolean, or null, but found (value, object) instead." + "key": "[2]", + "error": "\"==\" comparisons are not supported for type 'object'." } ] } diff --git a/test/integration/expression-tests/equal/value/test.json b/test/integration/expression-tests/equal/value/test.json index 253e95b190e..52d65fd40e2 100644 --- a/test/integration/expression-tests/equal/value/test.json +++ b/test/integration/expression-tests/equal/value/test.json @@ -4,17 +4,18 @@ [{}, {"properties": {"x": 0, "y": 0}}], [{}, {"properties": {"x": "0", "y": "0"}}], [{}, {"properties": {"x": 0, "y": false}}], - [{}, {"properties": {"x": 0, "y": "0"}}] + [{}, {"properties": {"x": 0, "y": "0"}}], + [{}, {"properties": {"x": 0, "y": null}}], + [{}, {"properties": {"x": "0", "y": null}}] ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected at least one argument to be a string, number, boolean, or null, but found (value, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [true, true, false, false, false, false], + "serialized": ["==", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/floor/basic/test.json b/test/integration/expression-tests/floor/basic/test.json index c0febcc36fd..ad28186cec9 100644 --- a/test/integration/expression-tests/floor/basic/test.json +++ b/test/integration/expression-tests/floor/basic/test.json @@ -17,7 +17,7 @@ "isZoomConstant": true, "type": "number" }, - "outputs": [ -3, -3, -3, -2, 2, 2, 2, 2 ], + "outputs": [-3, -3, -3, -2, 2, 2, 2, 2], "serialized": ["floor", ["number", ["get", "x"]]] } } diff --git a/test/integration/expression-tests/greater/boolean/test.json b/test/integration/expression-tests/greater/boolean/test.json index b65ca4be7de..0947dcf6eee 100644 --- a/test/integration/expression-tests/greater/boolean/test.json +++ b/test/integration/expression-tests/greater/boolean/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (boolean, boolean) instead." + "key": "[1]", + "error": "\">\" comparisons are not supported for type 'boolean'." } ] } diff --git a/test/integration/expression-tests/greater/mismatch/test.json b/test/integration/expression-tests/greater/mismatch/test.json index 6cf310c38bb..e073c4848a1 100644 --- a/test/integration/expression-tests/greater/mismatch/test.json +++ b/test/integration/expression-tests/greater/mismatch/test.json @@ -4,10 +4,7 @@ "compiled": { "result": "error", "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, number) instead." - } + {"key": "", "error": "Cannot compare types 'string' and 'number'."} ] } } diff --git a/test/integration/expression-tests/greater/null/test.json b/test/integration/expression-tests/greater/null/test.json index e55826acf7f..44c8e864d72 100644 --- a/test/integration/expression-tests/greater/null/test.json +++ b/test/integration/expression-tests/greater/null/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (null, null) instead." + "key": "[1]", + "error": "\">\" comparisons are not supported for type 'null'." } ] } diff --git a/test/integration/expression-tests/greater/string-and-value/test.json b/test/integration/expression-tests/greater/string-and-value/test.json new file mode 100644 index 00000000000..e345c087acb --- /dev/null +++ b/test/integration/expression-tests/greater/string-and-value/test.json @@ -0,0 +1,26 @@ +{ + "expression": [">", ["string", ["get", "x"]], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "2", "y": "1"}}], + [{}, {"properties": {"x": "2", "y": 1}}], + [{}, {"properties": {"x": 2, "y": "1"}}] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + { + "error": "Expected value to be of type string, but found number instead." + }, + { + "error": "Expected value to be of type string, but found number instead." + } + ], + "serialized": [">", ["string", ["get", "x"]], ["string", ["get", "y"]]] + } +} diff --git a/test/integration/expression-tests/greater/value/test.json b/test/integration/expression-tests/greater/value/test.json index 0cf1194110c..75c314eeccd 100644 --- a/test/integration/expression-tests/greater/value/test.json +++ b/test/integration/expression-tests/greater/value/test.json @@ -1,14 +1,28 @@ { - "expression": [">", ["string", ["get", "x"]], ["get", "y"]], + "expression": [">", ["get", "x"], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "2", "y": "10"}}], + [{}, {"properties": {"x": 10, "y": 1}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + true, + { + "error": "Expected arguments for \">\" to be (string, string) or (number, number), but found (string, number) instead." + }, + { + "error": "Expected arguments for \">\" to be (string, string) or (number, number), but found (number, string) instead." + } + ], + "serialized": [">", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/greater_or_equal/boolean/test.json b/test/integration/expression-tests/greater_or_equal/boolean/test.json index ec38d764258..5ec7d802104 100644 --- a/test/integration/expression-tests/greater_or_equal/boolean/test.json +++ b/test/integration/expression-tests/greater_or_equal/boolean/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (boolean, boolean) instead." + "key": "[1]", + "error": "\">=\" comparisons are not supported for type 'boolean'." } ] } diff --git a/test/integration/expression-tests/greater_or_equal/mismatch/test.json b/test/integration/expression-tests/greater_or_equal/mismatch/test.json index 6fe448d8974..9b2275f0f2c 100644 --- a/test/integration/expression-tests/greater_or_equal/mismatch/test.json +++ b/test/integration/expression-tests/greater_or_equal/mismatch/test.json @@ -4,10 +4,7 @@ "compiled": { "result": "error", "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, number) instead." - } + {"key": "", "error": "Cannot compare types 'string' and 'number'."} ] } } diff --git a/test/integration/expression-tests/greater_or_equal/null/test.json b/test/integration/expression-tests/greater_or_equal/null/test.json index 7b776bbf811..4fbf753fcf8 100644 --- a/test/integration/expression-tests/greater_or_equal/null/test.json +++ b/test/integration/expression-tests/greater_or_equal/null/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (null, null) instead." + "key": "[1]", + "error": "\">=\" comparisons are not supported for type 'null'." } ] } diff --git a/test/integration/expression-tests/greater_or_equal/string-and-value/test.json b/test/integration/expression-tests/greater_or_equal/string-and-value/test.json new file mode 100644 index 00000000000..0cdd25d88a2 --- /dev/null +++ b/test/integration/expression-tests/greater_or_equal/string-and-value/test.json @@ -0,0 +1,26 @@ +{ + "expression": [">=", ["string", ["get", "x"]], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "1", "y": "1"}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + { + "error": "Expected value to be of type string, but found number instead." + }, + { + "error": "Expected value to be of type string, but found number instead." + } + ], + "serialized": [">=", ["string", ["get", "x"]], ["string", ["get", "y"]]] + } +} diff --git a/test/integration/expression-tests/greater_or_equal/value/test.json b/test/integration/expression-tests/greater_or_equal/value/test.json index b37b9c96cdf..e92a5684bba 100644 --- a/test/integration/expression-tests/greater_or_equal/value/test.json +++ b/test/integration/expression-tests/greater_or_equal/value/test.json @@ -1,14 +1,28 @@ { - "expression": [">=", ["string", ["get", "x"]], ["get", "y"]], + "expression": [">=", ["get", "x"], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "2", "y": "10"}}], + [{}, {"properties": {"x": 10, "y": 1}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + true, + { + "error": "Expected arguments for \">=\" to be (string, string) or (number, number), but found (string, number) instead." + }, + { + "error": "Expected arguments for \">=\" to be (string, string) or (number, number), but found (number, string) instead." + } + ], + "serialized": [">=", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/heatmap-density/basic/test.json b/test/integration/expression-tests/heatmap-density/basic/test.json index ab9885b8061..f5f9b76f4aa 100644 --- a/test/integration/expression-tests/heatmap-density/basic/test.json +++ b/test/integration/expression-tests/heatmap-density/basic/test.json @@ -12,9 +12,7 @@ ] }, "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": [ "interpolate", diff --git a/test/integration/expression-tests/interpolate/infer-array-type/test.json b/test/integration/expression-tests/interpolate/infer-array-type/test.json index 1cd7e29a781..fa2b0ad1445 100644 --- a/test/integration/expression-tests/interpolate/infer-array-type/test.json +++ b/test/integration/expression-tests/interpolate/infer-array-type/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": [ "step", diff --git a/test/integration/expression-tests/interpolate/linear/test.json b/test/integration/expression-tests/interpolate/linear/test.json index 85797017172..e23ff29bfa4 100644 --- a/test/integration/expression-tests/interpolate/linear/test.json +++ b/test/integration/expression-tests/interpolate/linear/test.json @@ -2,9 +2,7 @@ "propertySpec": { "type": "number", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["interpolate", ["linear"], ["get", "x"], 0, 100, 10, 200], "inputs": [ diff --git a/test/integration/expression-tests/less/boolean/test.json b/test/integration/expression-tests/less/boolean/test.json index 0d0cb2a70a3..731d6dbb78c 100644 --- a/test/integration/expression-tests/less/boolean/test.json +++ b/test/integration/expression-tests/less/boolean/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (boolean, boolean) instead." + "key": "[1]", + "error": "\"<\" comparisons are not supported for type 'boolean'." } ] } diff --git a/test/integration/expression-tests/less/mismatch/test.json b/test/integration/expression-tests/less/mismatch/test.json index d59861c9579..c33f34feabd 100644 --- a/test/integration/expression-tests/less/mismatch/test.json +++ b/test/integration/expression-tests/less/mismatch/test.json @@ -4,10 +4,7 @@ "compiled": { "result": "error", "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, number) instead." - } + {"key": "", "error": "Cannot compare types 'string' and 'number'."} ] } } diff --git a/test/integration/expression-tests/less/null/test.json b/test/integration/expression-tests/less/null/test.json index 0b5e5c2a7a4..ac9a426fdd1 100644 --- a/test/integration/expression-tests/less/null/test.json +++ b/test/integration/expression-tests/less/null/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (null, null) instead." + "key": "[1]", + "error": "\"<\" comparisons are not supported for type 'null'." } ] } diff --git a/test/integration/expression-tests/less/string-and-value/test.json b/test/integration/expression-tests/less/string-and-value/test.json new file mode 100644 index 00000000000..1674ece373f --- /dev/null +++ b/test/integration/expression-tests/less/string-and-value/test.json @@ -0,0 +1,26 @@ +{ + "expression": ["<", ["string", ["get", "x"]], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "1", "y": "2"}}], + [{}, {"properties": {"x": "1", "y": 2}}], + [{}, {"properties": {"x": 1, "y": "2"}}] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + { + "error": "Expected value to be of type string, but found number instead." + }, + { + "error": "Expected value to be of type string, but found number instead." + } + ], + "serialized": ["<", ["string", ["get", "x"]], ["string", ["get", "y"]]] + } +} diff --git a/test/integration/expression-tests/less/value/test.json b/test/integration/expression-tests/less/value/test.json index fef2cab5a9c..1d12eb582c8 100644 --- a/test/integration/expression-tests/less/value/test.json +++ b/test/integration/expression-tests/less/value/test.json @@ -1,14 +1,28 @@ { - "expression": ["<", ["string", ["get", "x"]], ["get", "y"]], + "expression": ["<", ["get", "x"], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "10", "y": "2"}}], + [{}, {"properties": {"x": 1, "y": 10}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + true, + { + "error": "Expected arguments for \"<\" to be (string, string) or (number, number), but found (string, number) instead." + }, + { + "error": "Expected arguments for \"<\" to be (string, string) or (number, number), but found (number, string) instead." + } + ], + "serialized": ["<", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/less_or_equal/boolean/test.json b/test/integration/expression-tests/less_or_equal/boolean/test.json index 19f03ce5a8c..1b657359ef6 100644 --- a/test/integration/expression-tests/less_or_equal/boolean/test.json +++ b/test/integration/expression-tests/less_or_equal/boolean/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (boolean, boolean) instead." + "key": "[1]", + "error": "\"<=\" comparisons are not supported for type 'boolean'." } ] } diff --git a/test/integration/expression-tests/less_or_equal/mismatch/test.json b/test/integration/expression-tests/less_or_equal/mismatch/test.json index 22bac383c11..40e257af409 100644 --- a/test/integration/expression-tests/less_or_equal/mismatch/test.json +++ b/test/integration/expression-tests/less_or_equal/mismatch/test.json @@ -4,10 +4,7 @@ "compiled": { "result": "error", "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, number) instead." - } + {"key": "", "error": "Cannot compare types 'string' and 'number'."} ] } } diff --git a/test/integration/expression-tests/less_or_equal/null/test.json b/test/integration/expression-tests/less_or_equal/null/test.json index 343f73b7279..c16be2ca756 100644 --- a/test/integration/expression-tests/less_or_equal/null/test.json +++ b/test/integration/expression-tests/less_or_equal/null/test.json @@ -5,8 +5,8 @@ "result": "error", "errors": [ { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (null, null) instead." + "key": "[1]", + "error": "\"<=\" comparisons are not supported for type 'null'." } ] } diff --git a/test/integration/expression-tests/less_or_equal/string-and-value/test.json b/test/integration/expression-tests/less_or_equal/string-and-value/test.json new file mode 100644 index 00000000000..24b4fe682ce --- /dev/null +++ b/test/integration/expression-tests/less_or_equal/string-and-value/test.json @@ -0,0 +1,26 @@ +{ + "expression": ["<", ["string", ["get", "x"]], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "1", "y": "1"}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + false, + { + "error": "Expected value to be of type string, but found number instead." + }, + { + "error": "Expected value to be of type string, but found number instead." + } + ], + "serialized": ["<", ["string", ["get", "x"]], ["string", ["get", "y"]]] + } +} diff --git a/test/integration/expression-tests/less_or_equal/value/test.json b/test/integration/expression-tests/less_or_equal/value/test.json index 035d457e759..71b3fe81095 100644 --- a/test/integration/expression-tests/less_or_equal/value/test.json +++ b/test/integration/expression-tests/less_or_equal/value/test.json @@ -1,14 +1,28 @@ { - "expression": ["<=", ["string", ["get", "x"]], ["get", "y"]], + "expression": ["<=", ["get", "x"], ["get", "y"]], + "inputs": [ + [{}, {"properties": {"x": "10", "y": "2"}}], + [{}, {"properties": {"x": 1, "y": 10}}], + [{}, {"properties": {"x": "1", "y": 1}}], + [{}, {"properties": {"x": 1, "y": "1"}}] + ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected arguments of type (number, number) | (string, string), but found (string, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [ + true, + true, + { + "error": "Expected arguments for \"<=\" to be (string, string) or (number, number), but found (string, number) instead." + }, + { + "error": "Expected arguments for \"<=\" to be (string, string) or (number, number), but found (number, string) instead." + } + ], + "serialized": ["<=", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/literal/infer-empty-array-type/test.json b/test/integration/expression-tests/literal/infer-empty-array-type/test.json index 09c9e3dcb36..8ad21ba861c 100644 --- a/test/integration/expression-tests/literal/infer-empty-array-type/test.json +++ b/test/integration/expression-tests/literal/infer-empty-array-type/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "number", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["literal", []], "inputs": [], diff --git a/test/integration/expression-tests/match/basic/test.json b/test/integration/expression-tests/match/basic/test.json index 1178d0e80a6..266eacdb3b2 100644 --- a/test/integration/expression-tests/match/basic/test.json +++ b/test/integration/expression-tests/match/basic/test.json @@ -14,13 +14,7 @@ "isZoomConstant": true, "type": "string" }, - "outputs": [ - "Apple", - "Banana", - "Kumquat", - "Kumquat", - "Kumquat" - ], + "outputs": ["Apple", "Banana", "Kumquat", "Kumquat", "Kumquat"], "serialized": [ "match", ["get", "x"], diff --git a/test/integration/expression-tests/match/infer-array-type/test.json b/test/integration/expression-tests/match/infer-array-type/test.json index d5dea55e8f2..2b1041fd36a 100644 --- a/test/integration/expression-tests/match/infer-array-type/test.json +++ b/test/integration/expression-tests/match/infer-array-type/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": [ "match", diff --git a/test/integration/expression-tests/not_equal/mismatch/test.json b/test/integration/expression-tests/not_equal/mismatch/test.json index 568aa6bc23c..8a455e01bee 100644 --- a/test/integration/expression-tests/not_equal/mismatch/test.json +++ b/test/integration/expression-tests/not_equal/mismatch/test.json @@ -3,7 +3,9 @@ "expected": { "compiled": { "result": "error", - "errors": [{"key": "", "error": "Cannot compare string and number."}] + "errors": [ + {"key": "", "error": "Cannot compare types 'string' and 'number'."} + ] } } } diff --git a/test/integration/expression-tests/not_equal/value/test.json b/test/integration/expression-tests/not_equal/value/test.json index 0eb625b44d6..62951ff58d8 100644 --- a/test/integration/expression-tests/not_equal/value/test.json +++ b/test/integration/expression-tests/not_equal/value/test.json @@ -4,17 +4,18 @@ [{}, {"properties": {"x": 0, "y": 0}}], [{}, {"properties": {"x": "0", "y": "0"}}], [{}, {"properties": {"x": 0, "y": false}}], - [{}, {"properties": {"x": 0, "y": "0"}}] + [{}, {"properties": {"x": 0, "y": "0"}}], + [{}, {"properties": {"x": 0, "y": null}}], + [{}, {"properties": {"x": "0", "y": null}}] ], "expected": { "compiled": { - "result": "error", - "errors": [ - { - "key": "", - "error": "Expected at least one argument to be a string, number, boolean, or null, but found (value, value) instead." - } - ] - } + "result": "success", + "isFeatureConstant": false, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [false, false, true, true, true, true], + "serialized": ["!=", ["get", "x"], ["get", "y"]] } } diff --git a/test/integration/expression-tests/round/basic/test.json b/test/integration/expression-tests/round/basic/test.json index 7ce0d025b7a..5c6f4a6f45b 100644 --- a/test/integration/expression-tests/round/basic/test.json +++ b/test/integration/expression-tests/round/basic/test.json @@ -17,7 +17,7 @@ "isZoomConstant": true, "type": "number" }, - "outputs": [ -3, -3, -2, -2, 3, 3, 2, 2 ], + "outputs": [-3, -3, -2, -2, 3, 3, 2, 2], "serialized": ["round", ["number", ["get", "x"]]] } } diff --git a/test/integration/expression-tests/typecheck/array-invalid-item/test.json b/test/integration/expression-tests/typecheck/array-invalid-item/test.json index 6eeb381cee2..07c9f8f1701 100644 --- a/test/integration/expression-tests/typecheck/array-invalid-item/test.json +++ b/test/integration/expression-tests/typecheck/array-invalid-item/test.json @@ -4,9 +4,7 @@ "value": "string", "length": 2, "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["array", "number", 2, ["get", "x"]], "inputs": [], diff --git a/test/integration/expression-tests/typecheck/array-item-subtyping/test.json b/test/integration/expression-tests/typecheck/array-item-subtyping/test.json index 89e365f56d8..a2859348cc5 100644 --- a/test/integration/expression-tests/typecheck/array-item-subtyping/test.json +++ b/test/integration/expression-tests/typecheck/array-item-subtyping/test.json @@ -2,9 +2,7 @@ "propertySpec": { "type": "array", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["array", "number", 2, ["get", "x"]], "inputs": [], diff --git a/test/integration/expression-tests/typecheck/array-length-subtyping--no-length/test.json b/test/integration/expression-tests/typecheck/array-length-subtyping--no-length/test.json index 309312db483..d180e38bac3 100644 --- a/test/integration/expression-tests/typecheck/array-length-subtyping--no-length/test.json +++ b/test/integration/expression-tests/typecheck/array-length-subtyping--no-length/test.json @@ -4,9 +4,7 @@ "value": "number", "length": 3, "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["array", "number", ["get", "x"]], "inputs": [], diff --git a/test/integration/expression-tests/typecheck/array-length-subtyping/test.json b/test/integration/expression-tests/typecheck/array-length-subtyping/test.json index 28b9a050aff..2600dec7e2d 100644 --- a/test/integration/expression-tests/typecheck/array-length-subtyping/test.json +++ b/test/integration/expression-tests/typecheck/array-length-subtyping/test.json @@ -3,9 +3,7 @@ "type": "array", "value": "string", "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["array", "string", 2, ["get", "x"]], "inputs": [], diff --git a/test/integration/expression-tests/typecheck/array-wrong-length/test.json b/test/integration/expression-tests/typecheck/array-wrong-length/test.json index 4d09e80bf3f..a5e08bd02d6 100644 --- a/test/integration/expression-tests/typecheck/array-wrong-length/test.json +++ b/test/integration/expression-tests/typecheck/array-wrong-length/test.json @@ -4,9 +4,7 @@ "value": "number", "length": 3, "property-type": "data-driven", - "expression": { - "parameters": ["zoom", "feature"] - } + "expression": {"parameters": ["zoom", "feature"]} }, "expression": ["array", "number", 2, ["get", "x"]], "inputs": [], diff --git a/test/integration/lib/expression.js b/test/integration/lib/expression.js index 9e0f530ad9c..f247f669a64 100644 --- a/test/integration/lib/expression.js +++ b/test/integration/lib/expression.js @@ -101,6 +101,8 @@ exports.run = function (implementation, options, runExpressionTest) { serialized: result.serialized }; + delete fixture.metadata; + fs.writeFile(path.join(dir, 'test.json'), `${stringify(fixture, null, 2)}\n`, done); return; } @@ -169,12 +171,12 @@ exports.run = function (implementation, options, runExpressionTest) { }; if (compileOk && !evalOk) { - const differences = diffOutputs(result.outputs); + const differences = `Original\n${diffOutputs(result.outputs)}\n`; diffOutput.text += differences; diffOutput.html += differences; } if (recompileOk && !roundTripOk) { - const differences = diffOutputs(result.roundTripOutputs); + const differences = `\nRoundtripped through serialize()\n${diffOutputs(result.roundTripOutputs)}\n`; diffOutput.text += differences; diffOutput.html += differences; }