Files
firebase-ios-sdk/Crashlytics/UnitTests/FIRCLSFileTests.m
T

431 lines
17 KiB
Objective-C

// Copyright 2019 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
#import <XCTest/XCTest.h>
@interface FIRCLSFileTests : XCTestCase
@property(nonatomic, assign) FIRCLSFile unbufferedFile;
@property(nonatomic, assign) FIRCLSFile bufferedFile;
@property(nonatomic, strong) NSString *unbufferedPath;
@property(nonatomic, strong) NSString *bufferedPath;
@end
@implementation FIRCLSFileTests
- (void)setUp {
[super setUp];
self.unbufferedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"file_test"];
self.bufferedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"buffered_file_test"];
[[NSFileManager defaultManager] removeItemAtPath:self.unbufferedPath error:nil];
FIRCLSFileInitWithPathMode(&_unbufferedFile, [self.unbufferedPath fileSystemRepresentation],
false, false);
FIRCLSFileInitWithPathMode(&_bufferedFile, [self.bufferedPath fileSystemRepresentation], false,
true);
}
- (void)tearDown {
FIRCLSFileClose(&_unbufferedFile);
FIRCLSFileClose(&_bufferedFile);
[super tearDown];
}
- (NSString *)contentsOfFileAtPath:(NSString *)path {
return [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
}
#pragma mark -
- (void)testEmptySection {
[self emptySectionWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self emptySectionWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)emptySectionWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "empty");
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(
contents, @"{\"empty\":}\n",
@"Empty written to and retrieved from file does not match input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testSingleArrayCollection {
[self singleArrayCollectionWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self singleArrayCollectionWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)singleArrayCollectionWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "array");
FIRCLSFileWriteArrayStart(file);
FIRCLSFileWriteArrayEntryUint64(file, 1);
FIRCLSFileWriteArrayEnd(file);
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(
contents, @"{\"array\":[1]}\n",
@"Single array written to and retrieved from file does not match input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testEmptyCollectionFollowedByEntry {
[self emptyCollectionFollowedByEntryWithFile:&_unbufferedFile
filePath:self.unbufferedPath
buffered:NO];
[self emptyCollectionFollowedByEntryWithFile:&_bufferedFile
filePath:self.bufferedPath
buffered:YES];
}
- (void)emptyCollectionFollowedByEntryWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "empty_array");
FIRCLSFileWriteArrayStart(file);
FIRCLSFileWriteArrayEnd(file);
// and now put in another entry into the hash
FIRCLSFileWriteHashEntryUint64(file, "value", 1);
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(contents, @"{\"empty_array\":[],\"value\":1}\n",
@"Empty collection and entry written to and retrieved from file does not "
@"match input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testHexEncodingString {
[self hexEncodingStringWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self hexEncodingStringWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)hexEncodingStringWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryHexEncodedString(file, "hex", "hex string");
FIRCLSFileWriteHashEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(contents, @"{\"hex\":\"68657820737472696e67\"}",
@"Hex encoded string written to and retrieved from file does not match "
@"input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testHexEncodingLongString {
[self hexEncodingLongStringWithFile:&_unbufferedFile
filePath:self.unbufferedPath
length:CLS_FILE_HEX_BUFFER * 10
buffered:NO];
[self hexEncodingLongStringWithFile:&_bufferedFile
filePath:self.bufferedPath
length:FIRCLSWriteBufferLength * 10
buffered:YES];
}
- (void)hexEncodingLongStringWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
length:(size_t)length
buffered:(BOOL)buffered {
char *longString = malloc(length * sizeof(char));
memset(longString, 'a', length); // fill it with 'a' characters
longString[length - 1] = 0; // null terminate
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryHexEncodedString(file, "hex", longString);
FIRCLSFileWriteHashEnd(file);
NSDictionary *dict =
[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath]
options:0
error:nil];
if (buffered) {
XCTAssertNil(dict);
FIRCLSFileFlushWriteBuffer(file);
dict = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath]
options:0
error:nil];
}
XCTAssertNotNil(dict,
@"Expected a dictionary serialized from JSON file contents in %@buffered case",
buffered ? @"" : @"un");
XCTAssertEqual([dict[@"hex"] length], (length - 1) * 2,
@"Long hex encoded string written to and retrieved from file does not match input "
@"in %@buffered case",
buffered ? @"" : @"un");
free(longString);
}
#pragma mark -
- (void)testSignedInteger {
[self signedIntegerWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self signedIntegerWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)signedIntegerWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "signed");
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryInt64(file, "value", -1);
FIRCLSFileWriteHashEnd(file);
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(
contents, @"{\"signed\":{\"value\":-1}}\n",
@"Signed integer written to and retrieved from file does not match input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testBigInteger {
[self bigIntegerWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self bigIntegerWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)bigIntegerWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "big_int");
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryUint64(file, "value", 0x12345678900af8a1);
FIRCLSFileWriteHashEnd(file);
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(
contents, @"{\"big_int\":{\"value\":1311768467284359329}}\n",
@"Big integer written to and retrieved from file does not match input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testMaxUInt64 {
[self maxUInt64WithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self maxUInt64WithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)maxUInt64WithFile:(FIRCLSFile *)file filePath:(NSString *)filePath buffered:(BOOL)buffered {
FIRCLSFileWriteSectionStart(file, "big_int");
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryUint64(file, "value", 0xFFFFFFFFFFFFFFFF);
FIRCLSFileWriteHashEnd(file);
FIRCLSFileWriteSectionEnd(file);
NSString *contents = [self contentsOfFileAtPath:filePath];
if (buffered) {
XCTAssertEqualObjects(
contents, @"",
@"Expected empty file contents for buffered case: buffer has not yet been flushed to file");
FIRCLSFileFlushWriteBuffer(file);
contents = [self contentsOfFileAtPath:filePath];
}
XCTAssertEqualObjects(contents, @"{\"big_int\":{\"value\":18446744073709551615}}\n",
@"Max unsigned integer written to and retrieved from file does not match "
@"input in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testSimpleHashPerformanceWithUnbufferedFile {
[self measureBlock:^{
// just one run isn't sufficient
for (NSUInteger i = 0; i < 2000; ++i) {
FIRCLSFileWriteSectionStart(&self->_unbufferedFile, "hash_test");
FIRCLSFileWriteHashEntryString(&self->_unbufferedFile, "key1", "value1");
FIRCLSFileWriteHashEntryUint64(&self->_unbufferedFile, "key2", 64);
FIRCLSFileWriteHashEntryNSString(&self->_unbufferedFile, "key2", @"some string");
FIRCLSFileWriteSectionEnd(&self->_unbufferedFile);
}
}];
}
- (void)testSimpleHashPerformanceWithBufferedFile {
[self measureBlock:^{
// just one run isn't sufficient
for (NSUInteger i = 0; i < 2000; ++i) {
FIRCLSFileWriteSectionStart(&self->_bufferedFile, "hash_test");
FIRCLSFileWriteHashEntryString(&self->_bufferedFile, "key1", "value1");
FIRCLSFileWriteHashEntryUint64(&self->_bufferedFile, "key2", 64);
FIRCLSFileWriteHashEntryNSString(&self->_bufferedFile, "key2", @"some string");
FIRCLSFileWriteSectionEnd(&self->_bufferedFile);
}
}];
}
#pragma mark -
- (void)testOpenAndClose {
[self openAndCloseWithFile:&_unbufferedFile filePath:self.unbufferedPath buffered:NO];
[self openAndCloseWithFile:&_bufferedFile filePath:self.bufferedPath buffered:YES];
}
- (void)openAndCloseWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
XCTAssert(FIRCLSFileIsOpen(file), @"File should be opened by setup in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], buffered),
@"Re-opening the same file structure should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileIsOpen(file),
@"That file should be marked as open as well in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testCloseAndOpenAlternatingBufferedOption {
[self closeAndOpenAlternatingBufferedOptionWithFile:&_unbufferedFile
filePath:self.unbufferedPath
buffered:NO];
[self closeAndOpenAlternatingBufferedOptionWithFile:&_bufferedFile
filePath:self.bufferedPath
buffered:YES];
}
- (void)closeAndOpenAlternatingBufferedOptionWithFile:(FIRCLSFile *)file
filePath:(NSString *)filePath
buffered:(BOOL)buffered {
XCTAssert(FIRCLSFileIsOpen(file), @"File should be opened by setup in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], !buffered),
@"Re-opening the same file structure should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileIsOpen(file),
@"That file should be marked as open as well in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileClose(file), @"Closing the file should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssertFalse(FIRCLSFileIsOpen(file), @"File should now be marked as closed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileInitWithPath(file, [filePath fileSystemRepresentation], buffered),
@"Re-opening the same file structure should succeed in %@buffered case",
buffered ? @"" : @"un");
XCTAssert(FIRCLSFileIsOpen(file),
@"That file should be marked as open as well in %@buffered case",
buffered ? @"" : @"un");
}
#pragma mark -
- (void)testLoggingInputLongerThanBuffer {
size_t inputLength = (FIRCLSWriteBufferLength + 2) * sizeof(char);
char *input = malloc(inputLength);
for (size_t i = 0; i < inputLength - 1; i++) {
input[i] = i % 26 + 'a';
}
input[inputLength - 1] = '\0';
NSString *inputString = [NSString stringWithUTF8String:input];
FIRCLSFileWriteHashStart(&_bufferedFile);
FIRCLSFileWriteHashEntryString(&_bufferedFile, "value", input);
FIRCLSFileWriteHashEnd(&_bufferedFile);
FIRCLSFileFlushWriteBuffer(&_bufferedFile);
NSError *error;
NSDictionary *dict =
[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:self.bufferedPath]
options:0
error:&error];
XCTAssertNotNil(dict,
@"No data was retrieved from log file while writing long input with buffering");
NSString *writtenValue = dict[@"value"];
XCTAssertEqualObjects(inputString, writtenValue,
@"Data was lost while writing long input to file with write buffering");
free(input);
}
@end