diff options
author | Julian Rex <julian.rex@mapbox.com> | 2019-09-06 10:56:12 -0400 |
---|---|---|
committer | Julian Rex <julian.rex@mapbox.com> | 2019-09-18 16:50:25 -0400 |
commit | 940b46a42a06a33f26878d4537e4cd574a806aee (patch) | |
tree | 1e111d69dd200b5944afc1113fa87503ed9f45ef | |
parent | 30f2de6c2010c4991825f529b519b8669d340e7a (diff) | |
download | qtlocation-mapboxgl-940b46a42a06a33f26878d4537e4cd574a806aee.tar.gz |
[ios] Adds invalidate test, and test that reloads packs during removal
-rw-r--r-- | platform/darwin/src/MGLOfflinePack.mm | 5 | ||||
-rw-r--r-- | platform/darwin/src/MGLOfflineStorage.mm | 4 | ||||
-rw-r--r-- | platform/darwin/test/MGLOfflinePackTests.mm (renamed from platform/darwin/test/MGLOfflinePackTests.m) | 17 | ||||
-rw-r--r-- | platform/darwin/test/MGLOfflineStorageTests.mm | 194 | ||||
-rw-r--r-- | platform/ios/ios.xcodeproj/project.pbxproj | 8 |
5 files changed, 103 insertions, 125 deletions
diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 0f2e8180fa..6648867a62 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -135,9 +135,12 @@ private: - (void)invalidate { MGLLogInfo(@"Invalidating pack."); MGLAssert(_state != MGLOfflinePackStateInvalid, @"Cannot invalidate an already invalid offline pack."); + MGLAssert(self.mbglOfflineRegion, @"Should have a valid region"); self.state = MGLOfflinePackStateInvalid; - _mbglFileSource->setOfflineRegionObserver(*self.mbglOfflineRegion, nullptr); + if (self.mbglOfflineRegion) { + _mbglFileSource->setOfflineRegionObserver(*self.mbglOfflineRegion, nullptr); + } self.mbglOfflineRegion = nil; } diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 93f986a518..32d1735bc0 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -426,11 +426,15 @@ const MGLExceptionName MGLUnsupportedRegionTypeException = @"MGLUnsupportedRegio - (void)_removePack:(MGLOfflinePack *)pack withCompletionHandler:(MGLOfflinePackRemovalCompletionHandler)completion { mbgl::OfflineRegion *mbglOfflineRegion = pack.mbglOfflineRegion; + [pack invalidate]; + if (!mbglOfflineRegion) { + MGLAssert(pack.state == MGLOfflinePackStateInvalid, @"State should be invalid"); completion(nil); return; } + _mbglFileSource->deleteOfflineRegion(std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) { NSError *error; if (exception) { diff --git a/platform/darwin/test/MGLOfflinePackTests.m b/platform/darwin/test/MGLOfflinePackTests.mm index a33665ff0a..1306b182d1 100644 --- a/platform/darwin/test/MGLOfflinePackTests.m +++ b/platform/darwin/test/MGLOfflinePackTests.mm @@ -1,5 +1,7 @@ #import <Mapbox/Mapbox.h> #import <XCTest/XCTest.h> +#import "MGLOfflinePack_Private.h" +#import "MGLTestAssertionHandler.h" @interface MGLOfflinePackTests : XCTestCase @@ -18,6 +20,21 @@ XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, MGLInvalidOfflinePackException, @"Invalid offline pack should raise an exception when being suspended."); } +- (void)testInvalidatingAnInvalidPack { + MGLOfflinePack *invalidPack = [[MGLOfflinePack alloc] init]; + + XCTAssertThrowsSpecificNamed([invalidPack invalidate], NSException, NSInternalInconsistencyException, @"Invalid offline pack should raise an exception when being invalidated."); + + // Now try again, without asserts + NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler]; + [[[NSThread currentThread] threadDictionary] setValue:[[MGLTestAssertionHandler alloc] init] forKey:NSAssertionHandlerKey]; + + // Make sure this doesn't crash without asserts + [invalidPack invalidate]; + + [[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey]; +} + - (void)testProgressBoxing { MGLOfflinePackProgress progress = { .countOfResourcesCompleted = 3, diff --git a/platform/darwin/test/MGLOfflineStorageTests.mm b/platform/darwin/test/MGLOfflineStorageTests.mm index 0138d911bb..c4f8b84446 100644 --- a/platform/darwin/test/MGLOfflineStorageTests.mm +++ b/platform/darwin/test/MGLOfflineStorageTests.mm @@ -311,52 +311,10 @@ XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack."); } -- (void)testRemovePackTwiceInSuccession { - - [self addPackIfNeeded]; - - NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; - - MGLOfflinePack *pack = [MGLOfflineStorage sharedOfflineStorage].packs.lastObject; - XCTAssertNotNil(pack, @"Added pack should still exist."); - - [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) { - const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]); - NSIndexSet *indices = change[NSKeyValueChangeIndexesKey]; - return changeKind == NSKeyValueChangeRemoval && indices.count == 1; - }]; +- (void)addPacks:(NSInteger)count { - XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"]; - - [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:nil]; - - NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler]; - [[[NSThread currentThread] threadDictionary] setValue:[[MGLTestAssertionHandler alloc] init] forKey:NSAssertionHandlerKey]; - - [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) { - XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler."); - [completionHandlerExpectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:5 handler:nil]; - - [[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey]; - - XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously."); - - XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack."); -} - -- (void)addPackIfNeeded { XCTestExpectation *expectation = [self expectationWithDescription:@"added packs"]; - - [self addPacks:1 completion:^{ - [expectation fulfill]; - }]; - [self waitForExpectations:@[expectation] timeout:1.0]; -} -- (void)addPacks:(NSInteger)count completion:(dispatch_block_t)block { NSURL *styleURL = [MGLStyle lightStyleURLWithVersion:8]; MGLCoordinateBounds bounds[] = { @@ -390,101 +348,97 @@ }]; } - if (block) { - dispatch_group_notify(group, dispatch_get_main_queue(), block); - } + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + [expectation fulfill]; + }); + + [self waitForExpectations:@[expectation] timeout:1.0]; } -// Test to explore https://github.com/mapbox/mapbox-gl-native/issues/15536 -- (void)testRemovePacksOnBackgroundQueueWhileReloading { +- (void)testRemovePackTwiceInSuccession { + + [self addPacks:1]; + NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; - if (countOfPacks < 4) { - XCTestExpectation *expectation = [self expectationWithDescription:@"added packs"]; + MGLOfflinePack *pack = [MGLOfflineStorage sharedOfflineStorage].packs.lastObject; + XCTAssertNotNil(pack, @"Added pack should still exist."); - [self addPacks:4 completion:^{ - [expectation fulfill]; - }]; - [self waitForExpectations:@[expectation] timeout:10.0]; - } + [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) { + const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]); + NSIndexSet *indices = change[NSKeyValueChangeIndexesKey]; + return changeKind == NSKeyValueChangeRemoval && indices.count == 1; + }]; + + XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"]; + + [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:nil]; + + NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler]; + [[[NSThread currentThread] threadDictionary] setValue:[[MGLTestAssertionHandler alloc] init] forKey:NSAssertionHandlerKey]; + + [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) { + XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler."); + [completionHandlerExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; + + [[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey]; + + XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously."); + + XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack."); + + NSLog(@"Test `%@` complete", NSStringFromSelector(_cmd)); +} + +- (void)test15536RemovePacksWhileReloading { + + // This test triggers + // + // throw std::runtime_error("Malformed offline region definition"); + // + // in offline.cpp + // + // Reloading packs, while trying to remove them is currently problematic. - countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; + [self addPacks:4]; + + NSInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; XCTAssert(countOfPacks > 0); // Now delete packs one by one - dispatch_queue_t queue = dispatch_queue_create("com.mapbox.testRemovePacks", DISPATCH_QUEUE_SERIAL); - - XCTestExpectation *expectation = [self expectationWithDescription:@"all packs removed"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"All packs removed"]; expectation.expectedFulfillmentCount = countOfPacks; MGLOfflineStorage *storage = [MGLOfflineStorage sharedOfflineStorage]; - + NSArray *packs = [storage.packs copy]; + + // Simulate what happens the first time sharedOfflineStorage is accessed [storage reloadPacks]; - - dispatch_async(queue, ^{ - - NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler]; - [[[NSThread currentThread] threadDictionary] setValue:[[MGLTestAssertionHandler alloc] init] forKey:NSAssertionHandlerKey]; + + NSArray *validPacks = [packs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) { + MGLOfflinePack *pack = (MGLOfflinePack*)evaluatedObject; + return pack.state != MGLOfflinePackStateInvalid; + }]]; - - NSArray *packs = storage.packs; - - if (!packs) { - return; - } - - NSArray *validPacks = [packs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) { - MGLOfflinePack *pack = (MGLOfflinePack*)evaluatedObject; - return pack.state != MGLOfflinePackStateInvalid; - }]]; - - for (MGLOfflinePack *pack in validPacks) { - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); -// [storage removePack:pack withCompletionHandler:nil]; - [storage removePack:pack withCompletionHandler:^(NSError * _Nullable error) { - dispatch_group_leave(group); - }]; - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - [expectation fulfill]; - } - - [[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey]; + NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler]; + [[[NSThread currentThread] threadDictionary] setValue:[[MGLTestAssertionHandler alloc] init] forKey:NSAssertionHandlerKey]; - }); + for (MGLOfflinePack *pack in validPacks) { + [storage removePack:pack withCompletionHandler:^(NSError * _Nullable error) { + [expectation fulfill]; + }]; + } - [self waitForExpectations:@[expectation] timeout:60.0]; - - countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; - XCTAssertEqual(countOfPacks, 0); - - - - // so on `keyPath` `packs` change (`NSKeyValueChange.settings`) -// we dispatch to a `SerialQueue` and remove the pack one by one and wait for the first one to be completed before proceeding to reduce the load on the Database access. -// serialQueue.async { [weak self] in -// guard let packs = storage.packs else { -// return [] -// } -// let validPacks = packs.filter { $0.state != .invalid } -// -// for pack in validPacks { -// let packGroup = DispatchGroup() -// packGroup.enter() -// storage.removePack(pack) { [weak self] (error) in -// packGroup.leave() -// } -// // Wait until the pack completion handler is executed -// // Marking the pack as removed for us -// packGroup.wait() -// } -// } + [[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey]; + + [self waitForExpectations:@[expectation] timeout:10.0]; + + NSLog(@"Test `%@` complete", NSStringFromSelector(_cmd)); } - - - - (void)testCountOfBytesCompleted { XCTAssertGreaterThan([MGLOfflineStorage sharedOfflineStorage].countOfBytesCompleted, 0UL); } diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index a138e4ef06..66541ca813 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -552,7 +552,7 @@ DA2DBBCE1D51E80400D38FF9 /* MGLStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */; }; DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */; }; - DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */; }; + DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.mm */; }; DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */; }; DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */; }; DA35A29E1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1247,7 +1247,7 @@ DA2E88511CC036F400F24E7B /* test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = test.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DA2E88551CC036F400F24E7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLGeometryTests.mm; path = ../../darwin/test/MGLGeometryTests.mm; sourceTree = "<group>"; }; - DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflinePackTests.m; path = ../../darwin/test/MGLOfflinePackTests.m; sourceTree = "<group>"; }; + DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflinePackTests.mm; path = ../../darwin/test/MGLOfflinePackTests.mm; sourceTree = "<group>"; }; DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = "<group>"; }; DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = "<group>"; }; DA33895F1FA3EAB7001EA329 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Foundation.strings"; sourceTree = "<group>"; }; @@ -2089,7 +2089,7 @@ 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */, 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */, DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */, - DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */, + DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.mm */, DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */, 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */, 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */, @@ -3308,7 +3308,7 @@ 9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */, 409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */, 9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.mm in Sources */, - DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */, + DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.mm in Sources */, 55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */, 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */, 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */, |