FIRCLSFileTests.m 17 KB

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