Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Adds support for running Mapbox-gl-js expressions tests against the Objective-C expressions interface #11435

Closed
wants to merge 10 commits into from
192 changes: 192 additions & 0 deletions platform/darwin/test/MGLExpressionJSTests.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#import <XCTest/XCTest.h>

#import "MGLTypes.h"
#import "NSExpression+MGLPrivateAdditions.h"

@interface MGLExpressionJSTests : XCTestCase

@end

@implementation MGLExpressionJSTests

NSString *testRootPath = @"expression-tests";

NSSet *testsToSkip = [NSSet setWithObjects:
/* crashers */
@"to-boolean", // EXC_BAD_ACCESS (code=1, address=0x1)
@"concat/arity-1", // EXC_BAD_ACCESS (code=1, address=0x1)
@"plus/arity-1", // EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
@"minus/arity-0", // EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
@"minus/arity-1", // EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
@"times/arity-1", // EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

/* not yet implemented */
@"/log2",
@"/geometry-type",
@"/pow",
@"/not_equal",
@"/not",
@"/less",
@"/greater",
@"/array",
@"/match",
@"/less_or_equal",
@"/to-rgba",
@"/rgba",
@"/rgb",
@"/sin",
@"/acos",
@"/cos",
@"/step",
@"/all",
@"/to-color",
@"/number",
@"/to-number",
@"/any",
@"/object",
@"/boolean",
@"/coalesce",
@"/tan",
@"/atan",
@"/typeof",
@"/equal",
@"/has",
@"/string",
@"/ln2",
@"/interpolate", // Interpolation expressions lack underlying Objective-C implementations.
@"/asin",
@"/max",
@"/properties",
@"/id",
@"/at",
@"is-supported-script/default", //Mapbox GL function expressions lack underlying Objective-C implementations."
@"collator/accent-lt-en",
@"collator/base-default-locale",
@"collator/accent-not-equals-en",
@"collator/variant-gteq-en",
@"collator/case-not-equals-en",
@"collator/case-omitted-en",
@"collator/diacritic-omitted-en",
@"collator/base-gt-en",
@"collator/accent-equals-de",
@"collator/variant-equals-en",
@"collator/case-lteq-en",
@"collator/base-equals-en",

/* unmet dependencies */
@"get/from-literal", // number
@"get/basic", // number
@"get/from-literal--missing", // number
@"get/from-object-property", // number
@"to-string/color", // rgba
@"let/property-function", // number
@"min/arity-0", // all
@"at/basic", // number
@"case/precedence", // boolean
@"constant-folding/evaluation-error", // interpolation
@"zoom/nested-coalesce", // coalesce
@"zoom/invalid-nested-4", // coalesce
@"case/infer-array-type", // boolean

/* failures */
@"concat/basic", // ((expressionValue) equal to (expectedValue)) failed: ("ab") is not equal to ("abc")
@"concat/arity-0", // *** -[NSArray subarrayWithRange:]: range {1, 18446744073709551615} extends beyond bounds for empty array
@"constant-folding/var", // Can't get value for 'a' in bindings { }.
@"heatmap-density/basic", // Can't get value for 'heatmapDensity' in bindings { }.
@"literal/nested-array", // -[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance 0xb000000000000033
@"literal/multiple-args", // ((expressionValue) equal to (expectedValue)) failed: ("{ }") is not equal to ("(null)")
@"times/arity-0", // *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
@"parse/empty", // *** -[NSArray subarrayWithRange:]: range {1, 18446744073709551615} extends beyond bounds for empty array
@"parse/non-string", // -[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance 0xb000000000000013
@"pi/basic", // ((expressionValue) equal to (expectedValue)) failed: ("3.141592653589793") is not equal to ("3.14159")
@"e/basic", // ((expressionValue) equal to (expectedValue)) failed: ("2.718281828459045") is not equal to ("2.71828")
@"to-string/basic", // ((expressionValue) equal to (expectedValue)) failed: ("0") is not equal to ("false")
@"let/shadow", // Can't get value for 'a' in bindings { }
@"let/basic", // Can't get value for 'a' in bindings { }
@"let/zoom", // Can't get value for 'zoomLevel' in bindings { }
@"let/nested", // Can't get value for 'a' in bindings { }
@"case/basic", // Unrecognized expression conditional operator get.
@"zoom/invalid-no-curve", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/nested-let", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/basic", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/invalid-nested-1", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/invalid-nested-2", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/invalid-nested-3", // Can't get value for 'zoomLevel' in bindings { }
@"zoom/invalid-nested-5", // *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1]
@"plus/arity-0", // *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
@"plus/arity-1", // *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]

/* intentional failures */
@"length/invalid", // Invoked count with non-collection parameter.
@"length/implicit", // Invoked count with non-collection parameter.
@"parse/unknown-expression", // Expression operator FAKE-EXPRESSION not yet implemented.

nil
];

- (void)testAllJavascriptTests {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *rootURL = [[bundle bundleURL] URLByAppendingPathComponent:testRootPath];

NSFileManager *fileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:rootURL
includingPropertiesForKeys:@[NSURLIsDirectoryKey]
options:0
errorHandler:nil];
for (NSURL *fileURL in enumerator) {
if ([[fileURL lastPathComponent] isEqual:@"test.json"] && ![self shouldSkipTest:fileURL] ) {
NSData *data = [NSData dataWithContentsOfURL:fileURL];
NSError *error = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSString *testName = [self testNameFromURL:fileURL];

if (!jsonDict && error) {
XCTFail(@"JSON parse failed with error: %@", error.localizedDescription);
continue;
}

try {
NSLog(@"Running %@", testName);
[self runTestsNamed:testName withDictionary:jsonDict];
} catch (NSException *e) {
XCTFail(@"%@: %@", testName, e.reason);
continue;
}
}
}
}

