summaryrefslogtreecommitdiff
path: root/platform/ios/Integration Tests
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/Integration Tests')
-rw-r--r--platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm (renamed from platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m)273
-rw-r--r--platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm109
-rw-r--r--platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm (renamed from platform/ios/Integration Tests/MGLCameraTransitionTests.mm)24
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.h3
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m79
-rw-r--r--platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m366
-rw-r--r--platform/ios/Integration Tests/MGLSourceTests.swift45
-rw-r--r--platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m51
-rw-r--r--platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m16
-rw-r--r--platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift5
-rw-r--r--platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m111
11 files changed, 985 insertions, 97 deletions
diff --git a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm
index f129277e87..1b3603419e 100644
--- a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m
+++ b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm
@@ -4,6 +4,13 @@
#import "MGLTestLocationManager.h"
#import "MGLCompactCalloutView.h"
+#import "MGLGeometry_Private.h"
+#import "MGLMapView_Private.h"
+
+#include <mbgl/util/geo.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/map/map.hpp>
+
@interface MGLTestCalloutView : MGLCompactCalloutView
@property (nonatomic) BOOL implementsMarginHints;
@end
@@ -21,6 +28,10 @@
@interface MGLMapView (Tests)
- (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist;
+- (id <MGLAnnotation>)annotationWithTag:(MGLAnnotationTag)tag;
+- (MGLMapCamera *)cameraByRotatingToDirection:(CLLocationDirection)degrees aroundAnchorPoint:(CGPoint)anchorPoint;
+- (MGLMapCamera *)cameraByZoomingToZoomLevel:(double)zoom aroundAnchorPoint:(CGPoint)anchorPoint;
+- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions;
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
@end
@@ -502,6 +513,267 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
XCTAssertEqual(originalFrame.origin.y + offset.y, offsetFrame.origin.y);
}
+#pragma mark - Rotating/zooming
+
+- (void)testSelectingAnnotationWhenMapIsRotated {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 40.0, 40.0 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ // Rotate
+ CLLocationDirection lastAngle = 0.0;
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
+
+ CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Rotate to: %0.1f from: %0.1f", angle, lastAngle];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity) {
+
+ MGLMapCamera *toCamera = [self.mapView cameraByRotatingToDirection:angle aroundAnchorPoint:anchor];
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+
+ lastAngle = angle;
+ }
+}
+
+- (void)testSelectingAnnotationWhenMapIsScaled {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 0.005, 0.005 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ CGPoint anchor = CGPointMake(CGRectGetMidX(self.mapView.bounds), CGRectGetMidY(self.mapView.bounds));
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ double zoom = (double)(drand48()*14.0);
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity) {
+ MGLMapCamera *toCamera = [self.mapView cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:anchor];
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+ }
+}
+
+- (void)testSelectingAnnotationWhenMapIsScaledAndRotated {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 0.005, 0.005 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ double zoom = (double)(7.0 + drand48()*7.0);
+ CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
+
+ CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+ mbgl::CameraOptions currentCameraOptions;
+
+ currentCameraOptions.bearing = angle;
+ currentCameraOptions.anchor = mbgl::ScreenCoordinate { anchor.x, anchor.y };
+ currentCameraOptions.zoom = zoom;
+ MGLMapCamera *toCamera = [self.mapView cameraForCameraOptions:currentCameraOptions];
+
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+ }
+}
+
+
+- (void)testShowingAnnotationsThenSelectingAnimated {
+ [self internalTestShowingAnnotationsThenSelectingAnimated:YES];
+}
+
+- (void)testShowingAnnotationsThenSelecting {
+ [self internalTestShowingAnnotationsThenSelectingAnimated:NO];
+}
+
+- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated {
+ srand48(0);
+
+ CGFloat maxXPadding = std::max(CGRectGetWidth(self.mapView.bounds)/5.0, 100.0);
+ CGFloat maxYPadding = std::max(CGRectGetHeight(self.mapView.bounds)/5.0, 100.0);
+
+ for (int i = 0; i < 10; i++) {
+ UIEdgeInsets edgePadding;
+ edgePadding.top = floor(drand48()*maxYPadding);
+ edgePadding.bottom = floor(drand48()*maxYPadding);
+ edgePadding.left = floor(drand48()*maxXPadding);
+ edgePadding.right = floor(drand48()*maxXPadding);
+
+ UIEdgeInsets contentInsets;
+ contentInsets.top = floor(drand48()*maxYPadding);
+ contentInsets.bottom = floor(drand48()*maxYPadding);
+ contentInsets.left = floor(drand48()*maxXPadding);
+ contentInsets.right = floor(drand48()*maxXPadding);
+
+ [self internalTestShowingAnnotationsThenSelectingAnimated:animated edgePadding:edgePadding contentInsets:contentInsets];
+ }
+}
+
+- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated edgePadding:(UIEdgeInsets)edgeInsets contentInsets:(UIEdgeInsets)contentInsets {
+ CLLocationCoordinate2D coordinates[21];
+
+ for (int i = 0; i < (int)(sizeof(coordinates)/sizeof(coordinates[0])); i++)
+ {
+ coordinates[i].latitude = drand48();
+ coordinates[i].longitude = drand48();
+ }
+ coordinates[20] = CLLocationCoordinate2DMake(NAN, NAN);
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+
+ XCTestExpectation *showCompleted = [self expectationWithDescription:@"showCompleted"];
+
+ self.mapView.contentInset = contentInsets;
+ [self.mapView showAnnotations:annotations
+ edgePadding:edgeInsets
+ animated:animated
+ completionHandler:^{
+ [showCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[showCompleted] timeout:3.5];
+
+ // These tests will fail if this isn't here. But this isn't quite what we're
+ // seeing in https://github.com/mapbox/mapbox-gl-native/issues/15106
+ [self waitForCollisionDetectionToRun];
+
+ for (MGLPointAnnotation *point in annotations) {
+ [self internalSelectDeselectAnnotation:point];
+ }
+
+ [self.mapView removeAnnotations:annotations];
+ self.mapView.contentInset = UIEdgeInsetsZero;
+ [self waitForCollisionDetectionToRun];
+}
+
+- (NSArray*)internalAddAnnotationsAtCoordinates:(CLLocationCoordinate2D*)coordinates
+{
+ __block NSMutableArray *annotations = [NSMutableArray array];
+
+ [XCTContext runActivityNamed:@"Map setup"
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+
+ NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer";
+
+ CGSize annotationSize = CGSizeMake(40.0, 40.0);
+
+ self.viewForAnnotation = ^MGLAnnotationView*(MGLMapView *view, id<MGLAnnotation> annotation2) {
+
+ if (![annotation2 isKindOfClass:[MGLPointAnnotation class]]) {
+ return nil;
+ }
+
+ // No dequeue
+ MGLAnnotationView *annotationView = [[MGLAnnotationView alloc] initWithAnnotation:annotation2 reuseIdentifier:MGLTestAnnotationReuseIdentifer];
+ annotationView.bounds = (CGRect){ .origin = CGPointZero, .size = annotationSize };
+ annotationView.backgroundColor = UIColor.redColor;
+ annotationView.enabled = YES;
+
+ return annotationView;
+ };
+
+ CLLocationCoordinate2D *coordinatePtr = coordinates;
+ while (!isnan(coordinatePtr->latitude)) {
+ CLLocationCoordinate2D coordinate = *coordinatePtr++;
+
+ MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
+ annotation.title = NSStringFromSelector(_cmd);
+ annotation.coordinate = coordinate;
+ [annotations addObject:annotation];
+ }
+
+ [self.mapView addAnnotations:annotations];
+
+ }];
+
+ NSArray *copiedAnnotations = [annotations copy];
+ annotations = nil;
+
+ return copiedAnnotations;
+}
+
+- (void)internalTestSelecting:(MGLPointAnnotation*)point withCamera:(MGLMapCamera*)camera {
+
+ // Rotate
+ XCTestExpectation *rotationCompleted = [self expectationWithDescription:@"rotationCompleted"];
+ [self.mapView setCamera:camera withDuration:0.1 animationTimingFunction:nil completionHandler:^{
+ [rotationCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[rotationCompleted] timeout:1.5];
+
+ // Collision detection may not have completed, if not we may not get our annotation.
+ [self waitForCollisionDetectionToRun];
+
+ // Look up annotation at point
+ [self internalSelectDeselectAnnotation:point];
+}
+
+- (void)internalSelectDeselectAnnotation:(MGLPointAnnotation*)point {
+ [XCTContext runActivityNamed:[NSString stringWithFormat:@"Select annotation: %@", point]
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+ CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate toPointToView:self.mapView];
+
+ MGLAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:annotationPoint persistingResults:YES];
+ if (tagAtPoint != UINT32_MAX)
+ {
+ id <MGLAnnotation> annotation = [self.mapView annotationWithTag:tagAtPoint];
+ XCTAssertNotNil(annotation);
+
+ // Select
+ XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"];
+ [self.mapView selectAnnotation:annotation moveIntoView:NO animateSelection:NO completionHandler:^{
+ [selectionCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[selectionCompleted] timeout:0.05];
+
+ XCTAssert(self.mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation");
+ XCTAssertEqualObjects(self.mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [self.mapView deselectAnnotation:annotation animated:NO];
+ }
+ else
+ {
+ XCTFail(@"Should be an annotation at this point: %@", NSStringFromCGPoint(annotationPoint));
+ }
+ }];
+
+}
+
#pragma mark - Utilities
- (void)runRunLoop {
@@ -529,6 +801,7 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
- (void)waitForCollisionDetectionToRun {
XCTAssertNil(self.renderFinishedExpectation, @"Incorrect test setup");
+ [self.mapView setNeedsRerender];
self.renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
XCTestExpectation *timerExpired = [self expectationWithDescription:@"Timer expires"];
diff --git a/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm
new file mode 100644
index 0000000000..1527e8dbe5
--- /dev/null
+++ b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm
@@ -0,0 +1,109 @@
+#import "MGLMapViewIntegrationTest.h"
+#import "MGLTestUtility.h"
+#import "../../darwin/src/MGLGeometry_Private.h"
+
+#include <mbgl/map/camera.hpp>
+
+@interface MGLCameraTransitionFinishTests : MGLMapViewIntegrationTest
+@end
+
+@implementation MGLCameraTransitionFinishTests
+
+- (void)testEaseToCompletionHandler {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView setCamera:camera
+ withDuration:0.0
+ animationTimingFunction:nil
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testEaseToCompletionHandlerAnimated {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView setCamera:camera
+ withDuration:0.3
+ animationTimingFunction:nil
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testFlyToCompletionHandler {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView flyToCamera:camera
+ withDuration:0.0
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testFlyToCompletionHandlerAnimated {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView flyToCamera:camera
+ withDuration:0.3
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+@end
+
+#pragma mark - camera transitions with NaN values
+
+@interface MGLMapView (MGLCameraTransitionFinishNaNTests)
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets;
+@end
+
+@interface MGLCameraTransitionNaNZoomMapView: MGLMapView
+@end
+
+@implementation MGLCameraTransitionNaNZoomMapView
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets {
+ mbgl::CameraOptions options = [super cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
+ options.zoom = NAN;
+ return options;
+}
+@end
+
+// Subclass the entire test suite, but with a different MGLMapView subclass
+@interface MGLCameraTransitionFinishNaNTests : MGLCameraTransitionFinishTests
+@end
+
+@implementation MGLCameraTransitionFinishNaNTests
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL {
+ return [[MGLCameraTransitionNaNZoomMapView alloc] initWithFrame:rect styleURL:styleURL];
+}
+@end
+
diff --git a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm
index 9679c4c11f..27ab7964c1 100644
--- a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm
+++ b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm
@@ -2,10 +2,10 @@
#import "MGLTestUtility.h"
#import "../../darwin/src/MGLGeometry_Private.h"
-@interface MBCameraTransitionTests : MGLMapViewIntegrationTest
+@interface MGLCameraTransitionTests : MGLMapViewIntegrationTest
@end
-@implementation MBCameraTransitionTests
+@implementation MGLCameraTransitionTests
- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod {
@@ -17,7 +17,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -48,7 +48,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -79,7 +79,7 @@
__block BOOL finishedReset = NO;
self.regionIsChanging = ^(MGLMapView *mapView) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
if (!startedReset) {
@@ -91,7 +91,7 @@
};
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
MGLTestAssert(strongSelf, startedReset);
@@ -127,7 +127,7 @@
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
[strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO];
MGLTestAssertEqualWithAccuracy(strongSelf,
@@ -160,7 +160,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -248,7 +248,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -333,7 +333,7 @@
#pragma mark - Pending tests
-- (void)testContinuallyResettingNorthInIsChangingPENDING {
+- (void)testContinuallyResettingNorthInIsChanging🙁{
// See https://github.com/mapbox/mapbox-gl-native/pull/11614
// This test currently fails, unsurprisingly, since we're continually
// setting the camera to the same parameters during its update.
@@ -365,8 +365,8 @@
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction);
}
-- (void)testContinuallySettingCoordinateInIsChangingPENDING {
- // See above comment in `-testContinuallyResettingNorthInIsChangingPENDING`
+- (void)testContinuallySettingCoordinateInIsChanging🙁 {
+ // See above comment in `-testContinuallyResettingNorthInIsChanging🙁`
// Reset to non-zero, prior to testing
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(0.0, 0.0) animated:NO];
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
index dedafdb83a..08576e884a 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
@@ -26,6 +26,7 @@
@interface MGLMapViewIntegrationTest : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) UIWindow *window;
@property (nonatomic) MGLStyle *style;
@property (nonatomic) XCTestExpectation *styleLoadingExpectation;
@property (nonatomic) XCTestExpectation *renderFinishedExpectation;
@@ -38,7 +39,7 @@
@property (nonatomic) id<MGLCalloutView> (^mapViewCalloutViewForAnnotation)(MGLMapView *mapView, id<MGLAnnotation> annotation);
// Utility methods
-- (NSString*)validAccessToken;
- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout;
- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout;
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL;
@end
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
index 2c89bb1c77..4095b4620b 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -2,50 +2,76 @@
@interface MGLMapView (MGLMapViewIntegrationTest)
- (void)updateFromDisplayLink:(CADisplayLink *)displayLink;
+- (void)setNeedsRerender;
@end
@implementation MGLMapViewIntegrationTest
-- (void)invokeTest {
- NSString *selector = NSStringFromSelector(self.invocation.selector);
- BOOL isPendingTest = [selector hasSuffix:@"PENDING"];
++ (XCTestSuite*)defaultTestSuite {
- if (isPendingTest) {
- NSString *runPendingTests = [[NSProcessInfo processInfo] environment][@"MAPBOX_RUN_PENDING_TESTS"];
- if (![runPendingTests boolValue]) {
- printf("warning: '%s' is a pending test - skipping\n", selector.UTF8String);
- return;
+ XCTestSuite *defaultTestSuite = [super defaultTestSuite];
+
+ NSArray *tests = defaultTestSuite.tests;
+
+ XCTestSuite *newTestSuite = [XCTestSuite testSuiteWithName:defaultTestSuite.name];
+
+ BOOL runPendingTests = [[[NSProcessInfo processInfo] environment][@"MAPBOX_RUN_PENDING_TESTS"] boolValue];
+ NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+
+ for (XCTest *test in tests) {
+
+ // Check for pending tests
+ if ([test.name containsString:@"PENDING"] ||
+ [test.name containsString:@"🙁"]) {
+ if (!runPendingTests) {
+ printf("warning: '%s' is a pending test - skipping\n", test.name.UTF8String);
+ continue;
+ }
+ }
+
+ // Check for tests that require a valid access token
+ if ([test.name containsString:@"🔒"]) {
+ if (!accessToken) {
+ printf("warning: MAPBOX_ACCESS_TOKEN env var is required for test '%s' - skipping.\n", test.name.UTF8String);
+ continue;
+ }
}
- }
-
- [super invokeTest];
-}
-- (NSString*)validAccessToken {
- NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
- if (!accessToken) {
- printf("warning: MAPBOX_ACCESS_TOKEN env var is required for this test - skipping.\n");
- return nil;
+ [newTestSuite addTest:test];
}
+
+ return newTestSuite;
+}
- [MGLAccountManager setAccessToken:accessToken];
- return accessToken;
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL {
+ return [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
}
- (void)setUp {
[super setUp];
- [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
+ NSString *accessToken;
+
+ if ([self.name containsString:@"🔒"]) {
+ accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+
+ if (!accessToken) {
+ printf("warning: MAPBOX_ACCESS_TOKEN env var is required for test '%s' - trying anyway.\n", self.name.UTF8String);
+ }
+ }
+
+ [MGLAccountManager setAccessToken:accessToken ?: @"pk.feedcafedeadbeefbadebede"];
+
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
- self.mapView = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
+ self.mapView = [self mapViewForTestWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
self.mapView.delegate = self;
UIView *superView = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
[superView addSubview:self.mapView];
- UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
- [window addSubview:superView];
- [window makeKeyAndVisible];
+ self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ [self.window addSubview:superView];
+ [self.window makeKeyAndVisible];
if (!self.mapView.style) {
[self waitForMapViewToFinishLoadingStyleWithTimeout:10];
@@ -57,6 +83,7 @@
self.renderFinishedExpectation = nil;
self.mapView = nil;
self.style = nil;
+ self.window = nil;
[MGLAccountManager setAccessToken:nil];
[super tearDown];
@@ -129,13 +156,15 @@
XCTAssertNil(self.styleLoadingExpectation);
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
[self waitForExpectations:@[self.styleLoadingExpectation] timeout:timeout];
+ self.styleLoadingExpectation = nil;
}
- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout {
XCTAssertNil(self.renderFinishedExpectation);
- [self.mapView setNeedsDisplay];
+ [self.mapView setNeedsRerender];
self.renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
[self waitForExpectations:@[self.renderFinishedExpectation] timeout:timeout];
+ self.renderFinishedExpectation = nil;
}
- (void)waitForExpectations:(NSArray<XCTestExpectation *> *)expectations timeout:(NSTimeInterval)seconds {
diff --git a/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
new file mode 100644
index 0000000000..c7925d7896
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
@@ -0,0 +1,366 @@
+#import "MGLMapViewIntegrationTest.h"
+#import "MGLTestUtility.h"
+
+@interface MGLMapView (MGLMapViewPendingBlockTests)
+@property (nonatomic) NSMutableArray *pendingCompletionBlocks;
+- (void)pauseRendering:(__unused NSNotification *)notification;
+@end
+
+@interface MGLMapViewPendingBlockTests : MGLMapViewIntegrationTest
+@property (nonatomic, copy) void (^observation)(NSDictionary*);
+@property (nonatomic) BOOL completionHandlerCalled;
+@end
+
+@implementation MGLMapViewPendingBlockTests
+
+- (void)testSetCenterCoordinate {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCenterCoordinateAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:YES
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetVisibleCoordinateBounds {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ [strongSelf.mapView setVisibleCoordinateBounds:unitBounds
+ edgePadding:UIEdgeInsetsZero
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetVisibleCoordinateBoundsAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ [strongSelf.mapView setVisibleCoordinateBounds:unitBounds
+ edgePadding:UIEdgeInsetsZero
+ animated:YES
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCamera {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView setCamera:camera withDuration:0.0 animationTimingFunction:nil completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCameraAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView setCamera:camera withDuration:0.3 animationTimingFunction:nil completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testFlyToCamera {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView flyToCamera:camera withDuration:0.0 completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testFlyToCameraAnimated {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView flyToCamera:camera withDuration:0.3 completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+
+#pragma mark - test interrupting regular rendering
+
+- (void)testSetCenterCoordinateSetHidden {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Now hide the mapview
+ strongSelf.mapView.hidden = YES;
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+- (void)testSetCenterCoordinatePauseRendering {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Pause rendering, stopping display link
+ [strongSelf.mapView pauseRendering:nil];
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+- (void)testSetCenterCoordinateRemoveFromSuperview {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Remove from window, triggering validateDisplayLink
+ [strongSelf.mapView removeFromSuperview];
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+#pragma mark - Shared utility methods
+
+- (void)internalTestCompletionBlockAddedToPendingForTestName:(NSString *)testName
+ transition:(void (^)(dispatch_block_t))transition
+ addToPendingCallback:(dispatch_block_t)addToPendingCallback {
+
+ XCTestExpectation *expectation = [self expectationWithDescription:testName];
+
+ __weak __typeof__(self) myself = self;
+
+ dispatch_block_t block = ^{
+ myself.completionHandlerCalled = YES;
+ [expectation fulfill];
+ };
+
+ XCTAssertNotNil(transition);
+ transition(block);
+
+ XCTAssert(!self.completionHandlerCalled);
+ XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
+
+ __block BOOL blockAddedToPendingBlocks = NO;
+
+ // Observes changes to pendingCompletionBlocks (including additions)
+ self.observation = ^(NSDictionary *change){
+
+ NSLog(@"change = %@ count = %lu", change, myself.mapView.pendingCompletionBlocks.count);
+
+ NSArray *value = change[NSKeyValueChangeNewKey];
+
+ MGLTestAssert(myself, [value isKindOfClass:[NSArray class]]);
+
+ if (value.count > 0) {
+ MGLTestAssert(myself, [value containsObject:block]);
+ MGLTestAssert(myself, !blockAddedToPendingBlocks);
+ if ([myself.mapView.pendingCompletionBlocks containsObject:block]) {
+ blockAddedToPendingBlocks = YES;
+
+ if (addToPendingCallback) {
+ addToPendingCallback();
+ }
+ }
+ }
+ };
+
+ [self.mapView addObserver:self forKeyPath:@"pendingCompletionBlocks" options:NSKeyValueObservingOptionNew context:_cmd];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+
+ XCTAssert(blockAddedToPendingBlocks);
+ XCTAssert(self.completionHandlerCalled);
+ XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
+
+ [self.mapView removeObserver:self forKeyPath:@"pendingCompletionBlocks" context:_cmd];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+ if (self.observation) {
+ self.observation(change);
+ }
+}
+@end
diff --git a/platform/ios/Integration Tests/MGLSourceTests.swift b/platform/ios/Integration Tests/MGLSourceTests.swift
new file mode 100644
index 0000000000..69fa0182b5
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLSourceTests.swift
@@ -0,0 +1,45 @@
+import XCTest
+
+class MGLSourceTests: MGLMapViewIntegrationTest {
+
+ // See testForRaisingExceptionsOnStaleStyleObjects for Obj-C sibling.
+ func testForRaisingExceptionsOnStaleStyleObjectsOnRemoveFromMapView() {
+
+ guard
+ let configURL = URL(string: "mapbox://examples.2uf7qges") else {
+ XCTFail()
+ return
+ }
+
+ let source = MGLVectorTileSource(identifier: "trees", configurationURL: configURL)
+ mapView.style?.addSource(source)
+
+ let bundle = Bundle(for: type(of: self))
+
+ guard let styleURL = bundle.url(forResource: "one-liner", withExtension: "json") else {
+ XCTFail()
+ return
+ }
+
+ styleLoadingExpectation = nil;
+
+ mapView.centerCoordinate = CLLocationCoordinate2D(latitude : 38.897, longitude : -77.039)
+ mapView.zoomLevel = 10.5
+ mapView.styleURL = styleURL
+
+ waitForMapViewToFinishLoadingStyle(withTimeout: 10.0)
+
+ let expect = expectation(description: "Remove source should error")
+
+ do {
+ try mapView.style?.removeSource(source, error: ())
+ }
+ catch let error as NSError {
+ XCTAssertEqual(error.domain, MGLErrorDomain)
+ XCTAssertEqual(error.code, MGLErrorCode.sourceCannotBeRemovedFromStyle.rawValue)
+ expect.fulfill()
+ }
+
+ wait(for: [expect], timeout: 0.1)
+ }
+}
diff --git a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
index 4501294f72..c018c457b9 100644
--- a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
+++ b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
@@ -58,4 +58,55 @@
[self waitForMapViewToBeRenderedWithTimeout:10];
}
+- (void)testForRaisingExceptionsOnStaleStyleObjects {
+ self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039);
+ self.mapView.zoomLevel = 10.5;
+
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"trees" configurationURL:[NSURL URLWithString:@"mapbox://examples.2uf7qges"]];
+ [self.mapView.style addSource:source];
+
+ self.styleLoadingExpectation = nil;
+ [self.mapView setStyleURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"]];
+ [self waitForMapViewToFinishLoadingStyleWithTimeout:10];
+
+ XCTAssertNotNil(source.description);
+ XCTAssertThrowsSpecificNamed(source.configurationURL, NSException, MGLInvalidStyleSourceException, @"MGLSource should raise an exception if its core peer got invalidated");
+}
+
+- (void)testForRaisingExceptionsOnStaleLayerObject {
+ self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039);
+ self.mapView.zoomLevel = 10.5;
+
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ // Testing generated layers
+ MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"lineLayerID" source:source];
+ MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"rasterLayerID" source:source];
+
+ [self.mapView.style addSource:source];
+ [self.mapView.style addLayer:lineLayer];
+ [self.mapView.style addLayer:rasterLayer];
+
+ XCTAssertNoThrow(lineLayer.isVisible);
+ XCTAssertNoThrow(rasterLayer.isVisible);
+
+ XCTAssert(![source.description containsString:@"<unknown>"]);
+ XCTAssert(![lineLayer.description containsString:@"<unknown>"]);
+ XCTAssert(![rasterLayer.description containsString:@"<unknown>"]);
+
+ self.styleLoadingExpectation = nil;
+ [self.mapView setStyleURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"]];
+ [self waitForMapViewToFinishLoadingStyleWithTimeout:10];
+
+ XCTAssert([source.description containsString:@"<unknown>"]);
+ XCTAssert([lineLayer.description containsString:@"<unknown>"]);
+ XCTAssert([rasterLayer.description containsString:@"<unknown>"]);
+
+ XCTAssertThrowsSpecificNamed(lineLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated");
+ XCTAssertThrowsSpecificNamed(rasterLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated");
+
+ XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:lineLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)");
+ XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:rasterLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)");
+}
@end
diff --git a/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m b/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
index f9217bae5f..22de4c6aa5 100644
--- a/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
@@ -6,36 +6,32 @@
@implementation MGLStyleURLIntegrationTest
- (void)internalTestWithStyleSelector:(SEL)selector {
- if (![self validAccessToken]) {
- return;
- }
-
self.mapView.styleURL = [MGLStyle performSelector:selector];
[self waitForMapViewToFinishLoadingStyleWithTimeout:5];
}
-- (void)testLoadingStreetsStyleURL {
+- (void)testLoadingStreetsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(streetsStyleURL)];
}
-- (void)testLoadingOutdoorsStyleURL {
+- (void)testLoadingOutdoorsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(outdoorsStyleURL)];
}
-- (void)testLoadingLightStyleURL {
+- (void)testLoadingLightStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(lightStyleURL)];
}
-- (void)testLoadingDarkStyleURL {
+- (void)testLoadingDarkStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(darkStyleURL)];
}
-- (void)testLoadingSatelliteStyleURL {
+- (void)testLoadingSatelliteStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(satelliteStyleURL)];
}
-- (void)testLoadingSatelliteStreetsStyleURL {
+- (void)testLoadingSatelliteStreetsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(satelliteStreetsStyleURL)];
}
diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
index 0f2034b6b8..c3400b1fa2 100644
--- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
+++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
@@ -15,12 +15,9 @@ class MGLMapSnapshotterSwiftTests: MGLMapViewIntegrationTest {
return options
}
- func testCapturingSnapshotterInSnapshotCompletion() {
+ func testCapturingSnapshotterInSnapshotCompletion🔒() {
// See the Obj-C testDeallocatingSnapshotterDuringSnapshot
// This Swift test, is essentially the same except for capturing the snapshotter
- guard validAccessToken() != nil else {
- return
- }
let timeout: TimeInterval = 10.0
let expectation = self.expectation(description: "snapshot")
diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
index 32e5fc782d..7707896203 100644
--- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
+++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
@@ -26,11 +26,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
@implementation MGLMapSnapshotterTest
-- (void)testMultipleSnapshotsWithASingleSnapshotter {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testMultipleSnapshotsWithASingleSnapshotter🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
@@ -60,11 +56,8 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
-- (void)testDeallocatingSnapshotterDuringSnapshot {
+- (void)testDeallocatingSnapshotterDuringSnapshot🔒 {
// See also https://github.com/mapbox/mapbox-gl-native/issues/12336
- if (![self validAccessToken]) {
- return;
- }
NSTimeInterval timeout = 10.0;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -108,16 +101,12 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:timeout];
}
-- (void)testSnapshotterUsingNestedDispatchQueues {
+- (void)testSnapshotterUsingNestedDispatchQueues🔒 {
// This is the opposite pair to the above test `testDeallocatingSnapshotterDuringSnapshot`
// The only significant difference is that the snapshotter is a `__block` variable, so
// its lifetime should continue until it's set to nil in the completion block.
// See also https://github.com/mapbox/mapbox-gl-native/issues/12336
- if (![self validAccessToken]) {
- return;
- }
-
NSTimeInterval timeout = 10.0;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
CGSize size = self.mapView.bounds.size;
@@ -156,11 +145,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:timeout];
}
-- (void)testCancellingSnapshot {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testCancellingSnapshot🔒 {
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
expectation.assertForOverFulfill = YES;
expectation.expectedFulfillmentCount = 1;
@@ -189,11 +174,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:5.0];
}
-- (void)testAllocatingSnapshotOnBackgroundQueue {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testAllocatingSnapshotOnBackgroundQueue🔒 {
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
CGSize size = self.mapView.bounds.size;
@@ -226,11 +207,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:2.0];
}
-- (void)testSnapshotterFromBackgroundQueueShouldFail {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotterFromBackgroundQueueShouldFail🔒 {
CGSize size = self.mapView.bounds.size;
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
@@ -281,12 +258,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:60.0];
}
-- (void)testMultipleSnapshottersPENDING {
-
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testMultipleSnapshotters🔒🙁 {
NSUInteger numSnapshots = 8;
CGSize size = self.mapView.bounds.size;
@@ -340,11 +312,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:60.0];
}
-- (void)testSnapshotPointConversion {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotPointConversion🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -382,11 +350,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
-- (void)testSnapshotPointConversionCoordinateOrdering {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotPointConversionCoordinateOrdering🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -429,5 +393,62 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
+- (void)testSnapshotWithOverlayHandlerFailure {
+ if (![self validAccessToken]) {
+ return;
+ }
+
+ CGSize size = self.mapView.bounds.size;
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay fails"];
+ expectation.expectedFulfillmentCount = 2;
+
+ CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
+
+ MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size);
+ XCTAssertNotNil(snapshotter);
+
+ [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) {
+ UIGraphicsEndImageContext();
+ [expectation fulfill];
+ } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ XCTAssertNil(snapshot);
+ XCTAssertNotNil(error);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:10.0];
+}
+
+- (void)testSnapshotWithOverlayHandlerSuccess {
+ if (![self validAccessToken]) {
+ return;
+ }
+
+ CGSize size = self.mapView.bounds.size;
+ CGRect snapshotRect = CGRectMake(0, 0, size.width, size.height);
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay succeeds"];
+ expectation.expectedFulfillmentCount = 2;
+
+ CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
+
+ MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size);
+ XCTAssertNotNil(snapshotter);
+
+ [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) {
+ CGContextSetFillColorWithColor(snapshotOverlay.context, [UIColor.greenColor CGColor]);
+ CGContextSetAlpha(snapshotOverlay.context, 0.2);
+ CGContextAddRect(snapshotOverlay.context, snapshotRect);
+ CGContextFillRect(snapshotOverlay.context, snapshotRect);
+ [expectation fulfill];
+ } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertNotNil(snapshot);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:10.0];
+}
@end