AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdint.h>6 7/*8** Test suite for: int sqlite3PcacheSetPageSize(PCache *pCache, int szPage)9**10** Strategy:11** - Create a valid PCache using sqlite3PcacheOpen (using size returned by sqlite3PcacheSize).12** - Swap sqlite3GlobalConfig.pcache2 with a stub implementation to capture and verify13** xCreate, xCachesize, and xDestroy calls made by sqlite3PcacheSetPageSize.14** - Validate correct handling of success, OOM, and numberOfCachePages behavior.15*/16 17/* ---- Stub pcache2 machinery ---- */18static sqlite3_pcache_methods2 g_savedMethods;19static int g_stubInstalled = 0;20 21/* Captured call info */22static int g_xCreate_calls = 0;23static int g_xCreate_last_szPage = 0;24static int g_xCreate_last_szExtra = 0;25static int g_xCreate_last_bPurgeable = -1;26static int g_xCachesize_calls = 0;27static int g_xCachesize_last = -1;28static int g_xDestroy_calls = 0;29static sqlite3_pcache *g_xDestroy_last_arg = 0;30static int g_xCreate_return_null = 0;31static int g_xPagecount_return = 123; /* Sentinel for verifying handle switch */32 33/* Provide a stable, lifetime-long pointer to return from stub xCreate. */34static union { char z[16]; void *align; } g_stubPcacheObj1;35 36static sqlite3_pcache *stub_xCreate(int szPage, int szExtra, int bPurgeable){37 g_xCreate_calls++;38 g_xCreate_last_szPage = szPage;39 g_xCreate_last_szExtra = szExtra;40 g_xCreate_last_bPurgeable = bPurgeable;41 if( g_xCreate_return_null ) return 0;42 return (sqlite3_pcache*)(void*)&g_stubPcacheObj1;43}44static void stub_xCachesize(sqlite3_pcache *p, int nCachesize){45 (void)p;46 g_xCachesize_calls++;47 g_xCachesize_last = nCachesize;48}49static int stub_xPagecount(sqlite3_pcache *p){50 (void)p;51 return g_xPagecount_return;52}53static sqlite3_pcache_page *stub_xFetch(sqlite3_pcache *p, unsigned k, int cf){54 (void)p; (void)k; (void)cf; return 0;55}56static void stub_xUnpin(sqlite3_pcache *p, sqlite3_pcache_page *pg, int d){57 (void)p; (void)pg; (void)d;58}59static void stub_xRekey(sqlite3_pcache *p, sqlite3_pcache_page *pg, unsigned o, unsigned n){60 (void)p; (void)pg; (void)o; (void)n;61}62static void stub_xTruncate(sqlite3_pcache *p, unsigned lim){ (void)p; (void)lim; }63static void stub_xDestroy(sqlite3_pcache *p){ g_xDestroy_calls++; g_xDestroy_last_arg = p; }64static void stub_xShrink(sqlite3_pcache *p){ (void)p; }65static int stub_xInit(void *pArg){ (void)pArg; return SQLITE_OK; }66static void stub_xShutdown(void *pArg){ (void)pArg; }67 68static void installStub(void){69 if( !g_stubInstalled ){70 g_savedMethods = sqlite3GlobalConfig.pcache2;71 sqlite3_pcache_methods2 m;72 memset(&m, 0, sizeof(m));73 m.iVersion = 1;74 m.pArg = 0;75 m.xInit = stub_xInit; /* not used here */76 m.xShutdown = stub_xShutdown;/* not used here */77 m.xCreate = stub_xCreate;78 m.xCachesize = stub_xCachesize;79 m.xPagecount = stub_xPagecount;80 m.xFetch = stub_xFetch;81 m.xUnpin = stub_xUnpin;82 m.xRekey = stub_xRekey;83 m.xTruncate = stub_xTruncate;84 m.xDestroy = stub_xDestroy;85 m.xShrink = stub_xShrink;86 sqlite3GlobalConfig.pcache2 = m;87 g_stubInstalled = 1;88 }89}90static void restoreStub(void){91 if( g_stubInstalled ){92 sqlite3GlobalConfig.pcache2 = g_savedMethods;93 g_stubInstalled = 0;94 }95}96static void resetStubCapture(void){97 g_xCreate_calls = 0;98 g_xCreate_last_szPage = 0;99 g_xCreate_last_szExtra = 0;100 g_xCreate_last_bPurgeable = -1;101 g_xCachesize_calls = 0;102 g_xCachesize_last = -1;103 g_xDestroy_calls = 0;104 g_xDestroy_last_arg = 0;105 g_xCreate_return_null = 0;106 g_xPagecount_return = 123;107}108 109void setUp(void) {110 /* Ensure SQLite is initialized */111 sqlite3_initialize();112 resetStubCapture();113}114 115void tearDown(void) {116 /* Restore default pcache2 methods in case a test left the stub installed */117 restoreStub();118}119 120/* Helper: allocate and open a PCache with given parameters */121static PCache *openPCacheOrFail(int szPage, int szExtra, int bPurgeable){122 int rc;123 int n = sqlite3PcacheSize();124 PCache *pCache = (PCache*)sqlite3_malloc(n);125 TEST_ASSERT_NOT_NULL(pCache);126 rc = sqlite3PcacheOpen(szPage, szExtra, bPurgeable, 0, 0, pCache);127 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);128 return pCache;129}130 131/* Test 1: Success path. Verify xCreate args, xCachesize value, xDestroy fired, and handle switch. */132void test_sqlite3PcacheSetPageSize_success_and_calls(void){133 /* Open a cache with known parameters */134 const int initialSzPage = 512;135 const int szExtra = 64;136 const int bPurgeable = 1;137 PCache *p = openPCacheOrFail(initialSzPage, szExtra, bPurgeable);138 139 /* Set a positive cache-size and prepare stub */140 sqlite3PcacheSetCachesize(p, 7);141 installStub();142 resetStubCapture();143 144 /* Call under test with new page size */145 const int newSzPage = 1024;146 int rc = sqlite3PcacheSetPageSize(p, newSzPage);147 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);148 149 /* xCreate should be called once with: szPage=newSzPage,150 ** szExtra = p->szExtra + ROUND8(sizeof(PgHdr)) (151 ** we passed szExtra to sqlite3PcacheOpen),152 ** bPurgeable carried over from open. */153 TEST_ASSERT_EQUAL_INT(1, g_xCreate_calls);154 TEST_ASSERT_EQUAL_INT(newSzPage, g_xCreate_last_szPage);155 TEST_ASSERT_EQUAL_INT(szExtra + ROUND8(sizeof(PgHdr)), g_xCreate_last_szExtra);156 TEST_ASSERT_EQUAL_INT(bPurgeable, g_xCreate_last_bPurgeable);157 158 /* xCachesize should be called once with the configured cache size (7). */159 TEST_ASSERT_EQUAL_INT(1, g_xCachesize_calls);160 TEST_ASSERT_EQUAL_INT(7, g_xCachesize_last);161 162 /* Old cache should have been destroyed exactly once */163 TEST_ASSERT_EQUAL_INT(1, g_xDestroy_calls);164 165 /* Verify the PCache now uses the stub by checking pagecount sentinel */166 g_xPagecount_return = 42;167 TEST_ASSERT_EQUAL_INT(42, sqlite3PcachePagecount(p));168 169 /* Do not close this PCache: it now refers to a stub handle. */170 sqlite3_free(p);171}172 173/* Test 2: OOM path. xCreate returns NULL -> SQLITE_NOMEM_BKPT and no xDestroy/xCachesize. */174void test_sqlite3PcacheSetPageSize_oom_returns_nomem_and_no_side_effects(void){175 /* Open with default implementation */176 PCache *p = openPCacheOrFail(1024, 48, 0);177 178 installStub();179 resetStubCapture();180 g_xCreate_return_null = 1; /* Force OOM */181 182 int rc = sqlite3PcacheSetPageSize(p, 2048);183 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM_BKPT, rc);184 185 /* Ensure we did not proceed past xCreate failure */186 TEST_ASSERT_EQUAL_INT(1, g_xCreate_calls);187 TEST_ASSERT_EQUAL_INT(0, g_xCachesize_calls);188 TEST_ASSERT_EQUAL_INT(0, g_xDestroy_calls);189 190 /* Restore default vtable and close the cache safely (it still uses default). */191 restoreStub();192 sqlite3PcacheClose(p);193 sqlite3_free(p);194}195 196/* Test 3: numberOfCachePages behavior with negative cache-size.197** Verify xCachesize argument equals bytes/(oldPage+szExtra) and that the198** "old page size" used is the size before updating p->szPage. */199void test_sqlite3PcacheSetPageSize_negative_cachesize_uses_old_page_size(void){200 const int oldSzPage = 512;201 const int szExtra = 32;202 PCache *p = openPCacheOrFail(oldSzPage, szExtra, 1);203 204 /* Configure negative cache-size: approximately 4MB */205 const int negCacheKB = -4096; /* -4096 KB */206 sqlite3PcacheSetCachesize(p, negCacheKB);207 208 installStub();209 resetStubCapture();210 211 /* First call: new page size 1024, numberOfCachePages uses oldSzPage (512) */212 int rc = sqlite3PcacheSetPageSize(p, 1024);213 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);214 /* Expected pages = ((-1024 * (i64)negCacheKB) / (oldSzPage + szExtra)) */215 {216 i64 bytes = (-1024 * (i64)negCacheKB);217 int expectedPages = (int)(bytes / (oldSzPage + szExtra));218 TEST_ASSERT_EQUAL_INT(1, g_xCachesize_calls);219 TEST_ASSERT_EQUAL_INT(expectedPages, g_xCachesize_last);220 }221 222 /* Second call: new page size 2048, numberOfCachePages uses old=1024 now */223 resetStubCapture();224 rc = sqlite3PcacheSetPageSize(p, 2048);225 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);226 {227 i64 bytes = (-1024 * (i64)negCacheKB);228 int expectedPages = (int)(bytes / (1024 + szExtra));229 TEST_ASSERT_EQUAL_INT(1, g_xCachesize_calls);230 TEST_ASSERT_EQUAL_INT(expectedPages, g_xCachesize_last);231 }232 233 /* Two calls -> old handle destroyed twice */234 TEST_ASSERT_TRUE(g_xDestroy_calls >= 2);235 236 /* Leave stub handle attached; don't close with default vtable. */237 sqlite3_free(p);238}239 240int main(void){241 UNITY_BEGIN();242 RUN_TEST(test_sqlite3PcacheSetPageSize_success_and_calls);243 RUN_TEST(test_sqlite3PcacheSetPageSize_oom_returns_nomem_and_no_side_effects);244 RUN_TEST(test_sqlite3PcacheSetPageSize_negative_cachesize_uses_old_page_size);245 return UNITY_END();246}