FIRCLSReportManagerTests.m 25 KB

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