AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global callback tracking for two distinct callbacks */7static int cbA_call_count;8static int cbA_last_n;9static void* cbA_last_args[128];10 11static int cbB_call_count;12static int cbB_last_n;13static void* cbB_last_args[128];14 15static void test_cbA(void **ap, int n){16 cbA_call_count++;17 cbA_last_n = n;18 if( n > (int)(sizeof(cbA_last_args)/sizeof(cbA_last_args[0])) ) n = (int)(sizeof(cbA_last_args)/sizeof(cbA_last_args[0]));19 for(int i=0;i<n;i++){20 cbA_last_args[i] = ap[i];21 }22}23 24static void test_cbB(void **ap, int n){25 cbB_call_count++;26 cbB_last_n = n;27 if( n > (int)(sizeof(cbB_last_args)/sizeof(cbB_last_args[0])) ) n = (int)(sizeof(cbB_last_args)/sizeof(cbB_last_args[0]));28 for(int i=0;i<n;i++){29 cbB_last_args[i] = ap[i];30 }31}32 33/* Helpers */34static int array_contains(void **ap, int n, void *p){35 for(int i=0;i<n;i++){36 if( ap[i]==p ) return 1;37 }38 return 0;39}40 41void setUp(void) {42 cbA_call_count = 0;43 cbA_last_n = 0;44 memset(cbA_last_args, 0, sizeof(cbA_last_args));45 46 cbB_call_count = 0;47 cbB_last_n = 0;48 memset(cbB_last_args, 0, sizeof(cbB_last_args));49}50 51void tearDown(void) {52 /* Nothing */53}54 55/* Test: Immediate callback when there is no blocking connection */56void test_sqlite3_unlock_notify_immediate_callback(void){57#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY58 sqlite3 *db = 0;59 int rc = sqlite3_open(":memory:", &db);60 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);61 TEST_ASSERT_NOT_NULL(db);62 63 /* Ensure no blocker */64 db->pBlockingConnection = 0;65 66 int argVal = 123;67 void *pArg = &argVal;68 69 rc = sqlite3_unlock_notify(db, test_cbA, pArg);70 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);71 72 /* Callback should have been invoked immediately with one argument */73 TEST_ASSERT_EQUAL_INT(1, cbA_call_count);74 TEST_ASSERT_EQUAL_INT(1, cbA_last_n);75 TEST_ASSERT(cbA_last_args[0] == pArg);76 77 /* No registration should be left on db */78 TEST_ASSERT_NULL(db->pUnlockConnection);79 TEST_ASSERT_NULL(db->xUnlockNotify);80 TEST_ASSERT_NULL(db->pUnlockArg);81 82 sqlite3_close(db);83#else84 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not defined");85#endif86}87 88/* Test: Register with blocker and invoke later on unlock */89void test_sqlite3_unlock_notify_register_and_invoke_on_unlock(void){90#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY91 sqlite3 *dbBlocked = 0;92 sqlite3 *dbBlocker = 0;93 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbBlocked));94 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbBlocker));95 TEST_ASSERT_NOT_NULL(dbBlocked);96 TEST_ASSERT_NOT_NULL(dbBlocker);97 98 /* Simulate that dbBlocked is blocked by dbBlocker */99 dbBlocked->pBlockingConnection = dbBlocker;100 101 int arg = 456;102 void *pArg = &arg;103 104 int rc = sqlite3_unlock_notify(dbBlocked, test_cbA, pArg);105 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);106 107 /* The registration should be recorded on dbBlocked */108 TEST_ASSERT(dbBlocked->pUnlockConnection == dbBlocker);109 TEST_ASSERT(dbBlocked->xUnlockNotify == test_cbA);110 TEST_ASSERT(dbBlocked->pUnlockArg == pArg);111 112 /* No callback yet */113 TEST_ASSERT_EQUAL_INT(0, cbA_call_count);114 115 /* Now simulate that dbBlocker transaction finished */116 sqlite3ConnectionUnlocked(dbBlocker);117 118 /* Callback should have fired once with one argument, our pArg */119 TEST_ASSERT_EQUAL_INT(1, cbA_call_count);120 TEST_ASSERT_EQUAL_INT(1, cbA_last_n);121 TEST_ASSERT(cbA_last_args[0] == pArg);122 123 /* Registration should be cleared by unlock processing */124 TEST_ASSERT_NULL(dbBlocked->pUnlockConnection);125 TEST_ASSERT_NULL(dbBlocked->xUnlockNotify);126 TEST_ASSERT_NULL(dbBlocked->pUnlockArg);127 TEST_ASSERT_NULL(dbBlocked->pBlockingConnection);128 129 sqlite3_close(dbBlocked);130 sqlite3_close(dbBlocker);131#else132 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not defined");133#endif134}135 136/* Test: Cancellation clears prior registration and does not invoke callback */137void test_sqlite3_unlock_notify_cancel_registration(void){138#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY139 sqlite3 *dbBlocked = 0;140 sqlite3 *dbBlocker = 0;141 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbBlocked));142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbBlocker));143 144 /* Block dbBlocked on dbBlocker and register */145 dbBlocked->pBlockingConnection = dbBlocker;146 147 int arg = 789;148 void *pArg = &arg;149 150 int rc = sqlite3_unlock_notify(dbBlocked, test_cbA, pArg);151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);152 153 /* Now cancel the registration */154 rc = sqlite3_unlock_notify(dbBlocked, 0, 0);155 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);156 157 /* Fields should be cleared */158 TEST_ASSERT_NULL(dbBlocked->pUnlockConnection);159 TEST_ASSERT_NULL(dbBlocked->xUnlockNotify);160 TEST_ASSERT_NULL(dbBlocked->pUnlockArg);161 TEST_ASSERT_NULL(dbBlocked->pBlockingConnection);162 163 /* Simulate unlock of blocker - no callback should be invoked */164 sqlite3ConnectionUnlocked(dbBlocker);165 TEST_ASSERT_EQUAL_INT(0, cbA_call_count);166 167 sqlite3_close(dbBlocked);168 sqlite3_close(dbBlocker);169#else170 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not defined");171#endif172}173 174/* Test: Deadlock detection returns SQLITE_LOCKED and leaves fields unchanged */175void test_sqlite3_unlock_notify_deadlock(void){176#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY177 sqlite3 *dbA = 0;178 sqlite3 *dbB = 0;179 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbA));180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &dbB));181 182 /* Create mutual blocking relation */183 dbA->pBlockingConnection = dbB;184 dbB->pBlockingConnection = dbA;185 186 int argB = 1, argA = 2;187 188 /* First, register for dbB. This should succeed and set B->pUnlockConnection=A */189 int rc = sqlite3_unlock_notify(dbB, test_cbA, &argB);190 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);191 TEST_ASSERT(dbB->pUnlockConnection == dbA);192 TEST_ASSERT(dbB->xUnlockNotify == test_cbA);193 194 /* Now, attempt to register for dbA - should detect deadlock */195 rc = sqlite3_unlock_notify(dbA, test_cbA, &argA);196 TEST_ASSERT_EQUAL_INT(SQLITE_LOCKED, rc);197 198 /* No callbacks should have fired yet */199 TEST_ASSERT_EQUAL_INT(0, cbA_call_count);200 201 /* dbA should not have had registration fields set */202 TEST_ASSERT_NULL(dbA->pUnlockConnection);203 TEST_ASSERT_NULL(dbA->xUnlockNotify);204 TEST_ASSERT_NULL(dbA->pUnlockArg);205 206 /* Cleanup: cancel B's registration, then close */207 rc = sqlite3_unlock_notify(dbB, 0, 0);208 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);209 210 sqlite3_close(dbA);211 sqlite3_close(dbB);212#else213 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not defined");214#endif215}216 217/* Test: Grouping by callback pointer - two groups receive aggregated arguments */218void test_sqlite3_unlock_notify_grouping_by_callback(void){219#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY220 sqlite3 *blocker = 0;221 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &blocker));222 223 /* Create 3 blocked with cbA and 2 blocked with cbB */224 sqlite3 *a1=0,*a2=0,*a3=0,*b1=0,*b2=0;225 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &a1));226 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &a2));227 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &a3));228 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &b1));229 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &b2));230 231 int a1v=11,a2v=22,a3v=33,b1v=44,b2v=55;232 233 a1->pBlockingConnection = blocker;234 a2->pBlockingConnection = blocker;235 a3->pBlockingConnection = blocker;236 b1->pBlockingConnection = blocker;237 b2->pBlockingConnection = blocker;238 239 /* Register them. Order interleaves callback groups to verify grouping logic. */240 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(a1, test_cbA, &a1v));241 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(b1, test_cbB, &b1v));242 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(a2, test_cbA, &a2v));243 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(b2, test_cbB, &b2v));244 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_unlock_notify(a3, test_cbA, &a3v));245 246 /* No callbacks yet */247 TEST_ASSERT_EQUAL_INT(0, cbA_call_count);248 TEST_ASSERT_EQUAL_INT(0, cbB_call_count);249 250 /* Unlock blocker - should trigger two callback invocations, one per callback pointer */251 sqlite3ConnectionUnlocked(blocker);252 253 TEST_ASSERT_EQUAL_INT(1, cbA_call_count);254 TEST_ASSERT_EQUAL_INT(1, cbB_call_count);255 256 /* Check counts and membership of argument lists */257 TEST_ASSERT_EQUAL_INT(3, cbA_last_n);258 TEST_ASSERT_EQUAL_INT(2, cbB_last_n);259 260 TEST_ASSERT(array_contains(cbA_last_args, cbA_last_n, &a1v));261 TEST_ASSERT(array_contains(cbA_last_args, cbA_last_n, &a2v));262 TEST_ASSERT(array_contains(cbA_last_args, cbA_last_n, &a3v));263 264 TEST_ASSERT(array_contains(cbB_last_args, cbB_last_n, &b1v));265 TEST_ASSERT(array_contains(cbB_last_args, cbB_last_n, &b2v));266 267 /* Cleanup */268 sqlite3_close(a1);269 sqlite3_close(a2);270 sqlite3_close(a3);271 sqlite3_close(b1);272 sqlite3_close(b2);273 sqlite3_close(blocker);274#else275 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_UNLOCK_NOTIFY not defined");276#endif277}278 279int main(void) {280 UNITY_BEGIN();281 RUN_TEST(test_sqlite3_unlock_notify_immediate_callback);282 RUN_TEST(test_sqlite3_unlock_notify_register_and_invoke_on_unlock);283 RUN_TEST(test_sqlite3_unlock_notify_cancel_registration);284 RUN_TEST(test_sqlite3_unlock_notify_deadlock);285 RUN_TEST(test_sqlite3_unlock_notify_grouping_by_callback);286 return UNITY_END();287}