AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Helper destructor counters */8static int d1_count = 0;9static int d2_count = 0;10static int d3_count = 0;11static void *last_dtor_ptr = NULL;12 13static void d1(void *p){14 d1_count++;15 last_dtor_ptr = p;16}17static void d2(void *p){18 d2_count++;19 last_dtor_ptr = p;20}21static void d3(void *p){22 d3_count++;23 last_dtor_ptr = p;24}25 26/* Helper: find a DbClientData entry by name */27static DbClientData* findEntry(sqlite3 *db, const char *zName){28 DbClientData *p;29 for(p=db->pDbData; p; p=p->pNext){30 if(0==strcmp(p->zName, zName)) return p;31 }32 return NULL;33}34 35/* Memory allocator override to simulate OOM */36static sqlite3_mem_methods gOrigMem;37static sqlite3_mem_methods gFailMem;38static int gFailAlloc = 0;39 40static int failingInit(void *pAppData){41 /* Initialize the original allocator */42 if( gOrigMem.xInit ){43 return gOrigMem.xInit(gOrigMem.pAppData);44 }45 return SQLITE_OK;46}47static void failingShutdown(void *pAppData){48 if( gOrigMem.xShutdown ){49 gOrigMem.xShutdown(gOrigMem.pAppData);50 }51}52static void *failingMalloc(int n){53 if( gFailAlloc ) return NULL;54 return gOrigMem.xMalloc ? gOrigMem.xMalloc(n) : NULL;55}56static void failingFree(void *p){57 if( gOrigMem.xFree ) gOrigMem.xFree(p);58}59static void *failingRealloc(void *p, int n){60 if( gFailAlloc ) return NULL;61 return gOrigMem.xRealloc ? gOrigMem.xRealloc(p, n) : NULL;62}63static int failingSize(void *p){64 return gOrigMem.xSize ? gOrigMem.xSize(p) : 0;65}66static int failingRoundup(int n){67 return gOrigMem.xRoundup ? gOrigMem.xRoundup(n) : n;68}69 70static int installFailingAllocator(void){71 int rc;72 sqlite3_shutdown();73 rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &gOrigMem);74 if( rc!=SQLITE_OK ) return rc;75 76 memset(&gFailMem, 0, sizeof(gFailMem));77 gFailMem.xMalloc = failingMalloc;78 gFailMem.xFree = failingFree;79 gFailMem.xRealloc = failingRealloc;80 gFailMem.xSize = failingSize;81 gFailMem.xRoundup = failingRoundup;82 gFailMem.xInit = failingInit;83 gFailMem.xShutdown = failingShutdown;84 gFailMem.pAppData = gOrigMem.pAppData;85 86 rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &gFailMem);87 if( rc!=SQLITE_OK ) return rc;88 rc = sqlite3_initialize();89 return rc;90}91 92static void uninstallAllocator(void){93 sqlite3_shutdown();94 sqlite3_config(SQLITE_CONFIG_MALLOC, &gOrigMem);95 sqlite3_initialize();96}97 98/* Unity setup/teardown */99void setUp(void) {100 d1_count = d2_count = d3_count = 0;101 last_dtor_ptr = NULL;102 gFailAlloc = 0;103}104void tearDown(void) {105 /* nothing */106}107 108/* Tests */109 110void test_sqlite3_set_clientdata_insert_new_and_find(void){111 sqlite3 *db = NULL;112 int rc = sqlite3_open(":memory:", &db);113 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);114 TEST_ASSERT_NOT_NULL(db);115 TEST_ASSERT_NULL(db->pDbData);116 117 int payload = 123;118 rc = sqlite3_set_clientdata(db, "k1", &payload, NULL);119 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);120 121 DbClientData *p = findEntry(db, "k1");122 TEST_ASSERT_NOT_NULL(p);123 TEST_ASSERT_EQUAL_PTR(&payload, p->pData);124 TEST_ASSERT_NULL(p->xDestructor);125 TEST_ASSERT_EQUAL_INT(0, strcmp("k1", p->zName));126 127 sqlite3_close(db);128}129 130void test_sqlite3_set_clientdata_replace_calls_old_destructor(void){131 sqlite3 *db = NULL;132 int rc = sqlite3_open(":memory:", &db);133 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);134 135 int oldPayload = 0x11;136 int newPayload = 0x22;137 138 /* Insert with destructor d1 (should not be called on insert) */139 rc = sqlite3_set_clientdata(db, "k", &oldPayload, d1);140 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);141 TEST_ASSERT_EQUAL_INT(0, d1_count);142 143 /* Replace: must call old destructor (d1) with oldPayload */144 rc = sqlite3_set_clientdata(db, "k", &newPayload, d2);145 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);146 TEST_ASSERT_EQUAL_INT(1, d1_count);147 TEST_ASSERT_EQUAL_PTR(&oldPayload, last_dtor_ptr);148 TEST_ASSERT_EQUAL_INT(0, d2_count);149 150 /* Verify new data and destructor stored */151 DbClientData *p = findEntry(db, "k");152 TEST_ASSERT_NOT_NULL(p);153 TEST_ASSERT_EQUAL_PTR(&newPayload, p->pData);154 TEST_ASSERT_EQUAL_PTR(d2, p->xDestructor);155 156 sqlite3_close(db);157}158 159void test_sqlite3_set_clientdata_insert_with_null_destructor_then_replace_no_dtor_call_on_first(void){160 sqlite3 *db = NULL;161 int rc = sqlite3_open(":memory:", &db);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 164 int oldPayload = 0x100;165 int newPayload = 0x200;166 167 /* Insert with NULL destructor (should not call anything) */168 rc = sqlite3_set_clientdata(db, "knull", &oldPayload, NULL);169 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);170 TEST_ASSERT_EQUAL_INT(0, d1_count);171 TEST_ASSERT_EQUAL_INT(0, d2_count);172 173 /* Replace: since old destructor is NULL, no destructor call should occur here */174 rc = sqlite3_set_clientdata(db, "knull", &newPayload, d1);175 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);176 TEST_ASSERT_EQUAL_INT(0, d1_count);177 178 DbClientData *p = findEntry(db, "knull");179 TEST_ASSERT_NOT_NULL(p);180 TEST_ASSERT_EQUAL_PTR(&newPayload, p->pData);181 TEST_ASSERT_EQUAL_PTR(d1, p->xDestructor);182 183 sqlite3_close(db);184}185 186void test_sqlite3_set_clientdata_remove_existing_calls_destructor_and_unlinks(void){187 sqlite3 *db = NULL;188 int rc = sqlite3_open(":memory:", &db);189 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);190 191 int payload = 0x33;192 rc = sqlite3_set_clientdata(db, "rm", &payload, d2);193 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);194 195 /* Remove: pData==NULL triggers deletion; old destructor (d2) is called */196 rc = sqlite3_set_clientdata(db, "rm", NULL, d3 /* irrelevant */);197 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);198 TEST_ASSERT_EQUAL_INT(1, d2_count);199 TEST_ASSERT_EQUAL_PTR(&payload, last_dtor_ptr);200 TEST_ASSERT_EQUAL_INT(0, d3_count); /* the passed-in destructor is not used on removal */201 202 /* Ensure entry is gone */203 DbClientData *p = findEntry(db, "rm");204 TEST_ASSERT_NULL(p);205 206 sqlite3_close(db);207}208 209void test_sqlite3_set_clientdata_remove_nonexistent_is_noop(void){210 sqlite3 *db = NULL;211 int rc = sqlite3_open(":memory:", &db);212 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);213 214 /* Ensure not present */215 TEST_ASSERT_NULL(findEntry(db, "absent"));216 217 /* Removal of non-existent should be OK and not call destructor */218 rc = sqlite3_set_clientdata(db, "absent", NULL, d1);219 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);220 TEST_ASSERT_EQUAL_INT(0, d1_count);221 222 sqlite3_close(db);223}224 225void test_sqlite3_set_clientdata_multiple_entries_are_independent(void){226 sqlite3 *db = NULL;227 int rc = sqlite3_open(":memory:", &db);228 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);229 230 int pa = 1, pb = 2, pb2 = 3;231 232 rc = sqlite3_set_clientdata(db, "A", &pa, d1);233 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);234 rc = sqlite3_set_clientdata(db, "B", &pb, d2);235 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);236 237 /* Remove A: should call d1, leave B intact */238 rc = sqlite3_set_clientdata(db, "A", NULL, d3 /* irrelevant */);239 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);240 TEST_ASSERT_EQUAL_INT(1, d1_count);241 TEST_ASSERT_EQUAL_INT(0, d2_count);242 TEST_ASSERT_NULL(findEntry(db, "A"));243 DbClientData *pB = findEntry(db, "B");244 TEST_ASSERT_NOT_NULL(pB);245 TEST_ASSERT_EQUAL_PTR(&pb, pB->pData);246 TEST_ASSERT_EQUAL_PTR(d2, pB->xDestructor);247 248 /* Replace B: should call d2 and update to pb2 and destructor d3 */249 rc = sqlite3_set_clientdata(db, "B", &pb2, d3);250 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);251 TEST_ASSERT_EQUAL_INT(1, d2_count);252 pB = findEntry(db, "B");253 TEST_ASSERT_NOT_NULL(pB);254 TEST_ASSERT_EQUAL_PTR(&pb2, pB->pData);255 TEST_ASSERT_EQUAL_PTR(d3, pB->xDestructor);256 257 sqlite3_close(db);258}259 260void test_sqlite3_set_clientdata_allocation_failure_returns_nomem_and_calls_destructor(void){261 int rc = installFailingAllocator();262 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);263 264 sqlite3 *db = NULL;265 rc = sqlite3_open(":memory:", &db);266 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);267 268 /* Ensure name doesn't exist */269 TEST_ASSERT_NULL(findEntry(db, "oom"));270 271 /* Cause malloc to fail only for the following call */272 gFailAlloc = 1;273 int payload = 0x999;274 rc = sqlite3_set_clientdata(db, "oom", &payload, d1);275 gFailAlloc = 0;276 277 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, rc);278 /* Destructor should be called on the provided payload because allocation failed */279 TEST_ASSERT_EQUAL_INT(1, d1_count);280 TEST_ASSERT_EQUAL_PTR(&payload, last_dtor_ptr);281 282 /* Ensure entry was not added */283 DbClientData *p = findEntry(db, "oom");284 TEST_ASSERT_NULL(p);285 286 sqlite3_close(db);287 uninstallAllocator();288}289 290/* main */291int main(void){292 UNITY_BEGIN();293 RUN_TEST(test_sqlite3_set_clientdata_insert_new_and_find);294 RUN_TEST(test_sqlite3_set_clientdata_replace_calls_old_destructor);295 RUN_TEST(test_sqlite3_set_clientdata_insert_with_null_destructor_then_replace_no_dtor_call_on_first);296 RUN_TEST(test_sqlite3_set_clientdata_remove_existing_calls_destructor_and_unlinks);297 RUN_TEST(test_sqlite3_set_clientdata_remove_nonexistent_is_noop);298 RUN_TEST(test_sqlite3_set_clientdata_multiple_entries_are_independent);299 RUN_TEST(test_sqlite3_set_clientdata_allocation_failure_returns_nomem_and_calls_destructor);300 return UNITY_END();301}