AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY7 8/* Global callback trackers */9static int cbA_calls;10static int cbA_last_n;11static int cbA_total_args;12static void* cbA_args[1024];13 14static int cbB_calls;15static int cbB_last_n;16static int cbB_total_args;17static void* cbB_args[1024];18 19/* Sequence log to verify callback group order (e.g., 'A', then 'B') */20static char cbLog[64];21static int cbLogLen;22 23static void cbA(void **apArg, int nArg){24 int i;25 for(i=0; i<nArg; i++){26 if (cbA_total_args < (int)(sizeof(cbA_args)/sizeof(cbA_args[0]))){27 cbA_args[cbA_total_args++] = apArg[i];28 }29 }30 cbA_last_n = nArg;31 cbA_calls++;32 if (cbLogLen < (int)sizeof(cbLog)) cbLog[cbLogLen++] = 'A';33}34 35static void cbB(void **apArg, int nArg){36 int i;37 for(i=0; i<nArg; i++){38 if (cbB_total_args < (int)(sizeof(cbB_args)/sizeof(cbB_args[0]))){39 cbB_args[cbB_total_args++] = apArg[i];40 }41 }42 cbB_last_n = nArg;43 cbB_calls++;44 if (cbLogLen < (int)sizeof(cbLog)) cbLog[cbLogLen++] = 'B';45}46 47static sqlite3* open_memdb(void){48 sqlite3 *db = 0;49 int rc = sqlite3_open(":memory:", &db);50 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);51 TEST_ASSERT_NOT_NULL(db);52 return db;53}54 55static void close_db(sqlite3 *db){56 /* Ensure notify module is clean for this db before closing */57 sqlite3ConnectionClosed(db);58 int rc = sqlite3_close(db);59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);60}61 62static int contains_ptr(void **arr, int n, void *p){63 int i;64 for(i=0;i<n;i++){65 if(arr[i]==p) return 1;66 }67 return 0;68}69 70void setUp(void) {71 /* Reset callback trackers */72 cbA_calls = cbA_last_n = cbA_total_args = 0;73 cbB_calls = cbB_last_n = cbB_total_args = 0;74 cbLogLen = 0;75 76 /* Explicit initialization (also done implicitly by sqlite3_open) */77 sqlite3_initialize();78}79 80void tearDown(void) {81 /* Nothing to clean here; each test closes its dbs */82}83 84/* Test 1: Clearing a block without any registered notify. */85void test_sqlite3ConnectionUnlocked_clears_blocking_without_notify(void){86 sqlite3 *db1 = open_memdb();87 sqlite3 *db2 = open_memdb();88 89 sqlite3ConnectionBlocked(db1, db2);90 91 TEST_ASSERT_EQUAL_PTR(db2, db1->pBlockingConnection);92 TEST_ASSERT_NULL(db1->pUnlockConnection);93 TEST_ASSERT_EQUAL_PTR(NULL, db1->xUnlockNotify);94 95 sqlite3ConnectionUnlocked(db2);96 97 /* No callbacks should have been invoked */98 TEST_ASSERT_EQUAL_INT(0, cbA_calls);99 TEST_ASSERT_EQUAL_INT(0, cbB_calls);100 101 /* Entry should be cleared and removed from list */102 TEST_ASSERT_NULL(db1->pBlockingConnection);103 TEST_ASSERT_NULL(db1->pUnlockConnection);104 TEST_ASSERT_EQUAL_PTR(NULL, db1->xUnlockNotify);105 TEST_ASSERT_EQUAL_PTR(NULL, db1->pUnlockArg);106 TEST_ASSERT_NULL(db1->pNextBlocked);107 108 close_db(db1);109 close_db(db2);110}111 112/* Test 2: Single blocked connection with notify registered -> single callback. */113void test_sqlite3ConnectionUnlocked_single_notify(void){114 sqlite3 *blocker = open_memdb();115 sqlite3 *db1 = open_memdb();116 117 int arg1 = 0x11;118 119 sqlite3ConnectionBlocked(db1, blocker);120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(db1, cbA, &arg1));121 122 /* Should not have been called yet */123 TEST_ASSERT_EQUAL_INT(0, cbA_calls);124 125 sqlite3ConnectionUnlocked(blocker);126 127 TEST_ASSERT_EQUAL_INT(1, cbA_calls);128 TEST_ASSERT_EQUAL_INT(1, cbA_last_n);129 TEST_ASSERT_EQUAL_INT(1, cbA_total_args);130 TEST_ASSERT_EQUAL_PTR(&arg1, cbA_args[0]);131 132 /* Entry should be cleared and removed */133 TEST_ASSERT_NULL(db1->pBlockingConnection);134 TEST_ASSERT_NULL(db1->pUnlockConnection);135 TEST_ASSERT_EQUAL_PTR(NULL, db1->xUnlockNotify);136 TEST_ASSERT_EQUAL_PTR(NULL, db1->pUnlockArg);137 TEST_ASSERT_NULL(db1->pNextBlocked);138 139 close_db(db1);140 close_db(blocker);141}142 143/* Test 3: Multiple listeners using the same callback are grouped into a single notification. */144void test_sqlite3ConnectionUnlocked_group_same_callback_once(void){145 sqlite3 *blocker = open_memdb();146 147 const int N = 5;148 sqlite3 *dbs[N];149 int args[N];150 int i;151 152 for(i=0;i<N;i++){153 dbs[i] = open_memdb();154 args[i] = 100 + i;155 sqlite3ConnectionBlocked(dbs[i], blocker);156 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(dbs[i], cbA, &args[i]));157 }158 159 sqlite3ConnectionUnlocked(blocker);160 161 TEST_ASSERT_EQUAL_INT(1, cbA_calls);162 TEST_ASSERT_EQUAL_INT(N, cbA_total_args);163 TEST_ASSERT_EQUAL_INT(N, cbA_last_n);164 165 for(i=0;i<N;i++){166 TEST_ASSERT_TRUE( contains_ptr(cbA_args, cbA_total_args, &args[i]) );167 TEST_ASSERT_NULL(dbs[i]->pBlockingConnection);168 TEST_ASSERT_NULL(dbs[i]->pUnlockConnection);169 TEST_ASSERT_EQUAL_PTR(NULL, dbs[i]->xUnlockNotify);170 TEST_ASSERT_EQUAL_PTR(NULL, dbs[i]->pUnlockArg);171 TEST_ASSERT_NULL(dbs[i]->pNextBlocked);172 }173 174 for(i=0;i<N;i++){175 close_db(dbs[i]);176 }177 close_db(blocker);178}179 180/* Test 4: Two distinct callback groups are invoked in order (A group, then B group). */181void test_sqlite3ConnectionUnlocked_two_groups_in_order(void){182 sqlite3 *blocker = open_memdb();183 184 sqlite3 *a1 = open_memdb();185 sqlite3 *b1 = open_memdb();186 sqlite3 *a2 = open_memdb();187 sqlite3 *b2 = open_memdb();188 189 int argA1 = 201, argA2 = 202;190 int argB1 = 301, argB2 = 302;191 192 /* Interleaved registration to create A-group then B-group in list order */193 sqlite3ConnectionBlocked(a1, blocker);194 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(a1, cbA, &argA1));195 196 sqlite3ConnectionBlocked(b1, blocker);197 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(b1, cbB, &argB1));198 199 sqlite3ConnectionBlocked(a2, blocker);200 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(a2, cbA, &argA2));201 202 sqlite3ConnectionBlocked(b2, blocker);203 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(b2, cbB, &argB2));204 205 sqlite3ConnectionUnlocked(blocker);206 207 /* Expect A group callback once, then B group callback once */208 TEST_ASSERT_EQUAL_INT(1, cbA_calls);209 TEST_ASSERT_EQUAL_INT(1, cbB_calls);210 TEST_ASSERT_EQUAL_INT(2, cbA_last_n);211 TEST_ASSERT_EQUAL_INT(2, cbB_last_n);212 TEST_ASSERT_EQUAL_INT(2, cbA_total_args);213 TEST_ASSERT_EQUAL_INT(2, cbB_total_args);214 215 TEST_ASSERT_TRUE( contains_ptr(cbA_args, cbA_total_args, &argA1) );216 TEST_ASSERT_TRUE( contains_ptr(cbA_args, cbA_total_args, &argA2) );217 TEST_ASSERT_TRUE( contains_ptr(cbB_args, cbB_total_args, &argB1) );218 TEST_ASSERT_TRUE( contains_ptr(cbB_args, cbB_total_args, &argB2) );219 220 /* Verify order of group callbacks: 'A' then 'B' */221 TEST_ASSERT_EQUAL_INT(2, cbLogLen);222 TEST_ASSERT_EQUAL_CHAR('A', cbLog[0]);223 TEST_ASSERT_EQUAL_CHAR('B', cbLog[1]);224 225 /* All entries should be cleared and removed */226 TEST_ASSERT_NULL(a1->pBlockingConnection); TEST_ASSERT_NULL(a1->pUnlockConnection); TEST_ASSERT_NULL(a1->xUnlockNotify); TEST_ASSERT_NULL(a1->pUnlockArg); TEST_ASSERT_NULL(a1->pNextBlocked);227 TEST_ASSERT_NULL(a2->pBlockingConnection); TEST_ASSERT_NULL(a2->pUnlockConnection); TEST_ASSERT_NULL(a2->xUnlockNotify); TEST_ASSERT_NULL(a2->pUnlockArg); TEST_ASSERT_NULL(a2->pNextBlocked);228 TEST_ASSERT_NULL(b1->pBlockingConnection); TEST_ASSERT_NULL(b1->pUnlockConnection); TEST_ASSERT_NULL(b1->xUnlockNotify); TEST_ASSERT_NULL(b1->pUnlockArg); TEST_ASSERT_NULL(b1->pNextBlocked);229 TEST_ASSERT_NULL(b2->pBlockingConnection); TEST_ASSERT_NULL(b2->pUnlockConnection); TEST_ASSERT_NULL(b2->xUnlockNotify); TEST_ASSERT_NULL(b2->pUnlockArg); TEST_ASSERT_NULL(b2->pNextBlocked);230 231 close_db(a1); close_db(a2); close_db(b1); close_db(b2);232 close_db(blocker);233}234 235/* Test 5: Many listeners (>16) with same callback to exercise dynamic array growth path. */236void test_sqlite3ConnectionUnlocked_dynamic_growth_many_listeners(void){237 sqlite3 *blocker = open_memdb();238 239 const int M = 25; /* > 16 to exceed aStatic[] size in sqlite3ConnectionUnlocked */240 sqlite3 *dbs[M];241 int args[M];242 int i;243 244 for(i=0;i<M;i++){245 dbs[i] = open_memdb();246 args[i] = 400 + i;247 sqlite3ConnectionBlocked(dbs[i], blocker);248 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(dbs[i], cbA, &args[i]));249 }250 251 sqlite3ConnectionUnlocked(blocker);252 253 TEST_ASSERT_EQUAL_INT(1, cbA_calls);254 TEST_ASSERT_EQUAL_INT(M, cbA_total_args);255 TEST_ASSERT_EQUAL_INT(M, cbA_last_n);256 257 for(i=0;i<M;i++){258 TEST_ASSERT_TRUE( contains_ptr(cbA_args, cbA_total_args, &args[i]) );259 TEST_ASSERT_NULL(dbs[i]->pBlockingConnection);260 TEST_ASSERT_NULL(dbs[i]->pUnlockConnection);261 TEST_ASSERT_EQUAL_PTR(NULL, dbs[i]->xUnlockNotify);262 TEST_ASSERT_EQUAL_PTR(NULL, dbs[i]->pUnlockArg);263 TEST_ASSERT_NULL(dbs[i]->pNextBlocked);264 }265 266 for(i=0;i<M;i++){267 close_db(dbs[i]);268 }269 close_db(blocker);270}271 272int main(void){273 UNITY_BEGIN();274 RUN_TEST(test_sqlite3ConnectionUnlocked_clears_blocking_without_notify);275 RUN_TEST(test_sqlite3ConnectionUnlocked_single_notify);276 RUN_TEST(test_sqlite3ConnectionUnlocked_group_same_callback_once);277 RUN_TEST(test_sqlite3ConnectionUnlocked_two_groups_in_order);278 RUN_TEST(test_sqlite3ConnectionUnlocked_dynamic_growth_many_listeners);279 return UNITY_END();280}281 282#else /* SQLITE_ENABLE_UNLOCK_NOTIFY not defined */283 284void setUp(void) {}285void tearDown(void) {}286 287void test_unlock_notify_feature_not_enabled(void){288 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not enabled in this build.");289}290 291int main(void){292 UNITY_BEGIN();293 RUN_TEST(test_unlock_notify_feature_not_enabled);294 return UNITY_END();295}296 297#endif /* SQLITE_ENABLE_UNLOCK_NOTIFY */