From 949a2ec0d902de82c888623c6c1e01cf2124a62e Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Mon, 13 Nov 2017 11:08:39 -0500 Subject: [PATCH] Convert old-style filters to expressions --- src/style-spec/feature_filter/index.js | 112 +-- src/style-spec/util/eval_support.js | 10 + test/unit/style-spec/feature_filter.test.js | 917 ++++++++++---------- 3 files changed, 532 insertions(+), 507 deletions(-) create mode 100644 src/style-spec/util/eval_support.js diff --git a/src/style-spec/feature_filter/index.js b/src/style-spec/feature_filter/index.js index 20e871b5acf..237d4dec688 100644 --- a/src/style-spec/feature_filter/index.js +++ b/src/style-spec/feature_filter/index.js @@ -44,8 +44,6 @@ function isExpressionFilter(filter) { } } -const types = ['Unknown', 'Point', 'LineString', 'Polygon']; - const filterSpec = { 'type': 'boolean', 'default': false, @@ -69,7 +67,7 @@ function createFilter(filter: any): FeatureFilter { } if (!isExpressionFilter(filter)) { - return (new Function('g', 'f', `var p = (f && f.properties || {}); return ${compile(filter)}`): any); + filter = convertFilter(filter); } const compiled = createExpression(filter, filterSpec); @@ -80,70 +78,76 @@ function createFilter(filter: any): FeatureFilter { } } -function compile(filter) { - if (!filter) return 'true'; +// Comparison function to sort numbers and strings +function compare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +function convertFilter(filter: ?Array): mixed { + if (!filter) return true; const op = filter[0]; - if (filter.length <= 1) return op === 'any' ? 'false' : 'true'; - const str = - op === '==' ? compileComparisonOp(filter[1], filter[2], '===', false) : - op === '!=' ? compileComparisonOp(filter[1], filter[2], '!==', false) : + if (filter.length <= 1) return (op !== 'any'); + const converted = + op === '==' ? convertComparisonOp(filter[1], filter[2], '==') : + op === '!=' ? convertNegation(convertComparisonOp(filter[1], filter[2], '==')) : op === '<' || op === '>' || op === '<=' || - op === '>=' ? compileComparisonOp(filter[1], filter[2], op, true) : - op === 'any' ? compileLogicalOp(filter.slice(1), '||') : - op === 'all' ? compileLogicalOp(filter.slice(1), '&&') : - op === 'none' ? compileNegation(compileLogicalOp(filter.slice(1), '||')) : - op === 'in' ? compileInOp(filter[1], filter.slice(2)) : - op === '!in' ? compileNegation(compileInOp(filter[1], filter.slice(2))) : - op === 'has' ? compileHasOp(filter[1]) : - op === '!has' ? compileNegation(compileHasOp(filter[1])) : - 'true'; - return `(${str})`; + op === '>=' ? convertComparisonOp(filter[1], filter[2], op) : + op === 'any' ? convertDisjunctionOp(filter.slice(1)) : + op === 'all' ? ['all'].concat(filter.slice(1).map(convertFilter)) : + op === 'none' ? ['all'].concat(filter.slice(1).map(convertFilter).map(convertNegation)) : + op === 'in' ? convertInOp(filter[1], filter.slice(2)) : + op === '!in' ? convertNegation(convertInOp(filter[1], filter.slice(2))) : + op === 'has' ? convertHasOp(filter[1]) : + op === '!has' ? convertNegation(convertHasOp(filter[1])) : + true; + return converted; } -function compilePropertyReference(property) { - const ref = - property === '$type' ? 'f.type' : - property === '$id' ? 'f.id' : `p[${JSON.stringify(property)}]`; - return ref; -} - -function compileComparisonOp(property, value, op, checkType) { - const left = compilePropertyReference(property); - const right = property === '$type' ? types.indexOf(value) : JSON.stringify(value); - return (checkType ? `typeof ${left}=== typeof ${right}&&` : '') + left + op + right; +function convertComparisonOp(property: string, value: any, op: string) { + switch (property) { + case '$type': + return [`filter-type-${op}`, value]; + case '$id': + return [`filter-id-${op}`, value]; + default: + return [`filter-${op}`, property, value]; + } } -function compileLogicalOp(expressions, op) { - return expressions.map(compile).join(op); +function convertDisjunctionOp(filters: Array>) { + return ['any'].concat(filters.map(convertFilter)); } -function compileInOp(property, values) { - if (property === '$type') values = values.map((value) => { - return types.indexOf(value); - }); - const left = JSON.stringify(values.sort(compare)); - const right = compilePropertyReference(property); - - if (values.length <= 200 || values.some(v => typeof v !== typeof values[0])) return `${left}.indexOf(${right}) !== -1`; - - return `${'function(v, a, i, j) {' + - 'while (i <= j) { var m = (i + j) >> 1;' + - ' if (a[m] === v) return true; if (a[m] > v) j = m - 1; else i = m + 1;' + - '}' + - 'return false; }('}${right}, ${left},0,${values.length - 1})`; +function convertInOp(property: string, values: Array) { + if (values.length === 0) { return false; } + switch (property) { + case '$type': + return [`filter-type-in`, ['literal', values]]; + case '$id': + return [`filter-id-in`, ['literal', values]]; + default: + if (values.length > 200 && !values.some(v => typeof v !== typeof values[0])) { + return ['filter-in-large', property, ['literal', values.sort(compare)]]; + } else { + return ['filter-in-small', property, ['literal', values]]; + } + } } -function compileHasOp(property) { - return property === '$id' ? '"id" in f' : `${JSON.stringify(property)} in p`; +function convertHasOp(property: string) { + switch (property) { + case '$type': + return true; + case '$id': + return [`filter-has-id`]; + default: + return [`filter-has`, property]; + } } -function compileNegation(expression) { - return `!(${expression})`; +function convertNegation(filter: mixed) { + return ['!', filter]; } -// Comparison function to sort numbers and strings -function compare(a, b) { - return a < b ? -1 : a > b ? 1 : 0; -} diff --git a/src/style-spec/util/eval_support.js b/src/style-spec/util/eval_support.js new file mode 100644 index 00000000000..d2f14bda8a4 --- /dev/null +++ b/src/style-spec/util/eval_support.js @@ -0,0 +1,10 @@ +// @flow + +module.exports = (function () { + try { + new Function(''); + return true; + } catch (e) { + return false; + } +})(); diff --git a/test/unit/style-spec/feature_filter.test.js b/test/unit/style-spec/feature_filter.test.js index 13ab69ca22f..7938e483802 100644 --- a/test/unit/style-spec/feature_filter.test.js +++ b/test/unit/style-spec/feature_filter.test.js @@ -1,496 +1,507 @@ 'use strict'; const test = require('mapbox-gl-js-test').test; -const filter = require('../../../src/style-spec/feature_filter'); - -test('expression, zoom', (t) => { - const f = filter(['>=', ['number', ['get', 'x']], ['zoom']]); - t.equal(f({zoom: 1}, {properties: {x: 0}}), false); - t.equal(f({zoom: 1}, {properties: {x: 1.5}}), true); - t.equal(f({zoom: 1}, {properties: {x: 2.5}}), true); - t.equal(f({zoom: 2}, {properties: {x: 0}}), false); - t.equal(f({zoom: 2}, {properties: {x: 1.5}}), false); - t.equal(f({zoom: 2}, {properties: {x: 2.5}}), true); - t.end(); -}); - -test('expression, compare two properties', (t) => { - t.stub(console, 'warn'); - const f = filter(['==', ['string', ['get', 'x']], ['string', ['get', 'y']]]); - t.equal(f({zoom: 0}, {properties: {x: 1, y: 1}}), false); - t.equal(f({zoom: 0}, {properties: {x: '1', y: '1'}}), true); - t.equal(f({zoom: 0}, {properties: {x: 'same', y: 'same'}}), true); - t.equal(f({zoom: 0}, {properties: {x: null}}), false); - t.equal(f({zoom: 0}, {properties: {x: undefined}}), false); - t.end(); -}); - -test('expression, any/all', (t) => { - t.equal(filter(['all'])(), true); - t.equal(filter(['all', true])(), true); - t.equal(filter(['all', true, false])(), false); - t.equal(filter(['all', true, true])(), true); - t.equal(filter(['any'])(), false); - t.equal(filter(['any', true])(), true); - t.equal(filter(['any', true, false])(), true); - t.equal(filter(['any', false, false])(), false); - t.end(); -}); +const proxyquire = require('proxyquire'); -test('expression, type error', (t) => { - t.throws(() => { - filter(['==', ['number', ['get', 'x']], ['string', ['get', 'y']]]); +const filterTests = (isEvalSupported) => (t) => { + const filter = proxyquire('../../../src/style-spec/feature_filter', { + '../util/eval_support': isEvalSupported }); - t.throws(() => { - filter(['number', ['get', 'x']]); + t.test('expression, zoom', (t) => { + const f = filter(['>=', ['number', ['get', 'x']], ['zoom']]); + t.equal(f({zoom: 1}, {properties: {x: 0}}), false); + t.equal(f({zoom: 1}, {properties: {x: 1.5}}), true); + t.equal(f({zoom: 1}, {properties: {x: 2.5}}), true); + t.equal(f({zoom: 2}, {properties: {x: 0}}), false); + t.equal(f({zoom: 2}, {properties: {x: 1.5}}), false); + t.equal(f({zoom: 2}, {properties: {x: 2.5}}), true); + t.end(); }); - t.doesNotThrow(() => { - filter(['boolean', ['get', 'x']]); + t.test('expression, compare two properties', (t) => { + t.stub(console, 'warn'); + const f = filter(['==', ['string', ['get', 'x']], ['string', ['get', 'y']]]); + t.equal(f({zoom: 0}, {properties: {x: 1, y: 1}}), false); + t.equal(f({zoom: 0}, {properties: {x: '1', y: '1'}}), true); + t.equal(f({zoom: 0}, {properties: {x: 'same', y: 'same'}}), true); + t.equal(f({zoom: 0}, {properties: {x: null}}), false); + t.equal(f({zoom: 0}, {properties: {x: undefined}}), false); + t.end(); }); - t.end(); -}); + t.test('expression, any/all', (t) => { + t.equal(filter(['all'])(), true); + t.equal(filter(['all', true])(), true); + t.equal(filter(['all', true, false])(), false); + t.equal(filter(['all', true, true])(), true); + t.equal(filter(['any'])(), false); + t.equal(filter(['any', true])(), true); + t.equal(filter(['any', true, false])(), true); + t.equal(filter(['any', false, false])(), false); + t.end(); + }); + t.test('expression, type error', (t) => { + t.throws(() => { + filter(['==', ['number', ['get', 'x']], ['string', ['get', 'y']]]); + }); -test('degenerate', (t) => { - t.equal(filter()(), true); - t.equal(filter(undefined)(), true); - t.equal(filter(null)(), true); - t.end(); -}); + t.throws(() => { + filter(['number', ['get', 'x']]); + }); -test('==, string', (t) => { - const f = filter(['==', 'foo', 'bar']); - t.equal(f({zoom: 0}, {properties: {foo: 'bar'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 'baz'}}), false); - t.end(); -}); - -test('==, number', (t) => { - const f = filter(['==', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('==, null', (t) => { - const f = filter(['==', 'foo', null]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); + t.doesNotThrow(() => { + filter(['boolean', ['get', 'x']]); + }); -test('==, $type', (t) => { - const f = filter(['==', '$type', 'LineString']); - t.equal(f({zoom: 0}, {type: 1}), false); - t.equal(f({zoom: 0}, {type: 2}), true); - t.end(); -}); + t.end(); + }); -test('==, $id', (t) => { - const f = filter(['==', '$id', 1234]); - t.equal(f({zoom: 0}, {id: 1234}), true); - t.equal(f({zoom: 0}, {id: '1234'}), false); - t.equal(f({zoom: 0}, {properties: {id: 1234}}), false); + t.test('degenerate', (t) => { + t.equal(filter()(), true); + t.equal(filter(undefined)(), true); + t.equal(filter(null)(), true); + t.end(); + }); - t.end(); -}); + t.test('==, string', (t) => { + const f = filter(['==', 'foo', 'bar']); + t.equal(f({zoom: 0}, {properties: {foo: 'bar'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 'baz'}}), false); + t.end(); + }); -test('!=, string', (t) => { - const f = filter(['!=', 'foo', 'bar']); - t.equal(f({zoom: 0}, {properties: {foo: 'bar'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 'baz'}}), true); - t.end(); -}); - -test('!=, number', (t) => { - const f = filter(['!=', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), true); - t.equal(f({zoom: 0}, {properties: {foo: false}}), true); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.equal(f({zoom: 0}, {properties: {}}), true); - t.end(); -}); - -test('!=, null', (t) => { - const f = filter(['!=', 'foo', null]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), true); - t.equal(f({zoom: 0}, {properties: {foo: false}}), true); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.equal(f({zoom: 0}, {properties: {}}), true); - t.end(); -}); + t.test('==, number', (t) => { + const f = filter(['==', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); -test('!=, $type', (t) => { - const f = filter(['!=', '$type', 'LineString']); - t.equal(f({zoom: 0}, {type: 1}), true); - t.equal(f({zoom: 0}, {type: 2}), false); - t.end(); -}); - -test('<, number', (t) => { - const f = filter(['<', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('<, string', (t) => { - const f = filter(['<', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); - -test('<=, number', (t) => { - const f = filter(['<=', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('<=, string', (t) => { - const f = filter(['<=', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); - -test('>, number', (t) => { - const f = filter(['>', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('>, string', (t) => { - const f = filter(['>', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); - -test('>=, number', (t) => { - const f = filter(['>=', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('>=, string', (t) => { - const f = filter(['>=', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '1'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); + t.test('==, null', (t) => { + const f = filter(['==', 'foo', null]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); -test('in, degenerate', (t) => { - const f = filter(['in', 'foo']); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.end(); -}); - -test('in, string', (t) => { - const f = filter(['in', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('in, number', (t) => { - const f = filter(['in', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); - -test('in, null', (t) => { - const f = filter(['in', 'foo', null]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: true}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.end(); -}); + t.test('==, $type', (t) => { + const f = filter(['==', '$type', 'LineString']); + t.equal(f({zoom: 0}, {type: 1}), false); + t.equal(f({zoom: 0}, {type: 2}), true); + t.end(); + }); -test('in, multiple', (t) => { - const f = filter(['in', 'foo', 0, 1]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 3}}), false); - t.end(); -}); - -test('in, large_multiple', (t) => { - const values = Array.apply(null, {length: 2000}).map(Number.call, Number); - values.reverse(); - const f = filter(['in', 'foo'].concat(values)); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1999}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 2000}}), false); - t.end(); -}); - -test('in, large_multiple, heterogeneous', (t) => { - const values = Array.apply(null, {length: 2000}).map(Number.call, Number); - values.push('a'); - values.unshift('b'); - const f = filter(['in', 'foo'].concat(values)); - t.equal(f({zoom: 0}, {properties: {foo: 'b'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 'a'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1999}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 2000}}), false); - t.end(); -}); + t.test('==, $id', (t) => { + const f = filter(['==', '$id', 1234]); -test('in, $type', (t) => { - const f = filter(['in', '$type', 'LineString', 'Polygon']); - t.equal(f({zoom: 0}, {type: 1}), false); - t.equal(f({zoom: 0}, {type: 2}), true); - t.equal(f({zoom: 0}, {type: 3}), true); + t.equal(f({zoom: 0}, {id: 1234}), true); + t.equal(f({zoom: 0}, {id: '1234'}), false); + t.equal(f({zoom: 0}, {properties: {id: 1234}}), false); - const f1 = filter(['in', '$type', 'Polygon', 'LineString', 'Point']); - t.equal(f1({zoom: 0}, {type: 1}), true); - t.equal(f1({zoom: 0}, {type: 2}), true); - t.equal(f1({zoom: 0}, {type: 3}), true); + t.end(); + }); - t.end(); -}); + t.test('!=, string', (t) => { + const f = filter(['!=', 'foo', 'bar']); + t.equal(f({zoom: 0}, {properties: {foo: 'bar'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 'baz'}}), true); + t.end(); + }); -test('!in, degenerate', (t) => { - const f = filter(['!in', 'foo']); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.end(); -}); - -test('!in, string', (t) => { - const f = filter(['!in', 'foo', '0']); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.equal(f({zoom: 0}, {properties: {}}), true); - t.end(); -}); - -test('!in, number', (t) => { - const f = filter(['!in', 'foo', 0]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.end(); -}); - -test('!in, null', (t) => { - const f = filter(['!in', 'foo', null]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.end(); -}); + t.test('!=, number', (t) => { + const f = filter(['!=', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), true); + t.equal(f({zoom: 0}, {properties: {foo: false}}), true); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.equal(f({zoom: 0}, {properties: {}}), true); + t.end(); + }); -test('!in, multiple', (t) => { - const f = filter(['!in', 'foo', 0, 1]); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 3}}), true); - t.end(); -}); - -test('!in, large_multiple', (t) => { - const f = filter(['!in', 'foo'].concat(Array.apply(null, {length: 2000}).map(Number.call, Number))); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1999}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 2000}}), true); - t.end(); -}); + t.test('!=, null', (t) => { + const f = filter(['!=', 'foo', null]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), true); + t.equal(f({zoom: 0}, {properties: {foo: false}}), true); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.equal(f({zoom: 0}, {properties: {}}), true); + t.end(); + }); -test('!in, $type', (t) => { - const f = filter(['!in', '$type', 'LineString', 'Polygon']); - t.equal(f({zoom: 0}, {type: 1}), true); - t.equal(f({zoom: 0}, {type: 2}), false); - t.equal(f({zoom: 0}, {type: 3}), false); - t.end(); -}); + t.test('!=, $type', (t) => { + const f = filter(['!=', '$type', 'LineString']); + t.equal(f({zoom: 0}, {type: 1}), true); + t.equal(f({zoom: 0}, {type: 2}), false); + t.end(); + }); -test('any', (t) => { - const f1 = filter(['any']); - t.equal(f1({zoom: 0}, {properties: {foo: 1}}), false); + t.test('<, number', (t) => { + const f = filter(['<', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); - const f2 = filter(['any', ['==', 'foo', 1]]); - t.equal(f2({zoom: 0}, {properties: {foo: 1}}), true); + t.test('<, string', (t) => { + const f = filter(['<', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); - const f3 = filter(['any', ['==', 'foo', 0]]); - t.equal(f3({zoom: 0}, {properties: {foo: 1}}), false); + t.test('<=, number', (t) => { + const f = filter(['<=', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); - const f4 = filter(['any', ['==', 'foo', 0], ['==', 'foo', 1]]); - t.equal(f4({zoom: 0}, {properties: {foo: 1}}), true); + t.test('<=, string', (t) => { + const f = filter(['<=', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); - t.end(); -}); + t.test('>, number', (t) => { + const f = filter(['>', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); -test('all', (t) => { - const f1 = filter(['all']); - t.equal(f1({zoom: 0}, {properties: {foo: 1}}), true); + t.test('>, string', (t) => { + const f = filter(['>', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); - const f2 = filter(['all', ['==', 'foo', 1]]); - t.equal(f2({zoom: 0}, {properties: {foo: 1}}), true); + t.test('>=, number', (t) => { + const f = filter(['>=', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); - const f3 = filter(['all', ['==', 'foo', 0]]); - t.equal(f3({zoom: 0}, {properties: {foo: 1}}), false); + t.test('>=, string', (t) => { + const f = filter(['>=', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: -1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '1'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '-1'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); - const f4 = filter(['all', ['==', 'foo', 0], ['==', 'foo', 1]]); - t.equal(f4({zoom: 0}, {properties: {foo: 1}}), false); + t.test('in, degenerate', (t) => { + const f = filter(['in', 'foo']); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.end(); + }); - t.end(); -}); + t.test('in, string', (t) => { + const f = filter(['in', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); + + t.test('in, number', (t) => { + const f = filter(['in', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); -test('none', (t) => { - const f1 = filter(['none']); - t.equal(f1({zoom: 0}, {properties: {foo: 1}}), true); + t.test('in, null', (t) => { + const f = filter(['in', 'foo', null]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: true}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.end(); + }); - const f2 = filter(['none', ['==', 'foo', 1]]); - t.equal(f2({zoom: 0}, {properties: {foo: 1}}), false); + t.test('in, multiple', (t) => { + const f = filter(['in', 'foo', 0, 1]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 3}}), false); + t.end(); + }); - const f3 = filter(['none', ['==', 'foo', 0]]); - t.equal(f3({zoom: 0}, {properties: {foo: 1}}), true); + t.test('in, large_multiple', (t) => { + const values = Array.apply(null, {length: 2000}).map(Number.call, Number); + values.reverse(); + const f = filter(['in', 'foo'].concat(values)); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1999}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 2000}}), false); + t.end(); + }); - const f4 = filter(['none', ['==', 'foo', 0], ['==', 'foo', 1]]); - t.equal(f4({zoom: 0}, {properties: {foo: 1}}), false); + t.test('in, large_multiple, heterogeneous', (t) => { + const values = Array.apply(null, {length: 2000}).map(Number.call, Number); + values.push('a'); + values.unshift('b'); + const f = filter(['in', 'foo'].concat(values)); + t.equal(f({zoom: 0}, {properties: {foo: 'b'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 'a'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1999}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 2000}}), false); + t.end(); + }); + + t.test('in, $type', (t) => { + const f = filter(['in', '$type', 'LineString', 'Polygon']); + t.equal(f({zoom: 0}, {type: 1}), false); + t.equal(f({zoom: 0}, {type: 2}), true); + t.equal(f({zoom: 0}, {type: 3}), true); + + const f1 = filter(['in', '$type', 'Polygon', 'LineString', 'Point']); + t.equal(f1({zoom: 0}, {type: 1}), true); + t.equal(f1({zoom: 0}, {type: 2}), true); + t.equal(f1({zoom: 0}, {type: 3}), true); + + t.end(); + }); + + t.test('!in, degenerate', (t) => { + const f = filter(['!in', 'foo']); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.end(); + }); + + t.test('!in, string', (t) => { + const f = filter(['!in', 'foo', '0']); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.equal(f({zoom: 0}, {properties: {}}), true); + t.end(); + }); + + t.test('!in, number', (t) => { + const f = filter(['!in', 'foo', 0]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.end(); + }); + + t.test('!in, null', (t) => { + const f = filter(['!in', 'foo', null]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.end(); + }); + + t.test('!in, multiple', (t) => { + const f = filter(['!in', 'foo', 0, 1]); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 3}}), true); + t.end(); + }); + + t.test('!in, large_multiple', (t) => { + const f = filter(['!in', 'foo'].concat(Array.apply(null, {length: 2000}).map(Number.call, Number))); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1999}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 2000}}), true); + t.end(); + }); + + t.test('!in, $type', (t) => { + const f = filter(['!in', '$type', 'LineString', 'Polygon']); + t.equal(f({zoom: 0}, {type: 1}), true); + t.equal(f({zoom: 0}, {type: 2}), false); + t.equal(f({zoom: 0}, {type: 3}), false); + t.end(); + }); + + t.test('any', (t) => { + const f1 = filter(['any']); + t.equal(f1({zoom: 0}, {properties: {foo: 1}}), false); + + const f2 = filter(['any', ['==', 'foo', 1]]); + t.equal(f2({zoom: 0}, {properties: {foo: 1}}), true); + + const f3 = filter(['any', ['==', 'foo', 0]]); + t.equal(f3({zoom: 0}, {properties: {foo: 1}}), false); + + const f4 = filter(['any', ['==', 'foo', 0], ['==', 'foo', 1]]); + t.equal(f4({zoom: 0}, {properties: {foo: 1}}), true); + + t.end(); + }); + + t.test('all', (t) => { + const f1 = filter(['all']); + t.equal(f1({zoom: 0}, {properties: {foo: 1}}), true); + + const f2 = filter(['all', ['==', 'foo', 1]]); + t.equal(f2({zoom: 0}, {properties: {foo: 1}}), true); + + const f3 = filter(['all', ['==', 'foo', 0]]); + t.equal(f3({zoom: 0}, {properties: {foo: 1}}), false); + + const f4 = filter(['all', ['==', 'foo', 0], ['==', 'foo', 1]]); + t.equal(f4({zoom: 0}, {properties: {foo: 1}}), false); + + t.end(); + }); + + t.test('none', (t) => { + const f1 = filter(['none']); + t.equal(f1({zoom: 0}, {properties: {foo: 1}}), true); + + const f2 = filter(['none', ['==', 'foo', 1]]); + t.equal(f2({zoom: 0}, {properties: {foo: 1}}), false); + + const f3 = filter(['none', ['==', 'foo', 0]]); + t.equal(f3({zoom: 0}, {properties: {foo: 1}}), true); + + const f4 = filter(['none', ['==', 'foo', 0], ['==', 'foo', 1]]); + t.equal(f4({zoom: 0}, {properties: {foo: 1}}), false); + + t.end(); + }); + + t.test('has', (t) => { + const f = filter(['has', 'foo']); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); + t.equal(f({zoom: 0}, {properties: {foo: true}}), true); + t.equal(f({zoom: 0}, {properties: {foo: false}}), true); + t.equal(f({zoom: 0}, {properties: {foo: null}}), true); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); + t.equal(f({zoom: 0}, {properties: {}}), false); + t.end(); + }); + + t.test('!has', (t) => { + const f = filter(['!has', 'foo']); + t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); + t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); + t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: false}}), false); + t.equal(f({zoom: 0}, {properties: {foo: null}}), false); + t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); + t.equal(f({zoom: 0}, {properties: {}}), true); + t.end(); + }); t.end(); -}); - -test('has', (t) => { - const f = filter(['has', 'foo']); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), true); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), true); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), true); - t.equal(f({zoom: 0}, {properties: {foo: true}}), true); - t.equal(f({zoom: 0}, {properties: {foo: false}}), true); - t.equal(f({zoom: 0}, {properties: {foo: null}}), true); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), true); - t.equal(f({zoom: 0}, {properties: {}}), false); - t.end(); -}); - -test('!has', (t) => { - const f = filter(['!has', 'foo']); - t.equal(f({zoom: 0}, {properties: {foo: 0}}), false); - t.equal(f({zoom: 0}, {properties: {foo: 1}}), false); - t.equal(f({zoom: 0}, {properties: {foo: '0'}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: false}}), false); - t.equal(f({zoom: 0}, {properties: {foo: null}}), false); - t.equal(f({zoom: 0}, {properties: {foo: undefined}}), false); - t.equal(f({zoom: 0}, {properties: {}}), true); - t.end(); -}); +}; + +test('filter - eval supported', filterTests(true)); +test('filter - eval not supported', filterTests(false));