FIRCLSReportManagerTests.m 22 KB

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