Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 15 additions & 21 deletions Crashlytics/UnitTests/FIRCLSSettingsTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,33 @@
- (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;

XCTestExpectation *expectation =
[self expectationForNotification:FIRCLSMockFileManagerDidRemoveItemNotification
object:nil
handler:nil];
expectation.expectedFulfillmentCount = expectedRemoveCount;

[self.settings cacheSettingsWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp];

[self waitForExpectations:@[ self.fileManager.removeExpectation ] timeout:1];
[self waitForExpectations:@[ expectation ] timeout:10.0];

Check failure on line 140 in Crashlytics/UnitTests/FIRCLSSettingsTests.m

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

testCorruptCache, Asynchronous wait failed: Exceeded timeout of 10 seconds, with unfulfilled expectations: "Expect notification 'FIRCLSMockFileManagerDidRemoveItemNotification' from any object".
}

- (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;

XCTestExpectation *expectation =
[self expectationForNotification:FIRCLSMockFileManagerDidRemoveItemNotification
object:nil
handler:nil];
expectation.expectedFulfillmentCount = expectedRemoveCount;

[self.settings reloadFromCacheWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp];

[self waitForExpectations:@[ self.fileManager.removeExpectation ] timeout:5.0];
[self waitForExpectations:@[ expectation ] timeout:20.0];

Check failure on line 156 in Crashlytics/UnitTests/FIRCLSSettingsTests.m

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

testCorruptCacheKey, Asynchronous wait failed: Exceeded timeout of 20 seconds, with unfulfilled expectations: "Expect notification 'FIRCLSMockFileManagerDidRemoveItemNotification' from any object".

Check failure on line 156 in Crashlytics/UnitTests/FIRCLSSettingsTests.m

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

testActivatedSettingsMissingCacheKey, Asynchronous wait failed: Exceeded timeout of 20 seconds, with unfulfilled expectations: "Expect notification 'FIRCLSMockFileManagerDidRemoveItemNotification' from any object".
}

- (void)testActivatedSettingsCached {
Expand Down Expand Up @@ -205,10 +211,6 @@
[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];

Expand Down Expand Up @@ -238,10 +240,6 @@
[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];

Expand Down Expand Up @@ -272,10 +270,6 @@
[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];

Expand Down Expand Up @@ -387,7 +381,7 @@
// Cache them, and reload. Since it's corrupted we should delete it all
[self cacheSettingsWithGoogleAppID:TestGoogleAppID
currentTimestamp:currentTimestamp
expectedRemoveCount:2];
expectedRemoveCount:3];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we changed the expected count here without change any actual code logic?


// Should have default values because we deleted the cache and settingsDictionary
XCTAssertEqual(self.settings.isCacheExpired, YES);
Expand Down
3 changes: 3 additions & 0 deletions Crashlytics/UnitTests/FIRCLSUserDefaultsTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ - (void)setUp {

- (void)tearDown {
[_userDefaults removeAllObjects];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:_testKey1];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:_testKey2];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:_testKey3];
[super tearDown];
}

Expand Down
12 changes: 3 additions & 9 deletions Crashlytics/UnitTests/Mocks/FIRCLSMockFileManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,12 @@

#import <XCTest/XCTest.h>

@interface FIRCLSMockFileManager : FIRCLSFileManager
// Notification posted when an item is removed via `removeItemAtPath`.
extern NSNotificationName const FIRCLSMockFileManagerDidRemoveItemNotification;

// Number of calls to removeItemAtPath are expected for the unit test
@property(nonatomic) NSInteger expectedRemoveCount;
@interface FIRCLSMockFileManager : FIRCLSFileManager

// Incremented when a remove happens with removeItemAtPath
@property(nonatomic) NSInteger removeCount;

// Will be fulfilled when the expected number of removes have happened
// using removeItemAtPath
//
// Users should initialize this in their test.
@property(nonatomic, strong) XCTestExpectation *removeExpectation;

