Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_notify_sqlite3_unlock_notify.c287 linesDownload Raw Back to tests
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}