| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /*
- * Copyright 2018 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 <OCMock/OCMock.h>
- #import <XCTest/XCTest.h>
- #import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutHttpRequestSender.h"
- #import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"
- #import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutUploader.h"
- #import "FirebaseInAppMessaging/Sources/Private/Util/FIRIAMTimeFetcher.h"
- @interface FIRIAMClearcutUploaderTests : XCTestCase
- @property(nonatomic) id<FIRIAMTimeFetcher> mockTimeFetcher;
- @property(nonatomic) FIRIAMClearcutHttpRequestSender *mockRequestSender;
- @property(nonatomic) FIRIAMClearcutLogStorage *mockLogStorage;
- @property(nonatomic) FIRIAMClearcutStrategy *defaultStrategy;
- @property(nonatomic) NSUserDefaults *mockUserDefaults;
- @property(nonatomic) NSString *cachePath;
- @end
- // Expose certain internal things to help with unit testing.
- @interface FIRIAMClearcutUploader (UnitTest)
- @property(nonatomic, assign) int64_t nextValidSendTimeInMills;
- @end
- @implementation FIRIAMClearcutUploaderTests
- // Helper function to avoid conflicts between tests with the singleton cache path.
- - (NSString *)generatedCachePath {
- // Filter out any invalid filesystem characters.
- NSCharacterSet *invalidCharacters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
- // This will result in a string with the class name, a space, and the test name. We only care
- // about the test name so split it into components and return the last item.
- NSString *friendlyTestName = [self.name stringByTrimmingCharactersInSet:invalidCharacters];
- NSArray<NSString *> *components = [friendlyTestName componentsSeparatedByString:@" "];
- NSString *testName = [components lastObject];
- NSString *cacheDirPath =
- NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
- return [NSString stringWithFormat:@"%@/%@", cacheDirPath, testName];
- }
- - (void)setUp {
- [super setUp];
- self.mockTimeFetcher = OCMProtocolMock(@protocol(FIRIAMTimeFetcher));
- self.mockRequestSender = OCMClassMock(FIRIAMClearcutHttpRequestSender.class);
- self.mockLogStorage = OCMClassMock(FIRIAMClearcutLogStorage.class);
- self.defaultStrategy = [[FIRIAMClearcutStrategy alloc] initWithMinWaitTimeInMills:1000
- maxWaitTimeInMills:2000
- failureBackoffTimeInMills:1000
- batchSendSize:10];
- self.mockUserDefaults = OCMClassMock(NSUserDefaults.class);
- self.cachePath = [self generatedCachePath];
- OCMStub([self.mockUserDefaults integerForKey:[OCMArg any]]).andReturn(0);
- }
- - (void)tearDown {
- [[NSFileManager defaultManager] removeItemAtPath:self.cachePath error:NULL];
- [super tearDown];
- }
- - (void)testUploadTriggeredWhenWaitTimeConditionSatisfied {
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- // using a real storage in this case
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- // Upload right away: nextValidSendTimeInMills < current time
- uploader.nextValidSendTimeInMills = (int64_t)(currentMoment - 1) * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- OCMStub([self.mockRequestSender sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:[OCMArg any]])
- .andDo(^(NSInvocation *invocation) {
- [expectation fulfill];
- });
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- // We expect expectation to be fulfilled right away since the upload can be carried out without
- // delay.
- [self waitForExpectationsWithTimeout:1.0 handler:nil];
- }
- - (void)testUploadNotTriggeredWhenWaitTimeConditionNotSatisfied {
- // using a real storage in this case
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- // Forces uploading to be at least 5 seconds later.
- uploader.nextValidSendTimeInMills = (int64_t)(currentMoment + 5) * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- __block BOOL sendingAttempted = NO;
- // We don't expect sendClearcutHttpRequestForLogs:withCompletion: to be triggered
- // after wait for 2.0 seconds below. We have a BOOL flag to be used for that kind verification
- // checking.
- OCMStub([self.mockRequestSender sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:[OCMArg any]])
- .andDo(^(NSInvocation *invocation) {
- sendingAttempted = YES;
- });
- [uploader addNewLogRecord:newRecord];
- // We wait for 2 seconds and we expect nothing should happen to self.mockRequestSender right after
- // 2 seconds: the upload will eventually be attempted in after 10 seconds based on the setup
- // in this unit test.
- double delayInSeconds = 2.0;
- dispatch_time_t popTime =
- dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
- [expectation fulfill];
- });
- // We expect expectation to be fulfilled right away since the upload can be carried out without
- // delay.
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- XCTAssertFalse(sendingAttempted);
- }
- - (void)testUploadBatchSizeIsBasedOnStrategySetting {
- int batchSendSize = 5;
- // using a strategy with batch send size as 5
- FIRIAMClearcutStrategy *strategy =
- [[FIRIAMClearcutStrategy alloc] initWithMinWaitTimeInMills:1000
- maxWaitTimeInMills:2000
- failureBackoffTimeInMills:1000
- batchSendSize:batchSendSize];
- // Next upload is now.
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:self.mockLogStorage
- usingStrategy:strategy
- usingUserDefaults:self.mockUserDefaults];
- uploader.nextValidSendTimeInMills = (int64_t)currentMoment * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- OCMExpect([self.mockLogStorage popStillValidRecordsForUpTo:batchSendSize]);
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- // we wait for 2 seconds to ensure that the next send is attempted and then verify its
- // interacton with the underlying storage
- double delayInSeconds = 2.0;
- dispatch_time_t popTime =
- dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
- [expectation fulfill];
- });
- // we expect expectation to be fulfilled right away since the upload can be carried out without
- // delay
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- OCMVerifyAll((id)self.mockLogStorage);
- }
- - (void)testRespectingWaitTimeFromRequestSender {
- // The next upload is now.
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- // using a real storage in this case
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- uploader.nextValidSendTimeInMills = (int64_t)currentMoment * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- // notice that waitTime is between minWaitTimeInMills and maxWaitTimeInMills in the default
- // strategy
- NSNumber *waitTime = [NSNumber numberWithLongLong:1500];
- // set up request sender which triggers the callback with a wait time interval to be 1000
- // milliseconds
- OCMStub(
- [self.mockRequestSender
- sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:([OCMArg invokeBlockWithArgs:@YES, @NO, waitTime, nil])])
- .andDo(^(NSInvocation *invocation) {
- [expectation fulfill];
- });
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- // verify the update to nextValidSendTimeInMills is expected
- XCTAssertEqual(currentMoment * 1000 + 1500, uploader.nextValidSendTimeInMills);
- }
- - (void)disable_testWaitTimeFromRequestSenderAdjustedByMinWaitTimeInStrategy {
- // The next upload is now.
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- // using a real storage in this case
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- uploader.nextValidSendTimeInMills = (int64_t)currentMoment * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- // notice that waitTime is below minWaitTimeInMills in the default strategy
- NSNumber *waitTime =
- [NSNumber numberWithLongLong:self.defaultStrategy.minimalWaitTimeInMills - 200];
- // set up request sender which triggers the callback with a wait time interval to be 1000
- // milliseconds
- OCMStub(
- [self.mockRequestSender
- sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:([OCMArg invokeBlockWithArgs:@YES, @NO, waitTime, nil])])
- .andDo(^(NSInvocation *invocation) {
- [expectation fulfill];
- });
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- // verify the update to nextValidSendTimeInMills is expected
- XCTAssertEqual(currentMoment * 1000 + self.defaultStrategy.minimalWaitTimeInMills,
- uploader.nextValidSendTimeInMills);
- }
- - (void)testWaitTimeFromRequestSenderAdjustedByMaxWaitTimeInStrategy {
- // The next upload is now.
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- // using a real storage in this case
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- uploader.nextValidSendTimeInMills = (int64_t)currentMoment * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- // notice that waitTime is larger than maximumWaitTimeInMills in the default strategy
- NSNumber *waitTime =
- [NSNumber numberWithLongLong:self.defaultStrategy.maximumWaitTimeInMills + 200];
- // set up request sender which triggers the callback with a wait time interval to be 1000
- // milliseconds
- OCMStub(
- [self.mockRequestSender
- sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:([OCMArg invokeBlockWithArgs:@YES, @NO, waitTime, nil])])
- .andDo(^(NSInvocation *invocation) {
- [expectation fulfill];
- });
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- // verify the update to nextValidSendTimeInMills is expected
- XCTAssertEqual(currentMoment * 1000 + self.defaultStrategy.maximumWaitTimeInMills,
- uploader.nextValidSendTimeInMills);
- }
- - (void)testRepushLogsIfRequestSenderSaysSo {
- // The next upload is now.
- NSTimeInterval currentMoment = 10000;
- OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(currentMoment);
- // using a real storage in this case
- FIRIAMClearcutLogStorage *logStorage =
- [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000
- withTimeFetcher:self.mockTimeFetcher
- cachePath:self.cachePath];
- FIRIAMClearcutUploader *uploader =
- [[FIRIAMClearcutUploader alloc] initWithRequestSender:self.mockRequestSender
- timeFetcher:self.mockTimeFetcher
- logStorage:logStorage
- usingStrategy:self.defaultStrategy
- usingUserDefaults:self.mockUserDefaults];
- uploader.nextValidSendTimeInMills = (int64_t)currentMoment * 1000;
- XCTestExpectation *expectation = [self expectationWithDescription:@"Triggers send on sender"];
- // notice that waitTime is larger than maximumWaitTimeInMills in the default strategy
- NSNumber *waitTime =
- [NSNumber numberWithLongLong:self.defaultStrategy.maximumWaitTimeInMills + 200];
- // Notice that it's invoking completion with falure flag and a flag to re-push those logs
- OCMStub(
- [self.mockRequestSender
- sendClearcutHttpRequestForLogs:[OCMArg any]
- withCompletion:([OCMArg invokeBlockWithArgs:@NO, @YES, waitTime, nil])])
- .andDo(^(NSInvocation *invocation) {
- [expectation fulfill];
- });
- FIRIAMClearcutLogRecord *newRecord =
- [[FIRIAMClearcutLogRecord alloc] initWithExtensionJsonString:@"string"
- eventTimestampInSeconds:currentMoment];
- [uploader addNewLogRecord:newRecord];
- [self waitForExpectationsWithTimeout:10.0 handler:nil];
- // we should still be able to fetch one log record from storage since it's re-pushed due
- // to send failure
- XCTAssertEqual([logStorage popStillValidRecordsForUpTo:10].count, 1);
- }
- @end
|