From 8a3deab73373c8f3182e4337a3619ebcc58ecc2c Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 8 Mar 2018 15:48:08 -0800 Subject: [core] Add expression filter support (#11251) * WIP * WIP * WIP * Remove Filter::operator()(const Feature&) * WIP * WIP * WIP * WIP * Hook up expression filter evaluator * Replace `shared_ptr` with &reference * Fill in implementation of `void operator()(const ExpressionFilter&)` * Fix failing tests * Switch back to a shared_ptr per chat with @anandthakker * Fix benchmark compilation * Shot in the dark to fix CI * Shot in the dark to fix CI (part 2) * Shot in the dark to fix CI (part 3) * In src/mbgl/style/conversion/filter.cpp, add a port of isExpressionFilter and use it to decide in Converter::operator() whether to parse the incoming JSON as an ExpressionFilter or one of the legacy filter types * Remove bool Filter::operator()(const GeometryTileFeature&) const * Ensure the map zoom is passed into filtering operations wherever applicable * Add expression filter tests * Addressed PR feedback * Implement `NSPredicate *operator()(mbgl::style::ExpressionFilter filter)` * Fix formatting& nit --- test/style/filter.test.cpp | 140 +++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 76 deletions(-) (limited to 'test') diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 73f8e7626d..6f261c43ec 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -10,114 +11,101 @@ using namespace mbgl; using namespace mbgl::style; -Filter parse(const char * expression) { +bool filter(const char * json, const PropertyMap& featureProperties = {{}}, optional featureId = {}, FeatureType featureType = FeatureType::Point, GeometryCollection featureGeometry = {}) { conversion::Error error; - optional filter = conversion::convertJSON(expression, error); + optional filter = conversion::convertJSON(json, error); EXPECT_TRUE(bool(filter)); - return *filter; -} - -Feature feature(const PropertyMap& properties, const Geometry& geometry = Point()) { - Feature result { geometry }; - result.properties = properties; - return result; + EXPECT_EQ(error.message, ""); + + StubGeometryTileFeature feature { featureId, featureType, featureGeometry, featureProperties }; + expression::EvaluationContext context = { &feature }; + + return (*filter)(context); } TEST(Filter, EqualsString) { - Filter f = parse(R"(["==", "foo", "bar"])"); - ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }}))); + auto f = R"(["==", "foo", "bar"])"; + ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("baz") }})); } TEST(Filter, EqualsNumber) { - Filter f = parse(R"(["==", "foo", 0])"); - ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", double(0) }}))); - ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", double(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("0") }}))); - ASSERT_FALSE(f(feature({{ "foo", false }}))); - ASSERT_FALSE(f(feature({{ "foo", true }}))); - ASSERT_FALSE(f(feature({{ "foo", mapbox::geometry::null_value }}))); - ASSERT_FALSE(f(feature({{}}))); + auto f = R"(["==", "foo", 0])"; + ASSERT_TRUE(filter(f, {{ "foo", int64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", uint64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", double(0) }})); + ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", uint64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", double(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }})); + ASSERT_FALSE(filter(f, {{ "foo", false }})); + ASSERT_FALSE(filter(f, {{ "foo", true }})); + ASSERT_FALSE(filter(f, {{ "foo", mapbox::geometry::null_value }})); + ASSERT_FALSE(filter(f, {{}})); } TEST(Filter, EqualsType) { - Filter f = parse(R"(["==", "$type", "LineString"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); + auto f = R"(["==", "$type", "LineString"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {})); } TEST(Filter, InType) { - Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); - ASSERT_TRUE(f(feature({{}}, Polygon()))); + auto f = R"(["in", "$type", "LineString", "Polygon"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon)); } TEST(Filter, Any) { - ASSERT_FALSE(parse("[\"any\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_FALSE(filter("[\"any\"]")); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"any\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, All) { - ASSERT_TRUE(parse("[\"all\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"all\"]", {{}})); + ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, None) { - ASSERT_TRUE(parse("[\"none\"]")(feature({{}}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"none\"]")); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"none\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, Has) { - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_FALSE(parse("[\"has\", \"foo\"]")( - feature({{}}))); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }})); + ASSERT_FALSE(filter("[\"has\", \"foo\"]")); } TEST(Filter, NotHas) { - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_TRUE(parse("[\"!has\", \"foo\"]")( - feature({{}}))); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), false }})); + ASSERT_TRUE(filter("[\"!has\", \"foo\"]")); } TEST(Filter, ID) { - Feature feature1 { Point() }; - feature1.id = { uint64_t(1234) }; - - ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); - ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); + FeatureIdentifier id1 { uint64_t{ 1234 } }; + ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1)); + ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1)); + + ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); +} - Feature feature2 { Point() }; - feature2.properties["id"] = { uint64_t(1234) }; +TEST(Filter, Expression) { + ASSERT_TRUE(filter("[\"==\", [\"+\", 1, 1], 2]")); + ASSERT_FALSE(filter("[\"==\", [\"+\", 1, 1], 4]")); +} - ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2)); +TEST(Filter, PropertyExpression) { + ASSERT_TRUE(filter("[\"==\", [\"get\", \"two\"], 2]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}})); } -- cgit v1.2.1