- (BOOL)shouldSkipTest:(NSURL *)fileURL {
NSPredicate *blacklistPredicate = [NSPredicate predicateWithBlock:^BOOL( NSString * _Nullable evaluatedString, NSDictionary<NSString *,id> * _Nullable bindings) {
return [fileURL.absoluteString containsString:evaluatedString];
}];

BOOL shouldSkip = [testsToSkip filteredSetUsingPredicate:blacklistPredicate].count > 0;

if (shouldSkip) {
NSLog(@"Skipping blacklisted test: %@", [self testNameFromURL:fileURL]);
}

return shouldSkip;
}

- (void)runTestsNamed:(NSString *)testName withDictionary:(NSDictionary *)testcase {
NSArray *inputs = testcase[@"inputs"];

NSExpression *exp = [NSExpression expressionWithMGLJSONObject:testcase[@"expression"]];

for (int i = 0; i < inputs.count; i++) {
NSArray *input = inputs[i];
NSDictionary *actualInput = [input[1] objectForKey:@"properties"];

id expressionValue = [exp expressionValueWithObject:actualInput context:[NSMutableDictionary dictionary]];
id expectedValue = testcase[@"expected"][@"outputs"][i];
XCTAssertEqualObjects(expressionValue, expectedValue, @"in %@", testName);
}
}

- (NSString *)testNameFromURL:(NSURL *)testURL {
return [testURL.absoluteString componentsSeparatedByString:testRootPath].lastObject;
}

@end
24 changes: 23 additions & 1 deletion platform/ios/ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */; };
07D947521F67488800E37934 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
07D947531F67488E00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */; };
07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; };
160D82772054A99800D278D6 /* expression-tests in Resources */ = {isa = PBXBuildFile; fileRef = 160D82762054A99800D278D6 /* expression-tests */; };
07D947531F67488E00E37934 /* MGLComputedShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLComputedShapeSource_Private.h */; };
16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */; };
16376B331FFDB4B40000563E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B321FFDB4B40000563E /* AppDelegate.m */; };
Expand Down Expand Up @@ -307,6 +311,7 @@
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
9652981C2052EB2000062D73 /* MGLExpressionJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9652981B2052EB2000062D73 /* MGLExpressionJSTests.mm */; };
9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; };
9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */; };
Expand Down Expand Up @@ -735,6 +740,10 @@
0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLComputedShapeSourceTests.m; path = ../../darwin/test/MGLComputedShapeSourceTests.m; sourceTree = "<group>"; };
07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; };
07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; };
07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
160D82762054A99800D278D6 /* expression-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "expression-tests"; path = "../../../mapbox-gl-js/test/integration/expression-tests"; sourceTree = "<group>"; };
07D9474E1F67487E00E37934 /* MGLComputedShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource_Private.h; sourceTree = "<group>"; };
16376B071FFD9DAF0000563E /* integration.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = integration.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBGLIntegrationTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -967,6 +976,7 @@
960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = "<group>"; };
9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MGLSDKUpdateChecker.mm; sourceTree = "<group>"; };
9652981B2052EB2000062D73 /* MGLExpressionJSTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionJSTests.mm; path = ../../darwin/test/MGLExpressionJSTests.mm; sourceTree = "<group>"; };
9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline_Private.h; sourceTree = "<group>"; };
9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon_Private.h; sourceTree = "<group>"; };
9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewScaleBarTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1340,6 +1350,16 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
160D7E992054A94100D278D6 /* Expressions */ = {
isa = PBXGroup;
children = (
DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */,
9652981B2052EB2000062D73 /* MGLExpressionJSTests.mm */,
160D82762054A99800D278D6 /* expression-tests */,
);
name = Expressions;
sourceTree = "<group>";
};
16376B081FFD9DAF0000563E /* Integration Tests */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1798,6 +1818,7 @@
DA2E88521CC036F400F24E7B /* SDK Tests */ = {
isa = PBXGroup;
children = (
160D7E992054A94100D278D6 /* Expressions */,
4031ACFD1E9FD26900A3EA26 /* Test Helpers */,
409F43FB1E9E77D10048729D /* Swift Integration */,
357579811D502AD4000B822E /* Styling */,
Expand All @@ -1810,7 +1831,6 @@
3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */,
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */,
DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */,
DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */,
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */,
Expand Down Expand Up @@ -2745,6 +2765,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
160D82772054A99800D278D6 /* expression-tests in Resources */,
DA2784FC1DF02FF4001D5B8D /* Media.xcassets in Resources */,
353BAEF71D646370009A8DA9 /* amsterdam.geojson in Resources */,
DA35D0881E1A6309007DED41 /* one-liner.json in Resources */,
Expand Down Expand Up @@ -2837,6 +2858,7 @@
DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */,
DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */,
170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */,
9652981C2052EB2000062D73 /* MGLExpressionJSTests.mm in Sources */,
170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */,
357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */,
35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */,
Expand Down