summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Rex <julian.rex@mapbox.com>2019-03-24 17:44:41 -0400
committerJulian Rex <julian.rex@mapbox.com>2019-04-16 11:38:41 -0400
commite3d544ee1a34532681e746b90ff15a66fa17f535 (patch)
tree958685e0dce3628ed52799d504db771e85b99115
parentefdbcd95f1fcf0b81a243d3930677b35c732286b (diff)
downloadqtlocation-mapboxgl-e3d544ee1a34532681e746b90ff15a66fa17f535.tar.gz
[ios] Add support for custom subclass in integration tests.
-rw-r--r--platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m82
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.h1
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m6
-rw-r--r--platform/ios/Integration Tests/MGLMockApplication.m12
4 files changed, 89 insertions, 12 deletions
diff --git a/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m b/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m
index 148f019845..b004c05998 100644
--- a/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m
@@ -6,17 +6,37 @@
@property (nonatomic) BOOL rendererWasFlushed;
@property (nonatomic, getter=isDormant) BOOL dormant;
@property (nonatomic) CADisplayLink *displayLink;
+- (void)updateFromDisplayLink:(CADisplayLink *)displayLink;
@end
@protocol MGLApplication;
typedef void (^MGLNotificationBlock)(NSNotification*);
+#pragma mark - MGLBackgroundIntegrationTestMapView
+
+@interface MGLBackgroundIntegrationTestMapView : MGLMapView
+@property (nonatomic, copy) dispatch_block_t displayLinkDidUpdate;
+@end
+
+@implementation MGLBackgroundIntegrationTestMapView
+- (void)updateFromDisplayLink:(CADisplayLink *)displayLink {
+ [super updateFromDisplayLink:displayLink];
+
+ if (self.displayLinkDidUpdate) {
+ self.displayLinkDidUpdate();
+ }
+}
+@end
+
+#pragma mark - MGLBackgroundIntegrationTest
+
@interface MGLBackgroundIntegrationTest : MGLMapViewIntegrationTest
@property (nonatomic) id<MGLApplication> oldApplication;
@property (nonatomic) MGLMockApplication *mockApplication;
@property (nonatomic, copy) MGLNotificationBlock willEnterForeground;
@property (nonatomic, copy) MGLNotificationBlock didEnterBackground;
+@property (nonatomic, copy) dispatch_block_t displayLinkDidUpdate;
@end
@implementation MGLBackgroundIntegrationTest
@@ -65,6 +85,18 @@ typedef void (^MGLNotificationBlock)(NSNotification*);
}
}
+- (MGLMapView*)testMapViewWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL {
+ MGLBackgroundIntegrationTestMapView *mapView = [[MGLBackgroundIntegrationTestMapView alloc] initWithFrame:frame styleURL:styleURL];
+
+ mapView.displayLinkDidUpdate = ^{
+ if (self.displayLinkDidUpdate) {
+ self.displayLinkDidUpdate();
+ }
+ };
+
+ return mapView;
+}
+
#pragma mark - Tests
- (void)testRendererWhenGoingIntoBackground {
@@ -74,7 +106,7 @@ typedef void (^MGLNotificationBlock)(NSNotification*);
XCTAssert(self.mapView.application.applicationState == UIApplicationStateActive);
XCTAssertFalse(self.mapView.rendererWasFlushed);
- __weak MGLMapView *weakMapView = self.mapView;
+ __weak typeof(self) weakSelf = self;
//
// Enter background
@@ -84,26 +116,52 @@ typedef void (^MGLNotificationBlock)(NSNotification*);
didEnterBackgroundExpectation.expectedFulfillmentCount = 1;
didEnterBackgroundExpectation.assertForOverFulfill = YES;
+ __block NSInteger displayLinkCount = 0;
+
+ self.displayLinkDidUpdate = ^{
+ displayLinkCount++;
+ };
+
self.didEnterBackground = ^(__unused NSNotification *notification){
- MGLMapView *strongMapView = weakMapView;
+ typeof(self) strongSelf = weakSelf;
+ MGLMapView *mapView = strongSelf.mapView;
+
+ // In general, because order of notifications is not guaranteed
+ // the following asserts are somewhat meaningless (don't do this in
+ // production) - however, because we're mocking their delivery (and
+ // we're tracking a bug)...
+
+ // MGLMapView responds to UIApplicationDidEnterBackgroundNotification and
+ // marks the map view as dormant. However, depending on the order of
+ // creation it's totally possible for client code also responding to
+ // this notification to be called first - and then trigger a scenario where
+ // GL can be rendering in the background - which can cause crashes.
+
+ MGLTestAssert(strongSelf, !mapView.isDormant);
+ MGLTestAssert(strongSelf, !mapView.displayLink.isPaused);
+
+ displayLinkCount = 0;
// Remove the map view, and re-add to try and force a bad situation
- UIView *parentView = strongMapView.superview;
+ UIView *parentView = mapView.superview;
NSLog(@"Removing MGLMapView from super view");
- [strongMapView removeFromSuperview];
+ [mapView removeFromSuperview];
// Re-add
NSLog(@"Re-adding MGLMapView as child");
- [parentView addSubview:strongMapView];
- [strongMapView.topAnchor constraintEqualToAnchor:parentView.topAnchor].active = YES;
- [strongMapView.leftAnchor constraintEqualToAnchor:parentView.leftAnchor].active = YES;
- [strongMapView.rightAnchor constraintEqualToAnchor:parentView.rightAnchor].active = YES;
- [strongMapView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor].active = YES;
+ [parentView addSubview:mapView];
+
+ MGLTestAssert(strongSelf, displayLinkCount == 0, @"updateDisplayLink was called %ld times", displayLinkCount);
+
+ [mapView.topAnchor constraintEqualToAnchor:parentView.topAnchor].active = YES;
+ [mapView.leftAnchor constraintEqualToAnchor:parentView.leftAnchor].active = YES;
+ [mapView.rightAnchor constraintEqualToAnchor:parentView.rightAnchor].active = YES;
+ [mapView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor].active = YES;
};
[self.mockApplication enterBackground];
- [self waitForExpectations:@[didEnterBackgroundExpectation] timeout:2.0];
+ [self waitForExpectations:@[didEnterBackgroundExpectation] timeout:0.5];
XCTAssert(self.mapView.isDormant);
XCTAssert(self.mapView.displayLink.isPaused);
@@ -119,11 +177,13 @@ typedef void (^MGLNotificationBlock)(NSNotification*);
willEnterForegroundExpectation.assertForOverFulfill = YES;
self.willEnterForeground = ^(NSNotification *notification) {
+ displayLinkCount = 0;
[willEnterForegroundExpectation fulfill];
};
[self.mockApplication enterForeground];
- [self waitForExpectations:@[willEnterForegroundExpectation] timeout:2.0];
+ XCTAssert(displayLinkCount == 0, @"updateDisplayLink was called %ld times", displayLinkCount);
+ [self waitForExpectations:@[willEnterForegroundExpectation] timeout:0.5];
XCTAssertFalse(self.mapView.isDormant);
XCTAssertFalse(self.mapView.displayLink.isPaused);
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
index dedafdb83a..d7489ec52a 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
@@ -38,6 +38,7 @@
@property (nonatomic) id<MGLCalloutView> (^mapViewCalloutViewForAnnotation)(MGLMapView *mapView, id<MGLAnnotation> annotation);
// Utility methods
+- (MGLMapView*)testMapViewWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL;
- (NSString*)validAccessToken;
- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout;
- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout;
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
index 2c89bb1c77..2d329f3a09 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -32,13 +32,17 @@
return accessToken;
}
+- (MGLMapView*)testMapViewWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL {
+ return [[MGLMapView alloc] initWithFrame:frame styleURL:styleURL];
+}
+
- (void)setUp {
[super setUp];
[MGLAccountManager setAccessToken:@"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 testMapViewWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
self.mapView.delegate = self;
UIView *superView = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
diff --git a/platform/ios/Integration Tests/MGLMockApplication.m b/platform/ios/Integration Tests/MGLMockApplication.m
index e472b52d42..3654e3ed54 100644
--- a/platform/ios/Integration Tests/MGLMockApplication.m
+++ b/platform/ios/Integration Tests/MGLMockApplication.m
@@ -2,6 +2,10 @@
#define TRACE() NSLog(@"%s", __PRETTY_FUNCTION__)
+@interface MGLMockApplication ()
+@property(nonatomic, readwrite) UIApplicationState applicationState;
+@end
+
@implementation MGLMockApplication
- (instancetype)init {
@@ -14,10 +18,18 @@
- (void)enterBackground {
TRACE();
+ self.applicationState = UIApplicationStateInactive;
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillResignActiveNotification object:self];
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidEnterBackgroundNotification object:self];
+ self.applicationState = UIApplicationStateBackground;
}
- (void)enterForeground {
TRACE();
+ self.applicationState = UIApplicationStateInactive;
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillEnterForegroundNotification object:self];
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification object:self];
+ self.applicationState = UIApplicationStateActive;
}
- (BOOL)openURL:(NSURL*)url {