From 74730cf3fef9dc16ddfcc9d77debc6c3b7c575ea Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Thu, 17 May 2018 17:29:03 -0400 Subject: Rebase and update wip test. --- platform/ios/ios.xcodeproj/project.pbxproj | 8 +- platform/ios/test/MGLAnnotationViewTests.m | 340 ---------------------------- platform/ios/test/MGLAnnotationViewTests.mm | 336 +++++++++++++++++++++++++++ 3 files changed, 340 insertions(+), 344 deletions(-) delete mode 100644 platform/ios/test/MGLAnnotationViewTests.m create mode 100644 platform/ios/test/MGLAnnotationViewTests.mm diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 78cfff965c..b823efb8a8 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -98,7 +98,7 @@ 353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 353AFA131D65AB17005A69F4 /* NSDate+MGLAdditions.mm */; }; 353BAEF61D646370009A8DA9 /* amsterdam.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 353BAEF51D646370009A8DA9 /* amsterdam.geojson */; }; 353BAEF71D646370009A8DA9 /* amsterdam.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 353BAEF51D646370009A8DA9 /* amsterdam.geojson */; }; - 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */; }; + 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.mm */; }; 354B83961D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 354B83941D2E873E005D9406 /* MGLUserLocationAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 354B83971D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 354B83941D2E873E005D9406 /* MGLUserLocationAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 354B83951D2E873E005D9406 /* MGLUserLocationAnnotationView.m */; }; @@ -790,7 +790,7 @@ 353AFA121D65AB17005A69F4 /* NSDate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+MGLAdditions.h"; sourceTree = ""; }; 353AFA131D65AB17005A69F4 /* NSDate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+MGLAdditions.mm"; sourceTree = ""; }; 353BAEF51D646370009A8DA9 /* amsterdam.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = amsterdam.geojson; path = ../../darwin/test/amsterdam.geojson; sourceTree = ""; }; - 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationViewTests.m; sourceTree = ""; }; + 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationViewTests.mm; sourceTree = ""; }; 354B83941D2E873E005D9406 /* MGLUserLocationAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView.h; sourceTree = ""; }; 354B83951D2E873E005D9406 /* MGLUserLocationAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationAnnotationView.m; sourceTree = ""; }; 354B839A1D2E9B48005D9406 /* MBXUserLocationAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXUserLocationAnnotationView.h; sourceTree = ""; }; @@ -1812,7 +1812,7 @@ 4031ACFD1E9FD26900A3EA26 /* Test Helpers */, 409F43FB1E9E77D10048729D /* Swift Integration */, 357579811D502AD4000B822E /* Styling */, - 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */, + 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.mm */, DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */, DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */, 35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */, @@ -2883,7 +2883,7 @@ DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */, 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */, DAF25720201902BC00367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */, - 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */, + 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.mm in Sources */, 35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */, DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */, 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */, diff --git a/platform/ios/test/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m deleted file mode 100644 index 2aee9950be..0000000000 --- a/platform/ios/test/MGLAnnotationViewTests.m +++ /dev/null @@ -1,340 +0,0 @@ -#import -#import - -static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer"; - - -@interface MGLMapView (Tests) -@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask; -@end - - - -@interface MGLCustomAnnotationView : MGLAnnotationView - -@end - -@implementation MGLCustomAnnotationView - -- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { - return [super initWithReuseIdentifier:@"reuse-id"]; -} - -@end - -@interface MGLAnnotationView (Test) - -@property (nonatomic) MGLMapView *mapView; -@property (nonatomic, readwrite) MGLAnnotationViewDragState dragState; -- (void)setDragState:(MGLAnnotationViewDragState)dragState; - -@end - -@interface MGLMapView (Test) -@property (nonatomic) UIView *calloutViewForSelectedAnnotation; -@end - -@interface MGLTestAnnotation : NSObject -@property (nonatomic, assign) CLLocationCoordinate2D coordinate; -@end - -@implementation MGLTestAnnotation -@end - -@interface MGLTestCalloutView: UIView -@property (nonatomic) BOOL didCallDismissCalloutAnimated; -@property (nonatomic, strong) id representedObject; -@property (nonatomic, strong) UIView *leftAccessoryView; -@property (nonatomic, strong) UIView *rightAccessoryView; -@property (nonatomic, weak) id delegate; -@end - -@implementation MGLTestCalloutView - -- (void)dismissCalloutAnimated:(BOOL)animated -{ - _didCallDismissCalloutAnimated = YES; -} - -- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {} - -@end - -@interface MGLAnnotationViewTests : XCTestCase -@property (nonatomic) XCTestExpectation *expectation; -@property (nonatomic) MGLMapView *mapView; -@property (nonatomic, weak) MGLAnnotationView *annotationView; -@property (nonatomic) NSInteger annotationSelectedCount; -@property (nonatomic) void (^prepareAnnotationView)(MGLAnnotationView*); -@end - -@implementation MGLAnnotationViewTests - -- (void)setUp -{ - [super setUp]; - _mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)]; - _mapView.delegate = self; -} - -- (void)testAnnotationView -{ - _expectation = [self expectationWithDescription:@"annotation property"]; - - MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; - [_mapView addAnnotation:annotation]; - - [self waitForExpectationsWithTimeout:1 handler:nil]; - - XCTAssert(_mapView.annotations.count == 1, @"number of annotations should be 1"); - XCTAssertNotNil(_annotationView.annotation, @"annotation property should not be nil"); - XCTAssertNotNil(_annotationView.mapView, @"mapView property should not be nil"); - - MGLTestCalloutView *testCalloutView = [[MGLTestCalloutView alloc] init]; - _mapView.calloutViewForSelectedAnnotation = testCalloutView; - _annotationView.dragState = MGLAnnotationViewDragStateStarting; - XCTAssertTrue(testCalloutView.didCallDismissCalloutAnimated, @"callout view was not dismissed"); - - [_mapView removeAnnotation:_annotationView.annotation]; - - XCTAssert(_mapView.annotations.count == 0, @"number of annotations should be 0"); - XCTAssertNil(_annotationView.annotation, @"annotation property should be nil"); -} - -- (void)testCustomAnnotationView -{ - MGLCustomAnnotationView *customAnnotationView = [[MGLCustomAnnotationView alloc] initWithReuseIdentifier:@"reuse-id"]; - XCTAssertNotNil(customAnnotationView); -} - -- (void)testSelectingOffscreenAnnotation -{ - // Partial test for https://github.com/mapbox/mapbox-gl-native/issues/9790 - - // This isn't quite the same as in updateAnnotationViews, but should be sufficient for this test. - MGLCoordinateBounds coordinateBounds = [_mapView convertRect:_mapView.bounds toCoordinateBoundsFromView:_mapView]; - - // -90 latitude is invalid. TBD. - BOOL anyOffscreen = NO; - NSInteger selectionCount = 0; - - for (NSInteger latitude = -89; latitude <= 90; latitude += 10) - { - for (NSInteger longitude = -180; longitude <= 180; longitude += 10) - { - MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; - - annotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude); - [_mapView addAnnotation:annotation]; - - if (!(MGLCoordinateInCoordinateBounds(annotation.coordinate, coordinateBounds))) - anyOffscreen = YES; - - XCTAssertNil(_mapView.selectedAnnotations.firstObject, @"There should be no selected annotation"); - - // First selection - [_mapView selectAnnotation:annotation animated:NO]; - selectionCount++; - - XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation"); - XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected"); - - // Deselect - [_mapView deselectAnnotation:annotation animated:NO]; - XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations"); - - // Second selection - _mapView.selectedAnnotations = @[annotation]; - selectionCount++; - - XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should be 1 selected annotation"); - XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected"); - - // Deselect - [_mapView deselectAnnotation:annotation animated:NO]; - XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations"); - } - } - - XCTAssert(anyOffscreen, @"At least one of these annotations should be offscreen"); - XCTAssertEqual(selectionCount, self.annotationSelectedCount, @"-mapView:didSelectAnnotation: should be called for each selection"); -} - -- (void)testSelectingOnscreenAnnotationThatHasNotBeenAdded { - // See https://github.com/mapbox/mapbox-gl-native/issues/11476 - - // This bug occurs under the following conditions: - // - // - There are content insets (e.g. navigation bar) for the compare against - // CGRectZero (now CGRectNull) - // - annotationView.enabled == NO - Currently this can happen if you use - // `-initWithFrame:` rather than one of the provided initializers - // - - self.prepareAnnotationView = ^(MGLAnnotationView *view) { - view.enabled = NO; - }; - - self.mapView.contentInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0); - - MGLCameraChangeReason reasonBefore = self.mapView.cameraChangeReasonBitmask; - XCTAssert(reasonBefore == MGLCameraChangeReasonNone, @"Camera should not have moved at start of test"); - - // Create annotation - MGLPointFeature *point = [[MGLPointFeature alloc] init]; - point.title = NSStringFromSelector(_cmd); - point.coordinate = CLLocationCoordinate2DMake(0.0, 0.0); - - MGLCoordinateBounds coordinateBounds = [self.mapView convertRect:self.mapView.bounds toCoordinateBoundsFromView:self.mapView]; - XCTAssert(MGLCoordinateInCoordinateBounds(point.coordinate, coordinateBounds), @"The test point should be within the visible map view"); - - // Select on screen annotation (DO NOT ADD FIRST). - [self.mapView selectAnnotation:point animated:YES]; - - // Expect - the camera NOT to move. - MGLCameraChangeReason reasonAfter = self.mapView.cameraChangeReasonBitmask; - XCTAssert(reasonAfter == MGLCameraChangeReasonNone, @"Camera should not have moved"); -} - -- (void)checkDefaultPropertiesForAnnotationView:(MGLAnnotationView*)view { - XCTAssertNil(view.annotation); - XCTAssertNil(view.reuseIdentifier); - XCTAssertEqual(view.centerOffset.dx, 0.0); - XCTAssertEqual(view.centerOffset.dy, 0.0); - XCTAssertFalse(view.scalesWithViewingDistance); - XCTAssertFalse(view.rotatesToMatchCamera); - XCTAssertFalse(view.isSelected); - XCTAssert(view.isEnabled); - XCTAssertFalse(view.isDraggable); - XCTAssertEqual(view.dragState, MGLAnnotationViewDragStateNone); -} - -- (void)testAnnotationViewInitWithFrame { - CGRect frame = CGRectMake(10.0, 10.0, 100.0, 100.0); - MGLAnnotationView *view = [[MGLAnnotationView alloc] initWithFrame:frame]; - [self checkDefaultPropertiesForAnnotationView:view]; -} - -- (void)testAnnotationViewInitWithReuseIdentifier { - MGLAnnotationView *view = [[MGLAnnotationView alloc] initWithReuseIdentifier:nil]; - [self checkDefaultPropertiesForAnnotationView:view]; -} - -- (void)testSelectingADisabledAnnotationView { - self.prepareAnnotationView = ^(MGLAnnotationView *view) { - view.enabled = NO; - }; - - // Create annotation - MGLPointFeature *point = [[MGLPointFeature alloc] init]; - point.title = NSStringFromSelector(_cmd); - point.coordinate = CLLocationCoordinate2DMake(0.0, 0.0); - - XCTAssert(self.mapView.selectedAnnotations.count == 0, @"There should be 0 selected annotations"); - - [self.mapView selectAnnotation:point animated:NO]; - - XCTAssert(self.mapView.selectedAnnotations.count == 0, @"There should be 0 selected annotations"); -} - -- (void)testAddAnnotationWithBoundaryCoordinates -{ - typedef struct { - CLLocationDegrees lat; - CLLocationDegrees lon; - BOOL expectation; - } TestParam; - - TestParam params[] = { - // Lat Lon Valid - { -91.0, 0.0, NO}, - - // The follow coordinate fails, essentially because the following in projection.hpp - // - // util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) - // - // boils down to ln(0) - // - // It makes sense that -90° latitude (south pole) should be invalid from a projection point - // of view, but in that case shouldn't +90° (north pole) also be invalid? - // - // In Obj-C code, perhaps we need to replace usage of CLLocationCoordinate2DIsValid for an - // MGL one... - - { -90.0, 0.0, YES}, // South pole. Should this really be considered an invalid coordinate? - - // The rest for completeness - { -89.0, 0.0, YES}, - { 90.0, 0.0, YES}, // North pole. Similarly, should this one be considered invalid? - { 91.0, 0.0, NO}, - - { 0.0, -181.0, NO}, - { 0.0, -180.0, YES}, - { 0.0, 180.0, YES}, - { 0.0, 181.0, NO}, - }; - - for (int i = 0; i < sizeof(params)/sizeof(params[0]); i++) { - TestParam param = params[i]; - - // Essentially a deconstructed -[MGLMapView convertCoordinate:toPointToView] - CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(param.lat, param.lon); - NSString *coordDesc = [NSString stringWithFormat:@"(%0.1f,%0.1f)", param.lat, param.lon]; - - XCTAssert(CLLocationCoordinate2DIsValid(coordinate) == param.expectation, @"Unexpected valid result for coordinate %@", coordDesc); - - CGPoint point = [_mapView convertCoordinate:coordinate toPointToView:_mapView]; - (void)point; - XCTAssert(isnan(point.x) != param.expectation, @"Unexpected point.x for coordinate %@", coordDesc); - XCTAssert(isnan(point.y) != param.expectation, @"Unexpected point.y for coordinate %@", coordDesc); - - if (param.expectation) { - // If we expect a valid coordinate, let's finally try to add an annotation - - // The above method is called by the following, which will trigger CALayer to raise an - // exception - MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; - annotation.coordinate = coordinate; - - @try { - [_mapView addAnnotation:annotation]; - } - @catch (NSException *e) { - XCTFail("addAnnotation triggered exception: %@ for coordinate %@", e, coordDesc); - } - } - } -} - -#pragma mark - MGLMapViewDelegate - - - -- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id)annotation -{ - MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer]; - - if (!annotationView) - { - annotationView = [[MGLAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MGLTestAnnotationReuseIdentifer]; - } - - if (self.prepareAnnotationView) { - self.prepareAnnotationView(annotationView); - } - - _annotationView = annotationView; - - return annotationView; -} - -- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews -{ - [_expectation fulfill]; -} - -- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id)annotation -{ - self.annotationSelectedCount++; -} - -@end diff --git a/platform/ios/test/MGLAnnotationViewTests.mm b/platform/ios/test/MGLAnnotationViewTests.mm new file mode 100644 index 0000000000..b2ce145535 --- /dev/null +++ b/platform/ios/test/MGLAnnotationViewTests.mm @@ -0,0 +1,336 @@ +#import +#import + +#import "../../darwin/src/MGLGeometry_Private.h" + +static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer"; + + +@interface MGLMapView (Tests) +@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask; +@end + + + +@interface MGLCustomAnnotationView : MGLAnnotationView + +@end + +@implementation MGLCustomAnnotationView + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + return [super initWithReuseIdentifier:@"reuse-id"]; +} + +@end + +@interface MGLAnnotationView (Test) + +@property (nonatomic) MGLMapView *mapView; +@property (nonatomic, readwrite) MGLAnnotationViewDragState dragState; +- (void)setDragState:(MGLAnnotationViewDragState)dragState; + +@end + +@interface MGLMapView (Test) +@property (nonatomic) UIView *calloutViewForSelectedAnnotation; +@end + +@interface MGLTestAnnotation : NSObject +@property (nonatomic, assign) CLLocationCoordinate2D coordinate; +@end + +@implementation MGLTestAnnotation +@end + +@interface MGLTestCalloutView: UIView +@property (nonatomic) BOOL didCallDismissCalloutAnimated; +@property (nonatomic, strong) id representedObject; +@property (nonatomic, strong) UIView *leftAccessoryView; +@property (nonatomic, strong) UIView *rightAccessoryView; +@property (nonatomic, weak) id delegate; +@end + +@implementation MGLTestCalloutView + +- (void)dismissCalloutAnimated:(BOOL)animated +{ + _didCallDismissCalloutAnimated = YES; +} + +- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {} + +@end + +@interface MGLAnnotationViewTests : XCTestCase +@property (nonatomic) XCTestExpectation *expectation; +@property (nonatomic) MGLMapView *mapView; +@property (nonatomic, weak) MGLAnnotationView *annotationView; +@property (nonatomic) NSInteger annotationSelectedCount; +@property (nonatomic) void (^prepareAnnotationView)(MGLAnnotationView*); +@end + +@implementation MGLAnnotationViewTests + +- (void)setUp +{ + [super setUp]; + _mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)]; + _mapView.delegate = self; +} + +- (void)testAnnotationView +{ + _expectation = [self expectationWithDescription:@"annotation property"]; + + MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; + [_mapView addAnnotation:annotation]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + + XCTAssert(_mapView.annotations.count == 1, @"number of annotations should be 1"); + XCTAssertNotNil(_annotationView.annotation, @"annotation property should not be nil"); + XCTAssertNotNil(_annotationView.mapView, @"mapView property should not be nil"); + + MGLTestCalloutView *testCalloutView = [[MGLTestCalloutView alloc] init]; + _mapView.calloutViewForSelectedAnnotation = testCalloutView; + _annotationView.dragState = MGLAnnotationViewDragStateStarting; + XCTAssertTrue(testCalloutView.didCallDismissCalloutAnimated, @"callout view was not dismissed"); + + [_mapView removeAnnotation:_annotationView.annotation]; + + XCTAssert(_mapView.annotations.count == 0, @"number of annotations should be 0"); + XCTAssertNil(_annotationView.annotation, @"annotation property should be nil"); +} + +- (void)testCustomAnnotationView +{ + MGLCustomAnnotationView *customAnnotationView = [[MGLCustomAnnotationView alloc] initWithReuseIdentifier:@"reuse-id"]; + XCTAssertNotNil(customAnnotationView); +} + +- (void)testSelectingOffscreenAnnotation +{ + // Partial test for https://github.com/mapbox/mapbox-gl-native/issues/9790 + + // This isn't quite the same as in updateAnnotationViews, but should be sufficient for this test. + MGLCoordinateBounds coordinateBounds = [_mapView convertRect:_mapView.bounds toCoordinateBoundsFromView:_mapView]; + + // -90 latitude is invalid. TBD. + BOOL anyOffscreen = NO; + NSInteger selectionCount = 0; + + for (NSInteger latitude = -89; latitude <= 90; latitude += 10) + { + for (NSInteger longitude = -180; longitude <= 180; longitude += 10) + { + MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; + + annotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude); + [_mapView addAnnotation:annotation]; + + if (!(MGLCoordinateInCoordinateBounds(annotation.coordinate, coordinateBounds))) + anyOffscreen = YES; + + XCTAssertNil(_mapView.selectedAnnotations.firstObject, @"There should be no selected annotation"); + + // First selection + [_mapView selectAnnotation:annotation animated:NO]; + selectionCount++; + + XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation"); + XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected"); + + // Deselect + [_mapView deselectAnnotation:annotation animated:NO]; + XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations"); + + // Second selection + _mapView.selectedAnnotations = @[annotation]; + selectionCount++; + + XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should be 1 selected annotation"); + XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected"); + + // Deselect + [_mapView deselectAnnotation:annotation animated:NO]; + XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations"); + } + } + + XCTAssert(anyOffscreen, @"At least one of these annotations should be offscreen"); + XCTAssertEqual(selectionCount, self.annotationSelectedCount, @"-mapView:didSelectAnnotation: should be called for each selection"); +} + +- (void)testSelectingOnscreenAnnotationThatHasNotBeenAdded { + // See https://github.com/mapbox/mapbox-gl-native/issues/11476 + + // This bug occurs under the following conditions: + // + // - There are content insets (e.g. navigation bar) for the compare against + // CGRectZero (now CGRectNull) + // - annotationView.enabled == NO - Currently this can happen if you use + // `-initWithFrame:` rather than one of the provided initializers + // + + self.prepareAnnotationView = ^(MGLAnnotationView *view) { + view.enabled = NO; + }; + + self.mapView.contentInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0); + + MGLCameraChangeReason reasonBefore = self.mapView.cameraChangeReasonBitmask; + XCTAssert(reasonBefore == MGLCameraChangeReasonNone, @"Camera should not have moved at start of test"); + + // Create annotation + MGLPointFeature *point = [[MGLPointFeature alloc] init]; + point.title = NSStringFromSelector(_cmd); + point.coordinate = CLLocationCoordinate2DMake(0.0, 0.0); + + MGLCoordinateBounds coordinateBounds = [self.mapView convertRect:self.mapView.bounds toCoordinateBoundsFromView:self.mapView]; + XCTAssert(MGLCoordinateInCoordinateBounds(point.coordinate, coordinateBounds), @"The test point should be within the visible map view"); + + // Select on screen annotation (DO NOT ADD FIRST). + [self.mapView selectAnnotation:point animated:YES]; + + // Expect - the camera NOT to move. + MGLCameraChangeReason reasonAfter = self.mapView.cameraChangeReasonBitmask; + XCTAssert(reasonAfter == MGLCameraChangeReasonNone, @"Camera should not have moved"); +} + +- (void)checkDefaultPropertiesForAnnotationView:(MGLAnnotationView*)view { + XCTAssertNil(view.annotation); + XCTAssertNil(view.reuseIdentifier); + XCTAssertEqual(view.centerOffset.dx, 0.0); + XCTAssertEqual(view.centerOffset.dy, 0.0); + XCTAssertFalse(view.scalesWithViewingDistance); + XCTAssertFalse(view.rotatesToMatchCamera); + XCTAssertFalse(view.isSelected); + XCTAssert(view.isEnabled); + XCTAssertFalse(view.isDraggable); + XCTAssertEqual(view.dragState, MGLAnnotationViewDragStateNone); +} + +- (void)testAnnotationViewInitWithFrame { + CGRect frame = CGRectMake(10.0, 10.0, 100.0, 100.0); + MGLAnnotationView *view = [[MGLAnnotationView alloc] initWithFrame:frame]; + [self checkDefaultPropertiesForAnnotationView:view]; +} + +- (void)testAnnotationViewInitWithReuseIdentifier { + MGLAnnotationView *view = [[MGLAnnotationView alloc] initWithReuseIdentifier:nil]; + [self checkDefaultPropertiesForAnnotationView:view]; +} + +- (void)testSelectingADisabledAnnotationView { + self.prepareAnnotationView = ^(MGLAnnotationView *view) { + view.enabled = NO; + }; + + // Create annotation + MGLPointFeature *point = [[MGLPointFeature alloc] init]; + point.title = NSStringFromSelector(_cmd); + point.coordinate = CLLocationCoordinate2DMake(0.0, 0.0); + + XCTAssert(self.mapView.selectedAnnotations.count == 0, @"There should be 0 selected annotations"); + + [self.mapView selectAnnotation:point animated:NO]; + + XCTAssert(self.mapView.selectedAnnotations.count == 0, @"There should be 0 selected annotations"); +} + +- (void)disabled_testAddAnnotationWithBoundaryCoordinatesPENDING +{ + typedef struct { + CLLocationDegrees lat; + CLLocationDegrees lon; + BOOL expectation; // CLLocationCoordinate2DIsValid + BOOL mglExpectation; // MGLLocationCoordinate2DIsValid + } TestParam; + + TestParam params[] = { + // Lat Lon CL MGL + { -91.0, 0.0, NO, NO }, + + { -90.0, 0.0, YES, YES }, // South pole. Should this really be considered an invalid coordinate? + + // The rest for completeness + { -89.0, 0.0, YES, YES }, + { 90.0, 0.0, YES, YES }, // North pole. Similarly, should this one be considered invalid? + { 91.0, 0.0, NO, NO }, + + { 0.0, -181.0, NO, YES }, + { 0.0, -180.0, YES, YES }, + { 0.0, 180.0, YES, YES }, + { 0.0, 181.0, NO, YES }, + }; + + for (int i = 0; i < sizeof(params)/sizeof(params[0]); i++) { + TestParam param = params[i]; + + // Essentially a deconstructed -[MGLMapView convertCoordinate:toPointToView] + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(param.lat, param.lon); + NSString *coordDesc = [NSString stringWithFormat:@"(%0.1f,%0.1f)", param.lat, param.lon]; + + XCTAssert(CLLocationCoordinate2DIsValid(coordinate) == param.expectation, @"Unexpected CLLocationCoordinate2DIsValid for coordinate %@", coordDesc); + XCTAssert(MGLLocationCoordinate2DIsValid(coordinate) == param.mglExpectation, @"Unexpected MGLLocationCoordinate2DIsValid for coordinate %@", coordDesc); + + + CGPoint point = [_mapView convertCoordinate:coordinate toPointToView:_mapView]; + (void)point; + + // TODO: + XCTAssert(isnan(point.x) != param.expectation, @"Unexpected point.x for coordinate %@", coordDesc); + XCTAssert(isnan(point.y) != param.expectation, @"Unexpected point.y for coordinate %@", coordDesc); + + // TODO: which one + if (param.expectation) { + // If we expect a valid coordinate, let's finally try to add an annotation + + // The above method is called by the following, which will trigger CALayer to raise an + // exception + MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init]; + annotation.coordinate = coordinate; + + @try { + [_mapView addAnnotation:annotation]; + } + @catch (NSException *e) { + XCTFail("addAnnotation triggered exception: %@ for coordinate %@", e, coordDesc); + } + } + } +} + +#pragma mark - MGLMapViewDelegate - + + +- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id)annotation +{ + MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer]; + + if (!annotationView) + { + annotationView = [[MGLAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MGLTestAnnotationReuseIdentifer]; + } + + if (self.prepareAnnotationView) { + self.prepareAnnotationView(annotationView); + } + + _annotationView = annotationView; + + return annotationView; +} + +- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews +{ + [_expectation fulfill]; +} + +- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id)annotation +{ + self.annotationSelectedCount++; +} + +@end -- cgit v1.2.1