|
|
@@ -1,787 +0,0 @@
|
|
|
-/*
|
|
|
- * 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 <XCTest/XCTest.h>
|
|
|
-
|
|
|
-#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORStorageProtocol.h"
|
|
|
-#import "GoogleDataTransport/GDTCORTests/Common/Categories/GDTCORRegistrar+Testing.h"
|
|
|
-
|
|
|
-#import "GoogleDataTransport/GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h"
|
|
|
-#import "GoogleDataTransport/GDTCCTLibrary/Private/GDTCCTUploader.h"
|
|
|
-
|
|
|
-#import "GoogleDataTransport/GDTCCTTests/Common/TestStorage/GDTCCTTestStorage.h"
|
|
|
-
|
|
|
-#import "GoogleDataTransport/GDTCCTTests/Unit/Helpers/GDTCCTEventGenerator.h"
|
|
|
-#import "GoogleDataTransport/GDTCCTTests/Unit/TestServer/GDTCCTTestServer.h"
|
|
|
-
|
|
|
-@interface GDTCCTUploaderTest : XCTestCase
|
|
|
-
|
|
|
-@property(nonatomic) GDTCCTUploader *uploader;
|
|
|
-
|
|
|
-/** An event generator for testing. */
|
|
|
-@property(nonatomic) GDTCCTEventGenerator *generator;
|
|
|
-
|
|
|
-/** The local HTTP server to use for testing. */
|
|
|
-@property(nonatomic) GDTCCTTestServer *testServer;
|
|
|
-
|
|
|
-@property(nonatomic) GDTCCTTestStorage *testStorage;
|
|
|
-
|
|
|
-@end
|
|
|
-
|
|
|
-@implementation GDTCCTUploaderTest
|
|
|
-
|
|
|
-- (void)setUp {
|
|
|
- [super setUp];
|
|
|
-
|
|
|
- self.testStorage = [[GDTCCTTestStorage alloc] init];
|
|
|
-
|
|
|
- // Reset registrar to avoid real object access storage along with the tests.
|
|
|
- [[GDTCORRegistrar sharedInstance] reset];
|
|
|
- [[GDTCORRegistrar sharedInstance] registerStorage:self.testStorage target:kGDTCORTargetTest];
|
|
|
-
|
|
|
- self.generator = [[GDTCCTEventGenerator alloc] initWithTarget:kGDTCORTargetTest];
|
|
|
-
|
|
|
- self.testServer = [[GDTCCTTestServer alloc] init];
|
|
|
- [self.testServer registerLogBatchPath];
|
|
|
- [self.testServer start];
|
|
|
- XCTAssertTrue(self.testServer.isRunning);
|
|
|
-
|
|
|
- self.uploader = [[GDTCCTUploader alloc] init];
|
|
|
- GDTCCTUploader.testServerURL =
|
|
|
- [self.testServer.serverURL URLByAppendingPathComponent:@"logBatch"];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)tearDown {
|
|
|
- self.testServer.responseCompletedBlock = nil;
|
|
|
- [self.testServer stop];
|
|
|
- self.testStorage = nil;
|
|
|
- [super tearDown];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Upload flow tests
|
|
|
-
|
|
|
-- (void)testUploadTargetWhenThereAreEventsToUpload {
|
|
|
- // 0. Generate test events.
|
|
|
- id<GDTCORStorageProtocol> storage = GDTCORStorageInstanceForTarget(kGDTCORTargetTest);
|
|
|
- XCTAssertNotNil(storage);
|
|
|
- [[self.generator generateTheFiveConsistentEvents]
|
|
|
- enumerateObjectsUsingBlock:^(GDTCOREvent *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
|
|
|
- [storage storeEvent:obj onComplete:nil];
|
|
|
- }];
|
|
|
-
|
|
|
- // 1. Set up expectations.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- // 1.3. Don't expect previously batched events to be removed (no batch present).
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 1.4. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 2. Create uploader and start upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTargetWhenThereIsStoredBatchThenItIsUploadedFirst {
|
|
|
- // 0. Generate test events.
|
|
|
- // 0.1. Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
- // 0.2. Batch the event.
|
|
|
- [self batchEvents];
|
|
|
-
|
|
|
- // 1. Set up expectations.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- // 1.3. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 2. Create uploader and start upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:NO];
|
|
|
-
|
|
|
- // 4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-/** Tests that when there is an ongoing upload no other uploads are started until the 1st finishes.
|
|
|
- * Once 1st finished, another one can be started. */
|
|
|
-- (void)testUploadTargetWhenThereIsOngoingUploadThenNoOp {
|
|
|
- // 0. Set up expectations to track 1st upload progress.
|
|
|
- // 0.1. Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
- // 0.2. Configure server request expectation.
|
|
|
- // Block to call to finish the 1st request.
|
|
|
- __block dispatch_block_t requestCompletionBlock;
|
|
|
- __auto_type __weak weakSelf = self;
|
|
|
- XCTestExpectation *serverRequestExpectation1 =
|
|
|
- [self expectationWithDescription:@"serverRequestExpectation1"];
|
|
|
- self.testServer.requestHandler =
|
|
|
- ^(GCDWebServerRequest *_Nonnull request, GCDWebServerResponse *_Nullable suggestedResponse,
|
|
|
- GCDWebServerCompletionBlock _Nonnull completionBlock) {
|
|
|
- weakSelf.testServer.requestHandler = nil;
|
|
|
- requestCompletionBlock = ^{
|
|
|
- completionBlock(suggestedResponse);
|
|
|
- };
|
|
|
-
|
|
|
- [serverRequestExpectation1 fulfill];
|
|
|
- };
|
|
|
-
|
|
|
- // 0.3. Configure storage.
|
|
|
- XCTestExpectation *hasEventsExpectation1 = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- // 0.4. Start upload 1st upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 0.4. Wait for server request to be sent.
|
|
|
- [self waitForExpectations:@[ hasEventsExpectation1, serverRequestExpectation1 ] timeout:1];
|
|
|
-
|
|
|
- // 1. Test 2nd request.
|
|
|
- // 1.0 Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
-
|
|
|
- // 1.1. Configure expectations for the 2nd request.
|
|
|
- // 1.1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.1.2. Don't expect any storage.
|
|
|
- self.testStorage.batchIDsForTargetExpectation.inverted = YES;
|
|
|
- self.testStorage.batchWithEventSelectorExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- XCTestExpectation *hasEventsExpectation2 = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
- hasEventsExpectation2.inverted = YES;
|
|
|
-
|
|
|
- // 1.2. Start upload 2nd time.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 1.3. Wait for expectations.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation, hasEventsExpectation2,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3];
|
|
|
-
|
|
|
- // 1.4. Wait for 1st upload finish.
|
|
|
- requestCompletionBlock();
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-
|
|
|
- // 3. Test another upload after the 1st finished.
|
|
|
- // 3.1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 3.1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation3 = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- // 3.1.3. Don't expect previously batched events to be removed (no batch present).
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 3.1.4. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 3.3.2. Start 3rd upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 3.3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation3,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 3.4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenThereAreBothStoredBatchAndEvents_ThenRemoveBatchAndBatchThenAllEvents {
|
|
|
- // 0. Generate test events.
|
|
|
- // 0.1. Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
- // 0.2. Batch the event.
|
|
|
- [self batchEvents];
|
|
|
- // 0.3. Generate one more event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
-
|
|
|
- // 1. Set up expectations.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- // 1.3. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 1.2. Start upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 1.3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 1.4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenThereAreNoEventsFirstThenEventsAdded_ThenUploadNewEvent {
|
|
|
- GDTCCTUploader.testServerURL =
|
|
|
- [self.testServer.serverURL URLByAppendingPathComponent:@"logBatch"];
|
|
|
-
|
|
|
- // 1. Test stored batch upload.
|
|
|
- // 1.1. Set up expectations.
|
|
|
- // 1.1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:NO];
|
|
|
-
|
|
|
- // 1.1.3. Don't expect events to be batched or deleted.
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation.inverted = YES;
|
|
|
- self.testStorage.batchWithEventSelectorExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 1.1.4. Don't expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation1 = [self expectationTestServerSuccessRequestResponse];
|
|
|
- responseSentExpectation1.inverted = YES;
|
|
|
-
|
|
|
- // 1.2. Create uploader and start upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 1.3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation1,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 1.4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-
|
|
|
- // 2. Test stored events upload.
|
|
|
- // 2.0. Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
-
|
|
|
- // 2.1. Set up expectations.
|
|
|
- // 2.1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 2.1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest result:YES];
|
|
|
-
|
|
|
- // 2.1.3. Don't expect previously batched events to be removed (no batch present).
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 2.1.4. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 2.2. Create uploader and start upload.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 2.3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 2.4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Storage interaction tests
|
|
|
-
|
|
|
-- (void)testStorageSelectorWhenConditionsHighPriority {
|
|
|
- __weak id weakSelf = self;
|
|
|
- [self assertStorageSelectorWithCondition:GDTCORUploadConditionHighPriority
|
|
|
- validationBlock:^(GDTCORStorageEventSelector *_Nullable eventSelector,
|
|
|
- NSDate *expiration) {
|
|
|
- __unused id self = weakSelf;
|
|
|
- XCTAssertLessThan([expiration timeIntervalSinceNow], 600);
|
|
|
- XCTAssertEqual(eventSelector.selectedTarget, kGDTCORTargetTest);
|
|
|
- XCTAssertNil(eventSelector.selectedEventIDs);
|
|
|
- XCTAssertNil(eventSelector.selectedMappingIDs);
|
|
|
- XCTAssertNil(eventSelector.selectedQosTiers);
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testStorageSelectorWhenConditionsMobileData {
|
|
|
- __weak id weakSelf = self;
|
|
|
- [self
|
|
|
- assertStorageSelectorWithCondition:GDTCORUploadConditionMobileData
|
|
|
- validationBlock:^(GDTCORStorageEventSelector *_Nullable eventSelector,
|
|
|
- NSDate *expiration) {
|
|
|
- __unused id self = weakSelf;
|
|
|
- XCTAssertLessThan([expiration timeIntervalSinceNow], 600);
|
|
|
- XCTAssertEqual(eventSelector.selectedTarget, kGDTCORTargetTest);
|
|
|
- XCTAssertNil(eventSelector.selectedEventIDs);
|
|
|
- XCTAssertNil(eventSelector.selectedMappingIDs);
|
|
|
-
|
|
|
- NSSet *expectedQoSTiers = [NSSet
|
|
|
- setWithArray:@[ @(GDTCOREventQoSFast), @(GDTCOREventQosDefault) ]];
|
|
|
- XCTAssertEqualObjects(eventSelector.selectedQosTiers, expectedQoSTiers);
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testStorageSelectorWhenConditionsWifiData {
|
|
|
- __weak id weakSelf = self;
|
|
|
- [self
|
|
|
- assertStorageSelectorWithCondition:GDTCORUploadConditionWifiData
|
|
|
- validationBlock:^(GDTCORStorageEventSelector *_Nullable eventSelector,
|
|
|
- NSDate *expiration) {
|
|
|
- __unused id self = weakSelf;
|
|
|
- XCTAssertLessThan([expiration timeIntervalSinceNow], 600);
|
|
|
- XCTAssertEqual(eventSelector.selectedTarget, kGDTCORTargetTest);
|
|
|
- XCTAssertNil(eventSelector.selectedEventIDs);
|
|
|
- XCTAssertNil(eventSelector.selectedMappingIDs);
|
|
|
-
|
|
|
- NSSet *expectedQoSTiers = [NSSet setWithArray:@[
|
|
|
- @(GDTCOREventQoSFast), @(GDTCOREventQoSWifiOnly),
|
|
|
- @(GDTCOREventQosDefault), @(GDTCOREventQoSTelemetry),
|
|
|
- @(GDTCOREventQoSUnknown)
|
|
|
- ]];
|
|
|
- XCTAssertEqualObjects(eventSelector.selectedQosTiers, expectedQoSTiers);
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Test ready for upload based on conditions
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenNoConnection_ThenDoNotUpload {
|
|
|
- // 0. Generate and store and an event.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
-
|
|
|
- // 1. Configure expectations for the 2nd request.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.2. Don't expect any storage.
|
|
|
- self.testStorage.batchIDsForTargetExpectation.inverted = YES;
|
|
|
- self.testStorage.batchWithEventSelectorExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- XCTestExpectation *hasEventsExpectation2 = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
- hasEventsExpectation2.inverted = YES;
|
|
|
-
|
|
|
- // 2. Start upload 2nd time.
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:GDTCORUploadConditionNoNetwork];
|
|
|
-
|
|
|
- // 3. Wait for expectations.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation, hasEventsExpectation2,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3];
|
|
|
-
|
|
|
- // 4. Wait for 1st upload finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenBeforeServerNextUploadTimeForCCTAndFLLTargets_ThenDoNotUpload {
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetCCT
|
|
|
- QoS:GDTCOREventQoSFast
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:NO];
|
|
|
-
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetFLL
|
|
|
- QoS:GDTCOREventQosDefault
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:NO];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)
|
|
|
- testUploadTarget_WhenBeforeServerNextUploadTimeForCCTAndFLLTargetsAndHighPriority_ThenUpload {
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetCCT
|
|
|
- QoS:GDTCOREventQoSFast
|
|
|
- conditions:GDTCORUploadConditionHighPriority
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:YES];
|
|
|
-
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetFLL
|
|
|
- QoS:GDTCOREventQosDefault
|
|
|
- conditions:GDTCORUploadConditionHighPriority
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:YES];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenBeforeServerNextUploadTimeForOtherTargets_ThenUpload {
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetTest
|
|
|
- QoS:GDTCOREventQoSFast
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:YES];
|
|
|
-
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetCSH
|
|
|
- QoS:GDTCOREventQosDefault
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:YES];
|
|
|
-
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:60
|
|
|
- forTarget:kGDTCORTargetINT
|
|
|
- QoS:GDTCOREventQosDefault
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:NO
|
|
|
- expectRequest:YES];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)testUploadTarget_WhenAfterServerNextUploadTimeForCCTAndFLLTargets_ThenUpload {
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:1
|
|
|
- forTarget:kGDTCORTargetCCT
|
|
|
- QoS:GDTCOREventQoSFast
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:YES
|
|
|
- expectRequest:YES];
|
|
|
-
|
|
|
- [self assertUploadTargetRespectsNextRequestWaitTime:1
|
|
|
- forTarget:kGDTCORTargetFLL
|
|
|
- QoS:GDTCOREventQosDefault
|
|
|
- conditions:GDTCORUploadConditionWifiData
|
|
|
- shouldWaitForNextRequestTime:YES
|
|
|
- expectRequest:YES];
|
|
|
-}
|
|
|
-
|
|
|
-//// TODO: Tests for uploading several empty targets and then non-empty target.
|
|
|
-
|
|
|
-#pragma mark - Helpers
|
|
|
-
|
|
|
-- (NSNumber *)batchEvents {
|
|
|
- XCTestExpectation *eventsBatched = [self expectationWithDescription:@"eventsBatched"];
|
|
|
- __block NSNumber *batchID;
|
|
|
- [self.testStorage
|
|
|
- batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
|
|
|
- batchExpiration:[NSDate distantFuture]
|
|
|
- onComplete:^(NSNumber *_Nullable newBatchID,
|
|
|
- NSSet<GDTCOREvent *> *_Nullable batchEvents) {
|
|
|
- [eventsBatched fulfill];
|
|
|
- batchID = newBatchID;
|
|
|
- }];
|
|
|
- [self waitForExpectations:@[ eventsBatched ] timeout:0.5];
|
|
|
-
|
|
|
- XCTAssertNotNil(batchID);
|
|
|
- return batchID;
|
|
|
-}
|
|
|
-
|
|
|
-- (XCTestExpectation *)expectationTestServerSuccessRequestResponse {
|
|
|
- __weak id weakSelf = self;
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationWithDescription:@"response sent"];
|
|
|
-
|
|
|
- self.testServer.responseCompletedBlock =
|
|
|
- ^(GCDWebServerRequest *_Nonnull request, GCDWebServerResponse *_Nonnull response) {
|
|
|
- // Redefining the self var addresses strong self capturing in the XCTAssert macros.
|
|
|
- id self = weakSelf;
|
|
|
- XCTAssertNotNil(self);
|
|
|
- [responseSentExpectation fulfill];
|
|
|
- XCTAssertEqual(response.statusCode, 200);
|
|
|
- XCTAssertTrue(response.hasBody);
|
|
|
- };
|
|
|
- return responseSentExpectation;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)setUpStorageExpectations {
|
|
|
- self.testStorage.batchIDsForTargetExpectation =
|
|
|
- [self expectationWithDescription:@"batchIDsForTargetExpectation"];
|
|
|
- self.testStorage.batchWithEventSelectorExpectation =
|
|
|
- [self expectationWithDescription:@"batchWithEventSelectorExpectation"];
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation =
|
|
|
- [self expectationWithDescription:@"removeBatchWithoutDeletingEventsExpectation"];
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation =
|
|
|
- [self expectationWithDescription:@"removeBatchAndDeleteEventsExpectation"];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)waitForUploadOperationsToFinish:(GDTCCTUploader *)uploader {
|
|
|
- XCTestExpectation *uploadFinishedExpectation =
|
|
|
- [self expectationWithDescription:@"uploadFinishedExpectation"];
|
|
|
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
|
|
|
- uploader.uploaderQueue, ^{
|
|
|
- [uploadFinishedExpectation fulfill];
|
|
|
- XCTAssertNil(uploader.currentTask);
|
|
|
- });
|
|
|
- [self waitForExpectations:@[ uploadFinishedExpectation ] timeout:1];
|
|
|
-}
|
|
|
-
|
|
|
-- (XCTestExpectation *)expectStorageHasEventsForTarget:(GDTCORTarget)expectedTarget
|
|
|
- result:(BOOL)hasEvents {
|
|
|
- XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
|
|
|
-
|
|
|
- __weak __auto_type weakSelf = self;
|
|
|
- self.testStorage.hasEventsForTargetHandler =
|
|
|
- ^(GDTCORTarget target, GDTCCTTestStorageHasEventsCompletion _Nonnull completion) {
|
|
|
- __unused __auto_type self = weakSelf;
|
|
|
- [expectation fulfill];
|
|
|
- XCTAssertEqual(target, expectedTarget);
|
|
|
- completion(hasEvents);
|
|
|
- };
|
|
|
-
|
|
|
- return expectation;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)assertStorageSelectorWithCondition:(GDTCORUploadConditions)conditions
|
|
|
- validationBlock:(void (^)(GDTCORStorageEventSelector *_Nullable selector,
|
|
|
- NSDate *expirationDate))validationBlock {
|
|
|
- XCTestExpectation *hasEventsExpectation = [self expectStorageHasEventsForTarget:kGDTCORTargetTest
|
|
|
- result:YES];
|
|
|
-
|
|
|
- XCTestExpectation *storageBatchExpectation =
|
|
|
- [self expectationWithDescription:@"storageBatchExpectation"];
|
|
|
-
|
|
|
- self.testStorage.batchWithEventSelectorHandler =
|
|
|
- ^(GDTCORStorageEventSelector *_Nullable eventSelector, NSDate *_Nullable expiration,
|
|
|
- GDTCORStorageBatchBlock _Nullable completion) {
|
|
|
- // Redefining the self var addresses strong self capturing in the XCTAssert macros.
|
|
|
- [storageBatchExpectation fulfill];
|
|
|
-
|
|
|
- validationBlock(eventSelector, expiration);
|
|
|
- completion(nil, nil);
|
|
|
- };
|
|
|
-
|
|
|
- [self.uploader uploadTarget:kGDTCORTargetTest withConditions:conditions];
|
|
|
-
|
|
|
- [self waitForExpectations:@[ hasEventsExpectation, storageBatchExpectation ] timeout:1];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)sendEventSuccessfully {
|
|
|
- // 0. Generate test events.
|
|
|
- [self.generator generateEvent:GDTCOREventQoSFast];
|
|
|
-
|
|
|
- // 1. Set up expectations.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
-
|
|
|
- // 1.2. Expect `hasEventsForTarget:onComplete:` to be called.
|
|
|
- XCTestExpectation *hasEventsExpectation =
|
|
|
- [self expectStorageHasEventsForTarget:self.generator.target result:YES];
|
|
|
-
|
|
|
- // 1.3. Don't expect previously batched events to be removed (no batch present).
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 1.4. Expect a batch to be uploaded.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 2. Create uploader and start upload.
|
|
|
- [self.uploader uploadTarget:self.generator.target withConditions:GDTCORUploadConditionWifiData];
|
|
|
-
|
|
|
- // 3. Wait for operations to complete in the specified order.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation, hasEventsExpectation,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3
|
|
|
- enforceOrder:YES];
|
|
|
-
|
|
|
- // 4. Wait for upload operation to finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)assertUploadTargetRespectsNextRequestWaitTime:(NSTimeInterval)nextRequestWaitTime
|
|
|
- forTarget:(GDTCORTarget)target
|
|
|
- QoS:(GDTCOREventQoS)eventQoS
|
|
|
- conditions:(GDTCORUploadConditions)conditions
|
|
|
- shouldWaitForNextRequestTime:(BOOL)shouldWaitForNextRequestTime
|
|
|
- expectRequest:(BOOL)expectRequest {
|
|
|
- // 0.1. Set response next request wait time.
|
|
|
- self.testServer.responseNextRequestWaitTime = nextRequestWaitTime;
|
|
|
- // 0.2. Use a target that should respect next upload time.
|
|
|
- self.generator = [[GDTCCTEventGenerator alloc] initWithTarget:target];
|
|
|
- // 0.3. Register storage for the target.
|
|
|
- [[GDTCORRegistrar sharedInstance] reset];
|
|
|
- [[GDTCORRegistrar sharedInstance] registerStorage:self.testStorage target:self.generator.target];
|
|
|
- // 0.4. Send an event and receive response.
|
|
|
- [self sendEventSuccessfully];
|
|
|
- // 0.5. Generate another event to be sent.
|
|
|
- [self.generator generateEvent:eventQoS];
|
|
|
-
|
|
|
- // 0.6. Wait for the next request time.
|
|
|
- if (shouldWaitForNextRequestTime) {
|
|
|
- [[NSRunLoop currentRunLoop]
|
|
|
- runUntilDate:[NSDate dateWithTimeIntervalSinceNow:nextRequestWaitTime + 0.5]];
|
|
|
- }
|
|
|
-
|
|
|
- // 1. Configure expectations for the 2nd request.
|
|
|
- // 1.1. Set up all relevant storage expectations.
|
|
|
- [self setUpStorageExpectations];
|
|
|
- XCTestExpectation *hasEventsExpectation2 =
|
|
|
- [self expectStorageHasEventsForTarget:self.generator.target result:YES];
|
|
|
-
|
|
|
- // 1.2. Upload response expectation.
|
|
|
- XCTestExpectation *responseSentExpectation = [self expectationTestServerSuccessRequestResponse];
|
|
|
-
|
|
|
- // 1.3. Invert expectations if no actions expected.
|
|
|
- if (!expectRequest) {
|
|
|
- self.testStorage.batchIDsForTargetExpectation.inverted = YES;
|
|
|
- self.testStorage.batchWithEventSelectorExpectation.inverted = YES;
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation.inverted = YES;
|
|
|
- hasEventsExpectation2.inverted = YES;
|
|
|
- responseSentExpectation.inverted = YES;
|
|
|
- }
|
|
|
-
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation.inverted = YES;
|
|
|
-
|
|
|
- // 2. Start upload 2nd time.
|
|
|
- [self.uploader uploadTarget:self.generator.target withConditions:conditions];
|
|
|
-
|
|
|
- // 3. Wait for expectations.
|
|
|
- [self waitForExpectations:@[
|
|
|
- self.testStorage.batchIDsForTargetExpectation, hasEventsExpectation2,
|
|
|
- self.testStorage.batchWithEventSelectorExpectation, responseSentExpectation,
|
|
|
- self.testStorage.removeBatchWithoutDeletingEventsExpectation,
|
|
|
- self.testStorage.removeBatchAndDeleteEventsExpectation
|
|
|
- ]
|
|
|
- timeout:3];
|
|
|
-
|
|
|
- // 4. Wait for 1st upload finish.
|
|
|
- [self waitForUploadOperationsToFinish:self.uploader];
|
|
|
-}
|
|
|
-
|
|
|
-- (nullable NSURL *)serverURLForTarget:(GDTCORTarget)target {
|
|
|
- // These strings should be interleaved to construct the real URL. This is just to (hopefully)
|
|
|
- // fool github URL scanning bots.
|
|
|
- static NSURL *CCTServerURL;
|
|
|
- static NSString *const kINTServerURL =
|
|
|
- @"https://dummyapiverylong-dummy.dummy.com/dummy/api/very/long";
|
|
|
- static dispatch_once_t CCTOnceToken;
|
|
|
- dispatch_once(&CCTOnceToken, ^{
|
|
|
- const char *p1 = "hts/frbslgiggolai.o/0clgbth";
|
|
|
- const char *p2 = "tp:/ieaeogn.ogepscmvc/o/ac";
|
|
|
- const char URL[54] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4],
|
|
|
- p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8],
|
|
|
- p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13],
|
|
|
- p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17],
|
|
|
- p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22],
|
|
|
- p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], '\0'};
|
|
|
- CCTServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]];
|
|
|
- });
|
|
|
-
|
|
|
- static NSURL *FLLServerURL;
|
|
|
- static dispatch_once_t FLLOnceToken;
|
|
|
- dispatch_once(&FLLOnceToken, ^{
|
|
|
- const char *p1 = "hts/frbslgigp.ogepscmv/ieo/eaybtho";
|
|
|
- const char *p2 = "tp:/ieaeogn-agolai.o/1frlglgc/aclg";
|
|
|
- const char URL[69] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4],
|
|
|
- p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8],
|
|
|
- p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13],
|
|
|
- p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17],
|
|
|
- p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22],
|
|
|
- p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], p2[26],
|
|
|
- p1[27], p2[27], p1[28], p2[28], p1[29], p2[29], p1[30], p2[30], p1[31],
|
|
|
- p2[31], p1[32], p2[32], p1[33], p2[33], '\0'};
|
|
|
- FLLServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]];
|
|
|
- });
|
|
|
-
|
|
|
- static NSURL *CSHServerURL;
|
|
|
- static dispatch_once_t CSHOnceToken;
|
|
|
- dispatch_once(&CSHOnceToken, ^{
|
|
|
- // These strings should be interleaved to construct the real URL. This is just to (hopefully)
|
|
|
- // fool github URL scanning bots.
|
|
|
- const char *p1 = "hts/cahyiseot-agolai.o/1frlglgc/aclg";
|
|
|
- const char *p2 = "tp:/rsltcrprsp.ogepscmv/ieo/eaybtho";
|
|
|
- const char URL[72] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4],
|
|
|
- p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8],
|
|
|
- p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13],
|
|
|
- p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17],
|
|
|
- p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22],
|
|
|
- p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], p2[26],
|
|
|
- p1[27], p2[27], p1[28], p2[28], p1[29], p2[29], p1[30], p2[30], p1[31],
|
|
|
- p2[31], p1[32], p2[32], p1[33], p2[33], p1[34], p2[34], p1[35], '\0'};
|
|
|
- CSHServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]];
|
|
|
- });
|
|
|
-
|
|
|
- switch (target) {
|
|
|
- case kGDTCORTargetCCT:
|
|
|
- return CCTServerURL;
|
|
|
-
|
|
|
- case kGDTCORTargetFLL:
|
|
|
- return FLLServerURL;
|
|
|
-
|
|
|
- case kGDTCORTargetCSH:
|
|
|
- return CSHServerURL;
|
|
|
-
|
|
|
- case kGDTCORTargetINT:
|
|
|
- return [NSURL URLWithString:kINTServerURL];
|
|
|
-
|
|
|
- default:
|
|
|
- return nil;
|
|
|
- break;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@end
|