FIRCLSReportManagerTests.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import <Foundation/Foundation.h>
  15. #import <XCTest/XCTest.h>
  16. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  17. #if __has_include(<FBLPromises/FBLPromises.h>)
  18. #import <FBLPromises/FBLPromises.h>
  19. #else
  20. #import "FBLPromises.h"
  21. #endif
  22. #import "FBLPromise+Testing.h"
  23. #include "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
  24. #include "Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h"
  25. #import "Crashlytics/Crashlytics/Controllers/FIRCLSAnalyticsManager.h"
  26. #import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
  27. #import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h"
  28. #include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
  29. #import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
  30. #import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
  31. #import "Crashlytics/UnitTests/Mocks/FIRCLSMockExistingReportManager.h"
  32. #import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
  33. #import "Crashlytics/UnitTests/Mocks/FABMockApplicationIdentifierModel.h"
  34. #import "Crashlytics/UnitTests/Mocks/FIRAppFake.h"
  35. #import "Crashlytics/UnitTests/Mocks/FIRCLSMockReportManager.h"
  36. #import "Crashlytics/UnitTests/Mocks/FIRCLSMockReportUploader.h"
  37. #import "Crashlytics/UnitTests/Mocks/FIRCLSMockSettings.h"
  38. #import "Crashlytics/UnitTests/Mocks/FIRCLSTempMockFileManager.h"
  39. #import "Crashlytics/UnitTests/Mocks/FIRMockGDTCoreTransport.h"
  40. #import "Crashlytics/UnitTests/Mocks/FIRMockInstallations.h"
  41. #define TEST_API_KEY (@"DB5C8FA65C0D43419120FB96CFDBDE0C")
  42. #define TEST_GOOGLE_APP_ID (@"1:632950151350:ios:d5b0d08d4f00f4b1")
  43. #define TEST_INSTALL_ID (@"DC352568-33A7-4830-A9D8-20EA708F1905")
  44. #define TEST_API_ENDPOINT (@"http://test.com")
  45. #define TEST_BUNDLE_ID (@"com.crashlytics.test")
  46. #define TEST_ANALYTICS_JSON \
  47. (@"{\"name\":\"some_name\",\"nested\":{\"object\":\"with_stuff\"},\"price\":100}")
  48. @interface FIRCLSReportManagerTests : XCTestCase
  49. @property(nonatomic, strong) FIRCLSMockReportManager *reportManager;
  50. @property(nonatomic, strong) FIRCLSMockExistingReportManager *existingReportManager;
  51. @property(nonatomic, strong) FIRCLSMockSettings *mockSettings;
  52. @property(nonatomic, strong) FIRCLSMockReportUploader *mockReportUploader;
  53. @property(nonatomic, strong) FIRCLSTempMockFileManager *fileManager;
  54. @property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
  55. @property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
  56. @end
  57. @implementation FIRCLSReportManagerTests
  58. - (void)setUp {
  59. [super setUp];
  60. FIRSetLoggerLevel(FIRLoggerLevelMax);
  61. FIRCLSContextBaseInit();
  62. id fakeApp = [[FIRAppFake alloc] init];
  63. self.dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:fakeApp withAppInfo:@{}];
  64. self.fileManager = [[FIRCLSTempMockFileManager alloc] init];
  65. // Cleanup potential artifacts from other test files.
  66. if ([[NSFileManager defaultManager] fileExistsAtPath:[self.fileManager rootPath]]) {
  67. assert([self.fileManager removeItemAtPath:[self.fileManager rootPath]]);
  68. }
  69. // Delete cached settings
  70. [self.fileManager removeItemAtPath:_fileManager.settingsFilePath];
  71. FIRMockInstallations *iid = [[FIRMockInstallations alloc] initWithFID:@"test_token"];
  72. FIRMockGDTCORTransport *mockGoogleTransport =
  73. [[FIRMockGDTCORTransport alloc] initWithMappingID:@"id" transformers:nil target:0];
  74. self.appIDModel = [[FIRCLSApplicationIdentifierModel alloc] init];
  75. self.mockSettings = [[FIRCLSMockSettings alloc] initWithFileManager:self.fileManager
  76. appIDModel:self.appIDModel];
  77. // Allow nil values only in tests
  78. #pragma clang diagnostic push
  79. #pragma clang diagnostic ignored "-Wnonnull"
  80. FIRCLSManagerData *managerData =
  81. [[FIRCLSManagerData alloc] initWithGoogleAppID:TEST_GOOGLE_APP_ID
  82. googleTransport:mockGoogleTransport
  83. installations:iid
  84. analytics:nil
  85. fileManager:self.fileManager
  86. dataArbiter:self.dataArbiter
  87. settings:self.mockSettings
  88. onDemandModel:nil];
  89. #pragma clang diagnostic pop
  90. self.mockReportUploader = [[FIRCLSMockReportUploader alloc] initWithManagerData:managerData];
  91. self.existingReportManager =
  92. [[FIRCLSMockExistingReportManager alloc] initWithManagerData:managerData
  93. reportUploader:self.mockReportUploader];
  94. FIRCLSAnalyticsManager *analyticsManager = [[FIRCLSAnalyticsManager alloc] initWithAnalytics:nil];
  95. self.reportManager =
  96. [[FIRCLSMockReportManager alloc] initWithManagerData:managerData
  97. existingReportManager:self.existingReportManager
  98. analyticsManager:analyticsManager];
  99. }
  100. - (void)tearDown {
  101. self.reportManager = nil;
  102. if ([[NSFileManager defaultManager] fileExistsAtPath:[self.fileManager rootPath]]) {
  103. assert([self.fileManager removeItemAtPath:[self.fileManager rootPath]]);
  104. }
  105. FIRCLSContextBaseDeinit();
  106. [super tearDown];
  107. }
  108. #pragma mark - Path Helpers
  109. - (NSString *)resourcePath {
  110. #if SWIFT_PACKAGE
  111. NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
  112. return [bundle.resourcePath stringByAppendingPathComponent:@"Data"];
  113. #else
  114. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  115. return bundle.resourcePath;
  116. #endif
  117. }
  118. - (NSArray *)contentsOfActivePath {
  119. return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.fileManager.activePath
  120. error:nil];
  121. }
  122. - (NSArray *)contentsOfPreparedPath {
  123. return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.fileManager.preparedPath
  124. error:nil];
  125. }
  126. - (NSArray *)contentsOfProcessingPath {
  127. return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.fileManager.processingPath
  128. error:nil];
  129. }
  130. #pragma mark - Report Helpers
  131. - (FIRCLSInternalReport *)createActiveReport {
  132. NSString *reportPath =
  133. [self.fileManager.activePath stringByAppendingPathComponent:@"my_session_id"];
  134. FIRCLSInternalReport *report = [[FIRCLSInternalReport alloc] initWithPath:reportPath
  135. executionIdentifier:@"my_session_id"];
  136. if (![self.fileManager createDirectoryAtPath:report.path]) {
  137. return nil;
  138. }
  139. if (![self createMetadata:
  140. @"{\"identity\":{\"api_key\":\"my_key\",\"session_id\":\"my_session_id\"}}\n"
  141. forReport:report]) {
  142. return nil;
  143. }
  144. return report;
  145. }
  146. - (BOOL)createFileWithContents:(NSString *)contents atPath:(NSString *)path {
  147. return [self.fileManager.underlyingFileManager
  148. createFileAtPath:path
  149. contents:[contents dataUsingEncoding:NSUTF8StringEncoding]
  150. attributes:nil];
  151. }
  152. - (BOOL)createMetadata:(NSString *)value forReport:(FIRCLSInternalReport *)report {
  153. return [self createFileWithContents:value atPath:[report metadataPath]];
  154. }
  155. #pragma mark - Property Helpers
  156. - (NSArray *)prepareAndSubmitReportArray {
  157. return self.mockReportUploader.prepareAndSubmitReportArray;
  158. }
  159. - (NSArray *)uploadReportArray {
  160. return self.mockReportUploader.uploadReportArray;
  161. }
  162. #pragma mark - File/Directory Handling
  163. - (void)testCreatesNewReportOnStart {
  164. FBLPromise<NSNumber *> *promise = [self->_reportManager startWithProfiling];
  165. FBLWaitForPromisesWithTimeout(1.0);
  166. XCTAssertTrue([promise.value boolValue]);
  167. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  168. }
  169. - (void)waitForPromise:(FBLPromise<NSNumber *> *)promise {
  170. [self waitForPromise:promise withTimeout:1.0];
  171. }
  172. - (void)waitForPromise:(FBLPromise<NSNumber *> *)promise withTimeout:(double)timeout {
  173. __block NSNumber *value = nil;
  174. __block NSError *error = nil;
  175. XCTestExpectation *expectation =
  176. [[XCTestExpectation alloc] initWithDescription:@"waiting on promise"];
  177. [[promise then:^id _Nullable(NSNumber *_Nullable innerValue) {
  178. value = innerValue;
  179. [expectation fulfill];
  180. return nil;
  181. }] catch:^(NSError *_Nonnull innerError) {
  182. error = innerError;
  183. [expectation fulfill];
  184. }];
  185. [self waitForExpectations:@[ expectation ] timeout:timeout];
  186. XCTAssertNil(error);
  187. XCTAssertTrue([value boolValue]);
  188. }
  189. - (void)startReportManager {
  190. [self waitForPromise:[self startReportManagerWithDataCollectionEnabled:YES]];
  191. }
  192. - (FBLPromise<NSNumber *> *)startReportManagerWithDataCollectionEnabled:(BOOL)enabled {
  193. [self.dataArbiter setCrashlyticsCollectionEnabled:enabled];
  194. return [self.reportManager startWithProfiling];
  195. }
  196. - (void)processReports:(BOOL)send andExpectReports:(BOOL)reportsExpected {
  197. XCTestExpectation *processReportsComplete =
  198. [[XCTestExpectation alloc] initWithDescription:@"processReports: complete"];
  199. __block BOOL reportsAvailable = NO;
  200. [[[self.reportManager checkForUnsentReports]
  201. then:^id _Nullable(FIRCrashlyticsReport *_Nullable report) {
  202. reportsAvailable = report ? true : false;
  203. if (send) {
  204. return [self->_reportManager sendUnsentReports];
  205. } else {
  206. return [self->_reportManager deleteUnsentReports];
  207. }
  208. }] then:^id _Nullable(id _Nullable ignored) {
  209. [processReportsComplete fulfill];
  210. return nil;
  211. }];
  212. [self waitForExpectations:@[ processReportsComplete ] timeout:1.0];
  213. if (reportsExpected) {
  214. XCTAssertTrue(reportsAvailable, "should have unsent reports");
  215. } else {
  216. XCTAssertFalse(reportsAvailable, "should not have unsent reports");
  217. }
  218. }
  219. - (void)processReports:(BOOL)send {
  220. [self processReports:send andExpectReports:YES];
  221. }
  222. - (void)testExistingUnimportantReportOnStart {
  223. // Create a report representing the last run and put it in place
  224. [self createActiveReport];
  225. // Report from the last run should get deleted, and a new
  226. // one should be created for this run.
  227. [self startReportManager];
  228. // If this is > 1 it means we're not cleaning up reports from previous runs.
  229. // If this == 0, it means we're not creating new reports.
  230. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  231. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  232. XCTAssertEqual([self.uploadReportArray count], 0);
  233. }
  234. - (void)testMetricKitResolvesPromiseIfNoDiagnostics {
  235. // Create a report representing the last run and put it in place, then create a crashed file
  236. // marker and MetricKit diagnostic file so that MetricKit manager doesn't resolve the promise
  237. // immediately.
  238. [self createActiveReport];
  239. [self.existingReportManager setShouldHaveExistingReport];
  240. NSString *metricKitPath =
  241. [self.fileManager.cachesPath stringByAppendingString:@"/MetricKit/Diagnostics/"];
  242. [self.fileManager createFileAtPath:[[self.fileManager rootPath]
  243. stringByAppendingPathComponent:@"previously-crashed"]
  244. contents:nil
  245. attributes:nil];
  246. [self.fileManager createDirectoryAtPath:metricKitPath];
  247. [self.fileManager createFileAtPath:[metricKitPath stringByAppendingString:@"Diagnostics.txt"]
  248. contents:nil
  249. attributes:nil];
  250. // MetricKit manager should resolve its promise after 3 seconds.
  251. [self waitForPromise:[self startReportManagerWithDataCollectionEnabled:YES] withTimeout:4];
  252. }
  253. - (void)testExistingUnimportantReportOnStartWithDataCollectionDisabled {
  254. // create a report and put it in place
  255. XCTAssertNotNil([self createActiveReport]);
  256. // Starting with data collection disabled should report in nothing changing
  257. [self startReportManagerWithDataCollectionEnabled:NO];
  258. FBLWaitForPromisesWithTimeout(1.0);
  259. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  260. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  261. XCTAssertEqual([self.uploadReportArray count], 0);
  262. }
  263. - (void)testExistingReportOnStart {
  264. // create a report and put it in place
  265. FIRCLSInternalReport *report = [self createActiveReport];
  266. // create a signal file so it is considering worth reporting
  267. XCTAssertTrue([self createFileWithContents:@"signal"
  268. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  269. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  270. [self startReportManager];
  271. // verify that processReports won't get called.
  272. [self processReports:YES andExpectReports:NO];
  273. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only the current report");
  274. // should call report manager once for that report
  275. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  276. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(YES));
  277. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  278. }
  279. - (void)testExistingReportOnStartWithDataCollectionDisabledThenEnabled {
  280. // create a report and put it in place
  281. FIRCLSInternalReport *report = [self createActiveReport];
  282. // create a signal file so it is considering worth reporting
  283. XCTAssertTrue([self createFileWithContents:@"signal"
  284. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  285. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  286. FBLPromise<NSNumber *> *promise = [self startReportManagerWithDataCollectionEnabled:NO];
  287. XCTAssertEqual([[self contentsOfActivePath] count], 2,
  288. @"should contain the current and old reports");
  289. // should call report manager once for that report
  290. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  291. // We can turn data collection on instead of calling processReports.
  292. [self.dataArbiter setCrashlyticsCollectionEnabled:YES];
  293. [self waitForPromise:promise];
  294. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only the current report");
  295. // should call report manager once for that report
  296. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  297. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(YES));
  298. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  299. }
  300. - (void)testExistingReportOnStartWithDataCollectionDisabledAndSend {
  301. // create a report and put it in place
  302. FIRCLSInternalReport *report = [self createActiveReport];
  303. // create a signal file so it is considering worth reporting
  304. XCTAssertTrue([self createFileWithContents:@"signal"
  305. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  306. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  307. [self startReportManagerWithDataCollectionEnabled:NO];
  308. XCTAssertEqual([[self contentsOfActivePath] count], 2,
  309. @"should contain the current and old reports");
  310. // should call report manager once for that report
  311. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  312. [self processReports:YES];
  313. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only the current report");
  314. // should call report manager once for that report
  315. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  316. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(YES));
  317. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  318. // Calling processReports again should not call the callback.
  319. // Technically, the behavior is unspecified.
  320. [self processReports:YES andExpectReports:NO];
  321. }
  322. - (void)testExistingReportOnStartWithDataCollectionDisabledAndDelete {
  323. // create a report and put it in place
  324. FIRCLSInternalReport *report = [self createActiveReport];
  325. // create a signal file so it is considering worth reporting
  326. XCTAssertTrue([self createFileWithContents:@"signal"
  327. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  328. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  329. [self startReportManagerWithDataCollectionEnabled:NO];
  330. XCTAssertEqual([[self contentsOfActivePath] count], 2,
  331. @"should contain the current and old reports");
  332. // should call report manager once for that report
  333. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  334. [self processReports:NO];
  335. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only the current report");
  336. // Should not call report manager for that report.
  337. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  338. }
  339. - (void)testExistingUrgentReportOnStart {
  340. // create a report and put it in place
  341. FIRCLSInternalReport *report = [self createActiveReport];
  342. // create a signal file so it is considering worth reporting
  343. XCTAssertTrue([self createFileWithContents:@"signal"
  344. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  345. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  346. // Put the launch marker in place
  347. [self.reportManager.launchMarker createLaunchFailureMarker];
  348. // should call back to the delegate on start
  349. [self startReportManager];
  350. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only the current report");
  351. // should call report manager once for that report
  352. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  353. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(YES));
  354. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(YES));
  355. }
  356. - (void)testExistingUrgentReportOnStartWithDataCollectionDisabled {
  357. // create a report and put it in place
  358. FIRCLSInternalReport *report = [self createActiveReport];
  359. // create a signal file so it is considering worth reporting
  360. XCTAssertTrue([self createFileWithContents:@"signal"
  361. atPath:[report pathForContentFile:FIRCLSReportSignalFile]]);
  362. XCTAssertEqual([[self contentsOfActivePath] count], 1);
  363. // Put the launch marker in place
  364. [self.reportManager.launchMarker createLaunchFailureMarker];
  365. // Should wait for processReports: to be called.
  366. [self startReportManagerWithDataCollectionEnabled:NO];
  367. XCTAssertEqual([[self contentsOfActivePath] count], 2, @"the report hasn't been sent");
  368. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  369. [self processReports:YES];
  370. XCTAssertEqual([[self contentsOfActivePath] count], 1, @"should contain only current report");
  371. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  372. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(YES));
  373. // If data collection is disabled, you can never send the report urgently / blocking
  374. // startup because you need to call a method after startup to send the report
  375. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  376. }
  377. - (void)testFilesLeftInProcessing {
  378. // put report in processing
  379. FIRCLSInternalReport *report = [self createActiveReport];
  380. XCTAssert([_fileManager createDirectoryAtPath:_fileManager.processingPath]);
  381. XCTAssert([_fileManager moveItemAtPath:[report path] toDirectory:_fileManager.processingPath]);
  382. [self startReportManager];
  383. // we should not process reports left over in processing
  384. XCTAssertEqual([[self contentsOfProcessingPath] count], 0, @"Processing should be cleared");
  385. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  386. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(NO));
  387. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  388. }
  389. /*
  390. * This tests an edge case where there is a report in processing. For the purposes of unsent
  391. * reports these are not shown to the developer, but they are uploaded / deleted upon
  392. * calling send / delete.
  393. */
  394. - (void)testFilesLeftInProcessingWithDataCollectionDisabled {
  395. // Put report in processing.
  396. FIRCLSInternalReport *report = [self createActiveReport];
  397. XCTAssert([_fileManager createDirectoryAtPath:_fileManager.processingPath]);
  398. XCTAssert([_fileManager moveItemAtPath:[report path] toDirectory:_fileManager.processingPath]);
  399. [self startReportManagerWithDataCollectionEnabled:NO];
  400. // Nothing should have happened yet.
  401. XCTAssertEqual([[self contentsOfProcessingPath] count], 1,
  402. @"Processing should still have the report");
  403. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  404. // We don't expect reports here because we don't consider processing or prepared
  405. // reports as unsent as they need to be marked for sending before being placed
  406. // in those directories.
  407. [self processReports:YES andExpectReports:NO];
  408. // We should not process reports left over in processing.
  409. XCTAssertEqual([[self contentsOfProcessingPath] count], 0, @"Processing should be cleared");
  410. XCTAssertEqual([[self contentsOfPreparedPath] count], 0, @"Prepared should be cleared");
  411. XCTAssertEqual([self.prepareAndSubmitReportArray count], 1);
  412. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"process"], @(NO));
  413. XCTAssertEqualObjects(self.prepareAndSubmitReportArray[0][@"urgent"], @(NO));
  414. }
  415. - (void)testFilesLeftInPrepared {
  416. // Drop a phony multipart-mime file in here, with non-zero contents.
  417. XCTAssert([_fileManager createDirectoryAtPath:_fileManager.preparedPath]);
  418. NSString *path = [_fileManager.preparedPath stringByAppendingPathComponent:@"phony-report"];
  419. path = [path stringByAppendingPathExtension:@"multipart-mime"];
  420. XCTAssertTrue([[_fileManager underlyingFileManager]
  421. createFileAtPath:path
  422. contents:[@"contents" dataUsingEncoding:NSUTF8StringEncoding]
  423. attributes:nil]);
  424. [self startReportManager];
  425. // Reports should be moved out of prepared
  426. XCTAssertEqual([[self contentsOfPreparedPath] count], 0, @"Prepared should be cleared");
  427. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  428. XCTAssertEqual([self.uploadReportArray count], 1);
  429. XCTAssertEqualObjects(self.uploadReportArray[0][@"path"], path);
  430. }
  431. /*
  432. * This tests an edge case where there is a report in prepared. For the purposes of unsent
  433. * reports these are not shown to the developer, but they are uploaded / deleted upon
  434. * calling send / delete.
  435. */
  436. - (void)testFilesLeftInPreparedWithDataCollectionDisabled {
  437. // drop a phony multipart-mime file in here, with non-zero contents
  438. XCTAssert([_fileManager createDirectoryAtPath:_fileManager.preparedPath]);
  439. NSString *path = [_fileManager.preparedPath stringByAppendingPathComponent:@"phony-report"];
  440. path = [path stringByAppendingPathExtension:@"multipart-mime"];
  441. XCTAssertTrue([[_fileManager underlyingFileManager]
  442. createFileAtPath:path
  443. contents:[@"contents" dataUsingEncoding:NSUTF8StringEncoding]
  444. attributes:nil]);
  445. [self startReportManagerWithDataCollectionEnabled:NO];
  446. // Nothing should have happened yet.
  447. XCTAssertEqual([[self contentsOfPreparedPath] count], 1,
  448. @"Prepared should still have the report");
  449. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  450. // We don't expect reports here because we don't consider processing or prepared
  451. // reports as unsent as they need to be marked for sending before being placed
  452. // in those directories.
  453. [self processReports:YES andExpectReports:NO];
  454. // Reports should be moved out of prepared
  455. XCTAssertEqual([[self contentsOfPreparedPath] count], 0, @"Prepared should be cleared");
  456. XCTAssertEqual([[self contentsOfProcessingPath] count], 0, @"Processing should be cleared");
  457. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  458. XCTAssertEqual([self.uploadReportArray count], 1);
  459. XCTAssertEqualObjects(self.uploadReportArray[0][@"path"], path);
  460. }
  461. - (void)testSuccessfulSubmission {
  462. // drop a phony multipart-mime file in here, with non-zero contents
  463. XCTAssert([_fileManager createDirectoryAtPath:_fileManager.preparedPath]);
  464. NSString *path = [_fileManager.preparedPath stringByAppendingPathComponent:@"phony-report"];
  465. path = [path stringByAppendingPathExtension:@"multipart-mime"];
  466. XCTAssertTrue([[_fileManager underlyingFileManager]
  467. createFileAtPath:path
  468. contents:[@"contents" dataUsingEncoding:NSUTF8StringEncoding]
  469. attributes:nil]);
  470. [self startReportManager];
  471. // we should not process reports left over in processing
  472. XCTAssertEqual([[self contentsOfProcessingPath] count], 0, @"Processing should be cleared");
  473. XCTAssertEqual([self.prepareAndSubmitReportArray count], 0);
  474. XCTAssertEqual([self.uploadReportArray count], 1);
  475. XCTAssertEqualObjects(self.uploadReportArray[0][@"path"], path);
  476. [self.reportManager.operationQueue waitUntilAllOperationsAreFinished];
  477. // not 100% sure what to verify here
  478. // lol
  479. }
  480. - (void)testLogInvalidJSONAnalyticsEvents {
  481. NSDictionary *eventAsDict = @{
  482. @"price" : @(NAN),
  483. @"count" : @(INFINITY),
  484. };
  485. NSString *json = FIRCLSFIRAEventDictionaryToJSON(eventAsDict);
  486. XCTAssertEqualObjects(json, nil);
  487. }
  488. @end