FIRCLSReportManagerTests.m 23 KB

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