@end
84 changes: 48 additions & 36 deletions Crashlytics/UnitTests/Mocks/FIRCLSMockFileManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

#import "Crashlytics/UnitTests/Mocks/FIRCLSMockFileManager.h"

NSNotificationName const FIRCLSMockFileManagerDidRemoveItemNotification =
@"FIRCLSMockFileManagerDidRemoveItemNotification";

@interface FIRCLSMockFileManager ()

@property(nonatomic) NSMutableDictionary<NSString *, NSData *> *fileSystemDict;
Expand All @@ -34,68 +37,77 @@ - (instancetype)init {
}

- (BOOL)removeItemAtPath:(NSString *)path {
[self.fileSystemDict removeObjectForKey:path];
@synchronized(self) {
[self.fileSystemDict removeObjectForKey:path];
}

self.removeCount += 1;

// If we set up the expectation, and we went over the expected count or removes, fulfill the
// expectation
if (self.removeExpectation && self.removeCount >= self.expectedRemoveCount) {
[self.removeExpectation fulfill];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:FIRCLSMockFileManagerDidRemoveItemNotification
object:self];

return YES;
}

- (BOOL)fileExistsAtPath:(NSString *)path {
return self.fileSystemDict[path] != nil;
@synchronized(self) {
return self.fileSystemDict[path] != nil;
}
}

- (BOOL)createFileAtPath:(NSString *)path
contents:(NSData *)data
attributes:(NSDictionary<NSFileAttributeKey, id> *)attr {
self.fileSystemDict[path] = data;
@synchronized(self) {
self.fileSystemDict[path] = data;
}
return YES;
}

- (NSArray *)activePathContents {
NSMutableArray *pathsWithActive = [[NSMutableArray alloc] init];
for (NSString *path in [_fileSystemDict allKeys]) {
if ([path containsString:@"v5/reports/active"]) {
[pathsWithActive addObject:path];
@synchronized(self) {
NSMutableArray *pathsWithActive = [[NSMutableArray alloc] init];
for (NSString *path in [_fileSystemDict allKeys]) {
if ([path containsString:@"v5/reports/active"]) {
[pathsWithActive addObject:path];
}
}
return pathsWithActive;
}

return pathsWithActive;
}

- (NSData *)dataWithContentsOfFile:(NSString *)path {
return self.fileSystemDict[path];
@synchronized(self) {
return self.fileSystemDict[path];
}
}

- (void)enumerateFilesInDirectory:(NSString *)directory
usingBlock:(void (^)(NSString *filePath, NSString *extension))block {
NSArray<NSString *> *filteredPaths = [self.fileSystemDict.allKeys
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *path,
NSDictionary *bindings) {
return [path hasPrefix:directory];
}]];

for (NSString *path in filteredPaths) {
NSString *extension;
NSString *fullPath;

// Skip files that start with a dot. This is important, because if you try to move a .DS_Store
// file, it will fail if the target directory also has a .DS_Store file in it. Plus, its
// wasteful, because we don't care about dot files.
if ([path hasPrefix:@"."]) {
continue;
}

extension = [path pathExtension];
fullPath = [directory stringByAppendingPathComponent:path];
if (block) {
block(fullPath, extension);
@synchronized(self) {
NSArray<NSString *> *filteredPaths = [self.fileSystemDict.allKeys
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *path,
NSDictionary *bindings) {
return [path hasPrefix:directory];
}]];

for (NSString *path in filteredPaths) {
NSString *extension;
NSString *fullPath;

// Skip files that start with a dot. This is important, because if you try to move a
// .DS_Store file, it will fail if the target directory also has a .DS_Store file in
// it. Plus, its wasteful, because we don't care about dot files.
if ([path hasPrefix:@"."]) {
continue;
}

extension = [path pathExtension];
fullPath = [directory stringByAppendingPathComponent:path];
if (block) {
block(fullPath, extension);
}
}
}
}
Expand Down
Loading