AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Wrapper provided in the module for the static function */7void test_pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag);8 9/* Helpers to build minimal PCache1/PgHdr1 environments */10 11static PCache1* makeCache(12 int nHash,13 PGroup **ppGroupOut,14 sqlite3_mutex **ppMutexOut,15 int **ppPurgeableOut16){17 /* Initialize SQLite (mutex subsystem) */18 sqlite3_initialize();19 20 PGroup *pGroup = (PGroup*)sqlite3_malloc(sizeof(*pGroup));21 TEST_ASSERT_NOT_NULL(pGroup);22 memset(pGroup, 0, sizeof(*pGroup));23 24 sqlite3_mutex *pMx = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);25 TEST_ASSERT_NOT_NULL(pMx);26 pGroup->mutex = pMx;27 28 PCache1 *pCache = (PCache1*)sqlite3_malloc(sizeof(*pCache));29 TEST_ASSERT_NOT_NULL(pCache);30 memset(pCache, 0, sizeof(*pCache));31 pCache->pGroup = pGroup;32 pCache->nHash = (unsigned int)nHash;33 34 PgHdr1 **apHash = (PgHdr1**)sqlite3_malloc(sizeof(PgHdr1*) * nHash);35 TEST_ASSERT_NOT_NULL(apHash);36 memset(apHash, 0, sizeof(PgHdr1*) * nHash);37 pCache->apHash = apHash;38 39 int *pnPurgeable = (int*)sqlite3_malloc(sizeof(int));40 TEST_ASSERT_NOT_NULL(pnPurgeable);41 *pnPurgeable = 0;42 pCache->pnPurgeable = pnPurgeable;43 44 if( ppGroupOut ) *ppGroupOut = pGroup;45 if( ppMutexOut ) *ppMutexOut = pMx;46 if( ppPurgeableOut ) *ppPurgeableOut = pnPurgeable;47 return pCache;48}49 50static void freeCache(PCache1 *pCache){51 if( pCache==NULL ) return;52 /* Free any pages on pFree list that our tests may have stashed */53 PgHdr1 *p = pCache->pFree;54 while( p ){55 PgHdr1 *pNext = p->pNext;56 /* For bulk-local pages we didn't allocate p->page.pBuf, so no free of pBuf */57 sqlite3_free(p);58 p = pNext;59 }60 if( pCache->apHash ){61 sqlite3_free(pCache->apHash);62 }63 if( pCache->pnPurgeable ){64 sqlite3_free(pCache->pnPurgeable);65 }66 if( pCache->pGroup ){67 if( pCache->pGroup->mutex ){68 sqlite3_mutex_free(pCache->pGroup->mutex);69 }70 sqlite3_free(pCache->pGroup);71 }72 sqlite3_free(pCache);73}74 75static PgHdr1* makePage(PCache1 *pCache, unsigned int key, int isBulkLocal, int allocBuf){76 PgHdr1 *p = (PgHdr1*)sqlite3_malloc(sizeof(*p));77 TEST_ASSERT_NOT_NULL(p);78 memset(p, 0, sizeof(*p));79 p->pCache = pCache;80 p->iKey = key;81 p->isBulkLocal = (u8)(isBulkLocal!=0);82 p->pNext = NULL;83 p->page.pBuf = allocBuf ? sqlite3_malloc(64) : NULL;84 /* We don't use p->page.pExtra in these tests */85 return p;86}87 88/* Unity setUp/tearDown */89void setUp(void) {90 /* Ensure SQLite core is initialized for mutex/malloc */91 sqlite3_initialize();92}93void tearDown(void) {94 /* No global teardown needed */95}96 97/* Tests */98 99/* Remove single element bucket without freeing */100void test_pcache1RemoveFromHash_single_no_free(void){101 PGroup *pGroup = NULL;102 sqlite3_mutex *pMx = NULL;103 int *pnPurgeable = NULL;104 PCache1 *pCache = makeCache(8, &pGroup, &pMx, &pnPurgeable);105 106 unsigned int key = 3;107 PgHdr1 *p = makePage(pCache, key, /*isBulkLocal=*/0, /*allocBuf=*/1);108 unsigned int h = key % pCache->nHash;109 pCache->apHash[h] = p;110 pCache->nPage = 1;111 *pCache->pnPurgeable = 5;112 113 sqlite3_mutex_enter(pGroup->mutex);114 test_pcache1RemoveFromHash(p, /*freeFlag=*/0);115 sqlite3_mutex_leave(pGroup->mutex);116 117 TEST_ASSERT_NULL(pCache->apHash[h]);118 TEST_ASSERT_EQUAL_INT(0, pCache->nPage);119 TEST_ASSERT_EQUAL_INT(5, *pCache->pnPurgeable);120 TEST_ASSERT_NULL(pCache->pFree);121 122 /* Cleanup: page not freed by function since freeFlag=0 */123 sqlite3_free(p->page.pBuf);124 sqlite3_free(p);125 freeCache(pCache);126}127 128/* Remove middle of a 3-element chain without freeing */129void test_pcache1RemoveFromHash_middle_of_chain_no_free(void){130 PGroup *pGroup = NULL;131 sqlite3_mutex *pMx = NULL;132 PCache1 *pCache = makeCache(4, &pGroup, &pMx, NULL);133 134 unsigned int base = 1;135 unsigned int kA = base;136 unsigned int kB = base + pCache->nHash;137 unsigned int kC = base + 2*pCache->nHash;138 unsigned int h = base % pCache->nHash;139 140 PgHdr1 *A = makePage(pCache, kA, 0, 1);141 PgHdr1 *B = makePage(pCache, kB, 0, 1);142 PgHdr1 *C = makePage(pCache, kC, 0, 1);143 144 pCache->apHash[h] = A;145 A->pNext = B;146 B->pNext = C;147 C->pNext = NULL;148 pCache->nPage = 3;149 150 sqlite3_mutex_enter(pGroup->mutex);151 test_pcache1RemoveFromHash(B, 0);152 sqlite3_mutex_leave(pGroup->mutex);153 154 TEST_ASSERT_EQUAL_PTR(A, pCache->apHash[h]);155 TEST_ASSERT_EQUAL_PTR(C, A->pNext);156 TEST_ASSERT_NULL(C->pNext);157 TEST_ASSERT_EQUAL_INT(2, pCache->nPage);158 159 /* Cleanup */160 sqlite3_free(A->page.pBuf);161 sqlite3_free(B->page.pBuf);162 sqlite3_free(C->page.pBuf);163 sqlite3_free(A);164 sqlite3_free(B);165 sqlite3_free(C);166 freeCache(pCache);167}168 169/* Remove head of chain without freeing */170void test_pcache1RemoveFromHash_head_of_chain_no_free(void){171 PGroup *pGroup = NULL;172 sqlite3_mutex *pMx = NULL;173 PCache1 *pCache = makeCache(4, &pGroup, &pMx, NULL);174 175 unsigned int base = 2;176 unsigned int kH = base;177 unsigned int kX = base + pCache->nHash;178 unsigned int h = base % pCache->nHash;179 180 PgHdr1 *H = makePage(pCache, kH, 0, 1);181 PgHdr1 *X = makePage(pCache, kX, 0, 1);182 183 pCache->apHash[h] = H;184 H->pNext = X;185 X->pNext = NULL;186 pCache->nPage = 2;187 188 sqlite3_mutex_enter(pGroup->mutex);189 test_pcache1RemoveFromHash(H, 0);190 sqlite3_mutex_leave(pGroup->mutex);191 192 TEST_ASSERT_EQUAL_PTR(X, pCache->apHash[h]);193 TEST_ASSERT_NULL(X->pNext);194 TEST_ASSERT_EQUAL_INT(1, pCache->nPage);195 196 /* Cleanup */197 sqlite3_free(H->page.pBuf);198 sqlite3_free(X->page.pBuf);199 sqlite3_free(H);200 sqlite3_free(X);201 freeCache(pCache);202}203 204/* Remove tail of chain without freeing */205void test_pcache1RemoveFromHash_tail_of_chain_no_free(void){206 PGroup *pGroup = NULL;207 sqlite3_mutex *pMx = NULL;208 PCache1 *pCache = makeCache(4, &pGroup, &pMx, NULL);209 210 unsigned int base = 0;211 unsigned int kA = base;212 unsigned int kB = base + pCache->nHash;213 unsigned int h = base % pCache->nHash;214 215 PgHdr1 *A = makePage(pCache, kA, 0, 1);216 PgHdr1 *B = makePage(pCache, kB, 0, 1);217 218 pCache->apHash[h] = A;219 A->pNext = B;220 B->pNext = NULL;221 pCache->nPage = 2;222 223 sqlite3_mutex_enter(pGroup->mutex);224 test_pcache1RemoveFromHash(B, 0);225 sqlite3_mutex_leave(pGroup->mutex);226 227 TEST_ASSERT_EQUAL_PTR(A, pCache->apHash[h]);228 TEST_ASSERT_NULL(A->pNext);229 TEST_ASSERT_EQUAL_INT(1, pCache->nPage);230 231 /* Cleanup */232 sqlite3_free(A->page.pBuf);233 sqlite3_free(B->page.pBuf);234 sqlite3_free(A);235 sqlite3_free(B);236 freeCache(pCache);237}238 239/* Remove with freeFlag=1 where page is bulk-local: should go to pFree and pnPurgeable decremented */240void test_pcache1RemoveFromHash_free_bulkLocal(void){241 PGroup *pGroup = NULL;242 sqlite3_mutex *pMx = NULL;243 int *pnPurgeable = NULL;244 PCache1 *pCache = makeCache(8, &pGroup, &pMx, &pnPurgeable);245 246 unsigned int key = 5;247 unsigned int h = key % pCache->nHash;248 PgHdr1 *p = makePage(pCache, key, /*isBulkLocal=*/1, /*allocBuf=*/0);249 pCache->apHash[h] = p;250 pCache->nPage = 1;251 *pnPurgeable = 7;252 253 sqlite3_mutex_enter(pGroup->mutex);254 test_pcache1RemoveFromHash(p, /*freeFlag=*/1);255 sqlite3_mutex_leave(pGroup->mutex);256 257 TEST_ASSERT_NULL(pCache->apHash[h]);258 TEST_ASSERT_EQUAL_INT(0, pCache->nPage);259 TEST_ASSERT_EQUAL_INT(6, *pnPurgeable);260 TEST_ASSERT_EQUAL_PTR(p, pCache->pFree);261 TEST_ASSERT_NULL(p->pNext); /* was added to empty free list */262 263 /* Cleanup: remove from pFree and free node */264 pCache->pFree = NULL;265 sqlite3_free(p);266 freeCache(pCache);267}268 269/* Remove with freeFlag=1 where page is NOT bulk-local: should free buffer, not add to pFree, pnPurgeable decremented */270void test_pcache1RemoveFromHash_free_nonBulkLocal(void){271 PGroup *pGroup = NULL;272 sqlite3_mutex *pMx = NULL;273 int *pnPurgeable = NULL;274 PCache1 *pCache = makeCache(8, &pGroup, &pMx, &pnPurgeable);275 276 /* Prepare a dummy existing free-list head to ensure it remains unchanged */277 PgHdr1 *dummyFree = makePage(pCache, /*key*/123, /*isBulkLocal=*/1, /*allocBuf=*/0);278 dummyFree->pNext = NULL;279 pCache->pFree = dummyFree;280 281 unsigned int key = 6;282 unsigned int h = key % pCache->nHash;283 PgHdr1 *p = makePage(pCache, key, /*isBulkLocal=*/0, /*allocBuf=*/1);284 pCache->apHash[h] = p;285 pCache->nPage = 1;286 *pnPurgeable = 3;287 288 sqlite3_mutex_enter(pGroup->mutex);289 test_pcache1RemoveFromHash(p, /*freeFlag=*/1);290 sqlite3_mutex_leave(pGroup->mutex);291 292 TEST_ASSERT_NULL(pCache->apHash[h]);293 TEST_ASSERT_EQUAL_INT(0, pCache->nPage);294 TEST_ASSERT_EQUAL_INT(2, *pnPurgeable);295 /* Non-bulk pages should not be added to pFree; existing pFree remains unchanged */296 TEST_ASSERT_EQUAL_PTR(dummyFree, pCache->pFree);297 298 /* Cleanup: 'p->page.pBuf' should have been freed by pcache1FreePage, so only free 'p' itself */299 sqlite3_free(p);300 sqlite3_free(dummyFree);301 pCache->pFree = NULL;302 freeCache(pCache);303}304 305int main(void){306 UNITY_BEGIN();307 RUN_TEST(test_pcache1RemoveFromHash_single_no_free);308 RUN_TEST(test_pcache1RemoveFromHash_middle_of_chain_no_free);309 RUN_TEST(test_pcache1RemoveFromHash_head_of_chain_no_free);310 RUN_TEST(test_pcache1RemoveFromHash_tail_of_chain_no_free);311 RUN_TEST(test_pcache1RemoveFromHash_free_bulkLocal);312 RUN_TEST(test_pcache1RemoveFromHash_free_nonBulkLocal);313 return UNITY_END();314}