| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- // 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 "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
- #import <Foundation/Foundation.h>
- #import <XCTest/XCTest.h>
- #if __has_include(<FBLPromises/FBLPromises.h>)
- #import <FBLPromises/FBLPromises.h>
- #else
- #import "FBLPromises.h"
- #endif
- #import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
- #import "Crashlytics/UnitTests/Mocks/FABMockApplicationIdentifierModel.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockFileManager.h"
- const NSString *FIRCLSTestSettingsActivated =
- @"{\"settings_version\":3,\"cache_duration\":60,\"features\":{\"collect_logged_exceptions\":"
- @"true,\"collect_reports\":true, \"collect_metric_kit\":true},"
- @"\"fabric\":{\"org_id\":\"010101000000111111111111\",\"bundle_id\":\"com.lets.test."
- @"crashlytics\"}}";
- const NSString *FIRCLSTestSettingsInverse =
- @"{\"settings_version\":3,\"cache_duration\":12345,\"features\":{\"collect_logged_exceptions\":"
- @"false,\"collect_reports\":false, \"collect_metric_kit\":false},"
- @"\"fabric\":{\"org_id\":\"01e101a0000011b113115111\",\"bundle_id\":\"im.from.the.server\"},"
- @"\"session\":{\"log_buffer_size\":128000,\"max_chained_exception_depth\":32,\"max_complete_"
- @"sessions_count\":4,\"max_custom_exception_events\":1000,\"max_custom_key_value_pairs\":2000,"
- @"\"identifier_mask\":255}, \"on_demand_upload_rate_per_minute\":15.0, "
- @"\"on_demand_backoff_base\":3.0, \"on_demand_backoff_step_duration_seconds\":9}";
- const NSString *FIRCLSTestSettingsCorrupted = @"{{{{ non_key: non\"value {}";
- NSString *FIRCLSDefaultMockBuildInstanceID = @"12345abcdef";
- NSString *FIRCLSDifferentMockBuildInstanceID = @"98765zyxwv";
- NSString *FIRCLSDefaultMockAppDisplayVersion = @"1.2.3-beta.2";
- NSString *FIRCLSDifferentMockAppDisplayVersion = @"1.2.3-beta.3";
- NSString *FIRCLSDefaultMockAppBuildVersion = @"1024";
- NSString *FIRCLSDifferentMockAppBuildVersion = @"2048";
- NSString *const TestGoogleAppID = @"1:test:google:app:id";
- NSString *const TestChangedGoogleAppID = @"2:changed:google:app:id";
- @interface FIRCLSSettings (Testing)
- @property(nonatomic, strong) NSDictionary<NSString *, id> *settingsDictionary;
- @end
- @interface FIRCLSSettingsTests : XCTestCase
- @property(nonatomic, retain) FIRCLSMockFileManager *fileManager;
- @property(nonatomic, retain) FABMockApplicationIdentifierModel *appIDModel;
- @property(nonatomic, retain) FIRCLSSettings *settings;
- @end
- @implementation FIRCLSSettingsTests
- - (void)setUp {
- [super setUp];
- _fileManager = [[FIRCLSMockFileManager alloc] init];
- _appIDModel = [[FABMockApplicationIdentifierModel alloc] init];
- _appIDModel.buildInstanceID = FIRCLSDefaultMockBuildInstanceID;
- _appIDModel.displayVersion = FIRCLSDefaultMockAppDisplayVersion;
- _appIDModel.buildVersion = FIRCLSDefaultMockAppBuildVersion;
- _settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager appIDModel:_appIDModel];
- }
- - (void)testDefaultSettings {
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- // Default to an hour
- XCTAssertEqual(self.settings.cacheDurationSeconds, 60 * 60);
- XCTAssertTrue(self.settings.collectReportsEnabled);
- XCTAssertTrue(self.settings.errorReportingEnabled);
- XCTAssertTrue(self.settings.customExceptionsEnabled);
- XCTAssertFalse(self.settings.metricKitCollectionEnabled);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.logBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.maxCustomExceptions, 8);
- XCTAssertEqual(self.settings.maxCustomKeys, 64);
- XCTAssertEqual(self.settings.onDemandUploadRate, 10);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 1.5);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 6);
- }
- - (BOOL)writeSettings:(const NSString *)settings error:(NSError **)error {
- return [self writeSettings:settings error:error isCacheKey:NO];
- }
- - (BOOL)writeSettings:(const NSString *)settings
- error:(NSError **)error
- isCacheKey:(BOOL)isCacheKey {
- NSString *path = _fileManager.settingsFilePath;
- if (isCacheKey) {
- path = _fileManager.settingsCacheKeyPath;
- }
- return [self.fileManager createFileAtPath:path
- contents:[settings dataUsingEncoding:NSUTF8StringEncoding]
- attributes:nil];
- }
- - (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID
- currentTimestamp:(NSTimeInterval)currentTimestamp
- expectedRemoveCount:(NSInteger)expectedRemoveCount {
- self.fileManager.removeExpectation = [[XCTestExpectation alloc]
- initWithDescription:@"FIRCLSMockFileManager.removeExpectation.cache"];
- self.fileManager.removeCount = 0;
- self.fileManager.expectedRemoveCount = expectedRemoveCount;
- [self.settings cacheSettingsWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp];
- [self waitForExpectations:@[ self.fileManager.removeExpectation ] timeout:1];
- }
- - (void)reloadFromCacheWithGoogleAppID:(NSString *)googleAppID
- currentTimestamp:(NSTimeInterval)currentTimestamp
- expectedRemoveCount:(NSInteger)expectedRemoveCount {
- self.fileManager.removeExpectation = [[XCTestExpectation alloc]
- initWithDescription:@"FIRCLSMockFileManager.removeExpectation.reload"];
- self.fileManager.removeCount = 0;
- self.fileManager.expectedRemoveCount = expectedRemoveCount;
- [self.settings reloadFromCacheWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp];
- [self waitForExpectations:@[ self.fileManager.removeExpectation ] timeout:1];
- }
- - (void)testActivatedSettingsCached {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 60);
- XCTAssertTrue(self.settings.collectReportsEnabled);
- XCTAssertTrue(self.settings.errorReportingEnabled);
- XCTAssertTrue(self.settings.customExceptionsEnabled);
- XCTAssertTrue(self.settings.metricKitCollectionEnabled);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.logBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.maxCustomExceptions, 8);
- XCTAssertEqual(self.settings.maxCustomKeys, 64);
- XCTAssertEqual(self.settings.onDemandUploadRate, 10);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 1.5);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 6);
- }
- - (void)testInverseDefaultSettingsCached {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 12345);
- XCTAssertFalse(self.settings.collectReportsEnabled);
- XCTAssertFalse(self.settings.errorReportingEnabled);
- XCTAssertFalse(self.settings.customExceptionsEnabled);
- XCTAssertFalse(self.settings.metricKitCollectionEnabled);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- XCTAssertEqual(self.settings.logBufferSize, 128000);
- XCTAssertEqual(self.settings.maxCustomExceptions, 1000);
- XCTAssertEqual(self.settings.maxCustomKeys, 2000);
- XCTAssertEqual(self.settings.onDemandUploadRate, 15);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 3);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 9);
- }
- - (void)testCacheExpiredFromTTL {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- // 1 delete for clearing the cache key, plus 2 for the deletes from reloading and clearing the
- // cache and cache key
- self.fileManager.expectedRemoveCount = 3;
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Go forward in time by 2x the cache duration
- NSTimeInterval futureTimestamp = currentTimestamp + (2 * self.settings.cacheDurationSeconds);
- [self.settings reloadFromCacheWithGoogleAppID:TestGoogleAppID currentTimestamp:futureTimestamp];
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- // Since the TTL just expired, do not clear settings
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- // Pretend we fetched settings again, but they had different values
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- // Cache the settings
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // We should have the updated values that were fetched, and should not be expired
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- }
- - (void)testCacheExpiredFromBuildInstanceID {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- // 1 delete for clearing the cache key, plus 2 for the deletes from reloading and clearing the
- // cache and cache key
- self.fileManager.expectedRemoveCount = 3;
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Change the Build Instance ID
- self.appIDModel.buildInstanceID = FIRCLSDifferentMockBuildInstanceID;
- [self.settings reloadFromCacheWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- // Since the TTL just expired, do not clear settings
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- // Pretend we fetched settings again, but they had different values
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- // Cache the settings
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // We should have the updated values that were fetched, and should not be expired
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- }
- - (void)testCacheExpiredFromAppVersion {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- // 1 delete for clearing the cache key, plus 2 for the deletes from reloading and clearing the
- // cache and cache key
- self.fileManager.expectedRemoveCount = 3;
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Change the App Version
- self.appIDModel.displayVersion = FIRCLSDifferentMockAppDisplayVersion;
- self.appIDModel.buildVersion = FIRCLSDifferentMockAppBuildVersion;
- [self.settings reloadFromCacheWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- // Since the TTL just expired, do not clear settings
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- // Pretend we fetched settings again, but they had different values
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- // Cache the settings
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // We should have the updated values that were fetched, and should not be expired
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- }
- - (void)testGoogleAppIDChanged {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Different Google App ID
- [self reloadFromCacheWithGoogleAppID:TestChangedGoogleAppID
- currentTimestamp:currentTimestamp
- expectedRemoveCount:2];
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- // Clear the settings because they were for a different Google App ID
- // Pretend we fetched settings again, but they had different values
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- // Cache the settings with the new Google App ID
- [self.settings cacheSettingsWithGoogleAppID:TestChangedGoogleAppID
- currentTimestamp:currentTimestamp];
- // Should have new values and not expired
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- }
- // This is a weird case where we got settings, but never created a cache key for it. We are
- // treating this as if the cache was invalid and re-fetching in this case.
- - (void)testActivatedSettingsMissingCacheKey {
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsActivated error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- // We only expect 1 removal because the cache key doesn't exist,
- // and deleteCachedSettings deletes the cache and the cache key
- [self reloadFromCacheWithGoogleAppID:TestGoogleAppID
- currentTimestamp:currentTimestamp
- expectedRemoveCount:1];
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 3600);
- XCTAssertTrue(self.settings.collectReportsEnabled);
- XCTAssertTrue(self.settings.errorReportingEnabled);
- XCTAssertTrue(self.settings.customExceptionsEnabled);
- XCTAssertFalse(self.settings.metricKitCollectionEnabled);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.logBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.maxCustomExceptions, 8);
- XCTAssertEqual(self.settings.maxCustomKeys, 64);
- XCTAssertEqual(self.settings.onDemandUploadRate, 10);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 1.5);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 6);
- }
- // These tests are partially to make sure the SDK doesn't crash when it
- // has corrupted settings.
- - (void)testCorruptCache {
- // First write and load a good settings file
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Should have "Inverse" values
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 12345);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- // Then write a corrupted one and cache + reload it
- [self writeSettings:FIRCLSTestSettingsCorrupted error:&error];
- XCTAssertNil(error, "%@", error);
- // Cache them, and reload. Since it's corrupted we should delete it all
- [self cacheSettingsWithGoogleAppID:TestGoogleAppID
- currentTimestamp:currentTimestamp
- expectedRemoveCount:2];
- // Should have default values because we deleted the cache and settingsDictionary
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 3600);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- }
- - (void)testCorruptCacheKey {
- // First write and load a good settings file
- NSError *error = nil;
- [self writeSettings:FIRCLSTestSettingsInverse error:&error];
- XCTAssertNil(error, "%@", error);
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- // Should have "Inverse" values
- XCTAssertEqual(self.settings.isCacheExpired, NO);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 12345);
- XCTAssertEqual(self.settings.errorLogBufferSize, 128000);
- XCTAssertEqual(self.settings.onDemandUploadRate, 15);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 3);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 9);
- // Then pretend we wrote a corrupted cache key and just reload it
- [self writeSettings:FIRCLSTestSettingsCorrupted error:&error isCacheKey:YES];
- XCTAssertNil(error, "%@", error);
- // Since settings themselves are corrupted, delete it all
- [self reloadFromCacheWithGoogleAppID:TestGoogleAppID
- currentTimestamp:currentTimestamp
- expectedRemoveCount:2];
- // Should have default values because we deleted the cache and settingsDictionary
- XCTAssertEqual(self.settings.isCacheExpired, YES);
- XCTAssertEqual(self.settings.cacheDurationSeconds, 3600);
- XCTAssertEqual(self.settings.errorLogBufferSize, 64 * 1000);
- XCTAssertEqual(self.settings.onDemandUploadRate, 10);
- XCTAssertEqual(self.settings.onDemandBackoffBase, 1.5);
- XCTAssertEqual(self.settings.onDemandBackoffStepDuration, 6);
- }
- - (void)testNewReportEndpointSettings {
- NSString *settingsJSON =
- @"{\"settings_version\":3,\"cache_duration\":60,\"app\":{\"report_upload_variant\":2}}";
- NSError *error = nil;
- [self writeSettings:settingsJSON error:&error];
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertNil(error, "%@", error);
- XCTAssertNotNil(self.settings.settingsDictionary);
- NSLog(@"[Debug Log] %@", self.settings.settingsDictionary);
- }
- - (void)testLegacyReportEndpointSettings {
- NSString *settingsJSON =
- @"{\"settings_version\":3,\"cache_duration\":60,\"app\":{\"report_upload_variant\":1}}";
- NSError *error = nil;
- [self writeSettings:settingsJSON error:&error];
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertNil(error, "%@", error);
- }
- - (void)testLegacyReportEndpointSettingsWithNonExistentKey {
- NSString *settingsJSON = @"{\"settings_version\":3,\"cache_duration\":60}";
- NSError *error = nil;
- [self writeSettings:settingsJSON error:&error];
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertNil(error, "%@", error);
- }
- - (void)testLegacyReportEndpointSettingsWithUnknownValue {
- NSString *newEndpointJSON =
- @"{\"settings_version\":3,\"cache_duration\":60,\"app\":{\"report_upload_variant\":xyz}}";
- NSError *error = nil;
- [self writeSettings:newEndpointJSON error:&error];
- NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
- [self.settings cacheSettingsWithGoogleAppID:TestGoogleAppID currentTimestamp:currentTimestamp];
- XCTAssertNil(error, "%@", error);
- }
- @end
|