diff options
author | Julian Rex <julian.rex@mapbox.com> | 2019-08-23 14:43:47 -0400 |
---|---|---|
committer | Julian Rex <julian.rex@mapbox.com> | 2019-08-23 14:48:17 -0400 |
commit | c2975f0104f5126db9062e9492e3b83627614244 (patch) | |
tree | 8b0aeb1b1bf844163baf10d4f16300af41c74de1 | |
parent | 77138dde3ad30a120d6afe8507bf865433956e86 (diff) | |
download | qtlocation-mapboxgl-c2975f0104f5126db9062e9492e3b83627614244.tar.gz |
[ios] Added tests to check for pending block inclusion
-rw-r--r-- | platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m | 364 | ||||
-rw-r--r-- | platform/ios/ios.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 21 |
3 files changed, 380 insertions, 11 deletions
diff --git a/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m new file mode 100644 index 0000000000..ffb3b1ae0b --- /dev/null +++ b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m @@ -0,0 +1,364 @@ +#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); + + // Now hide the mapview + [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); + + // Now hide the mapview + [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; + 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]); + + 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/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 5a5cb00b49..018a5f9368 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -508,6 +508,7 @@ CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */; }; CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */; }; CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; }; + CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */; }; CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CA65C4F821E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1198,11 +1199,12 @@ CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapSnapshotterTest.m; sourceTree = "<group>"; }; CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = "<group>"; }; CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = "<group>"; }; + CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewPendingBlockTests.m; sourceTree = "<group>"; }; 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>"; }; - 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>"; }; + CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; 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>"; }; @@ -1554,6 +1556,7 @@ CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */, 077061DB215DA11F000FEF62 /* MGLTestLocationManager.h */, 077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */, + CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */, ); path = "Integration Tests"; sourceTree = "<group>"; @@ -3196,6 +3199,7 @@ CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */, CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */, 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.mm in Sources */, + CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */, CA88DC3021C85D900059ED5A /* MGLStyleURLIntegrationTest.m in Sources */, CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */, CA7766832229C10E0008DE9E /* MGLCompactCalloutView.m in Sources */, diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 8f0c451d63..aeae962cac 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1059,18 +1059,19 @@ public: } } -- (BOOL)addPendingBlock:(dispatch_block_t)block +- (BOOL)scheduleTransitionCompletion:(dispatch_block_t)block { // Only add a block if the display link (that calls processPendingBlocks) is // running, otherwise fall back to calling immediately. - BOOL addBlock = (_displayLink && !_displayLink.isPaused); - - if (addBlock) + if (_displayLink && !_displayLink.isPaused) { + [self willChangeValueForKey:@"pendingCompletionBlocks"]; [self.pendingCompletionBlocks addObject:block]; + [self didChangeValueForKey:@"pendingCompletionBlocks"]; + return YES; } - - return addBlock; + + return NO; } #pragma mark - Life Cycle - @@ -3366,7 +3367,7 @@ public: __weak __typeof__(self) weakSelf = self; pendingCompletion = ^{ - if (![weakSelf addPendingBlock:completion]) + if (![weakSelf scheduleTransitionCompletion:completion]) { completion(); } @@ -3561,7 +3562,7 @@ public: __weak __typeof__(self) weakSelf = self; pendingCompletion = ^{ - if (![weakSelf addPendingBlock:completion]) + if (![weakSelf scheduleTransitionCompletion:completion]) { completion(); } @@ -3729,7 +3730,7 @@ public: __weak __typeof__(self) weakSelf = self; pendingCompletion = ^{ - if (![weakSelf addPendingBlock:completion]) + if (![weakSelf scheduleTransitionCompletion:completion]) { completion(); } @@ -3808,7 +3809,7 @@ public: __weak __typeof__(self) weakSelf = self; pendingCompletion = ^{ - if (![weakSelf addPendingBlock:completion]) + if (![weakSelf scheduleTransitionCompletion:completion]) { completion(); } |