Files
Parse-SDK-iOS-OSX/Tests/Unit/SQLiteDatabaseTest.m
2015-12-16 20:42:03 -08:00

600 lines
26 KiB
Objective-C

/**
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
@import Bolts.BFTask;
#import "BFTask+Private.h"
#import "PFFileManager.h"
#import "PFSQLiteDatabase.h"
#import "PFSQLiteDatabaseResult.h"
#import "PFTestCase.h"
@interface SQLiteDatabaseTest : PFTestCase {
PFSQLiteDatabase *database;
}
@end
@implementation SQLiteDatabaseTest
///--------------------------------------
#pragma mark - Helpers
///--------------------------------------
- (NSString *)databasePath {
return [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.db"];
}
///--------------------------------------
#pragma mark - XCTestCase
///--------------------------------------
- (void)setUp {
[super setUp];
database = [PFSQLiteDatabase databaseWithPath:[self databasePath]];
}
- (void)tearDown {
if (database != NULL) {
[[[database isOpenAsync] continueWithBlock:^id(BFTask *task) {
BOOL isOpen = [task.result boolValue];
if (isOpen) {
return [database closeAsync];
}
return task;
}] waitUntilFinished];
}
// delete DB file;
[[NSFileManager defaultManager] removeItemAtPath:[self databasePath] error:NULL];
[super tearDown];
}
///--------------------------------------
#pragma mark - Helpers
///--------------------------------------
// Should return BFTask to not waste `waitUntilFinished`
- (BFTask *)createDatabaseAsync {
// Drop existing database first if any.
return [[[[database openAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"DROP TABLE test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
return [database openAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"CREATE TABLE test (a text, b text, c integer, d double)"
withArgumentsInArray:nil];
}];
}
///--------------------------------------
#pragma mark - Tests
///--------------------------------------
- (void)testOpen {
[[[[[[[[[database openAsync] continueWithBlock:^id(BFTask *task) {
return [database isOpenAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertTrue([task.result boolValue]);
return [database openAsync];
}] continueWithBlock:^id(BFTask *task) {
// Should error because DB is opened
XCTAssertNotNil(task.error);
return [database closeAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database isOpenAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertFalse([task.result boolValue]);
return [database closeAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
return [database openAsync];
}] continueWithBlock:^id(BFTask *task) {
// Should fail because database was closed already, and reopened.
XCTAssertNotNil(task.error);
return task;
}] waitUntilFinished];
}
- (void)testCRUD {
[[[[[[[[[[[[database openAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"CREATE TABLE test (a text, b text, c integer, d double)"
withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Make sure it success
XCTAssertNil(task.error);
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
// Make sure there's nothing more
XCTAssertFalse([result next]);
// Test the cached statement
// TODO (hallucinogen): how can we be sure we're getting this from cached statement?
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
// Make sure there's nothing more
XCTAssertFalse([result next]);
return [database executeSQLAsync:@"UPDATE test SET a = ?, c = ? WHERE c = ?"
withArgumentsInArray:@[ @"onenew", @5, @3 ]];
}] continueWithBlock:^id(BFTask *task) {
// Make sure there's nothing wrong
XCTAssertNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"onenew", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(5, [result intForColumnIndex:2]);
// Make sure there's nothing more
XCTAssertFalse([result next]);
return [database executeSQLAsync:@"DELETE FROM test WHERE c = ?"
withArgumentsInArray:@[ @5 ]];
}] continueWithBlock:^id(BFTask *task) {
// Make sure there's nothing wrong
XCTAssertNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Make sure there's nothing wrong
XCTAssertNil(task.error);
PFSQLiteDatabaseResult *result = task.result;
// Make sure there's nothing more
XCTAssertFalse(result.next);
// Clean up
return [database executeSQLAsync:@"DROP TABLE test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Make sure there's nothing wrong
XCTAssertNil(task.error);
return task;
}] waitUntilFinished];
}
// TODO (hallucinogen): this test consists of three units which can be separated.
- (void)testTransaction {
[[[[[[[[[[[[[[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database beginTransactionAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
// Make sure there's nothing more
XCTAssertFalse(result.next);
// Commit
return [database commitAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
// Make sure there's nothing more
XCTAssertFalse(result.next);
return [database beginTransactionAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"oneone", @"twotwo", @33, @44.44 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// should have two results
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
// There's second element
XCTAssertTrue(nextResult);
nextResult = [result next];
// There's nothing more
XCTAssertFalse(nextResult);
// Rollback
return [database rollbackAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Should have one result
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
// There's nothing more
XCTAssertFalse(nextResult);
// Now let's try making transaction, then close the database wbile it's in transaction
return [database beginTransactionAsync];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"oneone", @"twotwo", @33, @44.44 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Should have two results
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
XCTAssertTrue(nextResult);
nextResult = [result next];
XCTAssertFalse(nextResult);
// Let's close the database while in transaction
// The expected result: close successfully and the transaction would be rolled back
return [database closeAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return [BFTask taskWithResult:nil];
}] waitUntilFinished];
database = [PFSQLiteDatabase databaseWithPath:[self databasePath]];
[[[[[[[database openAsync] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"oneone", @"twotwo", @33, @44.44 ]];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Should have two results because the last one is rolled back
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
XCTAssertTrue(nextResult);
nextResult = [result next];
XCTAssertFalse(nextResult);
// Try rolling back previous transaction (which should fail because the database has been
// closed and currently there's no transaction)
return [database rollbackAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Should still have two results
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
XCTAssertTrue(nextResult);
nextResult = [result next];
XCTAssertFalse(nextResult);
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testOperationOnNonExistentTable {
[[[[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO testFake (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM testFake" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
// Should have one result
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
XCTAssertFalse(nextResult);
// Clean up
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testQuery {
[[[[[[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"oneone", @"twotwo", @33, @44.44 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
// Should have two results
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
BOOL nextResult = [result next];
XCTAssertTrue(nextResult);
nextResult = [result next];
XCTAssertFalse(nextResult);
return [database executeCachedQueryAsync:@"SELECT * FROM test WHERE c = ?"
withArgumentsInArray:@[ @3 ]];
}] continueWithBlock:^id(BFTask *task) {
// Check result
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
// Should have one result
BOOL nextResult = [result next];
XCTAssertFalse(nextResult);
return [database executeSQLAsync:@"UPDATE test SET a = ?, c = ? WHERE c = ?"
withArgumentsInArray:@[ @"onenew", @5, @3 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test WHERE c = ?"
withArgumentsInArray:@[ @5 ]];
}] continueWithBlock:^id(BFTask *task) {
// Check result
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"onenew", [result stringForColumn:@"a"]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(5, [result intForColumnIndex:2]);
// Should have one result
BOOL nextResult = [result next];
XCTAssertFalse(nextResult);
// Clean up
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testCursorAndOperationOnDifferentThread {
BFTask *taskWithCursor = [[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"oneone", @"twotwo", @33, @44.44 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id(BFTask *task) {
// Execute this in background
PFSQLiteDatabaseResult *result = task.result;
// Make sure first element exists
XCTAssertTrue([result next]);
// Check values
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
return result;
}];
// Make sure we can read result from main thread
[taskWithCursor waitUntilFinished];
PFSQLiteDatabaseResult *result = taskWithCursor.result;
// Try to access result in main thread
XCTAssertEqualObjects(@"one", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"two", [result stringForColumnIndex:1]);
XCTAssertEqual(3, [result intForColumnIndex:2]);
XCTAssertTrue([result next]);
XCTAssertEqualObjects(@"oneone", [result stringForColumnIndex:0]);
XCTAssertEqualObjects(@"twotwo", [result stringForColumnIndex:1]);
XCTAssertEqual(33, [result intForColumnIndex:2]);
// Test clean up fail
[[[[[[database executeSQLAsync:@"DROP TABLE test" withArgumentsInArray:nil] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
return task;
}] continueWithExecutor:[BFExecutor defaultExecutor] withBlock:^id(BFTask *task) {
// `result` should not increase
XCTAssertFalse([result next]);
return [database executeSQLAsync:@"DROP TABLE test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return task;
//return [database2 closeAsync];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testInvalidArgumentCount {
[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
XCTAssertEqual(PFSQLiteDatabaseInvalidArgumenCountErrorCode, [task.error.userInfo[@"code"] integerValue]);
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testInvalidSQL {
[[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @"one", @"two", @3, @4.4 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNotNil(task.error);
XCTAssertEqual(PFSQLiteDatabaseInvalidSQL, [task.error.userInfo[@"code"] integerValue]);
return [database closeAsync];
}] waitUntilFinished];
}
- (void)testColumnTypes {
[[[[[self createDatabaseAsync] continueWithBlock:^id(BFTask *task) {
return [database executeSQLAsync:@"INSERT INTO test (a, b, c, d) VALUES (?, ?, ?, ?)"
withArgumentsInArray:@[ @1, [NSNull null], @"string", @13.37 ]];
}] continueWithBlock:^id(BFTask *task) {
return [database executeCachedQueryAsync:@"SELECT * FROM test" withArgumentsInArray:nil];
}] continueWithBlock:^id(BFTask *task) {
XCTAssertNil(task.error);
PFSQLiteDatabaseResult *result = task.result;
XCTAssertTrue([result next]);
XCTAssertEqual([result intForColumn:@"a"], 1);
XCTAssertEqual([result intForColumn:@"b"], 0);
XCTAssertEqual([result intForColumn:@"c"], 0);
XCTAssertEqual([result intForColumn:@"d"], 13);
XCTAssertEqual([result intForColumnIndex:0], 1);
XCTAssertEqual([result intForColumnIndex:1], 0);
XCTAssertEqual([result intForColumnIndex:2], 0);
XCTAssertEqual([result intForColumnIndex:3], 13);
XCTAssertEqual([result longForColumn:@"a"], 1);
XCTAssertEqual([result longForColumn:@"b"], 0);
XCTAssertEqual([result longForColumn:@"c"], 0);
XCTAssertEqual([result longForColumn:@"d"], 13);
XCTAssertEqual([result longForColumnIndex:0], 1);
XCTAssertEqual([result longForColumnIndex:1], 0);
XCTAssertEqual([result longForColumnIndex:2], 0);
XCTAssertEqual([result longForColumnIndex:3], 13);
XCTAssertEqual([result boolForColumn:@"a"], YES);
XCTAssertEqual([result boolForColumn:@"b"], NO);
XCTAssertEqual([result boolForColumn:@"c"], NO);
XCTAssertEqual([result boolForColumn:@"d"], YES);
XCTAssertEqual([result boolForColumnIndex:0], YES);
XCTAssertEqual([result boolForColumnIndex:1], NO);
XCTAssertEqual([result boolForColumnIndex:2], NO);
XCTAssertEqual([result boolForColumnIndex:3], YES);
XCTAssertEqual([result doubleForColumn:@"a"], 1);
XCTAssertEqual([result doubleForColumn:@"b"], 0);
XCTAssertEqual([result doubleForColumn:@"c"], 0);
XCTAssertEqual([result doubleForColumn:@"d"], 13.37);
XCTAssertEqual([result doubleForColumnIndex:0], 1);
XCTAssertEqual([result doubleForColumnIndex:1], 0);
XCTAssertEqual([result doubleForColumnIndex:2], 0);
XCTAssertEqual([result doubleForColumnIndex:3], 13.37);
XCTAssertEqualObjects([result stringForColumn:@"a"], @"1");
XCTAssertEqualObjects([result stringForColumn:@"b"], nil);
XCTAssertEqualObjects([result stringForColumn:@"c"], @"string");
XCTAssertEqualObjects([result stringForColumn:@"d"], @"13.37");
XCTAssertEqualObjects([result stringForColumnIndex:0], @"1");
XCTAssertEqualObjects([result stringForColumnIndex:1], nil);
XCTAssertEqualObjects([result stringForColumnIndex:2], @"string");
XCTAssertEqualObjects([result stringForColumnIndex:3], @"13.37");
XCTAssertEqualObjects([result dateForColumn:@"a"], [NSDate dateWithTimeIntervalSince1970:1]);
XCTAssertEqualObjects([result dateForColumn:@"b"], [NSDate dateWithTimeIntervalSince1970:0]);
XCTAssertEqualObjects([result dateForColumn:@"c"], [NSDate dateWithTimeIntervalSince1970:0]);
XCTAssertEqualObjects([result dateForColumn:@"d"], [NSDate dateWithTimeIntervalSince1970:13.37]);
XCTAssertEqualObjects([result dateForColumnIndex:0], [NSDate dateWithTimeIntervalSince1970:1]);
XCTAssertEqualObjects([result dateForColumnIndex:1], [NSDate dateWithTimeIntervalSince1970:0]);
XCTAssertEqualObjects([result dateForColumnIndex:2], [NSDate dateWithTimeIntervalSince1970:0]);
XCTAssertEqualObjects([result dateForColumnIndex:3], [NSDate dateWithTimeIntervalSince1970:13.37]);
XCTAssertEqualObjects([result dataForColumn:@"a"], [NSData dataWithBytes:(char[]) { '1' } length:1]);
XCTAssertEqualObjects([result dataForColumn:@"b"], nil);
XCTAssertEqualObjects([result dataForColumn:@"c"], [NSData dataWithBytes:"string"length:6]);
XCTAssertEqualObjects([result dataForColumn:@"d"], [NSData dataWithBytes:"13.37" length:5]);
XCTAssertEqualObjects([result dataForColumnIndex:0], [NSData dataWithBytes:(char[]) { '1' } length:1]);
XCTAssertEqualObjects([result dataForColumnIndex:1], nil);
XCTAssertEqualObjects([result dataForColumnIndex:2], [NSData dataWithBytes:"string"length:6]);
XCTAssertEqualObjects([result dataForColumnIndex:3], [NSData dataWithBytes:"13.37" length:5]);
XCTAssertEqualObjects([result objectForColumn:@"a"], @"1");
XCTAssertEqualObjects([result objectForColumn:@"b"], nil);
XCTAssertEqualObjects([result objectForColumn:@"c"], @"string");
XCTAssertEqualObjects([result objectForColumn:@"d"], @13.37);
XCTAssertEqualObjects([result objectForColumnIndex:0], @"1");
XCTAssertEqualObjects([result objectForColumnIndex:1], nil);
XCTAssertEqualObjects([result objectForColumnIndex:2], @"string");
XCTAssertEqualObjects([result objectForColumnIndex:3], @13.37);
XCTAssertFalse([result columnIsNull:@"a"]);
XCTAssertTrue([result columnIsNull:@"b"]);
XCTAssertFalse([result columnIsNull:@"c"]);
XCTAssertFalse([result columnIsNull:@"d"]);
XCTAssertFalse([result columnIndexIsNull:0]);
XCTAssertTrue([result columnIndexIsNull:1]);
XCTAssertFalse([result columnIndexIsNull:2]);
XCTAssertFalse([result columnIndexIsNull:3]);
return [database closeAsync];
}] waitUntilFinished];
}
@end