From 1f4e3e0f6e1a568007f647102459e50cb98b63ca Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Thu, 26 Apr 2018 12:40:26 -0400 Subject: [ios, macos] Raise exceptions for empty expression stops dictionaries (#9539) --- platform/darwin/src/NSExpression+MGLAdditions.mm | 16 +++++- platform/darwin/test/MGLExpressionTests.mm | 10 ++++ .../MGLStyleLayerIntegrationTests.m | 61 ++++++++++++++++++++++ platform/ios/ios.xcodeproj/project.pbxproj | 4 ++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m (limited to 'platform') diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index c82a7dc008..8db839c5da 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -1258,9 +1258,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *controlPoints = [self.arguments[curveTypeIndex + 1].collection mgl_jsonExpressionObject]; [interpolationArray addObjectsFromArray:controlPoints]; } + + NSDictionary *stops = self.arguments[curveTypeIndex + 2].constantValue; + + if (stops.count == 0) { + [NSException raise:NSInvalidArgumentException format:@"‘stops‘ dictionary argument to ‘%@’ function must not be empty.", self.function]; + } + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolationArray, nil]; [expressionObject addObject:(isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject]; - NSDictionary *stops = self.arguments[curveTypeIndex + 2].constantValue; for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) { [expressionObject addObject:key]; [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]]; @@ -1272,8 +1278,14 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_step:from:stops:"]; NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0; id minimum = self.arguments[minimumIndex].mgl_jsonExpressionObject; - NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, minimum, nil]; NSDictionary *stops = self.arguments[minimumIndex + 1].constantValue; + + if (stops.count == 0) { + [NSException raise:NSInvalidArgumentException format:@"‘stops‘ dictionary argument to ‘%@’ function must not be empty.", self.function]; + } + + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, minimum, nil]; + for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) { [expressionObject addObject:key]; [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]]; diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index d54e961b00..9c75dedaa3 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -710,6 +710,16 @@ using namespace std::string_literals; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { + NSDictionary *stops = @{}; + NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, 'cubic-bezier', { 0.42, 0, 0.58, 1 }, %@)", stops]; + XCTAssertThrowsSpecificNamed(expression.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSDictionary *stops = @{}; + NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, 11, %@)", stops]; + XCTAssertThrowsSpecificNamed(expression.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } } - (void)testMatchExpressionObject { diff --git a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m new file mode 100644 index 0000000000..015a58d2d2 --- /dev/null +++ b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m @@ -0,0 +1,61 @@ +#import "MGLMapViewIntegrationTest.h" + +@interface MGLStyleLayerIntegrationTests : MGLMapViewIntegrationTest +@end + +@implementation MGLStyleLayerIntegrationTests + +- (MGLCircleStyleLayer*)setupCircleStyleLayer { + // Adapted from https://www.mapbox.com/ios-sdk/examples/dds-circle-layer/ + + // "mapbox://examples.2uf7qges" is a map ID referencing a tileset. For more + // more information, see mapbox.com/help/define-map-id/ + MGLSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"trees" configurationURL:[NSURL URLWithString:@"mapbox://examples.2uf7qges"]]; + [self.mapView.style addSource:source]; + + MGLCircleStyleLayer *layer = [[MGLCircleStyleLayer alloc] initWithIdentifier: @"tree-style" source:source]; + + // The source name from the source's TileJSON metadata: mapbox.com/api-documentation/#retrieve-tilejson-metadata + layer.sourceLayerIdentifier = @"yoshino-trees-a0puw5"; + + return layer; +} + +- (void)testForInterpolatingExpressionRenderCrashWithEmptyStops { + // Tests: https://github.com/mapbox/mapbox-gl-native/issues/9539 + // Adapted from https://www.mapbox.com/ios-sdk/examples/dds-circle-layer/ + self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039); + self.mapView.zoomLevel = 10.5; + + MGLCircleStyleLayer *layer = [self setupCircleStyleLayer]; + + NSExpression *interpExpression = [NSExpression mgl_expressionForInterpolatingExpression:NSExpression.zoomLevelVariableExpression + withCurveType:MGLExpressionInterpolationModeLinear + parameters:nil + stops:[NSExpression expressionForConstantValue:@{}]]; + + XCTAssertThrowsSpecificNamed((layer.circleColor = interpExpression), NSException, NSInvalidArgumentException); + + [self.mapView.style addLayer:layer]; + [self waitForMapViewToBeRenderedWithTimeout:1.0]; +} + +- (void)testForSteppingExpressionRenderCrashWithEmptyStops { + // Tests: https://github.com/mapbox/mapbox-gl-native/issues/9539 + // Adapted from https://www.mapbox.com/ios-sdk/examples/dds-circle-layer/ + self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039); + self.mapView.zoomLevel = 10.5; + + MGLCircleStyleLayer *layer = [self setupCircleStyleLayer]; + + NSExpression *steppingExpression = [NSExpression mgl_expressionForSteppingExpression:NSExpression.zoomLevelVariableExpression + fromExpression:[NSExpression expressionForConstantValue:[UIColor greenColor]] + stops:[NSExpression expressionForConstantValue:@{}]]; + + XCTAssertThrowsSpecificNamed((layer.circleColor = steppingExpression), NSException, NSInvalidArgumentException); + + [self.mapView.style addLayer:layer]; + [self waitForMapViewToBeRenderedWithTimeout:1.0]; +} + +@end diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index a9413accf2..6892760f59 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -362,6 +362,7 @@ AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */; }; + CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; }; CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CAA69DA4206DCD0E007279CD /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4A26961CB6E795000B7809 /* Mapbox.framework */; }; @@ -993,6 +994,7 @@ AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = ""; }; CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = ""; }; CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapViewIntegrationTest.h; sourceTree = ""; }; + CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = ""; }; CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = ""; }; DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = ""; }; DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = ""; }; @@ -1345,6 +1347,7 @@ 16376B0B1FFD9DAF0000563E /* Info.plist */, CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */, CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */, + CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */, ); path = "Integration Tests"; sourceTree = ""; @@ -2793,6 +2796,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */, 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */, CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */, ); -- cgit v1.2.1