5 // Created by Michael Brouwer on 11/15/12.
6 // Copyright (c) 2012 Apple Inc. All rights reserved.
9 #include <utilities/SecCFRelease.h>
10 #include <utilities/SecDb.h>
12 #include <CoreFoundation/CoreFoundation.h>
14 #include "utilities_regressions.h"
19 static 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;
23 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
24 dispatch_group_t group
= dispatch_group_create();
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
;
33 CFErrorRef error
= NULL
;
34 if (!perform(db
, &error
, ^void (SecDbConnectionRef dbconn
) {
36 if (count
> max_count
) {
39 struct timespec ts
= { .tv_nsec
= 200000 };
43 fail("perform %s %@", name
, error
);
48 dispatch_group_wait(group
, DISPATCH_TIME_FOREVER
);
49 dispatch_release(group
);
53 static 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
);
60 todo("can't guarantee all threads used");
61 is(count_func(db
, "writers", &max_conn_count
, SecDbPerformWrite
), kSecDbMaxWriters
, "max writers is %d", kSecDbMaxWriters
);
64 dispatch_group_async(group
, queue
, ^{
65 cmp_ok(count_func(db
, "readers", &max_conn_count
, SecDbPerformRead
), <=, kSecDbMaxReaders
, "max readers is %d", kSecDbMaxReaders
);
67 todo("can't guarantee all threads used");
68 is(count_func(db
, "readers", &max_conn_count
, SecDbPerformRead
), kSecDbMaxReaders
, "max readers is %d", kSecDbMaxReaders
);
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
);
75 todo("can't guarantee all threads idle");
76 is(max_conn_count
, kSecDbMaxIdleHandles
, "max idle connection count is %d", kSecDbMaxIdleHandles
);
81 static void tests(void)
83 CFTypeID typeID
= SecDbGetTypeID();
84 CFStringRef tid
= CFCopyTypeIDDescription(typeID
);
85 ok(CFEqual(CFSTR("SecDb"), tid
), "tid matches");
88 typeID
= SecDbConnectionGetTypeID();
89 tid
= CFCopyTypeIDDescription(typeID
);
90 ok(CFEqual(CFSTR("SecDbConnection"), tid
), "tid matches");
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
: "");
96 SecDbRef db
= SecDbCreate(dbName
, NULL
);
97 ok(db
, "SecDbCreate");
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
),
103 ok(SecDbExec(dbconn
, CFSTR("INSERT INTO tablea(key,value)VALUES(1,2);"), &error
),
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
);
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
122 }), "SecDbStep: %@", error
);
123 CFReleaseNull(error
);
124 }), "SecDbPrepare: %@", error
);
125 CFReleaseNull(error
);
127 ok(SecDbTransaction(dbconn
, kSecDbExclusiveTransactionType
, &error
, ^(bool *commit
) {
128 ok(SecDbExec(dbconn
, CFSTR("INSERT INTO tablea (key,value)VALUES(13,21);"), &error
),
130 ok(SecDbExec(dbconn
, CFSTR("INSERT INTO tablea (key,value)VALUES(2,5);"), &error
),
132 }), "SecDbTransaction: %@", error
);
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
);
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
);
145 }), "SecDbStep: %@", error
);
146 CFReleaseNull(error
);
148 }), "SecDbPrepare: %@", error
);
150 ok(SecDbExec(dbconn
, CFSTR("DROP TABLE tablea;"), &error
),
152 }), "SecDbPerformWrite: %@", error
);
153 CFReleaseNull(error
);
155 count_connections(db
);
160 int su_40_secdb(int argc
, char *const *argv
)
162 plan_tests(kTestCount
);
169 // The following still need tests.
170 ok(SecDbTransaction(dbconn
, kSecDbNoneTransactionType
, &error
, ^bool {}), "");
171 ok(SecDbTransaction(dbconn
, kSecDbImmediateTransactionType
, &error
, ^bool {}), "");
172 ok(SecDbTransaction(dbconn
, kSecDbNormalTransactionType
, &error
, ^bool {}), "");
173 ok(SecDbPerformRead(SecDbRef db
, CFErrorRef
*error
, void ^(SecDbConnectionRef dbconn
){}), "");
174 SecDbCheckpoint(SecDbConnectionRef dbconn
);