From d9741283aa325e5c892465490d717c82e8b9d1d3 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Tue, 28 Mar 2017 13:55:16 +0800 Subject: [PATCH 1/8] Allowing to re-save installation if LDS is enabled --- .../OfflineStore/PFOfflineStore.m | 8 +++++++- Parse/PFInstallation.m | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m index 262d3aaaa..39ada4300 100644 --- a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m +++ b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m @@ -1000,7 +1000,13 @@ - (void)updateObjectIdForObject:(PFObject *)object oldObjectId:(NSString *)oldObjectId newObjectId:(NSString *)newObjectId { if (oldObjectId != nil) { - PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); + if ([object isKindOfClass:[PFInstallation class]] + && newObjectId == nil) { + NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:oldObjectId]; + [self.classNameAndObjectIdToObjectMap removeObjectForKey:key]; + } else { + PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); + } return; } diff --git a/Parse/PFInstallation.m b/Parse/PFInstallation.m index 18b25223c..12e44cad4 100644 --- a/Parse/PFInstallation.m +++ b/Parse/PFInstallation.m @@ -28,6 +28,7 @@ #import "PFQueryPrivate.h" #import "Parse_Private.h" #import "PFErrorUtilities.h" +#import "PFObjectState_Private.h" @implementation PFInstallation (Private) @@ -83,6 +84,14 @@ - (NSString *)displayClassName { return NSStringFromClass([PFInstallation class]); } +///-------------------------------------- +#pragma mark - Properties +///-------------------------------------- + +- (void) setObjectId:(NSString *)objectId { + PFParameterAssertionFailure(@"Installation's objectIds cannot be changed"); +} + ///-------------------------------------- #pragma mark - Command Handlers ///-------------------------------------- @@ -223,16 +232,13 @@ - (void)setChannels:(NSArray *)channels { - (BFTask *)saveAsync:(BFTask *)toAwait { return [[super saveAsync:toAwait] continueWithBlock:^id(BFTask *task) { - // Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed. - if ([Parse _currentManager].offlineStoreLoaded) { - return task; - } - if (task.error.code == kPFErrorObjectNotFound) { @synchronized (self.lock) { // Retry the fetch as a save operation because this Installation was deleted on the server. // We always want [currentInstallation save] to succeed. - self.objectId = nil; + PFObjectState *state = [PFObjectState stateWithState:self._state]; + state.objectId = nil; + self._state = state; [self _markAllFieldsDirty]; return [super saveAsync:nil]; } From 9f38cdfea96202a85cfecfbafd86bf5862f91cb8 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Tue, 28 Mar 2017 15:54:30 +0800 Subject: [PATCH 2/8] fix target compatibility --- Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m index 39ada4300..f74693f68 100644 --- a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m +++ b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m @@ -1000,6 +1000,7 @@ - (void)updateObjectIdForObject:(PFObject *)object oldObjectId:(NSString *)oldObjectId newObjectId:(NSString *)newObjectId { if (oldObjectId != nil) { +#if TARGET_OS_IOS if ([object isKindOfClass:[PFInstallation class]] && newObjectId == nil) { NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:oldObjectId]; @@ -1007,6 +1008,9 @@ - (void)updateObjectIdForObject:(PFObject *)object } else { PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); } +#else + PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); +#endif return; } From 6e143c02613b8503d15793884ec1b49047e87912 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Thu, 6 Apr 2017 14:58:46 +0800 Subject: [PATCH 3/8] minor fixes & add test --- Parse.xcodeproj/project.pbxproj | 16 +++++++++++++ Parse/Internal/ParseManager.m | 6 +++++ Parse/Internal/ParseManagerPrivate.h | 16 +++++++++++++ Parse/PFInstallation.m | 4 +++- Tests/Unit/InstallationUnitTests.m | 36 ++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Parse/Internal/ParseManagerPrivate.h diff --git a/Parse.xcodeproj/project.pbxproj b/Parse.xcodeproj/project.pbxproj index d8df3f0a9..debffafb6 100644 --- a/Parse.xcodeproj/project.pbxproj +++ b/Parse.xcodeproj/project.pbxproj @@ -2566,6 +2566,13 @@ 97DE045A16321492007154E8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97DE045916321492007154E8 /* Security.framework */; }; 97DE045C163214C0007154E8 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97DE045B163214C0007154E8 /* SystemConfiguration.framework */; }; 97EB055516F7CCE400E09147 /* PFAnalytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 9739513916B9D28E0010B884 /* PFAnalytics.m */; }; + A6E2958C1E96173D009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E2958D1E96173F009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E2958E1E961741009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E2958F1E961742009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E295901E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E295911E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; + A6E295921E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; }; F50C66331B33A708001941A6 /* PFPushUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = F50C66311B33A708001941A6 /* PFPushUtilities.h */; }; F50C66341B33A708001941A6 /* PFPushUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = F50C66321B33A708001941A6 /* PFPushUtilities.m */; }; F50C667C1B34B231001941A6 /* PFPushUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = F50C66321B33A708001941A6 /* PFPushUtilities.m */; }; @@ -3470,6 +3477,7 @@ 97DE045B163214C0007154E8 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 97E18AE41623835600B17A67 /* PFLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFLocationManager.h; sourceTree = ""; }; 97E18AE51623835600B17A67 /* PFLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFLocationManager.m; sourceTree = ""; }; + A6E295801E961727009917BF /* ParseManagerPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParseManagerPrivate.h; sourceTree = ""; }; E9BBE98E16D18E5800CD7B52 /* PFObject+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "PFObject+Subclass.h"; path = "Parse/PFObject+Subclass.h"; sourceTree = SOURCE_ROOT; }; E9E81E8316EEF93E001D034F /* PFSubclassing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFSubclassing.h; sourceTree = ""; }; F50C66311B33A708001941A6 /* PFPushUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFPushUtilities.h; sourceTree = ""; }; @@ -3733,6 +3741,7 @@ 09EEA1351435143500E3A3FA /* ParseInternal.h */, 81A245F11B1FB188006A6953 /* PFDataProvider.h */, 812714861AE6F1270076AE8D /* ParseManager.h */, + A6E295801E961727009917BF /* ParseManagerPrivate.h */, 812714871AE6F1270076AE8D /* ParseManager.m */, 8124C8811B27542A00758E00 /* PFCoreDataProvider.h */, 8196D58B1B0BD23B000465A1 /* PFCoreManager.h */, @@ -5389,6 +5398,7 @@ 810156481BB3832700D7C7BD /* PFURLSessionDataTaskDelegate.h in Headers */, 81CA29D21C28DF8F00C4F34A /* PFAnonymousUtils+Deprecated.h in Headers */, 810156491BB3832700D7C7BD /* PFDateFormatter.h in Headers */, + A6E295911E961744009917BF /* ParseManagerPrivate.h in Headers */, 8101564A1BB3832700D7C7BD /* PFCloudCodeController.h in Headers */, 8101564B1BB3832700D7C7BD /* PFMultiProcessFileLockController.h in Headers */, 8101564C1BB3832700D7C7BD /* PFCurrentUserController.h in Headers */, @@ -5549,6 +5559,7 @@ 815F23C51BD04D150054659F /* PFURLSessionCommandRunner.h in Headers */, 815F23C61BD04D150054659F /* PFRESTObjectCommand.h in Headers */, 815F23C71BD04D150054659F /* PFCommandRunning.h in Headers */, + A6E2958F1E961742009917BF /* ParseManagerPrivate.h in Headers */, 815F23C81BD04D150054659F /* PFRESTCloudCommand.h in Headers */, 815F23C91BD04D150054659F /* PFProduct.h in Headers */, 815F23CA1BD04D150054659F /* PFQuery.h in Headers */, @@ -5654,6 +5665,7 @@ 818AAA7619D36B1C00FC1B3C /* PFFile.h in Headers */, 816AC9BA1A3F48250031D94C /* PFApplication.h in Headers */, F5B0B2EC1B449F1D00F3EBC4 /* BFTask+Private.h in Headers */, + A6E2958C1E96173D009917BF /* ParseManagerPrivate.h in Headers */, F5B0B2ED1B449F1D00F3EBC4 /* PFCategoryLoader.h in Headers */, F5B0B2EE1B449F1D00F3EBC4 /* PFThreadsafety.h in Headers */, F5B0B2EF1B449F1D00F3EBC4 /* PFRelationState_Private.h in Headers */, @@ -5997,6 +6009,7 @@ 81C584121C3B0A98000063C6 /* PFFilePersistenceGroup.h in Headers */, 81C584131C3B0A98000063C6 /* PFRESTQueryCommand.h in Headers */, 81C584141C3B0A98000063C6 /* PFACLState_Private.h in Headers */, + A6E2958D1E96173F009917BF /* ParseManagerPrivate.h in Headers */, 81C584151C3B0A98000063C6 /* PFConfig+Synchronous.h in Headers */, 81C584161C3B0A98000063C6 /* PFObject.h in Headers */, 81C584171C3B0A98000063C6 /* PFPushUtilities.h in Headers */, @@ -6201,6 +6214,7 @@ 81C585701C3B0AA1000063C6 /* PFURLSessionCommandRunner.h in Headers */, 81C585711C3B0AA1000063C6 /* PFRESTObjectCommand.h in Headers */, 81C585721C3B0AA1000063C6 /* PFCommandRunning.h in Headers */, + A6E295901E961744009917BF /* ParseManagerPrivate.h in Headers */, 81C585731C3B0AA1000063C6 /* PFRESTCloudCommand.h in Headers */, 81C585741C3B0AA1000063C6 /* PFProduct.h in Headers */, 81C585751C3B0AA1000063C6 /* PFQuery.h in Headers */, @@ -6444,6 +6458,7 @@ 81C586EF1C3B0AA9000063C6 /* PFURLSessionDataTaskDelegate.h in Headers */, 81C586F01C3B0AA9000063C6 /* PFAnonymousUtils+Deprecated.h in Headers */, 81C586F11C3B0AA9000063C6 /* PFDateFormatter.h in Headers */, + A6E295921E961744009917BF /* ParseManagerPrivate.h in Headers */, 81C586F21C3B0AA9000063C6 /* PFCloudCodeController.h in Headers */, 81C586F31C3B0AA9000063C6 /* PFMultiProcessFileLockController.h in Headers */, 81C586F41C3B0AA9000063C6 /* PFCurrentUserController.h in Headers */, @@ -6518,6 +6533,7 @@ F5B0B3261B44A33100F3EBC4 /* PFAssert.h in Headers */, F5B0B3271B44A33100F3EBC4 /* PFAsyncTaskQueue.h in Headers */, F5B0B3281B44A33100F3EBC4 /* PFBaseState.h in Headers */, + A6E2958E1E961741009917BF /* ParseManagerPrivate.h in Headers */, F51535091B57240900C49F56 /* PFMutableACLState.h in Headers */, F5B0B32B1B44A33100F3EBC4 /* PFCoreDataProvider.h in Headers */, 814881611B795CD4008763BF /* PFMultiProcessFileLock.h in Headers */, diff --git a/Parse/Internal/ParseManager.m b/Parse/Internal/ParseManager.m index daa908252..3c8be934b 100644 --- a/Parse/Internal/ParseManager.m +++ b/Parse/Internal/ParseManager.m @@ -27,6 +27,7 @@ #import "PFUser.h" #import "PFURLSessionCommandRunner.h" #import "PFPersistenceController.h" +#import "ParseManagerPrivate.h" #if !TARGET_OS_WATCH && !TARGET_OS_TV #import "PFPushManager.h" @@ -304,6 +305,11 @@ - (PFInstallationIdentifierStore *)installationIdentifierStore { #pragma mark CommandRunner +// Set Command Runner. Used for testing. +- (void)setCommandRunner:(id)commandRunner { + _commandRunner = commandRunner; +} + - (id)commandRunner { __block id runner = nil; dispatch_sync(_commandRunnerAccessQueue, ^{ diff --git a/Parse/Internal/ParseManagerPrivate.h b/Parse/Internal/ParseManagerPrivate.h new file mode 100644 index 000000000..dc95e93db --- /dev/null +++ b/Parse/Internal/ParseManagerPrivate.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ParseManager.h" + +@interface ParseManager () + +- (void)setCommandRunner:(id)commandRunner; + +@end diff --git a/Parse/PFInstallation.m b/Parse/PFInstallation.m index 12e44cad4..161ade665 100644 --- a/Parse/PFInstallation.m +++ b/Parse/PFInstallation.m @@ -29,6 +29,7 @@ #import "Parse_Private.h" #import "PFErrorUtilities.h" #import "PFObjectState_Private.h" +#import "PFObjectConstants.h" @implementation PFInstallation (Private) @@ -37,7 +38,8 @@ @implementation PFInstallation (Private) + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - protectedKeys = PF_SET(PFInstallationKeyDeviceType, + protectedKeys = PF_SET(PFObjectObjectIdRESTKey, + PFInstallationKeyDeviceType, PFInstallationKeyInstallationId, PFInstallationKeyTimeZone, PFInstallationKeyLocaleIdentifier, diff --git a/Tests/Unit/InstallationUnitTests.m b/Tests/Unit/InstallationUnitTests.m index 5aad50ab0..17a95e6f9 100644 --- a/Tests/Unit/InstallationUnitTests.m +++ b/Tests/Unit/InstallationUnitTests.m @@ -6,10 +6,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ +#import #import "PFInstallation.h" #import "PFUnitTestCase.h" #import "Parse.h" +#import "Parse_Private.h" +#import "PFCommandRunning.h" +#import "ParseManagerPrivate.h" +#import "PFObjectState.h" +#import "PFObjectPrivate.h" @interface InstallationUnitTests : PFUnitTestCase @@ -17,6 +23,36 @@ @interface InstallationUnitTests : PFUnitTestCase @implementation InstallationUnitTests +- (void)testInstallationObjectIdCannotBeChanged { + PFInstallation *installation = [PFInstallation currentInstallation]; + PFAssertThrowsInvalidArgumentException(installation.objectId = nil); + PFAssertThrowsInvalidArgumentException(installation[@"objectId"] = @"abc"); +} + +- (void)testReSaveInstallation { + + // enable LDS + [[Parse _currentManager]loadOfflineStoreWithOptions:0]; + + // create and save installation + PFInstallation *installation = [PFInstallation currentInstallation]; + PFObjectState *state = [PFObjectState stateWithParseClassName:[PFInstallation parseClassName] objectId:@"abc" isComplete:YES]; + installation._state = state; + [installation save]; + + // mocking installation was deleted on the server + id commandRunner = PFStrictProtocolMock(@protocol(PFCommandRunning)); + [Parse _currentManager].commandRunner = commandRunner; + + BFTask *mockedTask = [BFTask taskWithError:[NSError errorWithDomain:@"" code:kPFErrorObjectNotFound userInfo:nil]]; + + OCMStub([commandRunner runCommandAsync:[OCMArg any] withOptions:PFCommandRunningOptionRetryIfFailed]).andReturn(mockedTask); + + installation.deviceToken = @"11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + [installation save]; + OCMVerifyAll(commandRunner); +} + - (void)testInstallationImmutableFieldsCannotBeChanged { PFInstallation *installation = [PFInstallation currentInstallation]; installation.deviceToken = @"11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; From c418353c575f71740f495727eb85d46a5b9133a1 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Sat, 8 Apr 2017 09:44:23 +0800 Subject: [PATCH 4/8] minor optimization --- Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m index f74693f68..33f578dd7 100644 --- a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m +++ b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m @@ -1005,12 +1005,10 @@ - (void)updateObjectIdForObject:(PFObject *)object && newObjectId == nil) { NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:oldObjectId]; [self.classNameAndObjectIdToObjectMap removeObjectForKey:key]; - } else { - PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); + return; } -#else - PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); #endif + PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); return; } From 1ba958eae295c6fd80233372ab59e12ab755f025 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Wed, 19 Apr 2017 20:46:05 +0800 Subject: [PATCH 5/8] add call count assertion --- Tests/Unit/InstallationUnitTests.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Tests/Unit/InstallationUnitTests.m b/Tests/Unit/InstallationUnitTests.m index 17a95e6f9..29f7e1c13 100644 --- a/Tests/Unit/InstallationUnitTests.m +++ b/Tests/Unit/InstallationUnitTests.m @@ -29,8 +29,8 @@ - (void)testInstallationObjectIdCannotBeChanged { PFAssertThrowsInvalidArgumentException(installation[@"objectId"] = @"abc"); } -- (void)testReSaveInstallation { - +- (void)testObjectNotFoundWhenSave { +#if TARGET_OS_IOS // enable LDS [[Parse _currentManager]loadOfflineStoreWithOptions:0]; @@ -46,11 +46,18 @@ - (void)testReSaveInstallation { BFTask *mockedTask = [BFTask taskWithError:[NSError errorWithDomain:@"" code:kPFErrorObjectNotFound userInfo:nil]]; - OCMStub([commandRunner runCommandAsync:[OCMArg any] withOptions:PFCommandRunningOptionRetryIfFailed]).andReturn(mockedTask); + __block int callCount = 0; + OCMStub([commandRunner runCommandAsync:[OCMArg any] withOptions:PFCommandRunningOptionRetryIfFailed]) + .andReturn(mockedTask) + .andDo(^(NSInvocation *invocation) { + callCount++; + }); installation.deviceToken = @"11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; [installation save]; OCMVerifyAll(commandRunner); + XCTAssertEqual(2, callCount); +#endif } - (void)testInstallationImmutableFieldsCannotBeChanged { From cdc9e2d835c35079f9df035d90bdb052f634d63a Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Wed, 19 Apr 2017 21:09:27 +0800 Subject: [PATCH 6/8] update pod spec homepage --- Parse.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parse.podspec b/Parse.podspec index 59a8d67b2..35e2ff30b 100644 --- a/Parse.podspec +++ b/Parse.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |s| s.name = 'Parse' s.version = '1.14.4' s.license = { :type => 'BSD', :file => 'LICENSE' } - s.homepage = 'https://www.parse.com/' + s.homepage = 'http://parseplatform.org/' s.summary = 'A library that gives you access to the powerful Parse cloud platform from your iOS/OS X/watchOS/tvOS app.' s.authors = 'Parse' From 792844e70c9a10572a05ad758d7fb3b06066a6f1 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Thu, 20 Apr 2017 11:06:47 +0800 Subject: [PATCH 7/8] verify offline store operations --- Tests/Unit/InstallationUnitTests.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/Unit/InstallationUnitTests.m b/Tests/Unit/InstallationUnitTests.m index 29f7e1c13..3d5941873 100644 --- a/Tests/Unit/InstallationUnitTests.m +++ b/Tests/Unit/InstallationUnitTests.m @@ -33,6 +33,8 @@ - (void)testObjectNotFoundWhenSave { #if TARGET_OS_IOS // enable LDS [[Parse _currentManager]loadOfflineStoreWithOptions:0]; + PFOfflineStore *offlineStoreSpy = PFPartialMock([Parse _currentManager].offlineStore); + [Parse _currentManager].offlineStore = offlineStoreSpy; // create and save installation PFInstallation *installation = [PFInstallation currentInstallation]; @@ -44,7 +46,7 @@ - (void)testObjectNotFoundWhenSave { id commandRunner = PFStrictProtocolMock(@protocol(PFCommandRunning)); [Parse _currentManager].commandRunner = commandRunner; - BFTask *mockedTask = [BFTask taskWithError:[NSError errorWithDomain:@"" code:kPFErrorObjectNotFound userInfo:nil]]; + BFTask *mockedTask = [BFTask taskWithError:[NSError errorWithDomain:@"Object Not Found" code:kPFErrorObjectNotFound userInfo:nil]]; __block int callCount = 0; OCMStub([commandRunner runCommandAsync:[OCMArg any] withOptions:PFCommandRunningOptionRetryIfFailed]) @@ -57,6 +59,8 @@ - (void)testObjectNotFoundWhenSave { [installation save]; OCMVerifyAll(commandRunner); XCTAssertEqual(2, callCount); + OCMVerify([offlineStoreSpy updateObjectIdForObject:installation oldObjectId:nil newObjectId:@"abc"]); + OCMVerify([offlineStoreSpy updateObjectIdForObject:installation oldObjectId:@"abc" newObjectId:nil]); #endif } From a8132af193f6ccfd73862d4574b3a7b029ed853e Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Mon, 8 May 2017 11:46:33 +0800 Subject: [PATCH 8/8] add synchronized block to map operation --- Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m index 33f578dd7..f34386f6d 100644 --- a/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m +++ b/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m @@ -1004,7 +1004,9 @@ - (void)updateObjectIdForObject:(PFObject *)object if ([object isKindOfClass:[PFInstallation class]] && newObjectId == nil) { NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:oldObjectId]; - [self.classNameAndObjectIdToObjectMap removeObjectForKey:key]; + @synchronized(self.lock) { + [self.classNameAndObjectIdToObjectMap removeObjectForKey:key]; + } return; } #endif