AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Stress callback stub (not used by these tests but required by sqlite3PcacheOpen) */7static int stress_cb(void *p, PgHdr *pPg){8 (void)p; (void)pPg;9 return SQLITE_OK;10}11 12void setUp(void) {13 /* Ensure SQLite is initialized, so the default pcache module is ready */14 int rc = sqlite3_initialize();15 TEST_ASSERT_TRUE(rc==SQLITE_OK || rc==SQLITE_MISUSE);16}17 18void tearDown(void) {19 /* Shutdown between tests for a clean environment */20 sqlite3_shutdown();21}22 23/* Helper: initialize a PCache */24static void init_pcache(PCache *pCache, int purgeable){25 int rc = sqlite3PcacheOpen(512, 128, purgeable, stress_cb, NULL, pCache);26 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);27}28 29/* Helper: fetch a page and finish to get PgHdr */30static PgHdr* fetch_page(PCache *pCache, Pgno pgno, sqlite3_pcache_page **ppLower){31 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, pgno, 3);32 TEST_ASSERT_NOT_NULL(pLower);33 PgHdr *pPg = sqlite3PcacheFetchFinish(pCache, pgno, pLower);34 TEST_ASSERT_NOT_NULL(pPg);35 if( ppLower ) *ppLower = pLower;36 return pPg;37}38 39/* Helper: check if a given PgHdr appears on the dirty list */40static int dirty_list_contains(PCache *pCache, PgHdr *target){41 PgHdr *p;42 PgHdr *pList = sqlite3PcacheDirtyList(pCache);43 for(p = pList; p; p = p->pDirty){44 if( p==target ) return 1;45 }46 return 0;47}48 49void test_sqlite3PcacheRelease_clean_ref1_inmemory(void){50 PCache cache;51 init_pcache(&cache, 0); /* in-memory, unpin is no-op */52 53 PgHdr *p = fetch_page(&cache, 10, NULL);54 TEST_ASSERT_EQUAL_INT64(1, sqlite3PcacheRefCount(&cache));55 TEST_ASSERT_EQUAL_INT(1, p->nRef);56 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)!=0);57 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)==0);58 59 sqlite3PcacheRelease(p);60 61 TEST_ASSERT_EQUAL_INT(0, p->nRef);62 TEST_ASSERT_EQUAL_INT64(0, sqlite3PcacheRefCount(&cache));63 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)!=0);64 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)==0);65 66 /* Dirty list should be empty */67 TEST_ASSERT_NULL(sqlite3PcacheDirtyList(&cache));68 69 sqlite3PcacheClose(&cache);70}71 72void test_sqlite3PcacheRelease_dirty_ref1_inmemory(void){73 PCache cache;74 init_pcache(&cache, 0); /* in-memory, unpin is no-op */75 76 PgHdr *p = fetch_page(&cache, 3, NULL);77 sqlite3PcacheMakeDirty(p);78 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)!=0);79 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)==0);80 TEST_ASSERT_EQUAL_INT(1, p->nRef);81 TEST_ASSERT_EQUAL_INT64(1, sqlite3PcacheRefCount(&cache));82 83 sqlite3PcacheRelease(p);84 85 TEST_ASSERT_EQUAL_INT(0, p->nRef);86 TEST_ASSERT_EQUAL_INT64(0, sqlite3PcacheRefCount(&cache));87 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)!=0);88 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)==0);89 90 /* Page should still be on the dirty list */91 TEST_ASSERT_TRUE(dirty_list_contains(&cache, p));92 93 sqlite3PcacheClose(&cache);94}95 96void test_sqlite3PcacheRelease_clean_decrement_only(void){97 PCache cache;98 init_pcache(&cache, 0);99 100 PgHdr *p = fetch_page(&cache, 44, NULL);101 sqlite3PcacheRef(p); /* nRef becomes 2 */102 TEST_ASSERT_EQUAL_INT(2, p->nRef);103 TEST_ASSERT_EQUAL_INT64(2, sqlite3PcacheRefCount(&cache));104 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)!=0);105 106 sqlite3PcacheRelease(p); /* nRef -> 1, no unpin, no list change */107 108 TEST_ASSERT_EQUAL_INT(1, p->nRef);109 TEST_ASSERT_EQUAL_INT64(1, sqlite3PcacheRefCount(&cache));110 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)!=0);111 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)==0);112 113 /* Cleanup: release final ref */114 sqlite3PcacheRelease(p);115 TEST_ASSERT_EQUAL_INT(0, p->nRef);116 TEST_ASSERT_EQUAL_INT64(0, sqlite3PcacheRefCount(&cache));117 118 sqlite3PcacheClose(&cache);119}120 121void test_sqlite3PcacheRelease_dirty_decrement_only(void){122 PCache cache;123 init_pcache(&cache, 0);124 125 PgHdr *p = fetch_page(&cache, 45, NULL);126 sqlite3PcacheMakeDirty(p);127 sqlite3PcacheRef(p); /* nRef becomes 2 */128 TEST_ASSERT_EQUAL_INT(2, p->nRef);129 TEST_ASSERT_EQUAL_INT64(2, sqlite3PcacheRefCount(&cache));130 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)!=0);131 132 sqlite3PcacheRelease(p); /* nRef -> 1; since still >0, nothing else */133 134 TEST_ASSERT_EQUAL_INT(1, p->nRef);135 TEST_ASSERT_EQUAL_INT64(1, sqlite3PcacheRefCount(&cache));136 TEST_ASSERT_TRUE((p->flags & PGHDR_DIRTY)!=0);137 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)==0);138 139 /* Cleanup: release final ref */140 sqlite3PcacheRelease(p);141 TEST_ASSERT_EQUAL_INT(0, p->nRef);142 TEST_ASSERT_EQUAL_INT64(0, sqlite3PcacheRefCount(&cache));143 144 /* Still dirty */145 TEST_ASSERT_TRUE(dirty_list_contains(&cache, p));146 147 sqlite3PcacheClose(&cache);148}149 150void test_sqlite3PcacheRelease_clean_ref1_purgeable(void){151 PCache cache;152 init_pcache(&cache, 1); /* purgeable: unpin will be called on clean release */153 154 sqlite3_pcache_page *pLower1 = NULL;155 PgHdr *p = fetch_page(&cache, 7, &pLower1);156 TEST_ASSERT_NOT_NULL(pLower1);157 TEST_ASSERT_EQUAL_INT(1, p->nRef);158 TEST_ASSERT_TRUE((p->flags & PGHDR_CLEAN)!=0);159 160 sqlite3PcacheRelease(p); /* nRef -> 0, should unpin underlying page */161 TEST_ASSERT_EQUAL_INT(0, p->nRef);162 TEST_ASSERT_EQUAL_INT64(0, sqlite3PcacheRefCount(&cache));163 164 /* The page should still be present in cache and fetchable without creating */165 sqlite3_pcache_page *pLower2 = sqlite3PcacheFetch(&cache, 7, 0);166 TEST_ASSERT_NOT_NULL(pLower2);167 168 /* Typically the same underlying pointer remains */169 TEST_ASSERT_EQUAL_PTR(pLower1, pLower2);170 171 /* Finish and release to balance */172 PgHdr *p2 = sqlite3PcacheFetchFinish(&cache, 7, pLower2);173 TEST_ASSERT_NOT_NULL(p2);174 TEST_ASSERT_EQUAL_INT(1, p2->nRef);175 sqlite3PcacheRelease(p2);176 177 sqlite3PcacheClose(&cache);178}179 180int main(void) {181 UNITY_BEGIN();182 RUN_TEST(test_sqlite3PcacheRelease_clean_ref1_inmemory);183 RUN_TEST(test_sqlite3PcacheRelease_dirty_ref1_inmemory);184 RUN_TEST(test_sqlite3PcacheRelease_clean_decrement_only);185 RUN_TEST(test_sqlite3PcacheRelease_dirty_decrement_only);186 RUN_TEST(test_sqlite3PcacheRelease_clean_ref1_purgeable);187 return UNITY_END();188}