AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* Forward declaration for the internal PCache1 type so we can pass pointers */8typedef struct PCache1 PCache1;9/* Wrapper provided in the module for testing the static function */10extern void test_pcache1ResizeHash(PCache1 *p);11 12static sqlite3_pcache_methods2 g_pcacheMethods;13 14/* Acquire both static mutexes that pcache1 may use for its PCache1Group(s).15 This ensures the precondition "PCache mutex must be held" is satisfied. */16static sqlite3_mutex* g_mutex_lru = NULL;17static sqlite3_mutex* g_mutex_pmem = NULL;18 19static void lock_pcache_group_mutexes(void){20 if( g_mutex_lru==NULL ) g_mutex_lru = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);21 if( g_mutex_pmem==NULL ) g_mutex_pmem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);22 /* Both may be the same or different underlying mutexes. Lock both safely. */23 if( g_mutex_lru ) sqlite3_mutex_enter(g_mutex_lru);24 if( g_mutex_pmem ) sqlite3_mutex_enter(g_mutex_pmem);25}26static void unlock_pcache_group_mutexes(void){27 if( g_mutex_pmem ) sqlite3_mutex_leave(g_mutex_pmem);28 if( g_mutex_lru ) sqlite3_mutex_leave(g_mutex_lru);29}30 31void setUp(void) {32 int rc = sqlite3_initialize();33 (void)rc; /* rc is typically SQLITE_OK; ignore in tests setup */34 /* Capture default pcache2 methods installed by initialization */35 g_pcacheMethods = sqlite3GlobalConfig.pcache2;36 /* Ensure initialized, if xInit is provided */37 if( g_pcacheMethods.xInit ){38 g_pcacheMethods.xInit(g_pcacheMethods.pArg);39 }40 TEST_ASSERT_NOT_NULL(g_pcacheMethods.xCreate);41 TEST_ASSERT_NOT_NULL(g_pcacheMethods.xCachesize);42 TEST_ASSERT_NOT_NULL(g_pcacheMethods.xFetch);43 TEST_ASSERT_NOT_NULL(g_pcacheMethods.xUnpin);44 TEST_ASSERT_NOT_NULL(g_pcacheMethods.xDestroy);45}46 47void tearDown(void) {48 /* Nothing to clean here; individual tests destroy their caches */49}50 51/* Helpers */52static sqlite3_pcache* new_cache(int pageSize, int extraSize, int purgeable, int cacheSize){53 sqlite3_pcache *pc = g_pcacheMethods.xCreate(pageSize, extraSize, purgeable);54 TEST_ASSERT_NOT_NULL(pc);55 g_pcacheMethods.xCachesize(pc, cacheSize);56 return pc;57}58 59static void fill_cache_with_pages(sqlite3_pcache *pc, int nPages){60 for(int i=1; i<=nPages; i++){61 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, (unsigned int)i, 2);62 TEST_ASSERT_NOT_NULL(pg);63 /* Release into cache (do not discard) so it is stored and findable */64 g_pcacheMethods.xUnpin(pc, pg, 0);65 }66}67 68static void verify_cached_pages_findable(sqlite3_pcache *pc, int nPages){69 for(int i=1; i<=nPages; i++){70 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, (unsigned int)i, 0);71 TEST_ASSERT_NOT_NULL(pg);72 g_pcacheMethods.xUnpin(pc, pg, 0);73 }74}75 76/* Call the tested function on a given sqlite3_pcache* by casting to PCache1*,77 while holding the pcache1 group mutex(es). */78static void invoke_resize(sqlite3_pcache *pc){79 lock_pcache_group_mutexes();80 test_pcache1ResizeHash((PCache1*)pc);81 unlock_pcache_group_mutexes();82}83 84/* Tests */85 86/* 1) Initial call on a new cache: ensure no crash and cache operates normally. */87void test_pcache1ResizeHash_initial_on_empty_cache(void){88 sqlite3_pcache *pc = new_cache(1024, 0, 1, 32);89 /* Invoke resize before any page is added */90 invoke_resize(pc);91 92 /* Add a few pages and ensure they are retrievable */93 fill_cache_with_pages(pc, 16);94 verify_cached_pages_findable(pc, 16);95 96 g_pcacheMethods.xDestroy(pc);97}98 99/* 2) Rehash after moderate population: all entries preserved. */100void test_pcache1ResizeHash_rehash_preserves_100_pages(void){101 sqlite3_pcache *pc = new_cache(1024, 0, 1, 200);102 fill_cache_with_pages(pc, 100);103 104 /* Verify they are findable before resize */105 verify_cached_pages_findable(pc, 100);106 107 /* Resize the hash table explicitly */108 invoke_resize(pc);109 110 /* Verify they are still all findable after resize */111 verify_cached_pages_findable(pc, 100);112 113 g_pcacheMethods.xDestroy(pc);114}115 116/* 3) Rehash after large population: all entries preserved. */117void test_pcache1ResizeHash_rehash_preserves_1000_pages(void){118 sqlite3_pcache *pc = new_cache(2048, 0, 1, 2048);119 fill_cache_with_pages(pc, 1000);120 121 /* Sanity pre-check */122 verify_cached_pages_findable(pc, 1000);123 124 /* Perform explicit resize */125 invoke_resize(pc);126 127 /* All entries should remain accessible */128 verify_cached_pages_findable(pc, 1000);129 130 g_pcacheMethods.xDestroy(pc);131}132 133/* 4) Multiple consecutive resizes do not affect correctness of lookups. */134void test_pcache1ResizeHash_multiple_consecutive_resizes(void){135 sqlite3_pcache *pc = new_cache(512, 0, 1, 512);136 fill_cache_with_pages(pc, 256);137 138 /* Perform two consecutive resizes to force multiple rehash cycles */139 invoke_resize(pc);140 invoke_resize(pc);141 142 /* Spot-check retrieval across a pattern of keys */143 for(int i=1; i<=256; i+=7){144 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, (unsigned int)i, 0);145 TEST_ASSERT_NOT_NULL(pg);146 g_pcacheMethods.xUnpin(pc, pg, 0);147 }148 149 /* And verify full set remains intact */150 verify_cached_pages_findable(pc, 256);151 152 g_pcacheMethods.xDestroy(pc);153}154 155/* 5) Resize amidst mixed key distribution (including potential high-collision sets). */156void test_pcache1ResizeHash_mixed_keys_distribution(void){157 sqlite3_pcache *pc = new_cache(1024, 0, 1, 1024);158 159 /* Insert a mix: sequential and spaced keys to exercise different buckets */160 for(int i=1; i<=300; i++){161 unsigned int key = (i%2==0) ? (unsigned int)i : (unsigned int)(i*257); /* 257 is prime; wraps modulo various nHash */162 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, key, 2);163 TEST_ASSERT_NOT_NULL(pg);164 g_pcacheMethods.xUnpin(pc, pg, 0);165 }166 167 /* Resize explicitly */168 invoke_resize(pc);169 170 /* Verify a subset then all */171 for(int i=1; i<=300; i+=5){172 unsigned int key = (i%2==0) ? (unsigned int)i : (unsigned int)(i*257);173 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, key, 0);174 TEST_ASSERT_NOT_NULL(pg);175 g_pcacheMethods.xUnpin(pc, pg, 0);176 }177 /* Full verification */178 for(int i=1; i<=300; i++){179 unsigned int key = (i%2==0) ? (unsigned int)i : (unsigned int)(i*257);180 sqlite3_pcache_page *pg = g_pcacheMethods.xFetch(pc, key, 0);181 TEST_ASSERT_NOT_NULL(pg);182 g_pcacheMethods.xUnpin(pc, pg, 0);183 }184 185 g_pcacheMethods.xDestroy(pc);186}187 188int main(void) {189 UNITY_BEGIN();190 RUN_TEST(test_pcache1ResizeHash_initial_on_empty_cache);191 RUN_TEST(test_pcache1ResizeHash_rehash_preserves_100_pages);192 RUN_TEST(test_pcache1ResizeHash_rehash_preserves_1000_pages);193 RUN_TEST(test_pcache1ResizeHash_multiple_consecutive_resizes);194 RUN_TEST(test_pcache1ResizeHash_mixed_keys_distribution);195 return UNITY_END();196}