diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLRegistrar.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLRegistrar.m index d23baf42467..b7d0eda780f 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLRegistrar.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLRegistrar.m @@ -18,7 +18,13 @@ #import "GDLRegistrar_Private.h" -@implementation GDLRegistrar +@implementation GDLRegistrar { + /** Backing ivar for logTargetToUploader property. */ + NSMutableDictionary> *_logTargetToUploader; + + /** Backing ivar for logTargetToPrioritizer property. */ + NSMutableDictionary> *_logTargetToPrioritizer; +} + (instancetype)sharedInstance { static GDLRegistrar *sharedInstance; @@ -32,19 +38,55 @@ + (instancetype)sharedInstance { - (instancetype)init { self = [super init]; if (self) { + _registrarQueue = dispatch_queue_create("com.google.GDLRegistrar", DISPATCH_QUEUE_CONCURRENT); _logTargetToPrioritizer = [[NSMutableDictionary alloc] init]; _logTargetToUploader = [[NSMutableDictionary alloc] init]; } return self; } -- (void)registerBackend:(id)backend forLogTarget:(GDLLogTarget)logTarget { - self.logTargetToUploader[@(logTarget)] = backend; +- (void)registerUploader:(id)backend logTarget:(GDLLogTarget)logTarget { + __weak GDLRegistrar *weakSelf = self; + dispatch_barrier_async(_registrarQueue, ^{ + GDLRegistrar *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_logTargetToUploader[@(logTarget)] = backend; + } + }); +} + +- (void)registerPrioritizer:(id)prioritizer logTarget:(GDLLogTarget)logTarget { + __weak GDLRegistrar *weakSelf = self; + dispatch_barrier_async(_registrarQueue, ^{ + GDLRegistrar *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_logTargetToPrioritizer[@(logTarget)] = prioritizer; + } + }); } -- (void)registerLogPrioritizer:(id)prioritizer - forLogTarget:(GDLLogTarget)logTarget { - self.logTargetToPrioritizer[@(logTarget)] = prioritizer; +- (NSMutableDictionary> *)logTargetToUploader { + __block NSMutableDictionary> *logTargetToUploader; + __weak GDLRegistrar *weakSelf = self; + dispatch_sync(_registrarQueue, ^{ + GDLRegistrar *strongSelf = weakSelf; + if (strongSelf) { + logTargetToUploader = strongSelf->_logTargetToUploader; + } + }); + return logTargetToUploader; +} + +- (NSMutableDictionary> *)logTargetToPrioritizer { + __block NSMutableDictionary> *logTargetToPrioritizer; + __weak GDLRegistrar *weakSelf = self; + dispatch_sync(_registrarQueue, ^{ + GDLRegistrar *strongSelf = weakSelf; + if (strongSelf) { + logTargetToPrioritizer = strongSelf->_logTargetToPrioritizer; + } + }); + return logTargetToPrioritizer; } @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLRegistrar_Private.h b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLRegistrar_Private.h index f133cffd2b4..b4d4d19b75c 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLRegistrar_Private.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLRegistrar_Private.h @@ -18,10 +18,15 @@ @interface GDLRegistrar () +/** The concurrent queue on which all registration occurs. */ +@property(nonatomic, readonly) dispatch_queue_t registrarQueue; + /** A map of logTargets to backend implementations. */ -@property(nonatomic) NSMutableDictionary> *logTargetToUploader; +@property(atomic, readonly) + NSMutableDictionary> *logTargetToUploader; /** A map of logTargets to prioritizer implementations. */ -@property(nonatomic) NSMutableDictionary> *logTargetToPrioritizer; +@property(atomic, readonly) + NSMutableDictionary> *logTargetToPrioritizer; @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/Public/GDLRegistrar.h b/GoogleDataLogger/GoogleDataLogger/Classes/Public/GDLRegistrar.h index da3f583a41e..171bbf42151 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/Public/GDLRegistrar.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/Public/GDLRegistrar.h @@ -36,15 +36,14 @@ NS_ASSUME_NONNULL_BEGIN * @param backend The backend object to register. * @param logTarget The logTarget this backend object will be responsible for. */ -- (void)registerBackend:(id)backend forLogTarget:(GDLLogTarget)logTarget; +- (void)registerUploader:(id)backend logTarget:(GDLLogTarget)logTarget; /** Registers a log prioritizer implementation with the GoogleDataLogger infrastructure. * * @param prioritizer The prioritizer object to register. * @param logTarget The logTarget this prioritizer object will be responsible for. */ -- (void)registerLogPrioritizer:(id)prioritizer - forLogTarget:(GDLLogTarget)logTarget; +- (void)registerPrioritizer:(id)prioritizer logTarget:(GDLLogTarget)logTarget; @end diff --git a/GoogleDataLogger/GoogleDataLogger/DependencyWrappers/GDLConsoleLogger.h b/GoogleDataLogger/GoogleDataLogger/DependencyWrappers/GDLConsoleLogger.h index a62b02486aa..98fa197d305 100644 --- a/GoogleDataLogger/GoogleDataLogger/DependencyWrappers/GDLConsoleLogger.h +++ b/GoogleDataLogger/GoogleDataLogger/DependencyWrappers/GDLConsoleLogger.h @@ -33,6 +33,9 @@ typedef NS_ENUM(NSInteger, GDLMessageCode) { /** For warning messages concerning protoBytes: not being implemented by a log extension. */ GDLMCWExtensionMissingBytesImpl = 1, + /** For warning message concerning a failed log upload. */ + GDLMCWUploadFailed = 2, + /** For error messages concerning transform: not being implemented by a log transformer. */ GDLMCETransformerDoesntImplementTransform = 1000, diff --git a/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.h b/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.h index 4d2f0fa4518..e242d144339 100644 --- a/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.h +++ b/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.h @@ -15,6 +15,7 @@ */ #import "GDLRegistrar.h" +#import "GDLRegistrar_Private.h" NS_ASSUME_NONNULL_BEGIN diff --git a/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.m b/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.m index 5aa9a2d5a10..ce64c79c4fe 100644 --- a/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.m +++ b/GoogleDataLogger/Tests/Unit/Categories/GDLRegistrar+Testing.m @@ -21,8 +21,10 @@ @implementation GDLRegistrar (Testing) - (void)reset { - [self.logTargetToPrioritizer removeAllObjects]; - [self.logTargetToUploader removeAllObjects]; + dispatch_sync(self.registrarQueue, ^{ + [self.logTargetToPrioritizer removeAllObjects]; + [self.logTargetToUploader removeAllObjects]; + }); } @end diff --git a/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.h b/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.h index 389c64e6dc9..03136e6bcea 100644 --- a/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.h +++ b/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.h @@ -21,6 +21,9 @@ NS_ASSUME_NONNULL_BEGIN /** A functionless fake that can be injected into classes that need it. */ @interface GDLLogStorageFake : GDLLogStorage +/** The logs to return from -logHashesToFiles. */ +@property(nonatomic) NSSet *logsToReturnFromLogHashesToFiles; + @end NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.m b/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.m index bed9979dd7c..dfc1baca5fd 100644 --- a/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.m +++ b/GoogleDataLogger/Tests/Unit/Fakes/GDLLogStorageFake.m @@ -21,4 +21,18 @@ @implementation GDLLogStorageFake - (void)storeLog:(GDLLogEvent *)log { } +- (NSSet *)logHashesToFiles:(NSSet *)logHashes { + if (_logsToReturnFromLogHashesToFiles) { + return _logsToReturnFromLogHashesToFiles; + } else { + return [[NSSet alloc] init]; + } +} + +- (void)removeLog:(NSNumber *)logHash logTarget:(NSNumber *)logTarget { +} + +- (void)removeLogs:(NSSet *)logHashes logTarget:(NSNumber *)logTarget { +} + @end diff --git a/GoogleDataLogger/Tests/Unit/GDLLogStorageTest.m b/GoogleDataLogger/Tests/Unit/GDLLogStorageTest.m index 9e5f5b61cac..aaa64b5bd4f 100644 --- a/GoogleDataLogger/Tests/Unit/GDLLogStorageTest.m +++ b/GoogleDataLogger/Tests/Unit/GDLLogStorageTest.m @@ -53,8 +53,8 @@ - (void)setUp { [super setUp]; self.testBackend = [[GDLTestUploader alloc] init]; self.testPrioritizer = [[GDLTestPrioritizer alloc] init]; - [[GDLRegistrar sharedInstance] registerBackend:_testBackend forLogTarget:logTarget]; - [[GDLRegistrar sharedInstance] registerLogPrioritizer:_testPrioritizer forLogTarget:logTarget]; + [[GDLRegistrar sharedInstance] registerUploader:_testBackend logTarget:logTarget]; + [[GDLRegistrar sharedInstance] registerPrioritizer:_testPrioritizer logTarget:logTarget]; self.uploaderFake = [[GDLUploadCoordinatorFake alloc] init]; [GDLLogStorage sharedInstance].uploader = self.uploaderFake; } diff --git a/GoogleDataLogger/Tests/Unit/GDLRegistrarTest.m b/GoogleDataLogger/Tests/Unit/GDLRegistrarTest.m index 1aafc2952d5..ace2b313d41 100644 --- a/GoogleDataLogger/Tests/Unit/GDLRegistrarTest.m +++ b/GoogleDataLogger/Tests/Unit/GDLRegistrarTest.m @@ -18,15 +18,41 @@ #import +#import "GDLRegistrar_Private.h" +#import "GDLTestPrioritizer.h" +#import "GDLTestUploader.h" + @interface GDLRegistrarTest : GDLTestCase +@property(nonatomic) GDLLogTarget logTarget; + @end @implementation GDLRegistrarTest +- (void)setUp { + _logTarget = 23; +} + /** Tests the default initializer. */ - (void)testInit { XCTAssertNotNil([[GDLRegistrarTest alloc] init]); } +/** Test registering an uploader. */ +- (void)testRegisterUpload { + GDLRegistrar *registrar = [GDLRegistrar sharedInstance]; + GDLTestUploader *uploader = [[GDLTestUploader alloc] init]; + XCTAssertNoThrow([registrar registerUploader:uploader logTarget:self.logTarget]); + XCTAssertEqual(uploader, registrar.logTargetToUploader[@(_logTarget)]); +} + +/** Test registering a prioritizer. */ +- (void)testRegisterPrioritizer { + GDLRegistrar *registrar = [GDLRegistrar sharedInstance]; + GDLTestPrioritizer *prioritizer = [[GDLTestPrioritizer alloc] init]; + XCTAssertNoThrow([registrar registerPrioritizer:prioritizer logTarget:self.logTarget]); + XCTAssertEqual(prioritizer, registrar.logTargetToPrioritizer[@(_logTarget)]); +} + @end diff --git a/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.h b/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.h index c5fd17dbf4c..3775af06ac7 100644 --- a/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.h +++ b/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.h @@ -31,6 +31,9 @@ NS_ASSUME_NONNULL_BEGIN /** Allows the running of a block of code during -prioritizeLog. */ @property(nullable, nonatomic) void (^prioritizeLogBlock)(GDLLogEvent *logEvent); +/** A block that can run before -logsForNextUpload completes. */ +@property(nullable, nonatomic) void (^logsForNextUploadBlock)(void); + @end NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.m b/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.m index 8db2f3a418f..d0a0f75a79b 100644 --- a/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.m +++ b/GoogleDataLogger/Tests/Unit/Helpers/GDLTestPrioritizer.m @@ -27,6 +27,9 @@ - (instancetype)init { } - (NSSet *)logsForNextUpload { + if (_logsForNextUploadBlock) { + _logsForNextUploadBlock(); + } return _logsForNextUploadFake; }