Skip to content

Commit 4dd48bd

Browse files
GDT: Retry uploading batches failed to upload before the app termination (#5942)
* GDTCCTUploaderTest: test a lost batch upload retry * GDTCCTUploader: refactoring to prepare for lost batches uploading * GDTCCTUploader: Upload previously batched events first * Fix GDTCCTTestStorage * Refactor GDTCCTUploaderTest * GDTCORFlatFileStorageTest: batch tests * GDTCORFlatFileStorage: refactor removeBatchWithID to prepare for eventsForbatchID * eventsInBatchWithID implementation * Fix tests * Uploader fix for empty batch state * GDTCORUploadCoordinator: high priority targets * style * GDTCCTUploaderTest * GDTCCTUploaderTest: cleanup * Cleanup and API docs. * GDTCORUploadCoordinator: revert high priority targets * GDTCCTUploader: make sure GDTCOREventQoSFast is part of a regular batch as well. * Cleanup * GDTCCTUploader: don't report ready for upload until there are events. * GDTCCTUploaderTest: move failed batch back to the queue * GDTCORFlatFileStorage: removeBatchWithID with deleteEvents==NO * GDTCORFlatFileStorage: remove batch directory * style * GDTCORFlatFileStorage: comments and debug logs * formatting * GDTCCTUploaderTest: recover tests * GDTCCTTestServer: support for async request handler * GDTCCTUploaderTest: ongoing request tests * GDTCORStorageProtocol: remove redundant `eventsInBatchWithID` method. * GDTCORFlatFileStorageTest: `removeBatchWithID` tests * Cleanup * GDTCORFlatFileStorage: cleanup * GDTCORFlatFileStorageTest: better coverage and cleanup * Comments and improvements * GDTCCTUploaderTest: reduce code duplication * GDTCCTUploaderTest: conditions tests * GDTCCTUploader: fix next request wait time * run ./scripts/style.sh
1 parent d882c23 commit 4dd48bd

File tree

11 files changed

+1482
-353
lines changed

11 files changed

+1482
-353
lines changed

GoogleDataTransport.podspec

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ Shared library for iOS SDK data transport needs.
125125
end
126126
end
127127

128-
common_test_sources = ['GoogleDataTransport/GDTCCTTests/Common/**/*.{h,m}']
128+
common_cct_test_sources = ['GoogleDataTransport/GDTCCTTests/Common/**/*.{h,m}']
129129

130130
# Test specs
131131
s.test_spec 'CCT-Tests-Unit' do |test_spec|
132132
test_spec.platforms = {:ios => '8.0', :osx => '10.11', :tvos => '10.0'}
133133
test_spec.requires_app_host = false
134-
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Unit/**/*.{h,m}'] + common_test_sources
134+
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Unit/**/*.{h,m}'] + common_cct_test_sources + common_test_sources
135135
test_spec.resources = ['GoogleDataTransport/GDTCCTTests/Data/**/*']
136136
test_spec.pod_target_xcconfig = header_search_paths
137137
test_spec.dependency 'GCDWebServer'
@@ -140,7 +140,7 @@ Shared library for iOS SDK data transport needs.
140140
s.test_spec 'CCT-Tests-Integration' do |test_spec|
141141
test_spec.platforms = {:ios => '8.0', :osx => '10.11', :tvos => '10.0'}
142142
test_spec.requires_app_host = false
143-
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Integration/**/*.{h,m}'] + common_test_sources
143+
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Integration/**/*.{h,m}'] + common_cct_test_sources
144144
test_spec.resources = ['GoogleDataTransport/GDTCCTTests/Data/**/*']
145145
test_spec.pod_target_xcconfig = header_search_paths
146146
end
@@ -152,7 +152,7 @@ Shared library for iOS SDK data transport needs.
152152
test_spec.requires_app_host = true
153153
test_spec.app_host_name = 'GoogleDataTransport/CCTTestApp'
154154
test_spec.dependency 'GoogleDataTransport/CCTTestApp'
155-
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Monkey/**/*.{swift}'] + common_test_sources
155+
test_spec.source_files = ['GoogleDataTransport/GDTCCTTests/Monkey/**/*.{swift}'] + common_cct_test_sources
156156
test_spec.info_plist = {
157157
'GDT_MONKEYTEST' => '1'
158158
}

GoogleDataTransport/GDTCCTLibrary/GDTCCTUploader.m

Lines changed: 298 additions & 152 deletions
Large diffs are not rendered by default.

GoogleDataTransport/GDTCCTTests/Common/TestStorage/GDTCCTTestStorage.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,43 @@
1515
*/
1616

1717
#import <Foundation/Foundation.h>
18+
#import <XCTest/XCTest.h>
1819

1920
#import "GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h"
2021

22+
NS_ASSUME_NONNULL_BEGIN
23+
24+
typedef void (^GDTCCTTestStorageBatchHandler)(GDTCORStorageEventSelector *_Nullable eventSelector,
25+
NSDate *_Nullable expiration,
26+
GDTCORStorageBatchBlock _Nullable completion);
27+
28+
typedef void (^GDTCCTTestStorageHasEventsCompletion)(BOOL hasEvents);
29+
typedef void (^GDTCCTTestStorageHasEventsHandler)(GDTCORTarget target,
30+
GDTCCTTestStorageHasEventsCompletion completion);
31+
2132
@interface GDTCCTTestStorage : NSObject <GDTCORStorageProtocol>
2233

34+
#pragma mark - Method call expectations.
35+
36+
@property(nonatomic, nullable) XCTestExpectation *batchWithEventSelectorExpectation;
37+
@property(nonatomic, nullable) XCTestExpectation *removeBatchAndDeleteEventsExpectation;
38+
@property(nonatomic, nullable) XCTestExpectation *removeBatchWithoutDeletingEventsExpectation;
39+
@property(nonatomic, nullable) XCTestExpectation *batchIDsForTargetExpectation;
40+
41+
#pragma mark - Blocks to provide custom implementations for the methods.
42+
43+
/// A block to override `batchWithEventSelector:batchExpiration:onComplete:` implementation.
44+
@property(nonatomic, copy, nullable) GDTCCTTestStorageBatchHandler batchWithEventSelectorHandler;
45+
/// A block to override `hasEventsForTarget:onComplete:` implementation.
46+
@property(nonatomic, copy, nullable) GDTCCTTestStorageHasEventsHandler hasEventsForTargetHandler;
47+
48+
#pragma mark - Default test implementations
49+
50+
/// Default test implementation for `batchWithEventSelector:batchExpiration:onComplete:` method.
51+
- (void)defaultBatchWithEventSelector:(nonnull GDTCORStorageEventSelector *)eventSelector
52+
batchExpiration:(nonnull NSDate *)expiration
53+
onComplete:(nonnull GDTCORStorageBatchBlock)onComplete;
54+
2355
@end
56+
57+
NS_ASSUME_NONNULL_END

GoogleDataTransport/GDTCCTTests/Common/TestStorage/GDTCCTTestStorage.m

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ @implementation GDTCCTTestStorage {
2626
NSMutableDictionary<NSNumber *, NSSet<GDTCOREvent *> *> *_batches;
2727
}
2828

29+
- (instancetype)init {
30+
self = [super init];
31+
if (self) {
32+
_storedEvents = [[NSMutableDictionary alloc] init];
33+
_batches = [[NSMutableDictionary alloc] init];
34+
}
35+
return self;
36+
}
37+
2938
- (void)storeEvent:(GDTCOREvent *)event
3039
onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable))completion {
31-
static dispatch_once_t onceToken;
32-
dispatch_once(&onceToken, ^{
33-
self->_storedEvents = [[NSMutableDictionary alloc] init];
34-
});
3540
_storedEvents[event.eventID] = event;
3641
if (completion) {
3742
completion(YES, nil);
@@ -44,28 +49,29 @@ - (void)removeEvents:(NSSet<NSString *> *)eventIDs {
4449

4550
- (void)batchWithEventSelector:(nonnull GDTCORStorageEventSelector *)eventSelector
4651
batchExpiration:(nonnull NSDate *)expiration
47-
onComplete:
48-
(nonnull void (^)(NSNumber *_Nullable batchID,
49-
NSSet<GDTCOREvent *> *_Nullable events))onComplete {
50-
static NSInteger count = 0;
51-
NSNumber *batchID = @(count);
52-
count++;
53-
static dispatch_once_t onceToken;
54-
dispatch_once(&onceToken, ^{
55-
self->_batches = [[NSMutableDictionary alloc] init];
56-
});
57-
NSSet<GDTCOREvent *> *batchEvents = [NSSet setWithArray:[_storedEvents allValues]];
58-
_batches[batchID] = batchEvents;
59-
[_storedEvents removeAllObjects];
60-
if (onComplete) {
61-
onComplete(batchID, batchEvents);
52+
onComplete:(nonnull GDTCORStorageBatchBlock)onComplete {
53+
if (self.batchWithEventSelectorHandler) {
54+
self.batchWithEventSelectorHandler(eventSelector, expiration, onComplete);
55+
} else {
56+
[self defaultBatchWithEventSelector:eventSelector
57+
batchExpiration:expiration
58+
onComplete:onComplete];
6259
}
6360
}
6461

6562
- (void)removeBatchWithID:(nonnull NSNumber *)batchID
6663
deleteEvents:(BOOL)deleteEvents
6764
onComplete:(void (^_Nullable)(void))onComplete {
68-
[_batches removeObjectForKey:batchID];
65+
if (deleteEvents) {
66+
[_batches removeObjectForKey:batchID];
67+
[self.removeBatchAndDeleteEventsExpectation fulfill];
68+
} else {
69+
for (GDTCOREvent *batchedEvent in _batches[batchID]) {
70+
_storedEvents[batchedEvent.eventID] = batchedEvent;
71+
}
72+
[self.removeBatchWithoutDeletingEventsExpectation fulfill];
73+
}
74+
6975
if (onComplete) {
7076
onComplete();
7177
}
@@ -95,7 +101,9 @@ - (void)removeLibraryDataForKey:(nonnull NSString *)key
95101
}
96102

97103
- (void)hasEventsForTarget:(GDTCORTarget)target onComplete:(nonnull void (^)(BOOL))onComplete {
98-
if (onComplete) {
104+
if (self.hasEventsForTargetHandler) {
105+
self.hasEventsForTargetHandler(target, onComplete);
106+
} else if (onComplete) {
99107
onComplete(NO);
100108
}
101109
}
@@ -105,12 +113,32 @@ - (void)storageSizeWithCallback:(void (^)(uint64_t storageSize))onComplete {
105113

106114
- (void)batchIDsForTarget:(GDTCORTarget)target
107115
onComplete:(nonnull void (^)(NSSet<NSNumber *> *_Nullable))onComplete {
116+
[self.batchIDsForTargetExpectation fulfill];
108117
if (onComplete) {
109-
onComplete(nil);
118+
onComplete([NSSet setWithArray:[self->_batches allKeys]]);
110119
}
111120
}
112121

113122
- (void)checkForExpirations {
114123
}
115124

125+
#pragma mark - Default Implementations
126+
127+
- (void)defaultBatchWithEventSelector:(nonnull GDTCORStorageEventSelector *)eventSelector
128+
batchExpiration:(nonnull NSDate *)expiration
129+
onComplete:(nonnull GDTCORStorageBatchBlock)onComplete {
130+
static NSInteger count = 0;
131+
NSNumber *batchID = @(count);
132+
count++;
133+
134+
NSSet<GDTCOREvent *> *batchEvents = [NSSet setWithArray:[_storedEvents allValues]];
135+
_batches[batchID] = batchEvents;
136+
[_storedEvents removeAllObjects];
137+
138+
[self.batchWithEventSelectorExpectation fulfill];
139+
if (onComplete) {
140+
onComplete(batchID, batchEvents);
141+
}
142+
}
143+
116144
@end

0 commit comments

Comments
 (0)