AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Dummy stress callback - not used by these tests but required by sqlite3PcacheOpen */7static int dummyStress(void *pStress, PgHdr *pPg){8 UNUSED_PARAMETER(pStress);9 UNUSED_PARAMETER(pPg);10 return SQLITE_OK;11}12 13static void *allocPCacheMem(void){14 int n = sqlite3PcacheSize();15 void *p = sqlite3_malloc(n);16 TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3_malloc for PCache failed");17 memset(p, 0, n);18 return p;19}20 21static void freePCacheMem(void *p){22 sqlite3_free(p);23}24 25void setUp(void) {26 /* Ensure the pcache subsystem is initialized for each test */27 int rc = sqlite3PcacheInitialize();28 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheInitialize failed");29}30 31void tearDown(void) {32 /* No global teardown needed specifically for these tests */33}34 35/* Helper: Try to fetch a page without creating it. If present, finish and release it.36** Returns 1 if page existed, 0 if not. */37static int pageExistsNoCreate(PCache *pCache, Pgno pgno){38 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, pgno, 0);39 if( pLower ){40 PgHdr *pHdr = sqlite3PcacheFetchFinish(pCache, pgno, pLower);41 /* Release immediately to avoid leaking a reference */42 sqlite3PcacheRelease(pHdr);43 return 1;44 }45 return 0;46}47 48/* Helper: Count and verify dirty list contents using sqlite3PcacheDirtyList */49static int countDirtyPagesAndCheckMax(PCache *pCache, Pgno maxPgno, int *pHas2){50 PgHdr *p;51 int cnt = 0;52 if( pHas2 ) *pHas2 = 0;53 p = sqlite3PcacheDirtyList(pCache);54 while( p ){55 cnt++;56 TEST_ASSERT_TRUE_MESSAGE(p->pgno <= maxPgno, "Dirty page with pgno greater than expected max found after truncate");57 if( pHas2 && p->pgno==2 ) *pHas2 = 1;58 p = p->pDirty;59 }60 return cnt;61}62 63void test_sqlite3PcacheTruncate_cleans_and_drops_pages_above_threshold(void){64 const int pageSize = 64;65 void *pMem = allocPCacheMem();66 PCache *pCache = (PCache*)pMem;67 68 int rc = sqlite3PcacheOpen(pageSize, 32, 0, dummyStress, 0, pCache);69 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheOpen failed");70 71 /* Create pages 1..6 and mark 2,4,6 as dirty */72 for(Pgno i=1; i<=6; i++){73 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, i, 3);74 TEST_ASSERT_NOT_NULL_MESSAGE(pLower, "Fetch(create) failed");75 PgHdr *pHdr = sqlite3PcacheFetchFinish(pCache, i, pLower);76 TEST_ASSERT_NOT_NULL(pHdr);77 if( i==2 || i==4 || i==6 ){78 sqlite3PcacheMakeDirty(pHdr);79 }80 sqlite3PcacheRelease(pHdr);81 }82 83 /* Truncate to pgno=3: pages >3 should be dropped; dirty pages >3 should be cleaned */84 sqlite3PcacheTruncate(pCache, 3);85 86 int has2 = 0;87 int dirtyCnt = countDirtyPagesAndCheckMax(pCache, 3, &has2);88 TEST_ASSERT_EQUAL_INT_MESSAGE(1, dirtyCnt, "Unexpected number of dirty pages after truncate to 3");89 TEST_ASSERT_TRUE_MESSAGE(has2, "Dirty page 2 expected to remain dirty after truncate to 3");90 91 /* Pages 4,5,6 should not exist anymore without creation */92 for(Pgno i=4; i<=6; i++){93 int exists = pageExistsNoCreate(pCache, i);94 TEST_ASSERT_EQUAL_INT_MESSAGE(0, exists, "Page >3 still present after truncate");95 }96 97 sqlite3PcacheClose(pCache);98 freePCacheMem(pMem);99}100 101void test_sqlite3PcacheTruncate_zeroes_page1_when_pgno_zero_and_ref_held(void){102 const int pageSize = 128;103 void *pMem = allocPCacheMem();104 PCache *pCache = (PCache*)pMem;105 106 int rc = sqlite3PcacheOpen(pageSize, 32, 0, dummyStress, 0, pCache);107 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheOpen failed");108 109 /* Create page 1 and keep a reference, fill with non-zero data */110 sqlite3_pcache_page *pLower1 = sqlite3PcacheFetch(pCache, 1, 3);111 TEST_ASSERT_NOT_NULL(pLower1);112 PgHdr *p1 = sqlite3PcacheFetchFinish(pCache, 1, pLower1);113 TEST_ASSERT_NOT_NULL(p1);114 memset(p1->pData, 0xAA, pageSize);115 116 /* Create pages 2 and 3, mark dirty, then release */117 for(Pgno i=2; i<=3; i++){118 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, i, 3);119 TEST_ASSERT_NOT_NULL(pLower);120 PgHdr *pHdr = sqlite3PcacheFetchFinish(pCache, i, pLower);121 sqlite3PcacheMakeDirty(pHdr);122 sqlite3PcacheRelease(pHdr);123 }124 125 /* Truncate to 0: page 1 should be zeroed (still referenced), others dropped */126 sqlite3PcacheTruncate(pCache, 0);127 128 /* Verify page 1 content is zeroed */129 unsigned char *z = (unsigned char*)p1->pData;130 for(int i=0; i<pageSize; i++){131 if( z[i] != 0 ){132 TEST_FAIL_MESSAGE("Page 1 data not zeroed by truncate to 0");133 }134 }135 136 /* No dirty pages should remain */137 int dirtyCnt = countDirtyPagesAndCheckMax(pCache, 0, 0);138 TEST_ASSERT_EQUAL_INT_MESSAGE(0, dirtyCnt, "Dirty list should be empty after truncate to 0");139 140 /* Pages 2 and 3 should no longer exist without creation */141 for(Pgno i=2; i<=3; i++){142 int exists = pageExistsNoCreate(pCache, i);143 TEST_ASSERT_EQUAL_INT_MESSAGE(0, exists, "Page present after truncate to 0");144 }145 146 sqlite3PcacheRelease(p1); /* release page 1 reference */147 sqlite3PcacheClose(pCache);148 freePCacheMem(pMem);149}150 151void test_sqlite3PcacheTruncate_no_dirty_pages_still_drops_above_pgno(void){152 const int pageSize = 64;153 void *pMem = allocPCacheMem();154 PCache *pCache = (PCache*)pMem;155 156 int rc = sqlite3PcacheOpen(pageSize, 16, 0, dummyStress, 0, pCache);157 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheOpen failed");158 159 /* Create clean pages 1..5 and release */160 for(Pgno i=1; i<=5; i++){161 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, i, 3);162 TEST_ASSERT_NOT_NULL(pLower);163 PgHdr *pHdr = sqlite3PcacheFetchFinish(pCache, i, pLower);164 /* Do not mark dirty; keep clean */165 sqlite3PcacheRelease(pHdr);166 }167 168 sqlite3PcacheTruncate(pCache, 3);169 170 /* Pages 4 and 5 should not be present without creation */171 for(Pgno i=4; i<=5; i++){172 int exists = pageExistsNoCreate(pCache, i);173 TEST_ASSERT_EQUAL_INT_MESSAGE(0, exists, "Clean page >pgno still present after truncate");174 }175 176 sqlite3PcacheClose(pCache);177 freePCacheMem(pMem);178}179 180int main(void){181 UNITY_BEGIN();182 RUN_TEST(test_sqlite3PcacheTruncate_cleans_and_drops_pages_above_threshold);183 RUN_TEST(test_sqlite3PcacheTruncate_zeroes_page1_when_pgno_zero_and_ref_held);184 RUN_TEST(test_sqlite3PcacheTruncate_no_dirty_pages_still_drops_above_pgno);185 return UNITY_END();186}