[ios, macos] NSPredicate expression filters. (#11587)
* [ios, macos] Add NSAndPredicateType predicate expression filter. * [ios, macos] Add type to NSPredicate expressions. * [ios, macos] Add Expression filter support. * [ios, macos] Update NSPredicate expression based tests. * [ios, macos] Refactor ExpressionFilters. * [ios, macos] Add symmetric test to ExpressionFilters. * [ios, macos] Update NSPredicate test to ExpressionFilters. * [ios, macos] Re-introduce Filter tests. * [ios, macos] Remove typed NSComparisonPredicate's comparable values. * [ios, macos] Update style layers predicate tests. * [ios, macos] Remove unused predicate conversion code. * [ios, macos] Update documentation exaple's test. * [ios, macos] Update Predicate and Expressions guide.. * [ios, macos] Remove mgl_ prefix from variable expressions. * [ios, macos] Update predicates and expressions documentation. * [ios, macos] Update changelogs.
This commit is contained in:
committed by
GitHub
parent
fe4348eec3
commit
62dd097328
@@ -31,6 +31,9 @@ The following comparison operators are supported:
|
||||
`NSNotEqualToPredicateOperatorType` | `key != value`<br />`key <> value`
|
||||
`NSBetweenPredicateOperatorType` | `key BETWEEN { 32, 212 }`
|
||||
|
||||
The `key` may be cast explicitly into the key's type. Example: `CAST(key, 'NSNumber')` or
|
||||
`CAST(key, 'NSString')`
|
||||
|
||||
The following compound operators are supported:
|
||||
|
||||
`NSCompoundPredicateType` | Format string syntax
|
||||
@@ -56,8 +59,11 @@ Format String Syntax” chapter of the
|
||||
_[Predicate Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)_
|
||||
in Apple developer documentation.
|
||||
|
||||
The predicate's left-hand expression must be a string that identifies a feature
|
||||
attribute or, alternatively, one of the following special attributes:
|
||||
Either the predicate’s left-hand or right-hand expression can be an `NSString`
|
||||
(to match strings) or `NSNumber` (to match numbers, including Boolean values)
|
||||
or an array of `NSString`s or `NSNumber`s, depending on the operator and the
|
||||
type of values expected for the attribute being tested or, alternatively,
|
||||
one of the following special attributes:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
@@ -101,12 +107,8 @@ attribute or, alternatively, one of the following special attributes:
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
The predicate’s right-hand expression must be an `NSString` (to match strings)
|
||||
or `NSNumber` (to match numbers, including Boolean values) or an array of
|
||||
`NSString`s or `NSNumber`s, depending on the operator and the type of values
|
||||
expected for the attribute being tested. For floating-point values, use
|
||||
`-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]`
|
||||
to avoid precision issues.
|
||||
For floating-point values, use `-[NSNumber numberWithDouble:]` instead of
|
||||
`-[NSNumber numberWithFloat:]` to avoid precision issues.
|
||||
|
||||
Automatic type casting is not performed. Therefore, a feature only matches this
|
||||
predicate if its value for the attribute in question is of the same type as the
|
||||
@@ -116,10 +118,10 @@ sensitivity) are unsupported for comparison and aggregate operators that are
|
||||
used in the predicate.
|
||||
|
||||
It is possible to create expressions that contain special characters in the
|
||||
predicate format syntax. This includes the `$` in the `$id` and `$type` special
|
||||
style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use
|
||||
`%K` in the format string to represent these variables:
|
||||
`@"%K == 'LineString'", @"$type"`.
|
||||
predicate format syntax. This includes the `$` in the `$featureIdentifier` and
|
||||
`$geometryType` special style attributes and also `hyphen-minus` and `tag:subtag`.
|
||||
However, you must use `%@` in the format string to represent these variables:
|
||||
`@"$geometryType == 'LineString'"`.
|
||||
|
||||
## Using expressions to configure layout and paint attributes
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ MGL_EXPORT
|
||||
```swift
|
||||
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
|
||||
layer.sourceLayerIdentifier = "contours"
|
||||
layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
|
||||
layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
|
||||
mapView.style?.addLayer(layer)
|
||||
```
|
||||
*/
|
||||
|
||||
@@ -1,231 +1,12 @@
|
||||
#import "NSComparisonPredicate+MGLAdditions.h"
|
||||
|
||||
#import "MGLStyleValue_Private.h"
|
||||
|
||||
#import "NSPredicate+MGLAdditions.h"
|
||||
#import "NSExpression+MGLPrivateAdditions.h"
|
||||
|
||||
@implementation NSComparisonPredicate (MGLAdditions)
|
||||
|
||||
- (mbgl::style::Filter)mgl_filter {
|
||||
NSExpression *leftExpression = self.leftExpression;
|
||||
NSExpression *rightExpression = self.rightExpression;
|
||||
NSExpressionType leftType = leftExpression.expressionType;
|
||||
NSExpressionType rightType = rightExpression.expressionType;
|
||||
BOOL isReversed = ((leftType == NSConstantValueExpressionType || leftType == NSAggregateExpressionType)
|
||||
&& rightType == NSKeyPathExpressionType);
|
||||
switch (self.predicateOperatorType) {
|
||||
case NSEqualToPredicateOperatorType: {
|
||||
mbgl::style::EqualsFilter eqFilter;
|
||||
eqFilter.key = self.mgl_keyPath.UTF8String;
|
||||
eqFilter.value = self.mgl_constantValue;
|
||||
|
||||
// Convert $type == to TypeEqualsFilter.
|
||||
if (eqFilter.key == "$type") {
|
||||
mbgl::style::TypeEqualsFilter typeEqFilter;
|
||||
typeEqFilter.value = self.mgl_featureType;
|
||||
return typeEqFilter;
|
||||
}
|
||||
|
||||
// Convert $id == to IdentifierEqualsFilter.
|
||||
if (eqFilter.key == "$id") {
|
||||
// Convert $id == nil to NotHasIdentifierFilter.
|
||||
if (eqFilter.value.is<mbgl::NullValue>()) {
|
||||
return mbgl::style::NotHasIdentifierFilter();
|
||||
}
|
||||
|
||||
mbgl::style::IdentifierEqualsFilter idEqFilter;
|
||||
idEqFilter.value = self.mgl_featureIdentifier;
|
||||
return idEqFilter;
|
||||
}
|
||||
|
||||
// Convert == nil to NotHasFilter.
|
||||
if (eqFilter.value.is<mbgl::NullValue>()) {
|
||||
mbgl::style::NotHasFilter notHasFilter;
|
||||
notHasFilter.key = eqFilter.key;
|
||||
return notHasFilter;
|
||||
}
|
||||
|
||||
return eqFilter;
|
||||
}
|
||||
case NSNotEqualToPredicateOperatorType: {
|
||||
mbgl::style::NotEqualsFilter neFilter;
|
||||
neFilter.key = self.mgl_keyPath.UTF8String;
|
||||
neFilter.value = self.mgl_constantValue;
|
||||
|
||||
// Convert $type != to TypeNotEqualsFilter.
|
||||
if (neFilter.key == "$type") {
|
||||
mbgl::style::TypeNotEqualsFilter typeNeFilter;
|
||||
typeNeFilter.value = self.mgl_featureType;
|
||||
return typeNeFilter;
|
||||
}
|
||||
|
||||
// Convert $id != to IdentifierNotEqualsFilter.
|
||||
if (neFilter.key == "$id") {
|
||||
// Convert $id != nil to HasIdentifierFilter.
|
||||
if (neFilter.value.is<mbgl::NullValue>()) {
|
||||
return mbgl::style::HasIdentifierFilter();
|
||||
}
|
||||
|
||||
mbgl::style::IdentifierNotEqualsFilter idNeFilter;
|
||||
idNeFilter.value = self.mgl_featureIdentifier;
|
||||
return idNeFilter;
|
||||
}
|
||||
|
||||
// Convert != nil to HasFilter.
|
||||
if (neFilter.value.is<mbgl::NullValue>()) {
|
||||
mbgl::style::HasFilter hasFilter;
|
||||
hasFilter.key = neFilter.key;
|
||||
return hasFilter;
|
||||
}
|
||||
|
||||
return neFilter;
|
||||
}
|
||||
case NSGreaterThanPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
mbgl::style::LessThanFilter ltFilter;
|
||||
ltFilter.key = self.mgl_keyPath.UTF8String;
|
||||
ltFilter.value = self.mgl_constantValue;
|
||||
return ltFilter;
|
||||
} else {
|
||||
mbgl::style::GreaterThanFilter gtFilter;
|
||||
gtFilter.key = self.mgl_keyPath.UTF8String;
|
||||
gtFilter.value = self.mgl_constantValue;
|
||||
return gtFilter;
|
||||
}
|
||||
}
|
||||
case NSGreaterThanOrEqualToPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
mbgl::style::LessThanEqualsFilter lteFilter;
|
||||
lteFilter.key = self.mgl_keyPath.UTF8String;
|
||||
lteFilter.value = self.mgl_constantValue;
|
||||
return lteFilter;
|
||||
} else {
|
||||
mbgl::style::GreaterThanEqualsFilter gteFilter;
|
||||
gteFilter.key = self.mgl_keyPath.UTF8String;
|
||||
gteFilter.value = self.mgl_constantValue;
|
||||
return gteFilter;
|
||||
}
|
||||
}
|
||||
case NSLessThanPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
mbgl::style::GreaterThanFilter gtFilter;
|
||||
gtFilter.key = self.mgl_keyPath.UTF8String;
|
||||
gtFilter.value = self.mgl_constantValue;
|
||||
return gtFilter;
|
||||
} else {
|
||||
mbgl::style::LessThanFilter ltFilter;
|
||||
ltFilter.key = self.mgl_keyPath.UTF8String;
|
||||
ltFilter.value = self.mgl_constantValue;
|
||||
return ltFilter;
|
||||
}
|
||||
}
|
||||
case NSLessThanOrEqualToPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
mbgl::style::GreaterThanEqualsFilter gteFilter;
|
||||
gteFilter.key = self.mgl_keyPath.UTF8String;
|
||||
gteFilter.value = self.mgl_constantValue;
|
||||
return gteFilter;
|
||||
} else {
|
||||
mbgl::style::LessThanEqualsFilter lteFilter;
|
||||
lteFilter.key = self.mgl_keyPath.UTF8String;
|
||||
lteFilter.value = self.mgl_constantValue;
|
||||
return lteFilter;
|
||||
}
|
||||
}
|
||||
case NSInPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
if (leftType == NSConstantValueExpressionType && [leftExpression.constantValue isKindOfClass:[NSString class]]) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"CONTAINS not supported for string comparison."];
|
||||
}
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Predicate cannot compare values IN attribute."];
|
||||
}
|
||||
|
||||
// Convert $type IN to TypeInFilter.
|
||||
if ([leftExpression.keyPath isEqualToString:@"$type"]) {
|
||||
mbgl::style::TypeInFilter typeInFilter;
|
||||
typeInFilter.values = rightExpression.mgl_aggregateFeatureType;
|
||||
return typeInFilter;
|
||||
}
|
||||
|
||||
// Convert $id IN to IdentifierInFilter.
|
||||
if ([leftExpression.keyPath isEqualToString:@"$id"]) {
|
||||
mbgl::style::IdentifierInFilter idInFilter;
|
||||
idInFilter.values = rightExpression.mgl_aggregateFeatureIdentifier;
|
||||
return idInFilter;
|
||||
}
|
||||
|
||||
mbgl::style::InFilter inFilter;
|
||||
inFilter.key = leftExpression.keyPath.UTF8String;
|
||||
inFilter.values = rightExpression.mgl_aggregateMBGLValue;
|
||||
return inFilter;
|
||||
}
|
||||
case NSContainsPredicateOperatorType: {
|
||||
if (!isReversed) {
|
||||
if (rightType == NSConstantValueExpressionType && [rightExpression.constantValue isKindOfClass:[NSString class]]) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"IN not supported for string comparison."];
|
||||
}
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Predicate cannot compare attribute CONTAINS values."];
|
||||
}
|
||||
|
||||
// Convert CONTAINS $type to TypeInFilter.
|
||||
if ([rightExpression.keyPath isEqualToString:@"$type"]) {
|
||||
mbgl::style::TypeInFilter typeInFilter;
|
||||
typeInFilter.values = leftExpression.mgl_aggregateFeatureType;
|
||||
return typeInFilter;
|
||||
}
|
||||
|
||||
// Convert CONTAINS $id to IdentifierInFilter.
|
||||
if ([rightExpression.keyPath isEqualToString:@"$id"]) {
|
||||
mbgl::style::IdentifierInFilter idInFilter;
|
||||
idInFilter.values = leftExpression.mgl_aggregateFeatureIdentifier;
|
||||
return idInFilter;
|
||||
}
|
||||
|
||||
mbgl::style::InFilter inFilter;
|
||||
inFilter.key = rightExpression.keyPath.UTF8String;
|
||||
inFilter.values = leftExpression.mgl_aggregateMBGLValue;
|
||||
return inFilter;
|
||||
}
|
||||
case NSBetweenPredicateOperatorType: {
|
||||
if (isReversed) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Predicate cannot compare bounds BETWEEN attribute."];
|
||||
}
|
||||
if (![rightExpression.constantValue isKindOfClass:[NSArray class]]) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Right side of BETWEEN predicate must be an array."]; // not NSSet
|
||||
}
|
||||
auto values = rightExpression.mgl_aggregateMBGLValue;
|
||||
if (values.size() != 2) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Right side of BETWEEN predicate must have two items."];
|
||||
}
|
||||
mbgl::style::AllFilter allFilter;
|
||||
mbgl::style::GreaterThanEqualsFilter gteFilter;
|
||||
gteFilter.key = leftExpression.keyPath.UTF8String;
|
||||
gteFilter.value = values[0];
|
||||
allFilter.filters.push_back(gteFilter);
|
||||
mbgl::style::LessThanEqualsFilter lteFilter;
|
||||
lteFilter.key = leftExpression.keyPath.UTF8String;
|
||||
lteFilter.value = values[1];
|
||||
allFilter.filters.push_back(lteFilter);
|
||||
return allFilter;
|
||||
}
|
||||
case NSMatchesPredicateOperatorType:
|
||||
case NSLikePredicateOperatorType:
|
||||
case NSBeginsWithPredicateOperatorType:
|
||||
case NSEndsWithPredicateOperatorType:
|
||||
case NSCustomSelectorPredicateOperatorType:
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
- (NSString *)mgl_keyPath {
|
||||
NSExpression *leftExpression = self.leftExpression;
|
||||
NSExpression *rightExpression = self.rightExpression;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#import "NSCompoundPredicate+MGLAdditions.h"
|
||||
|
||||
#import "MGLStyleValue_Private.h"
|
||||
|
||||
#import "NSPredicate+MGLAdditions.h"
|
||||
#import "NSExpression+MGLPrivateAdditions.h"
|
||||
|
||||
#include <mbgl/style/conversion/property_value.hpp>
|
||||
|
||||
@implementation NSCompoundPredicate (MGLAdditions)
|
||||
|
||||
- (std::vector<mbgl::style::Filter>)mgl_subfilters
|
||||
@@ -14,62 +18,6 @@
|
||||
return filters;
|
||||
}
|
||||
|
||||
- (mbgl::style::Filter)mgl_filter
|
||||
{
|
||||
switch (self.compoundPredicateType) {
|
||||
case NSNotPredicateType: {
|
||||
NSAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates.");
|
||||
NSPredicate *subpredicate = self.subpredicates.firstObject;
|
||||
mbgl::style::Filter subfilter = subpredicate.mgl_filter;
|
||||
|
||||
// Convert NOT(!= nil) to NotHasFilter.
|
||||
if (subfilter.is<mbgl::style::HasFilter>()) {
|
||||
auto hasFilter = subfilter.get<mbgl::style::HasFilter>();
|
||||
return mbgl::style::NotHasFilter { .key = hasFilter.key };
|
||||
}
|
||||
|
||||
// Convert NOT(== nil) to HasFilter.
|
||||
if (subfilter.is<mbgl::style::NotHasFilter>()) {
|
||||
auto hasFilter = subfilter.get<mbgl::style::NotHasFilter>();
|
||||
return mbgl::style::HasFilter { .key = hasFilter.key };
|
||||
}
|
||||
|
||||
// Convert NOT(IN) or NOT(CONTAINS) to NotInFilter.
|
||||
if (subfilter.is<mbgl::style::InFilter>()) {
|
||||
auto inFilter = subfilter.get<mbgl::style::InFilter>();
|
||||
mbgl::style::NotInFilter notInFilter;
|
||||
notInFilter.key = inFilter.key;
|
||||
notInFilter.values = inFilter.values;
|
||||
return notInFilter;
|
||||
}
|
||||
|
||||
// Convert NOT(), NOT(AND), NOT(NOT), NOT(==), etc. into NoneFilter.
|
||||
mbgl::style::NoneFilter noneFilter;
|
||||
if (subfilter.is<mbgl::style::AnyFilter>()) {
|
||||
// Flatten NOT(OR).
|
||||
noneFilter.filters = subfilter.get<mbgl::style::AnyFilter>().filters;
|
||||
} else if (subpredicate) {
|
||||
noneFilter.filters = { subfilter };
|
||||
}
|
||||
return noneFilter;
|
||||
}
|
||||
case NSAndPredicateType: {
|
||||
mbgl::style::AllFilter filter;
|
||||
filter.filters = self.mgl_subfilters;
|
||||
return filter;
|
||||
}
|
||||
case NSOrPredicateType: {
|
||||
mbgl::style::AnyFilter filter;
|
||||
filter.filters = self.mgl_subfilters;
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
[NSException raise:@"Compound predicate type not handled"
|
||||
format:@""];
|
||||
return {};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSCompoundPredicate (MGLExpressionAdditions)
|
||||
|
||||
@@ -562,7 +562,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
|
||||
argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
|
||||
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
|
||||
return [NSExpression expressionForFunction:operand selectorName:@"mgl_numberWithFallbackValues:" arguments:subexpressions];
|
||||
} else if ([op isEqualToString:@"to-string"]) {
|
||||
} else if ([op isEqualToString:@"to-string"] || [op isEqualToString:@"string"]) {
|
||||
NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
|
||||
return [NSExpression expressionWithFormat:@"CAST(%@, 'NSString')", operand];
|
||||
} else if ([op isEqualToString:@"get"]) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#import "MGLValueEvaluator.h"
|
||||
#import "MGLStyleValue_Private.h"
|
||||
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
|
||||
class FilterEvaluator {
|
||||
public:
|
||||
|
||||
@@ -206,25 +208,18 @@ public:
|
||||
|
||||
- (mbgl::style::Filter)mgl_filter
|
||||
{
|
||||
if ([self isEqual:[NSPredicate predicateWithValue:YES]])
|
||||
{
|
||||
return mbgl::style::AllFilter();
|
||||
}
|
||||
|
||||
if ([self isEqual:[NSPredicate predicateWithValue:NO]])
|
||||
{
|
||||
return mbgl::style::AnyFilter();
|
||||
}
|
||||
|
||||
if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("])
|
||||
{
|
||||
mbgl::style::conversion::Error valueError;
|
||||
NSArray *jsonObject = self.mgl_jsonExpressionObject;
|
||||
auto value = mbgl::style::conversion::convert<mbgl::style::Filter>(mbgl::style::conversion::makeConvertible(jsonObject), valueError);
|
||||
|
||||
if (!value) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Block-based predicates are not supported."];
|
||||
format:@"Invalid filter value: %@", @(valueError.message.c_str())];
|
||||
return {};
|
||||
}
|
||||
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Unrecognized predicate type."];
|
||||
return {};
|
||||
mbgl::style::Filter filter = std::move(*value);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
|
||||
//#-example-code
|
||||
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
|
||||
layer.sourceLayerIdentifier = "contours"
|
||||
layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
|
||||
layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
|
||||
mapView.style?.addLayer(layer)
|
||||
//#-end-example-code
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -23,295 +23,9 @@ namespace mbgl {
|
||||
|
||||
@implementation MGLPredicateTests
|
||||
|
||||
- (void)testFilterization {
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithValue:YES].mgl_filter;
|
||||
mbgl::style::AllFilter expected;
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithValue:NO].mgl_filter;
|
||||
mbgl::style::AnyFilter expected;
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a = 'b'"].mgl_filter;
|
||||
mbgl::style::EqualsFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"].mgl_filter;
|
||||
mbgl::style::TypeEqualsFilter expected = { .value = mbgl::FeatureType::Point };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"].mgl_filter;
|
||||
mbgl::style::IdentifierEqualsFilter expected = { .value = UINT64_C(67086180) };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"].mgl_filter;
|
||||
mbgl::style::NotHasIdentifierFilter expected;
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a = nil"].mgl_filter;
|
||||
mbgl::style::NotHasFilter expected = { .key = "a" };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"].mgl_filter;
|
||||
mbgl::style::TypeNotEqualsFilter expected = { .value = mbgl::FeatureType::Point };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"].mgl_filter;
|
||||
mbgl::style::IdentifierNotEqualsFilter expected = { .value = UINT64_C(67086180) };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"].mgl_filter;
|
||||
mbgl::style::HasIdentifierFilter expected;
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a != 'b'"].mgl_filter;
|
||||
mbgl::style::NotEqualsFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a != nil"].mgl_filter;
|
||||
mbgl::style::HasFilter expected = { .key = "a" };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a < 'b'"].mgl_filter;
|
||||
mbgl::style::LessThanFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a <= 'b'"].mgl_filter;
|
||||
mbgl::style::LessThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a > 'b'"].mgl_filter;
|
||||
mbgl::style::GreaterThanFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a >= 'b'"].mgl_filter;
|
||||
mbgl::style::GreaterThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"].mgl_filter;
|
||||
mbgl::style::AllFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@"b", @"z"]].mgl_filter;
|
||||
mbgl::style::AllFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].mgl_filter;
|
||||
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a IN %@", @[@"b", @"c"]].mgl_filter;
|
||||
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"].mgl_filter;
|
||||
mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", @[@"LineString", @"Polygon"]].mgl_filter;
|
||||
mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"].mgl_filter;
|
||||
mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", @[@67086180, @3709678893, @3352016856, @4189833989]].mgl_filter;
|
||||
mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"'Mapbox' IN a"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"{'b', 'c'} CONTAINS a"].mgl_filter;
|
||||
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
|
||||
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@"LineString", @"Polygon"], @"$type"].mgl_filter;
|
||||
mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"{67086180, 3709678893, 3352016856, 4189833989} CONTAINS %K", @"$id"].mgl_filter;
|
||||
mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@67086180, @3709678893, @3352016856, @4189833989], @"$id"].mgl_filter;
|
||||
mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a CONTAINS 'Mapbox'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"].mgl_filter;
|
||||
mbgl::style::AllFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"].mgl_filter;
|
||||
mbgl::style::AnyFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"].mgl_filter;
|
||||
mbgl::style::NoneFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::AllFilter {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"].mgl_filter;
|
||||
mbgl::style::NoneFilter expected = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT a == nil"].mgl_filter;
|
||||
mbgl::style::HasFilter expected = { .key = "a" };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT a != nil"].mgl_filter;
|
||||
mbgl::style::NotHasFilter expected = { .key = "a" };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].mgl_filter;
|
||||
mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT a IN %@", @[@"b", @"c"]].mgl_filter;
|
||||
mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT {'b', 'c'} CONTAINS a"].mgl_filter;
|
||||
mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto actual = [NSPredicate predicateWithFormat:@"NOT %@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
|
||||
mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
MGLAssertEqualFilters(actual, expected);
|
||||
}
|
||||
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
|
||||
XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
|
||||
XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
|
||||
return NO;
|
||||
}].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
}
|
||||
|
||||
- (void)testPredication {
|
||||
XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]);
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
|
||||
@@ -351,12 +65,12 @@ namespace mbgl {
|
||||
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown };
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::NotHasFilter filter = { .key = "a" };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
|
||||
@@ -379,32 +93,32 @@ namespace mbgl {
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::HasFilter filter = { .key = "a" };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
@@ -414,7 +128,7 @@ namespace mbgl {
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
@@ -424,7 +138,7 @@ namespace mbgl {
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
|
||||
@@ -441,7 +155,7 @@ namespace mbgl {
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
|
||||
@@ -458,12 +172,12 @@ namespace mbgl {
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
@@ -473,12 +187,12 @@ namespace mbgl {
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AnyFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::AnyFilter filter = {
|
||||
.filters = {
|
||||
@@ -488,12 +202,12 @@ namespace mbgl {
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::NoneFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
mbgl::style::NoneFilter filter = {
|
||||
.filters = {
|
||||
@@ -505,141 +219,339 @@ namespace mbgl {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testSymmetry {
|
||||
[self testSymmetryWithFormat:@"a = 1" reverseFormat:@"1 = a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithFormat:@"a != 1" reverseFormat:@"1 != a" mustRoundTrip:YES];
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = b"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 = 1"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
// In the predicate format language, $ is a special character denoting a
|
||||
// variable. Use %K to escape the special feature attribute $id.
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"$id == 670861802"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = $id"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
[self testSymmetryWithFormat:@"a = nil" reverseFormat:@"nil = a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithFormat:@"a != nil" reverseFormat:@"nil != a" mustRoundTrip:YES];
|
||||
|
||||
[self testSymmetryWithFormat:@"a < 1" reverseFormat:@"1 > a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithFormat:@"a <= 1" reverseFormat:@"1 >= a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithFormat:@"a > 1" reverseFormat:@"1 < a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithFormat:@"a >= 1" reverseFormat:@"1 <= a" mustRoundTrip:YES];
|
||||
|
||||
[self testSymmetryWithFormat:@"a BETWEEN {1, 2}" reverseFormat:@"1 <= a && 2 >= a" mustRoundTrip:YES];
|
||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@1, @2]]
|
||||
reversePredicate:[NSPredicate predicateWithFormat:@"1 <= a && 2 >= a"]
|
||||
mustRoundTrip:YES];
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"{1, 2} BETWEEN a"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
NSPredicate *betweenSetPredicate = [NSPredicate predicateWithFormat:@"a BETWEEN %@", [NSSet setWithObjects:@1, @2, nil]];
|
||||
XCTAssertThrowsSpecificNamed(betweenSetPredicate.mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1}"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1, 2, 3}"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
[self testSymmetryWithFormat:@"a IN {1, 2}" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
|
||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a IN %@", @[@1, @2]]
|
||||
reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
|
||||
mustRoundTrip:YES];
|
||||
|
||||
// The reverse formats here are a bit backwards because we canonicalize
|
||||
// a reverse CONTAINS to a forward IN.
|
||||
[self testSymmetryWithFormat:@"{1, 2} CONTAINS a" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
|
||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
|
||||
reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
|
||||
mustRoundTrip:NO];
|
||||
- (void)testUnsupportedFilterPredicates {
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
|
||||
XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
|
||||
XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
|
||||
return NO;
|
||||
}].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
}
|
||||
|
||||
- (void)testSymmetryWithFormat:(NSString *)forwardFormat reverseFormat:(NSString *)reverseFormat mustRoundTrip:(BOOL)mustRoundTrip {
|
||||
NSPredicate *forwardPredicate = [NSPredicate predicateWithFormat:forwardFormat];
|
||||
NSPredicate *reversePredicate = reverseFormat ? [NSPredicate predicateWithFormat:reverseFormat] : nil;
|
||||
[self testSymmetryWithPredicate:forwardPredicate reversePredicate:reversePredicate mustRoundTrip:mustRoundTrip];
|
||||
- (void)testComparisonPredicates {
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"];
|
||||
NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"];
|
||||
NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"];
|
||||
NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"];
|
||||
NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"];
|
||||
NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = 'b'"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType = 'Point'"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"geometry-type"], @"Point"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 67086180"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"id"], @67086180];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = nil"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"id"], [NSNull null]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = nil"];
|
||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], [NSNull null]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType != 'Point'"];
|
||||
NSArray *jsonExpression = @[@"!=", @[@"geometry-type"], @"Point"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != 67086180"];
|
||||
NSArray *jsonExpression = @[@"!=", @[@"id"], @67086180];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != nil"];
|
||||
NSArray *jsonExpression = @[@"!=", @[@"id"], [NSNull null]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != 'b'"];
|
||||
NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != nil"];
|
||||
NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], [NSNull null]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"];
|
||||
NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"];
|
||||
NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"];
|
||||
NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"];
|
||||
NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"];
|
||||
NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||
NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
|
||||
auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate reversePredicate:(NSPredicate *)reversePredicate mustRoundTrip:(BOOL)mustRoundTrip {
|
||||
- (void)testCompoundPredicates {
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
|
||||
NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"];
|
||||
NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"];
|
||||
NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"];
|
||||
NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
|
||||
[self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a == nil"];
|
||||
NSArray *jsonExpression = @[@"!", @[@"==", @[@"get", @"a"], [NSNull null]]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
{
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a != nil"];
|
||||
NSArray *jsonExpression = @[@"!", @[@"!=", @[@"get", @"a"], [NSNull null]]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||
[self testSymmetryWithPredicate:predicate
|
||||
mustRoundTrip:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate mustRoundTrip:(BOOL)mustRoundTrip {
|
||||
auto forwardFilter = forwardPredicate.mgl_filter;
|
||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||
if (mustRoundTrip) {
|
||||
// A collection of ints may turn into an aggregate of longs, for
|
||||
// example, so compare formats instead of the predicates themselves.
|
||||
XCTAssertEqualObjects(forwardPredicate.predicateFormat, forwardPredicateAfter.predicateFormat);
|
||||
}
|
||||
|
||||
if (reversePredicate) {
|
||||
auto reverseFilter = reversePredicate.mgl_filter;
|
||||
NSPredicate *reversePredicateAfter = [NSPredicate mgl_predicateWithFilter:reverseFilter];
|
||||
XCTAssertNotEqualObjects(reversePredicate, reversePredicateAfter);
|
||||
|
||||
XCTAssertEqualObjects(forwardPredicateAfter, reversePredicateAfter);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testComparisonExpressionArray {
|
||||
{
|
||||
NSArray *expected = @[@"==", @1, @2];
|
||||
XCTAssertEqualObjects([NSPredicate predicateWithFormat:@"1 = 2"].mgl_jsonExpressionObject, expected);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN {10, 100}"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@">=", @[@"get", @"x"], @10], @[@"<=", @[@"get", @"x"], @100]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@">=", @100, @[@"get", @"x"]]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@">=", @[@"get", @"x"], @10], @[@">=", @100, @[@"get", @"x"]]];
|
||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"all", @[@"==", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == x && x <= %@", [NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(x, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(x, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter);
|
||||
}
|
||||
{
|
||||
NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
|
||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter);
|
||||
} else {
|
||||
XCTAssertEqualObjects(forwardPredicate, forwardPredicateAfter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
XCTAssertNil(layer.sourceLayerIdentifier);
|
||||
|
||||
XCTAssertNil(layer.predicate);
|
||||
layer.predicate = [NSPredicate predicateWithValue:NO];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
|
||||
layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
|
||||
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
|
||||
layer.predicate = nil;
|
||||
XCTAssertNil(layer.predicate);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iO
|
||||
* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850))
|
||||
* Added `MGLShapeSourceOptionWrapsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be wrapped to accommodate coordinates with longitudes beyond −180 and 180; and `MGLShapeSourceOptionClipsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be clipped at the edge of each tile. ([#11041](https://github.com/mapbox/mapbox-gl-native/pull/11041))
|
||||
* Added support for Mapzen Terrarium DEM encoding. ([#11274](https://github.com/mapbox/mapbox-gl-native/pull/11274))
|
||||
* The layer filtering predicate's format strings now can contain arithmetic and calls to built-in `NSExpression` functions. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
|
||||
* The layer filtering predicate's key paths now may need to be cast to `NSString` or `NSNumber`. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
|
||||
|
||||
### Map rendering
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
* Fixed incorrect color calibration on macOS 10.13 High Sierra when using color-related methods of `MGLStyleLayer` subclasses, as well as when displaying an `MGLAttributionInfo`. It is no longer necessary to explicitly convert an `NSColor` to the sRGB color space before using these classes on High Sierra. ([#11391](https://github.com/mapbox/mapbox-gl-native/pull/11391))
|
||||
* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850))
|
||||
* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
|
||||
* The layer filtering predicate's format strings now can contain arithmetic and calls to built-in `NSExpression` functions. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
|
||||
* The layer filtering predicate's key paths now may need to be cast to `NSString` or `NSNumber`. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
|
||||
|
||||
### Map rendering
|
||||
|
||||
|
||||
Reference in New Issue
Block a user