Skip to content

Commit eea09ef

Browse files
authored
Fix and enable flakey Crashlytics tests (#5145)
1 parent 313af2d commit eea09ef

13 files changed

+195
-83
lines changed

Crashlytics/Crashlytics/Models/FIRCLSFileManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@
8282

8383
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
8484

85+
- (NSData *)dataWithContentsOfFile:(NSString *)path;
86+
8587
@end

Crashlytics/Crashlytics/Models/FIRCLSFileManager.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,4 +332,9 @@ - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSE
332332
return [self.underlyingFileManager moveItemAtPath:srcPath toPath:dstPath error:error];
333333
}
334334

335+
// Wrapper over NSData so the method can be mocked for unit tests
336+
- (NSData *)dataWithContentsOfFile:(NSString *)path {
337+
return [NSData dataWithContentsOfFile:path];
338+
}
339+
335340
@end

Crashlytics/Crashlytics/Models/FIRCLSSettings.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ - (void)reloadFromCacheWithGoogleAppID:(NSString *)googleAppID
6666
currentTimestamp:(NSTimeInterval)currentTimestamp {
6767
NSString *settingsFilePath = self.fileManager.settingsFilePath;
6868

69-
NSData *data = [NSData dataWithContentsOfFile:settingsFilePath];
69+
NSData *data = [self.fileManager dataWithContentsOfFile:settingsFilePath];
70+
7071
if (!data) {
7172
FIRCLSDebugLog(@"[Crashlytics:Settings] No settings were cached");
7273

@@ -173,7 +174,8 @@ - (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID
173174
#pragma mark - Convenience Methods
174175

175176
- (NSDictionary *)loadCacheKey {
176-
NSData *cacheKeyData = [NSData dataWithContentsOfFile:self.fileManager.settingsCacheKeyPath];
177+
NSData *cacheKeyData =
178+
[self.fileManager dataWithContentsOfFile:self.fileManager.settingsCacheKeyPath];
177179

178180
if (!cacheKeyData) {
179181
return nil;

Crashlytics/UnitTests/FIRCLSFileManagerTests.m

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
#import <Foundation/Foundation.h>
1616
#import <XCTest/XCTest.h>
1717

18-
#import "FIRCLSMockFileManager.h"
18+
#import "FIRCLSFileManager.h"
1919

2020
@interface FIRCLSFileManagerTests : XCTestCase {
21-
FIRCLSMockFileManager* _manager;
21+
FIRCLSFileManager* _manager;
2222
}
2323

24-
@property(nonatomic, retain, readonly) FIRCLSMockFileManager* manager;
24+
@property(nonatomic, retain, readonly) FIRCLSFileManager* manager;
2525

2626
@end
2727

@@ -30,8 +30,7 @@ @implementation FIRCLSFileManagerTests
3030
- (void)setUp {
3131
[super setUp];
3232

33-
_manager = [[FIRCLSMockFileManager alloc] init];
34-
[_manager setPathNamespace:@"com.crashlytics.unittests"];
33+
_manager = [[FIRCLSFileManager alloc] init];
3534

3635
[self removeRootDirectory];
3736
}

Crashlytics/UnitTests/FIRCLSPackageReportsOperationTests.m

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717

1818
#import "FABMockApplicationIdentifierModel.h"
1919
#import "FIRCLSInternalReport.h"
20-
#import "FIRCLSMockFileManager.h"
2120
#import "FIRCLSMockSettings.h"
2221
#import "FIRCLSPackageReportOperation.h"
2322
#import "FIRCLSSettings.h"
23+
#import "FIRCLSTempMockFileManager.h"
2424

2525
NSString *const TestOrgID = @"TestOrgID";
2626

@@ -43,8 +43,7 @@ - (void)setUp {
4343
self.settings = [[FIRCLSMockSettings alloc] initWithFileManager:self.fileManager
4444
appIDModel:appIDModel];
4545

46-
FIRCLSMockFileManager *manager = [[FIRCLSMockFileManager alloc] init];
47-
[manager setPathNamespace:@"com.crashlytics.unittests"];
46+
FIRCLSTempMockFileManager *manager = [[FIRCLSTempMockFileManager alloc] init];
4847

4948
self.fileManager = manager;
5049

Crashlytics/UnitTests/FIRCLSReportManagerTests.m

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
#import "FABMockApplicationIdentifierModel.h"
3434
#import "FIRAppFake.h"
3535
#import "FIRCLSApplicationIdentifierModel.h"
36-
#import "FIRCLSMockFileManager.h"
3736
#import "FIRCLSMockReportManager.h"
3837
#import "FIRCLSMockReportUploader.h"
3938
#import "FIRCLSMockSettings.h"
39+
#import "FIRCLSTempMockFileManager.h"
4040
#import "FIRMockGDTCoreTransport.h"
4141
#import "FIRMockInstallations.h"
4242

@@ -50,7 +50,7 @@
5050

5151
@interface FIRCLSReportManagerTests : XCTestCase
5252

53-
@property(nonatomic, strong) FIRCLSMockFileManager *fileManager;
53+
@property(nonatomic, strong) FIRCLSTempMockFileManager *fileManager;
5454
@property(nonatomic, strong) FIRCLSMockReportManager *reportManager;
5555
@property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
5656
@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
@@ -70,8 +70,7 @@ - (void)setUp {
7070
id fakeApp = [[FIRAppFake alloc] init];
7171
self.dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:fakeApp withAppInfo:@{}];
7272

73-
self.fileManager = [[FIRCLSMockFileManager alloc] init];
74-
[self.fileManager setPathNamespace:TEST_BUNDLE_ID];
73+
self.fileManager = [[FIRCLSTempMockFileManager alloc] init];
7574

7675
// Delete cached settings
7776
[self.fileManager removeItemAtPath:_fileManager.settingsFilePath];

Crashlytics/UnitTests/FIRCLSReportUploaderTests.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
#include "FIRCLSDefines.h"
2424
#include "FIRCLSFileManager.h"
2525
#include "FIRCLSInternalReport.h"
26-
#include "FIRCLSMockFileManager.h"
2726
#include "FIRCLSMockNetworkClient.h"
2827
#include "FIRCLSMockSettings.h"
2928
#include "FIRCLSSettings.h"
29+
#include "FIRCLSTempMockFileManager.h"
3030
#include "FIRMockGDTCoreTransport.h"
3131

3232
NSString *const TestEndpoint = @"https://reports.crashlytics.com";
@@ -35,7 +35,7 @@ @interface FIRCLSReportUploaderTests
3535
: XCTestCase <FIRCLSReportUploaderDelegate, FIRCLSReportUploaderDataSource>
3636

3737
@property(nonatomic, strong) FIRCLSReportUploader *uploader;
38-
@property(nonatomic, strong) FIRCLSMockFileManager *fileManager;
38+
@property(nonatomic, strong) FIRCLSTempMockFileManager *fileManager;
3939
@property(nonatomic, strong) NSOperationQueue *queue;
4040
@property(nonatomic, strong) FIRCLSMockNetworkClient *networkClient;
4141

@@ -58,7 +58,7 @@ - (void)setUp {
5858
self.networkClient = [[FIRCLSMockNetworkClient alloc] initWithQueue:self.queue
5959
fileManager:self.fileManager
6060
delegate:nil];
61-
self.fileManager = [[FIRCLSMockFileManager alloc] init];
61+
self.fileManager = [[FIRCLSTempMockFileManager alloc] init];
6262
self.uploader = [[FIRCLSReportUploader alloc] initWithQueue:self.queue
6363
delegate:self
6464
dataSource:self

Crashlytics/UnitTests/FIRCLSSettingsTests.m

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
// TODO: There is some unreliability with this test.
16-
// self.settings.settingsDictionary returns nil.
17-
// Abstract FileManager so actual disk operations are not happening.
18-
19-
/*
2015
#import "FIRCLSSettings.h"
2116

2217
#import <Foundation/Foundation.h>
@@ -82,10 +77,6 @@ - (void)setUp {
8277

8378
_fileManager = [[FIRCLSMockFileManager alloc] init];
8479

85-
// Delete the cache
86-
[_fileManager removeItemAtPath:_fileManager.settingsFilePath];
87-
[_fileManager removeItemAtPath:_fileManager.settingsCacheKeyPath];
88-
8980
_appIDModel = [[FABMockApplicationIdentifierModel alloc] init];
9081
_appIDModel.buildInstanceID = FIRCLSDefaultMockBuildInstanceID;
9182
_appIDModel.displayVersion = FIRCLSDefaultMockAppDisplayVersion;
@@ -94,10 +85,6 @@ - (void)setUp {
9485
_settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager appIDModel:_appIDModel];
9586
}
9687

97-
- (void)tearDown {
98-
[_fileManager removeContentsOfDirectoryAtPath:_fileManager.settingsDirectoryPath];
99-
}
100-
10188
- (void)testDefaultSettings {
10289
XCTAssertEqual(self.settings.isCacheExpired, YES);
10390

@@ -135,18 +122,9 @@ - (BOOL)writeSettings:(const NSString *)settings
135122
path = _fileManager.settingsCacheKeyPath;
136123
}
137124

138-
// Create the directory.
139-
[[NSFileManager defaultManager] createDirectoryAtPath:path.stringByDeletingLastPathComponent
140-
withIntermediateDirectories:YES
141-
attributes:nil
142-
error:error];
143-
if (*error != nil) {
144-
return NO;
145-
}
146-
147-
// Create the file.
148-
[settings writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:error];
149-
return YES;
125+
return [self.fileManager createFileAtPath:path
126+
contents:[settings dataUsingEncoding:NSUTF8StringEncoding]
127+
attributes:nil];
150128
}
151129

152130
- (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID
@@ -378,8 +356,8 @@ - (void)testGoogleAppIDChanged {
378356
}
379357

380358
// This is a weird case where we got settings, but never created a cache key for it. We are
381-
/ treating / this as if the cache was invalid and re - fetching in this case.-
382-
(void)testActivatedSettingsMissingCacheKey {
359+
// treating this as if the cache was invalid and re-fetching in this case.
360+
- (void)testActivatedSettingsMissingCacheKey {
383361
NSError *error = nil;
384362
[self writeSettings:FIRCLSTestSettingsActivated error:&error];
385363
XCTAssertNil(error, "%@", error);
@@ -550,4 +528,3 @@ - (void)testShouldUseNewReportEndpointWithEmptyDictionary {
550528
}
551529

552530
@end
553-
*/

Crashlytics/UnitTests/FIRRecordExceptionModelTests.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ @implementation FIRRecordExceptionModelTests
3939

4040
- (void)setUp {
4141
self.fileManager = [[FIRCLSMockFileManager alloc] init];
42-
[self.fileManager setPathNamespace:TEST_BUNDLE_ID];
4342

4443
FABMockApplicationIdentifierModel *appIDModel = [[FABMockApplicationIdentifierModel alloc] init];
4544
self.mockSettings = [[FIRCLSMockSettings alloc] initWithFileManager:self.fileManager

Crashlytics/UnitTests/Mocks/FIRCLSMockFileManager.h

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,16 @@
1818

1919
@interface FIRCLSMockFileManager : FIRCLSFileManager
2020

21-
@property(nonatomic, copy) NSString *pathNamespace;
22-
2321
// Number of calls to removeItemAtPath are expected for the unit test
2422
@property(nonatomic) NSInteger expectedRemoveCount;
2523

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

29-
// Overrides fileSizeAtPath if set
30-
@property(nonatomic, copy) NSNumber *fileSizeAtPathResult;
31-
3227
// Will be fulfilled when the expected number of removes have happened
3328
// using removeItemAtPath
3429
//
3530
// Users should initialize this in their test.
3631
@property(nonatomic, strong) XCTestExpectation *removeExpectation;
3732

38-
@property(nonatomic, copy) NSString *removedItemAtPath_path;
39-
40-
// Overriding the method for testing Settings
41-
- (BOOL)removeItemAtPath:(NSString *)path;
42-
43-
// Overrides moveItemAtPath if set
44-
@property(nonatomic) NSNumber *moveItemAtPathResult;
45-
@property(nonatomic, copy) NSString *moveItemAtPath_path;
46-
@property(nonatomic, copy) NSString *moveItemAtPath_destDir;
47-
48-
- (BOOL)moveItemAtPath:(NSString *)path toDirectory:(NSString *)destDir;
49-
5033
@end
Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019 Google
1+
// Copyright 2020 Google
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -14,26 +14,27 @@
1414

1515
#import "FIRCLSMockFileManager.h"
1616

17-
@implementation FIRCLSMockFileManager
17+
@interface FIRCLSMockFileManager ()
18+
19+
@property(nonatomic) NSMutableDictionary<NSString *, NSData *> *fileSystemDict;
20+
21+
@end
1822

19-
@synthesize pathNamespace;
23+
@implementation FIRCLSMockFileManager
2024

2125
- (instancetype)init {
2226
self = [super init];
2327
if (!self) {
2428
return nil;
2529
}
2630

27-
// Should be set by the tests when needed
28-
_removeExpectation = nil;
31+
_fileSystemDict = [[NSMutableDictionary<NSString *, NSData *> alloc] init];
2932

3033
return self;
3134
}
3235

3336
- (BOOL)removeItemAtPath:(NSString *)path {
34-
self.removedItemAtPath_path = path;
35-
36-
[super removeItemAtPath:path];
37+
[self.fileSystemDict removeObjectForKey:path];
3738

3839
self.removeCount += 1;
3940

@@ -46,23 +47,46 @@ - (BOOL)removeItemAtPath:(NSString *)path {
4647
return YES;
4748
}
4849

49-
- (NSNumber *)fileSizeAtPath:(NSString *)path {
50-
if (self.fileSizeAtPathResult != nil) {
51-
return self.fileSizeAtPathResult;
52-
}
50+
- (BOOL)fileExistsAtPath:(NSString *)path {
51+
return self.fileSystemDict[path] != nil;
52+
}
5353

54-
return [super fileSizeAtPath:path];
54+
- (BOOL)createFileAtPath:(NSString *)path
55+
contents:(NSData *)data
56+
attributes:(NSDictionary<NSFileAttributeKey, id> *)attr {
57+
self.fileSystemDict[path] = data;
58+
return YES;
5559
}
5660

57-
- (BOOL)moveItemAtPath:(NSString *)path toDirectory:(NSString *)destDir {
58-
self.moveItemAtPath_path = path;
59-
self.moveItemAtPath_destDir = destDir;
61+
- (NSData *)dataWithContentsOfFile:(NSString *)path {
62+
return self.fileSystemDict[path];
63+
}
6064

61-
if (self.moveItemAtPathResult != nil) {
62-
return self.moveItemAtPathResult.intValue > 0;
65+
- (void)enumerateFilesInDirectory:(NSString *)directory
66+
usingBlock:(void (^)(NSString *filePath, NSString *extension))block {
67+
NSArray<NSString *> *filteredPaths = [self.fileSystemDict.allKeys
68+
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *path,
69+
NSDictionary *bindings) {
70+
return [path hasPrefix:directory];
71+
}]];
72+
73+
for (NSString *path in filteredPaths) {
74+
NSString *extension;
75+
NSString *fullPath;
76+
77+
// Skip files that start with a dot. This is important, because if you try to move a .DS_Store
78+
// file, it will fail if the target directory also has a .DS_Store file in it. Plus, its
79+
// wasteful, because we don't care about dot files.
80+
if ([path hasPrefix:@"."]) {
81+
continue;
82+
}
83+
84+
extension = [path pathExtension];
85+
fullPath = [directory stringByAppendingPathComponent:path];
86+
if (block) {
87+
block(fullPath, extension);
88+
}
6389
}
64-
65-
return [super moveItemAtPath:path toDirectory:destDir];
6690
}
6791

6892
@end

0 commit comments

Comments
 (0)