FIRCLSFileTests.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. #include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
  15. #if SWIFT_PACKAGE
  16. @import FirebaseCrashlyticsSwift;
  17. #else // Swift Package Manager
  18. #import <FirebaseCrashlytics/FirebaseCrashlytics-Swift.h>
  19. #endif // CocoaPods
  20. #import <XCTest/XCTest.h>
  21. @interface FIRCLSFileTests : XCTestCase
  22. @property(nonatomic, assign) FIRCLSFile unbufferedFile;
  23. @property(nonatomic, assign) FIRCLSFile bufferedFile;
  24. @property(nonatomic, strong) NSString *unbufferedPath;
  25. @property(nonatomic, strong) NSString *bufferedPath;
  26. @end
  27. @implementation FIRCLSFileTests
  28. - (void)setUp {
  29. [super setUp];
  30. self.unbufferedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"file_test"];
  31. self.bufferedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"buffered_file_test"];
  32. [[NSFileManager defaultManager] removeItemAtPath:self.unbufferedPath error:nil];
  33. FIRCLSFileInitWithPathMode(&_unbufferedFile, [self.unbufferedPath fileSystemRepresentation],
  34. false, false);
  35. FIRCLSFileInitWithPathMode(&_bufferedFile, [self.bufferedPath fileSystemRepresentation], false,
  36. true);
  37. }
  38. - (void)tearDown {
  39. FIRCLSFileClose(&_unbufferedFile);
  40. FIRCLSFileClose(&_bufferedFile);
  41. [super tearDown];
  42. }
  43. - (NSString *)contentsOfFileAtPath:(NSString *)path {
  44. return [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
  45. }
  46. #pragma mark -
  47. - (void)testEmptySection {
  48. [self emptySectionWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  49. [self emptySectionWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  50. }
  51. - (void)emptySectionWithFile:(FIRCLSFile *)file
  52. filePath:(NSString *)filePath
  53. buffered:(BOOL)buffered {
  54. FIRCLSFileWriteSectionStart(file, "empty");
  55. FIRCLSFileWriteSectionEnd(file);
  56. NSString *contents = [self contentsOfFileAtPath:filePath];
  57. if (buffered) {
  58. XCTAssertEqualObjects(
  59. contents, @"",
  60. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  61. FIRCLSFileFlushWriteBuffer(file);
  62. contents = [self contentsOfFileAtPath:filePath];
  63. }
  64. XCTAssertEqualObjects(
  65. contents, @"{\"empty\":}\n",
  66. @"Empty written to and retrieved from file does not match input in %@buffered case",
  67. buffered ? @"" : @"un");
  68. }
  69. #pragma mark -
  70. - (void)testSingleArrayCollection {
  71. [self singleArrayCollectionWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  72. [self singleArrayCollectionWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  73. }
  74. - (void)singleArrayCollectionWithFile:(FIRCLSFile *)file
  75. filePath:(NSString *)filePath
  76. buffered:(BOOL)buffered {
  77. FIRCLSFileWriteSectionStart(file, "array");
  78. FIRCLSFileWriteArrayStart(file);
  79. FIRCLSFileWriteArrayEntryUint64(file, 1);
  80. FIRCLSFileWriteArrayEnd(file);
  81. FIRCLSFileWriteSectionEnd(file);
  82. NSString *contents = [self contentsOfFileAtPath:filePath];
  83. if (buffered) {
  84. XCTAssertEqualObjects(
  85. contents, @"",
  86. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  87. FIRCLSFileFlushWriteBuffer(file);
  88. contents = [self contentsOfFileAtPath:filePath];
  89. }
  90. XCTAssertEqualObjects(
  91. contents, @"{\"array\":[1]}\n",
  92. @"Single array written to and retrieved from file does not match input in %@buffered case",
  93. buffered ? @"" : @"un");
  94. }
  95. #pragma mark -
  96. - (void)testEmptyCollectionFollowedByEntry {
  97. [self emptyCollectionFollowedByEntryWithFile:&_unbufferedFile
  98. filePath:self.unbufferedPath
  99. buffered:NO];
  100. [self emptyCollectionFollowedByEntryWithFile:&_bufferedFile
  101. filePath:self.bufferedPath
  102. buffered:YES];
  103. }
  104. - (void)emptyCollectionFollowedByEntryWithFile:(FIRCLSFile *)file
  105. filePath:(NSString *)filePath
  106. buffered:(BOOL)buffered {
  107. FIRCLSFileWriteSectionStart(file, "empty_array");
  108. FIRCLSFileWriteArrayStart(file);
  109. FIRCLSFileWriteArrayEnd(file);
  110. // and now put in another entry into the hash
  111. FIRCLSFileWriteHashEntryUint64(file, "value", 1);
  112. FIRCLSFileWriteSectionEnd(file);
  113. NSString *contents = [self contentsOfFileAtPath:filePath];
  114. if (buffered) {
  115. XCTAssertEqualObjects(
  116. contents, @"",
  117. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  118. FIRCLSFileFlushWriteBuffer(file);
  119. contents = [self contentsOfFileAtPath:filePath];
  120. }
  121. XCTAssertEqualObjects(contents, @"{\"empty_array\":[],\"value\":1}\n",
  122. @"Empty collection and entry written to and retrieved from file does not "
  123. @"match input in %@buffered case",
  124. buffered ? @"" : @"un");
  125. }
  126. #pragma mark -
  127. - (void)testHexEncodingString {
  128. [self hexEncodingStringWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  129. [self hexEncodingStringWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  130. }
  131. - (void)hexEncodingStringWithFile:(FIRCLSFile *)file
  132. filePath:(NSString *)filePath
  133. buffered:(BOOL)buffered {
  134. FIRCLSFileWriteHashStart(file);
  135. FIRCLSFileWriteHashEntryHexEncodedString(file, "hex", "hex string");
  136. FIRCLSFileWriteHashEnd(file);
  137. NSString *contents = [self contentsOfFileAtPath:filePath];
  138. if (buffered) {
  139. XCTAssertEqualObjects(
  140. contents, @"",
  141. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  142. FIRCLSFileFlushWriteBuffer(file);
  143. contents = [self contentsOfFileAtPath:filePath];
  144. }
  145. XCTAssertEqualObjects(contents, @"{\"hex\":\"68657820737472696e67\"}",
  146. @"Hex encoded string written to and retrieved from file does not match "
  147. @"input in %@buffered case",
  148. buffered ? @"" : @"un");
  149. }
  150. // This is the test to compare FIRCLSwiftFileUtility.stringToHexConverter(for:) and
  151. // FIRCLSFileWriteHexEncodedString return the same hex encoding value
  152. - (void)testHexEncodingStringObjcAndSwiftResultsSame {
  153. NSString *testedValueString = @"是themis的测试数据,输入中文";
  154. FIRCLSFile *unbufferedFile = &_unbufferedFile;
  155. FIRCLSFileWriteHashStart(unbufferedFile);
  156. FIRCLSFileWriteHashEntryHexEncodedString(unbufferedFile, "hex", [testedValueString UTF8String]);
  157. FIRCLSFileWriteHashEnd(unbufferedFile);
  158. NSString *contentsFromObjcHexEncoding = [self contentsOfFileAtPath:self.unbufferedPath];
  159. FIRCLSFile *bufferedFile = &_bufferedFile;
  160. NSString *encodedValue = [FIRCLSwiftFileUtility stringToHexConverterFor:testedValueString];
  161. FIRCLSFileWriteHashStart(bufferedFile);
  162. FIRCLSFileWriteHashKey(bufferedFile, "hex");
  163. FIRCLSFileWriteStringUnquoted(bufferedFile, "\"");
  164. FIRCLSFileWriteStringUnquoted(bufferedFile, [encodedValue UTF8String]);
  165. FIRCLSFileWriteStringUnquoted(bufferedFile, "\"");
  166. FIRCLSFileWriteHashEnd(bufferedFile);
  167. FIRCLSFileFlushWriteBuffer(bufferedFile);
  168. NSString *contentsFromSwiftHexEncoding = [self contentsOfFileAtPath:self.bufferedPath];
  169. XCTAssertTrue([contentsFromObjcHexEncoding isEqualToString:contentsFromSwiftHexEncoding]);
  170. }
  171. #pragma mark -
  172. - (void)testHexEncodingLongString {
  173. [self hexEncodingLongStringWithFile:&_unbufferedFile
  174. filePath:self.unbufferedPath
  175. length:CLS_FILE_HEX_BUFFER * 10
  176. buffered:NO];
  177. [self hexEncodingLongStringWithFile:&_bufferedFile
  178. filePath:self.bufferedPath
  179. length:FIRCLSWriteBufferLength * 10
  180. buffered:YES];
  181. }
  182. - (void)hexEncodingLongStringWithFile:(FIRCLSFile *)file
  183. filePath:(NSString *)filePath
  184. length:(size_t)length
  185. buffered:(BOOL)buffered {
  186. char *longString = malloc(length * sizeof(char));
  187. memset(longString, 'a', length); // fill it with 'a' characters
  188. longString[length - 1] = 0; // null terminate
  189. FIRCLSFileWriteHashStart(file);
  190. FIRCLSFileWriteHashEntryHexEncodedString(file, "hex", longString);
  191. FIRCLSFileWriteHashEnd(file);
  192. NSDictionary *dict =
  193. [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath]
  194. options:0
  195. error:nil];
  196. if (buffered) {
  197. XCTAssertNil(dict);
  198. FIRCLSFileFlushWriteBuffer(file);
  199. dict = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath]
  200. options:0
  201. error:nil];
  202. }
  203. XCTAssertNotNil(dict,
  204. @"Expected a dictionary serialized from JSON file contents in %@buffered case",
  205. buffered ? @"" : @"un");
  206. XCTAssertEqual([dict[@"hex"] length], (length - 1) * 2,
  207. @"Long hex encoded string written to and retrieved from file does not match input "
  208. @"in %@buffered case",
  209. buffered ? @"" : @"un");
  210. free(longString);
  211. }
  212. #pragma mark -
  213. - (void)testSignedInteger {
  214. [self signedIntegerWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  215. [self signedIntegerWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  216. }
  217. - (void)signedIntegerWithFile:(FIRCLSFile *)file
  218. filePath:(NSString *)filePath
  219. buffered:(BOOL)buffered {
  220. FIRCLSFileWriteSectionStart(file, "signed");
  221. FIRCLSFileWriteHashStart(file);
  222. FIRCLSFileWriteHashEntryInt64(file, "value", -1);
  223. FIRCLSFileWriteHashEnd(file);
  224. FIRCLSFileWriteSectionEnd(file);
  225. NSString *contents = [self contentsOfFileAtPath:filePath];
  226. if (buffered) {
  227. XCTAssertEqualObjects(
  228. contents, @"",
  229. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  230. FIRCLSFileFlushWriteBuffer(file);
  231. contents = [self contentsOfFileAtPath:filePath];
  232. }
  233. XCTAssertEqualObjects(
  234. contents, @"{\"signed\":{\"value\":-1}}\n",
  235. @"Signed integer written to and retrieved from file does not match input in %@buffered case",
  236. buffered ? @"" : @"un");
  237. }
  238. #pragma mark -
  239. - (void)testBigInteger {
  240. [self bigIntegerWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  241. [self bigIntegerWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  242. }
  243. - (void)bigIntegerWithFile:(FIRCLSFile *)file
  244. filePath:(NSString *)filePath
  245. buffered:(BOOL)buffered {
  246. FIRCLSFileWriteSectionStart(file, "big_int");
  247. FIRCLSFileWriteHashStart(file);
  248. FIRCLSFileWriteHashEntryUint64(file, "value", 0x12345678900af8a1);
  249. FIRCLSFileWriteHashEnd(file);
  250. FIRCLSFileWriteSectionEnd(file);
  251. NSString *contents = [self contentsOfFileAtPath:filePath];
  252. if (buffered) {
  253. XCTAssertEqualObjects(
  254. contents, @"",
  255. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  256. FIRCLSFileFlushWriteBuffer(file);
  257. contents = [self contentsOfFileAtPath:filePath];
  258. }
  259. XCTAssertEqualObjects(
  260. contents, @"{\"big_int\":{\"value\":1311768467284359329}}\n",
  261. @"Big integer written to and retrieved from file does not match input in %@buffered case",
  262. buffered ? @"" : @"un");
  263. }
  264. #pragma mark -
  265. - (void)testMaxUInt64 {
  266. [self maxUInt64WithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  267. [self maxUInt64WithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  268. }
  269. - (void)maxUInt64WithFile:(FIRCLSFile *)file filePath:(NSString *)filePath buffered:(BOOL)buffered {
  270. FIRCLSFileWriteSectionStart(file, "big_int");
  271. FIRCLSFileWriteHashStart(file);
  272. FIRCLSFileWriteHashEntryUint64(file, "value", 0xFFFFFFFFFFFFFFFF);
  273. FIRCLSFileWriteHashEnd(file);
  274. FIRCLSFileWriteSectionEnd(file);
  275. NSString *contents = [self contentsOfFileAtPath:filePath];
  276. if (buffered) {
  277. XCTAssertEqualObjects(
  278. contents, @"",
  279. @"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
  280. FIRCLSFileFlushWriteBuffer(file);
  281. contents = [self contentsOfFileAtPath:filePath];
  282. }
  283. XCTAssertEqualObjects(contents, @"{\"big_int\":{\"value\":18446744073709551615}}\n",
  284. @"Max unsigned integer written to and retrieved from file does not match "
  285. @"input in %@buffered case",
  286. buffered ? @"" : @"un");
  287. }
  288. #pragma mark -
  289. - (void)testSimpleHashPerformanceWithUnbufferedFile {
  290. [self measureBlock:^{
  291. // just one run isn't sufficient
  292. for (NSUInteger i = 0; i < 2000; ++i) {
  293. FIRCLSFileWriteSectionStart(&self->_unbufferedFile, "hash_test");
  294. FIRCLSFileWriteHashEntryString(&self->_unbufferedFile, "key1", "value1");
  295. FIRCLSFileWriteHashEntryUint64(&self->_unbufferedFile, "key2", 64);
  296. FIRCLSFileWriteHashEntryNSString(&self->_unbufferedFile, "key2", @"some string");
  297. FIRCLSFileWriteSectionEnd(&self->_unbufferedFile);
  298. }
  299. }];
  300. }
  301. - (void)testSimpleHashPerformanceWithBufferedFile {
  302. [self measureBlock:^{
  303. // just one run isn't sufficient
  304. for (NSUInteger i = 0; i < 2000; ++i) {
  305. FIRCLSFileWriteSectionStart(&self->_bufferedFile, "hash_test");
  306. FIRCLSFileWriteHashEntryString(&self->_bufferedFile, "key1", "value1");
  307. FIRCLSFileWriteHashEntryUint64(&self->_bufferedFile, "key2", 64);
  308. FIRCLSFileWriteHashEntryNSString(&self->_bufferedFile, "key2", @"some string");
  309. FIRCLSFileWriteSectionEnd(&self->_bufferedFile);
  310. }
  311. }];
  312. }
  313. #pragma mark -
  314. - (void)testOpenAndClose {
  315. [self openAndCloseWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
  316. [self openAndCloseWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
  317. }
  318. - (void)openAndCloseWithFile:(FIRCLSFile *)file
  319. filePath:(NSString *)filePath
  320. buffered:(BOOL)buffered {
  321. XCTAssert(FIRCLSFileIsOpen(file), @"File should be opened by setup in %@buffered case",
  322. buffered ? @"" : @"un");
  323. XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
  324. buffered ? @"" : @"un");
  325. XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
  326. buffered ? @"" : @"un");
  327. XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], buffered),
  328. @"Re-opening the same file structure should succeed in %@buffered case",
  329. buffered ? @"" : @"un");
  330. XCTAssert(FIRCLSFileIsOpen(file),
  331. @"That file should be marked as open as well in %@buffered case",
  332. buffered ? @"" : @"un");
  333. }
  334. #pragma mark -
  335. - (void)testCloseAndOpenAlternatingBufferedOption {
  336. [self closeAndOpenAlternatingBufferedOptionWithFile:&_unbufferedFile
  337. filePath:self.unbufferedPath
  338. buffered:NO];
  339. [self closeAndOpenAlternatingBufferedOptionWithFile:&_bufferedFile
  340. filePath:self.bufferedPath
  341. buffered:YES];
  342. }
  343. - (void)closeAndOpenAlternatingBufferedOptionWithFile:(FIRCLSFile *)file
  344. filePath:(NSString *)filePath
  345. buffered:(BOOL)buffered {
  346. XCTAssert(FIRCLSFileIsOpen(file), @"File should be opened by setup in %@buffered case",
  347. buffered ? @"" : @"un");
  348. XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
  349. buffered ? @"" : @"un");
  350. XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
  351. buffered ? @"" : @"un");
  352. XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], !buffered),
  353. @"Re-opening the same file structure should succeed in %@buffered case",
  354. buffered ? @"" : @"un");
  355. XCTAssert(FIRCLSFileIsOpen(file),
  356. @"That file should be marked as open as well in %@buffered case",
  357. buffered ? @"" : @"un");
  358. XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
  359. buffered ? @"" : @"un");
  360. XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
  361. buffered ? @"" : @"un");
  362. XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], buffered),
  363. @"Re-opening the same file structure should succeed in %@buffered case",
  364. buffered ? @"" : @"un");
  365. XCTAssert(FIRCLSFileIsOpen(file),
  366. @"That file should be marked as open as well in %@buffered case",
  367. buffered ? @"" : @"un");
  368. }
  369. #pragma mark -
  370. - (void)testLoggingInputLongerThanBuffer {
  371. size_t inputLength = (FIRCLSWriteBufferLength + 2) * sizeof(char);
  372. char *input = malloc(inputLength);
  373. for (size_t i = 0; i < inputLength - 1; i++) {
  374. input[i] = i % 26 + 'a';
  375. }
  376. input[inputLength - 1] = '\0';
  377. NSString *inputString = [NSString stringWithUTF8String:input];
  378. FIRCLSFileWriteHashStart(&_bufferedFile);
  379. FIRCLSFileWriteHashEntryString(&_bufferedFile, "value", input);
  380. FIRCLSFileWriteHashEnd(&_bufferedFile);
  381. FIRCLSFileFlushWriteBuffer(&_bufferedFile);
  382. NSError *error;
  383. NSDictionary *dict =
  384. [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:self.bufferedPath]
  385. options:0
  386. error:&error];
  387. XCTAssertNotNil(dict,
  388. @"No data was retrieved from log file while writing long input with buffering");
  389. NSString *writtenValue = dict[@"value"];
  390. XCTAssertEqualObjects(inputString, writtenValue,
  391. @"Data was lost while writing long input to file with write buffering");
  392. free(input);
  393. }
  394. @end