diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h b/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h index 5b82c46c1db..cd7e27b0d9c 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h @@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN * @param eventHashes A set of event hashes to get the files of. * @return A set of equivalent length, containing all the filenames corresponding to the hashes. */ -- (NSSet *)eventHashesToFiles:(NSSet *)eventHashes; +- (NSDictionary *)eventHashesToFiles:(NSSet *)eventHashes; @end diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m b/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m index 566d8664011..1ab827dbf7e 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m +++ b/GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m @@ -113,16 +113,16 @@ - (void)removeEvents:(NSSet *)eventHashes target:(NSNumber *)target }); } -- (NSSet *)eventHashesToFiles:(NSSet *)eventHashes { - NSMutableSet *eventFiles = [[NSMutableSet alloc] init]; +- (NSDictionary *)eventHashesToFiles:(NSSet *)eventHashes { + NSMutableDictionary *eventHashesToFiles = [[NSMutableDictionary alloc] init]; dispatch_sync(_storageQueue, ^{ for (NSNumber *hashNumber in eventHashes) { NSURL *eventURL = self.eventHashToFile[hashNumber]; GDTAssert(eventURL, @"An event file URL couldn't be found for the given hash"); - [eventFiles addObject:eventURL]; + eventHashesToFiles[hashNumber] = eventURL; } }); - return eventFiles; + return eventHashesToFiles; } #pragma mark - Private helper methods diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m b/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m index 0819ac8fa05..eb582381d98 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m +++ b/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m @@ -22,6 +22,7 @@ #import "GDTConsoleLogger.h" #import "GDTRegistrar_Private.h" #import "GDTStorage.h" +#import "GDTUploadPackage_Private.h" @implementation GDTUploadCoordinator @@ -57,9 +58,10 @@ - (void)forceUploadEvents:(NSSet *)eventHashes target:(GDTTarget)tar GDTUploadCoordinatorForceUploadBlock forceUploadBlock = ^{ GDTAssert(eventHashes.count, @"It doesn't make sense to force upload of 0 events"); id uploader = registrar.targetToUploader[targetNumber]; - NSSet *eventFiles = [self.storage eventHashesToFiles:eventHashes]; + GDTUploadPackage *package = [[GDTUploadPackage alloc] init]; + package.eventHashes = [eventHashes copy]; GDTAssert(uploader, @"Target '%@' is missing an implementation", targetNumber); - [uploader uploadEvents:eventFiles onComplete:self.onCompleteBlock]; + [uploader uploadPackage:package onComplete:self.onCompleteBlock]; self->_targetToInFlightEventSet[targetNumber] = eventHashes; }; @@ -157,16 +159,13 @@ - (void)checkPrioritizersAndUploadEvents { id uploader = strongSelf->_registrar.targetToUploader[target]; GDTAssert(prioritizer && uploader, @"Target '%@' is missing an implementation", target); GDTUploadConditions conds = [self uploadConditions]; - NSSet *eventHashesToUpload = - [[prioritizer eventsToUploadGivenConditions:conds] copy]; - if (eventHashesToUpload && eventHashesToUpload.count > 0) { - NSAssert(eventHashesToUpload.count > 0, @""); - NSSet *eventFilesToUpload = - [strongSelf.storage eventHashesToFiles:eventHashesToUpload]; - NSAssert(eventFilesToUpload.count == eventHashesToUpload.count, + GDTUploadPackage *package = [[prioritizer uploadPackageWithConditions:conds] copy]; + package.storage = strongSelf.storage; + if (package.eventHashes && package.eventHashes.count > 0) { + NSAssert(package.eventHashesToFiles.count == package.eventHashes.count, @"There should be the same number of files to events"); - strongSelf->_targetToInFlightEventSet[target] = eventHashesToUpload; - [uploader uploadEvents:eventFilesToUpload onComplete:self.onCompleteBlock]; + strongSelf->_targetToInFlightEventSet[target] = package.eventHashes; + [uploader uploadPackage:package onComplete:self.onCompleteBlock]; } } } diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadPackage.m b/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadPackage.m new file mode 100644 index 00000000000..6994adb3e1a --- /dev/null +++ b/GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadPackage.m @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import "GDTStorage_Private.h" +#import "GDTUploadPackage_Private.h" + +@implementation GDTUploadPackage + +- (instancetype)init { + self = [super init]; + if (self) { + _storage = [GDTStorage sharedInstance]; + } + return self; +} + +- (instancetype)copy { + GDTUploadPackage *newPackage = [[GDTUploadPackage alloc] init]; + newPackage->_eventHashes = _eventHashes; + return newPackage; +} + +- (NSUInteger)hash { + return [_eventHashes hash]; +} + +- (BOOL)isEqual:(id)object { + return [self hash] == [object hash]; +} + +- (void)setEventHashes:(NSSet *)eventHashes { + if (eventHashes != _eventHashes) { + _eventHashes = [eventHashes copy]; + } +} + +- (NSDictionary *)eventHashesToFiles { + return [_storage eventHashesToFiles:_eventHashes]; +} + +- (void)setStorage:(GDTStorage *)storage { + if (storage != _storage) { + _storage = storage; + } +} + +@end diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTEvent_Private.h b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTEvent_Private.h index 67914e7b91a..5c10c472033 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTEvent_Private.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTEvent_Private.h @@ -25,9 +25,6 @@ NS_ASSUME_NONNULL_BEGIN /** The serialized bytes of the event data object. */ @property(nonatomic) NSData *dataObjectTransportBytes; -/** The clock snapshot at the time of the event. */ -@property(nonatomic) GDTClock *clockSnapshot; - @end NS_ASSUME_NONNULL_END diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTUploadPackage_Private.h b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTUploadPackage_Private.h new file mode 100644 index 00000000000..2dc6b0a0440 --- /dev/null +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTUploadPackage_Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class GDTStorage; + +@interface GDTUploadPackage () + +/** The storage object this upload package will use to resolve event hashes to files. */ +@property(nonatomic) GDTStorage *storage; + +@end diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEvent.h b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEvent.h index 13edfe10705..6f0a8b77e21 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEvent.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEvent.h @@ -18,6 +18,8 @@ #import "GDTEventDataObject.h" +@class GDTClock; + NS_ASSUME_NONNULL_BEGIN /** The different possible quality of service specifiers. High values indicate high priority. */ @@ -56,6 +58,9 @@ typedef NS_ENUM(NSInteger, GDTEventQoS) { /** The quality of service tier this event belongs to. */ @property(nonatomic) GDTEventQoS qosTier; +/** The clock snapshot at the time of the event. */ +@property(nonatomic) GDTClock *clockSnapshot; + /** A dictionary provided to aid prioritizers by allowing the passing of arbitrary data. It will be * retained by a copy in -copy, but not used for -hash. */ diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEventDataObject.h b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEventDataObject.h index 2cb0ccc649e..f9031cbe835 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEventDataObject.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTEventDataObject.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN */ @protocol GDTEventDataObject +@required + /** Returns the serialized proto bytes of the implementing event proto. * * @return the serialized proto bytes of the implementing event proto. diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTPrioritizer.h b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTPrioritizer.h index 0fb001f6fb7..526b6f750dc 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTPrioritizer.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTPrioritizer.h @@ -16,6 +16,8 @@ #import +#import + @class GDTEvent; NS_ASSUME_NONNULL_BEGIN @@ -46,7 +48,7 @@ typedef NS_OPTIONS(NSInteger, GDTUploadConditions) { * * @note A couple of things: 1. The event cannot be retained for longer than the execution time of * this method. 2. You should retain the event hashes, because those are returned in - * -eventsForNextUpload. + * -uploadPackageWithConditions. * * @param event The event to prioritize. */ @@ -60,9 +62,10 @@ typedef NS_OPTIONS(NSInteger, GDTUploadConditions) { /** Returns a set of events to upload given a set of conditions. * * @param conditions A bit mask specifying the current upload conditions. - * @return A set of events to upload with respect to the current conditions. + * @return An object to be used by the uploader to determine file URLs to upload with respect to the + * current conditions. */ -- (NSSet *)eventsToUploadGivenConditions:(GDTUploadConditions)conditions; +- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)conditions; @end diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploadPackage.h b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploadPackage.h new file mode 100644 index 00000000000..08dc4d01321 --- /dev/null +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploadPackage.h @@ -0,0 +1,28 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +/** This class is a container that's handed off to uploaders. */ +@interface GDTUploadPackage : NSObject + +/** The set of event hashes in this upload package. */ +@property(nonatomic) NSSet *eventHashes; + +/** A lazily-determined map of event hashes to their files. */ +@property(nonatomic, readonly) NSDictionary *eventHashesToFiles; + +@end diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploader.h b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploader.h index a7f5216d7b8..e9e1a36d266 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploader.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTUploader.h @@ -39,12 +39,13 @@ typedef void (^GDTUploaderCompletionBlock)(GDTTarget target, /** Uploads events to the backend using this specific backend's chosen format. * - * @param eventFiles The set of event files to upload. - * @param onComplete A block to invoke upon completing the upload. Has two arguments: - * - successfulUploads: The set of filenames uploaded successfully. - * - unsuccessfulUploads: The set of filenames not uploaded successfully. + * @param package The event package to upload. + * @param onComplete A block to invoke upon completing the upload. Has three arguments: + * - target: The GDTTarget that just uploaded. + * - nextUploadAttemptUTC: A clock representing the next upload attempt. + * - uploadError: An error object describing the upload error, or nil if upload was successful. */ -- (void)uploadEvents:(NSSet *)eventFiles onComplete:(GDTUploaderCompletionBlock)onComplete; +- (void)uploadPackage:(GDTUploadPackage *)package onComplete:(GDTUploaderCompletionBlock)onComplete; @end diff --git a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestPrioritizer.m b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestPrioritizer.m index 07a937cccb1..0e55b2905d8 100644 --- a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestPrioritizer.m +++ b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestPrioritizer.m @@ -16,6 +16,8 @@ #import "GDTIntegrationTestPrioritizer.h" +#import "GDTIntegrationTestUploadPackage.h" + @interface GDTIntegrationTestPrioritizer () /** Events that are only supposed to be uploaded whilst on wifi. */ @@ -44,32 +46,35 @@ - (instancetype)init { } - (void)prioritizeEvent:(GDTEvent *)event { - dispatch_sync(_queue, ^{ - if (event.qosTier == GDTEventQoSWifiOnly) { - [self.wifiOnlyEvents addObject:@(event.hash)]; + NSUInteger eventHash = event.hash; + NSInteger qosTier = event.qosTier; + dispatch_async(_queue, ^{ + if (qosTier == GDTEventQoSWifiOnly) { + [self.wifiOnlyEvents addObject:@(eventHash)]; } else { - [self.nonWifiEvents addObject:@(event.hash)]; + [self.nonWifiEvents addObject:@(eventHash)]; } }); } - (void)unprioritizeEvent:(NSNumber *)eventHash { - dispatch_sync(_queue, ^{ + dispatch_async(_queue, ^{ [self.wifiOnlyEvents removeObject:eventHash]; [self.nonWifiEvents removeObject:eventHash]; }); } -- (nonnull NSSet *)eventsToUploadGivenConditions:(GDTUploadConditions)conditions { - __block NSSet *events; +- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)conditions { + __block GDTIntegrationTestUploadPackage *uploadPackage = + [[GDTIntegrationTestUploadPackage alloc] init]; dispatch_sync(_queue, ^{ if ((conditions & GDTUploadConditionWifiData) == GDTUploadConditionWifiData) { - events = self.wifiOnlyEvents; + uploadPackage.eventHashes = self.wifiOnlyEvents; } else { - events = self.nonWifiEvents; + uploadPackage.eventHashes = self.nonWifiEvents; } }); - return events; + return uploadPackage; } @end diff --git a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.h b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.h new file mode 100644 index 00000000000..ebe5bdfa6bf --- /dev/null +++ b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTTestPrioritizer.h" + +/** An upload package used in testing. */ +@interface GDTIntegrationTestUploadPackage : GDTUploadPackage + +@end diff --git a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.m b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.m new file mode 100644 index 00000000000..d1b834f6798 --- /dev/null +++ b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploadPackage.m @@ -0,0 +1,21 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTIntegrationTestUploadPackage.h" + +@implementation GDTIntegrationTestUploadPackage + +@end diff --git a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploader.m b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploader.m index 1a47e89fd78..77f306d1a82 100644 --- a/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploader.m +++ b/GoogleDataTransport/Tests/Integration/Helpers/GDTIntegrationTestUploader.m @@ -37,8 +37,8 @@ - (instancetype)initWithServerURL:(NSURL *)serverURL { return self; } -- (void)uploadEvents:(NSSet *)eventFiles - onComplete:(GDTUploaderCompletionBlock)onComplete { +- (void)uploadPackage:(GDTUploadPackage *)package + onComplete:(GDTUploaderCompletionBlock)onComplete { NSAssert(!_currentUploadTask, @"An upload shouldn't be initiated with another in progress."); NSURL *serverURL = arc4random_uniform(2) ? [_serverURL URLByAppendingPathComponent:@"log"] : [_serverURL URLByAppendingPathComponent:@"logBatch"]; @@ -47,10 +47,10 @@ - (void)uploadEvents:(NSSet *)eventFiles request.HTTPMethod = @"POST"; NSMutableData *uploadData = [[NSMutableData alloc] init]; - NSLog(@"Uploading batch of %lu events: ", (unsigned long)eventFiles.count); + NSLog(@"Uploading batch of %lu events: ", (unsigned long)[package eventHashes].count); // In real usage, you'd create an instance of whatever request proto your server needs. - for (NSURL *eventFile in eventFiles) { + for (NSURL *eventFile in [package.eventHashesToFiles allValues]) { NSData *fileData = [NSData dataWithContentsOfURL:eventFile]; NSAssert(fileData, @"A event file shouldn't be empty"); [uploadData appendData:fileData]; diff --git a/GoogleDataTransport/Tests/Unit/GDTStorageTest.m b/GoogleDataTransport/Tests/Unit/GDTStorageTest.m index 021c280ba29..e16185f983a 100644 --- a/GoogleDataTransport/Tests/Unit/GDTStorageTest.m +++ b/GoogleDataTransport/Tests/Unit/GDTStorageTest.m @@ -144,14 +144,14 @@ - (void)testRemoveEvents { } NSSet *eventHashSet = [NSSet setWithObjects:@(event1Hash), @(event2Hash), @(event3Hash), nil]; - NSSet *eventFiles = [storage eventHashesToFiles:eventHashSet]; + NSDictionary *eventFiles = [storage eventHashesToFiles:eventHashSet]; [storage removeEvents:eventHashSet target:@(target)]; dispatch_sync(storage.storageQueue, ^{ XCTAssertNil(storage.eventHashToFile[@(event1Hash)]); XCTAssertNil(storage.eventHashToFile[@(event2Hash)]); XCTAssertNil(storage.eventHashToFile[@(event3Hash)]); XCTAssertEqual(storage.targetToEventHashSet[@(target)].count, 0); - for (NSURL *eventFile in eventFiles) { + for (NSURL *eventFile in [eventFiles allValues]) { XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]); } }); @@ -309,12 +309,12 @@ - (void)testEventHashesToFiles { } NSSet *eventHashSet = [NSSet setWithObjects:@(event1Hash), @(event2Hash), @(event3Hash), nil]; - NSSet *eventFiles = [storage eventHashesToFiles:eventHashSet]; + NSDictionary *eventFiles = [storage eventHashesToFiles:eventHashSet]; dispatch_sync(storage.storageQueue, ^{ XCTAssertEqual(eventFiles.count, 3); - XCTAssertTrue([eventFiles containsObject:storage.eventHashToFile[@(event1Hash)]]); - XCTAssertTrue([eventFiles containsObject:storage.eventHashToFile[@(event2Hash)]]); - XCTAssertTrue([eventFiles containsObject:storage.eventHashToFile[@(event3Hash)]]); + XCTAssertEqualObjects(eventFiles[@(event1Hash)], storage.eventHashToFile[@(event1Hash)]); + XCTAssertEqualObjects(eventFiles[@(event2Hash)], storage.eventHashToFile[@(event2Hash)]); + XCTAssertEqualObjects(eventFiles[@(event3Hash)], storage.eventHashToFile[@(event3Hash)]); }); } diff --git a/GoogleDataTransport/Tests/Unit/GDTUploadCoordinatorTest.m b/GoogleDataTransport/Tests/Unit/GDTUploadCoordinatorTest.m index 24df204a17b..e5955f1e90b 100644 --- a/GoogleDataTransport/Tests/Unit/GDTUploadCoordinatorTest.m +++ b/GoogleDataTransport/Tests/Unit/GDTUploadCoordinatorTest.m @@ -22,6 +22,7 @@ #import "GDTRegistrar+Testing.h" #import "GDTStorageFake.h" #import "GDTTestPrioritizer.h" +#import "GDTTestUploadPackage.h" #import "GDTTestUploader.h" #import "GDTUploadCoordinator+Testing.h" @@ -79,7 +80,7 @@ - (void)testSharedInstance { - (void)testForceUploadEvents { XCTestExpectation *expectation = [self expectationWithDescription:@"uploader will upload"]; self.uploader.uploadEventsBlock = - ^(NSSet *_Nonnull eventFiles, GDTUploaderCompletionBlock _Nonnull completionBlock) { + ^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) { [expectation fulfill]; }; NSSet *fakeEventSet = [NSSet setWithObjects:[NSURL URLWithString:@"file:///fake"], nil]; @@ -98,7 +99,7 @@ - (void)testForceUploadEventsEnqueuesIftargetAlreadyHasEventsInFlight { [GDTUploadCoordinator sharedInstance].timerLeeway = NSEC_PER_SEC / 1000; XCTestExpectation *expectation = [self expectationWithDescription:@"uploader will upload"]; self.uploader.uploadEventsBlock = - ^(NSSet *_Nonnull eventFiles, GDTUploaderCompletionBlock _Nonnull completionBlock) { + ^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) { [expectation fulfill]; }; NSSet *fakeEventSet = [NSSet setWithObjects:[NSURL URLWithString:@"file:///fake"], nil]; @@ -123,7 +124,7 @@ - (void)testForceUploadEventsEnqueuesIftargetAlreadyHasEventsInFlight { /** Tests the timer is running at the desired frequency. */ - (void)testTimerIsRunningAtDesiredFrequency { __block int numberOfTimesCalled = 0; - self.prioritizer.eventsForNextUploadBlock = ^{ + self.prioritizer.uploadPackageWithConditionsBlock = ^{ numberOfTimesCalled++; }; dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{ @@ -149,9 +150,11 @@ - (void)testUploadingEventsViaTimer { self.storageFake.eventsToReturnFromEventHashesToFiles = fakeEventSet; __block int uploadAttempts = 0; __weak GDTUploadCoordinatorTest *weakSelf = self; - self.prioritizer.eventsForNextUploadFake = [NSSet setWithObjects:@(1234), nil]; + GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init]; + uploadPackage.eventHashes = [NSSet setWithObjects:@(1234), nil]; + self.prioritizer.uploadPackage = uploadPackage; self.uploader.uploadEventsBlock = - ^(NSSet *_Nonnull eventFiles, GDTUploaderCompletionBlock _Nonnull completionBlock) { + ^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) { GDTUploadCoordinatorTest *strongSelf = weakSelf; completionBlock(strongSelf->_target, [GDTClock clockSnapshotInTheFuture:100], nil); uploadAttempts++; @@ -174,9 +177,11 @@ - (void)testThatAFailedUploadResultsInAnEventualRetry { self.storageFake.eventsToReturnFromEventHashesToFiles = fakeEventSet; __block int uploadAttempts = 0; __weak GDTUploadCoordinatorTest *weakSelf = self; - self.prioritizer.eventsForNextUploadFake = [NSSet setWithObjects:@(1234), nil]; + GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init]; + uploadPackage.eventHashes = [NSSet setWithObjects:@(1234), nil]; + self.prioritizer.uploadPackage = uploadPackage; self.uploader.uploadEventsBlock = - ^(NSSet *_Nonnull eventFiles, GDTUploaderCompletionBlock _Nonnull completionBlock) { + ^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) { GDTUploadCoordinatorTest *strongSelf = weakSelf; NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:1337 userInfo:nil]; completionBlock(strongSelf->_target, [GDTClock clockSnapshotInTheFuture:100], error); diff --git a/GoogleDataTransport/Tests/Unit/GDTUploadPackageTest.m b/GoogleDataTransport/Tests/Unit/GDTUploadPackageTest.m new file mode 100644 index 00000000000..22cf65cbaa1 --- /dev/null +++ b/GoogleDataTransport/Tests/Unit/GDTUploadPackageTest.m @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTTestCase.h" + +#import + +#import "GDTUploadPackage_Private.h" + +@interface GDTUploadPackageTest : GDTTestCase + +@end + +@implementation GDTUploadPackageTest + +/** Tests the default initializer. */ +- (void)testInit { + XCTAssertNotNil([[GDTUploadPackage alloc] init]); +} + +/** Tests copying indicates that the underlying sets of events can't be changed from underneath. */ +- (void)testRegisterUpload { + GDTUploadPackage *uploadPackage = [[GDTUploadPackage alloc] init]; + GDTUploadPackage *uploadPackageCopy = [uploadPackage copy]; + XCTAssertNotEqual(uploadPackage, uploadPackageCopy); + XCTAssertEqualObjects(uploadPackage.eventHashes, uploadPackageCopy.eventHashes); + XCTAssertEqualObjects(uploadPackage, uploadPackageCopy); + + uploadPackage.eventHashes = [NSSet set]; + uploadPackageCopy = [uploadPackage copy]; + XCTAssertNotEqual(uploadPackage, uploadPackageCopy); + XCTAssertEqualObjects(uploadPackage.eventHashes, uploadPackageCopy.eventHashes); + XCTAssertEqualObjects(uploadPackage, uploadPackageCopy); + + NSMutableSet *set = [[NSMutableSet alloc] initWithObjects:@1, @2, @3, nil]; + [set addObject:@4]; + uploadPackage.eventHashes = set; + uploadPackageCopy = [uploadPackage copy]; + XCTAssertNotEqual(uploadPackage, uploadPackageCopy); + [set addObject:@5]; + XCTAssertFalse([uploadPackageCopy.eventHashes containsObject:@5]); + XCTAssertEqualObjects(uploadPackage.eventHashes, uploadPackageCopy.eventHashes); + XCTAssertEqualObjects(uploadPackage, uploadPackageCopy); +} + +@end diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.h b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.h index 5fa5716efe5..7b061148c21 100644 --- a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.h +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.h @@ -25,14 +25,14 @@ NS_ASSUME_NONNULL_BEGIN */ @interface GDTTestPrioritizer : NSObject -/** The return value of -eventsForNextUpload. */ -@property(nullable, nonatomic) NSSet *eventsForNextUploadFake; +/** The return value of -uploadPackageWithConditions. */ +@property(nullable, nonatomic) GDTUploadPackage *uploadPackage; /** Allows the running of a block of code during -prioritizeEvent. */ @property(nullable, nonatomic) void (^prioritizeEventBlock)(GDTEvent *event); -/** A block that can run before -eventsForNextUpload completes. */ -@property(nullable, nonatomic) void (^eventsForNextUploadBlock)(void); +/** A block that can run before -uploadPackageWithConditions completes. */ +@property(nullable, nonatomic) void (^uploadPackageWithConditionsBlock)(void); @end diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.m b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.m index 0c48ed875a7..99da6d99947 100644 --- a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.m +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestPrioritizer.m @@ -16,21 +16,23 @@ #import "GDTTestPrioritizer.h" +#import "GDTTestUploadPackage.h" + @implementation GDTTestPrioritizer - (instancetype)init { self = [super init]; if (self) { - _eventsForNextUploadFake = [[NSSet alloc] init]; + _uploadPackage = [[GDTTestUploadPackage alloc] init]; } return self; } -- (NSSet *)eventsToUploadGivenConditions:(GDTUploadConditions)conditions { - if (_eventsForNextUploadBlock) { - _eventsForNextUploadBlock(); +- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)conditions { + if (_uploadPackageWithConditionsBlock) { + _uploadPackageWithConditionsBlock(); } - return _eventsForNextUploadFake; + return _uploadPackage; } - (void)prioritizeEvent:(GDTEvent *)event { diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.h b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.h new file mode 100644 index 00000000000..28a85bcb21e --- /dev/null +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTTestPrioritizer.h" + +/** An upload package used in testing. */ +@interface GDTTestUploadPackage : GDTUploadPackage + +@end diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.m b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.m new file mode 100644 index 00000000000..b7d7894a33f --- /dev/null +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploadPackage.m @@ -0,0 +1,21 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTTestUploadPackage.h" + +@implementation GDTTestUploadPackage + +@end diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.h b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.h index 8995f789c19..56f6d6388a0 100644 --- a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.h +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.h @@ -18,6 +18,8 @@ #import "GDTUploader.h" +@class GDTUploadPackage; + NS_ASSUME_NONNULL_BEGIN /** This class implements a backend uploader protocol for testing purposes, providing APIs to allow @@ -25,9 +27,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface GDTTestUploader : NSObject -/** A block that can be ran in -uploadEvents:onComplete:. */ +/** A block that can be ran in -uploadPackage:onComplete:. */ @property(nullable, nonatomic) void (^uploadEventsBlock) - (NSSet *eventFiles, GDTUploaderCompletionBlock completionBlock); + (GDTUploadPackage *package, GDTUploaderCompletionBlock completionBlock); @end diff --git a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.m b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.m index 74a84b78efa..dcc90ed1d5c 100644 --- a/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.m +++ b/GoogleDataTransport/Tests/Unit/Helpers/GDTTestUploader.m @@ -18,10 +18,10 @@ @implementation GDTTestUploader -- (void)uploadEvents:(NSSet *)eventFiles - onComplete:(GDTUploaderCompletionBlock)onComplete { +- (void)uploadPackage:(GDTUploadPackage *)package + onComplete:(GDTUploaderCompletionBlock)onComplete { if (_uploadEventsBlock) { - _uploadEventsBlock(eventFiles, onComplete); + _uploadEventsBlock(package, onComplete); } else if (onComplete) { onComplete(kGDTTargetCCT, [GDTClock snapshot], nil); }