summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Rex <julian.rex@mapbox.com>2019-08-20 10:51:14 -0400
committerGitHub <noreply@github.com>2019-08-20 10:51:14 -0400
commitc0cd8d021d828e9c3b66d4e11e3a4c8df9e2b559 (patch)
tree0dac7cfcedaa09f9e5da3501fcadd3761194e652
parent888d2b06ec95514aea02fa36569d9c84b3b2fa31 (diff)
downloadqtlocation-mapboxgl-c0cd8d021d828e9c3b66d4e11e3a4c8df9e2b559.tar.gz
[ios] Adds annotation selection tests with rotation & scaling of the map (#15123)
-rw-r--r--platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm (renamed from platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m)272
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m3
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj8
-rw-r--r--platform/ios/src/MGLMapView_Private.h1
4 files changed, 278 insertions, 6 deletions
diff --git a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm
index f129277e87..eb3c85e30d 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 {
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
index 2c89bb1c77..62e038855d 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -2,6 +2,7 @@
@interface MGLMapView (MGLMapViewIntegrationTest)
- (void)updateFromDisplayLink:(CADisplayLink *)displayLink;
+- (void)setNeedsRerender;
@end
@implementation MGLMapViewIntegrationTest
@@ -133,7 +134,7 @@
- (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];
}
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index de2bec5d99..49043bdeee 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -510,7 +510,7 @@
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA65C4F821E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA65C4F921E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
- CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */; };
+ CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */; };
CA7766832229C10E0008DE9E /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; };
CA86FF0E22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */; };
@@ -1197,8 +1197,8 @@
CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = "<group>"; };
CA5E5042209BDC5F001A8A81 /* MGLTestUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MGLTestUtility.h; path = ../../darwin/test/MGLTestUtility.h; sourceTree = "<group>"; };
CA65C4F721E9BB080068B0D4 /* MGLCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCluster.h; sourceTree = "<group>"; };
- CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLAnnotationViewIntegrationTests.m; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.m"; sourceTree = "<group>"; };
CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; sourceTree = "<group>"; };
+ CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLAnnotationViewIntegrationTests.mm; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.mm"; sourceTree = "<group>"; };
CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleURLIntegrationTest.m; sourceTree = "<group>"; };
CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRendererConfigurationTests.mm; path = ../../darwin/test/MGLRendererConfigurationTests.mm; sourceTree = "<group>"; };
CAD9D0A922A86D6F001B25EE /* MGLResourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLResourceTests.mm; path = ../../darwin/test/MGLResourceTests.mm; sourceTree = "<group>"; };
@@ -1920,7 +1920,7 @@
CA6914B320E67F07002DB0EE /* Annotations */ = {
isa = PBXGroup;
children = (
- CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */,
+ CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */,
);
name = Annotations;
sourceTree = "<group>";
@@ -3196,7 +3196,7 @@
CAE7AD5520F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift in Sources */,
CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */,
077061DA215DA00E000FEF62 /* MGLTestLocationManager.m in Sources */,
- CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m in Sources */,
+ CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm in Sources */,
CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h
index e53dc8519c..12840b3586 100644
--- a/platform/ios/src/MGLMapView_Private.h
+++ b/platform/ios/src/MGLMapView_Private.h
@@ -53,7 +53,6 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const _Nonnull MGLUnderlyingMapUna
- (void)renderSync;
- (nonnull mbgl::Map *)mbglMap;
-
- (nonnull mbgl::Renderer *)renderer;
/** Returns whether the map view is currently loading or processing any assets required to render the map */