1// 2// su-40-secdb.c 3// utilities 4// 5// Created by Michael Brouwer on 11/15/12. 6// Copyright (c) 2012 Apple Inc. All rights reserved. 7// 8 9#include <utilities/SecCFRelease.h> 10#include <utilities/SecDb.h> 11 12#include <CoreFoundation/CoreFoundation.h> 13 14#include "utilities_regressions.h" 15#include <time.h> 16 17#define kTestCount 31 18 19static int count_func(SecDbRef db, const char *name, CFIndex *max_conn_count, bool (*perform)(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn))) { 20 __block int count = 0; 21 __block int max_count = 0; 22 *max_conn_count = 0; 23 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 24 dispatch_group_t group = dispatch_group_create(); 25 26 for (int i = 0; i < 100; ++i) { 27 dispatch_group_async(group, queue, ^{ 28 CFIndex conn_count = SecDbIdleConnectionCount(db); 29 if (conn_count > *max_conn_count) { 30 *max_conn_count = conn_count; 31 } 32 33 CFErrorRef error = NULL; 34 if (!perform(db, &error, ^void (SecDbConnectionRef dbconn) { 35 count++; 36 if (count > max_count) { 37 max_count = count; 38 } 39 struct timespec ts = { .tv_nsec = 200000 }; 40 nanosleep(&ts, NULL); 41 count--; 42 })) { 43 fail("perform %s %@", name, error); 44 CFReleaseNull(error); 45 } 46 }); 47 } 48 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 49 dispatch_release(group); 50 return max_count; 51} 52 53static void count_connections(SecDbRef db) { 54 __block CFIndex max_conn_count = 0; 55 dispatch_group_t group = dispatch_group_create(); 56 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 57 dispatch_group_async(group, queue, ^{ 58 cmp_ok(count_func(db, "writers", &max_conn_count, SecDbPerformWrite), <=, kSecDbMaxWriters, "max writers is %d", kSecDbMaxWriters); 59 TODO: { 60 todo("can't guarantee all threads used"); 61 is(count_func(db, "writers", &max_conn_count, SecDbPerformWrite), kSecDbMaxWriters, "max writers is %d", kSecDbMaxWriters); 62 } 63 }); 64 dispatch_group_async(group, queue, ^{ 65 cmp_ok(count_func(db, "readers", &max_conn_count, SecDbPerformRead), <=, kSecDbMaxReaders, "max readers is %d", kSecDbMaxReaders); 66 TODO: { 67 todo("can't guarantee all threads used"); 68 is(count_func(db, "readers", &max_conn_count, SecDbPerformRead), kSecDbMaxReaders, "max readers is %d", kSecDbMaxReaders); 69 } 70 }); 71 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 72 dispatch_release(group); 73 cmp_ok(max_conn_count, <=, kSecDbMaxIdleHandles, "max idle connection count is %d", kSecDbMaxIdleHandles); 74 TODO: { 75 todo("can't guarantee all threads idle"); 76 is(max_conn_count, kSecDbMaxIdleHandles, "max idle connection count is %d", kSecDbMaxIdleHandles); 77 } 78 79} 80 81static void tests(void) 82{ 83 CFTypeID typeID = SecDbGetTypeID(); 84 CFStringRef tid = CFCopyTypeIDDescription(typeID); 85 ok(CFEqual(CFSTR("SecDb"), tid), "tid matches"); 86 CFReleaseNull(tid); 87 88 typeID = SecDbConnectionGetTypeID(); 89 tid = CFCopyTypeIDDescription(typeID); 90 ok(CFEqual(CFSTR("SecDbConnection"), tid), "tid matches"); 91 CFReleaseNull(tid); 92 93 const char *home_var = getenv("HOME"); 94 CFStringRef dbName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/Library/Keychains/su-40-sqldb.db"), home_var ? home_var : ""); 95 96 SecDbRef db = SecDbCreate(dbName, NULL); 97 ok(db, "SecDbCreate"); 98 99 __block CFErrorRef error = NULL; 100 ok(SecDbPerformWrite(db, &error, ^void (SecDbConnectionRef dbconn) { 101 ok(SecDbExec(dbconn, CFSTR("CREATE TABLE tablea(key TEXT,value BLOB);"), &error), 102 "exec: %@", error); 103 ok(SecDbExec(dbconn, CFSTR("INSERT INTO tablea(key,value)VALUES(1,2);"), &error), 104 "exec: %@", error); 105 106 CFStringRef sql = CFSTR("INSERT INTO tablea(key,value)VALUES(?,?);"); 107 ok(SecDbPrepare(dbconn, sql, &error, ^void (sqlite3_stmt *stmt) { 108 ok_status(sqlite3_bind_text(stmt, 1, "key1", 4, NULL), "bind_text[1]"); 109 ok_status(sqlite3_bind_blob(stmt, 2, "value1", 6, NULL), "bind_blob[2]"); 110 ok(SecDbStep(dbconn, stmt, &error, NULL), "SecDbStep: %@", error); 111 CFReleaseNull(error); 112 }), "SecDbPrepare: %@", error); 113 CFReleaseNull(error); 114 115 sql = CFSTR("SELECT key,value FROM tablea;"); 116 ok(SecDbPrepare(dbconn, sql, &error, ^void (sqlite3_stmt *stmt) { 117 ok(SecDbStep(dbconn, stmt, &error, ^(bool *stop) { 118 const unsigned char *key = sqlite3_column_text(stmt, 1); 119 pass("got a row key: %s", key); 120 // A row happened, we're done 121 *stop = true; 122 }), "SecDbStep: %@", error); 123 CFReleaseNull(error); 124 }), "SecDbPrepare: %@", error); 125 CFReleaseNull(error); 126 127 ok(SecDbTransaction(dbconn, kSecDbExclusiveTransactionType, &error, ^(bool *commit) { 128 ok(SecDbExec(dbconn, CFSTR("INSERT INTO tablea (key,value)VALUES(13,21);"), &error), 129 "exec: %@", error); 130 ok(SecDbExec(dbconn, CFSTR("INSERT INTO tablea (key,value)VALUES(2,5);"), &error), 131 "exec: %@", error); 132 }), "SecDbTransaction: %@", error); 133 134 ok(SecDbPrepare(dbconn, sql, &error, ^void (sqlite3_stmt *stmt) { 135 ok(SecDbStep(dbconn, stmt, &error, ^(bool *stop) { 136 const unsigned char *key = sqlite3_column_text(stmt, 1); 137 pass("got a row key: %s", key); 138 }), "SecDbStep: %@", error); 139 CFReleaseNull(error); 140 sqlite3_reset(stmt); 141 ok(SecDbStep(dbconn, stmt, &error, ^(bool *stop) { 142 const unsigned char *key = sqlite3_column_text(stmt, 1); 143 pass("got a row key: %s", key); 144 *stop = true; 145 }), "SecDbStep: %@", error); 146 CFReleaseNull(error); 147 148 }), "SecDbPrepare: %@", error); 149 150 ok(SecDbExec(dbconn, CFSTR("DROP TABLE tablea;"), &error), 151 "exec: %@", error); 152 }), "SecDbPerformWrite: %@", error); 153 CFReleaseNull(error); 154 155 count_connections(db); 156 157 CFReleaseNull(db); 158} 159 160int su_40_secdb(int argc, char *const *argv) 161{ 162 plan_tests(kTestCount); 163 tests(); 164 165 return 0; 166} 167 168#if 0 169// The following still need tests. 170ok(SecDbTransaction(dbconn, kSecDbNoneTransactionType, &error, ^bool {}), ""); 171ok(SecDbTransaction(dbconn, kSecDbImmediateTransactionType, &error, ^bool {}), ""); 172ok(SecDbTransaction(dbconn, kSecDbNormalTransactionType, &error, ^bool {}), ""); 173ok(SecDbPerformRead(SecDbRef db, CFErrorRef *error, void ^(SecDbConnectionRef dbconn){}), ""); 174SecDbCheckpoint(SecDbConnectionRef dbconn); 175#endif 176