AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* No-op stress callback used for pcache open */7static int xStressNoop(void *p, PgHdr *pPg){ (void)p; (void)pPg; return SQLITE_OK; }8 9static PCache *allocCache(int purgeable){10 int rc;11 PCache *pc = (PCache*)sqlite3_malloc(sqlite3PcacheSize());12 TEST_ASSERT_NOT_NULL_MESSAGE(pc, "sqlite3_malloc for PCache failed");13 rc = sqlite3PcacheOpen(1024, 64, purgeable, xStressNoop, NULL, pc);14 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheOpen failed");15 return pc;16}17 18static PgHdr *getPage(PCache *pc, Pgno pgno){19 sqlite3_pcache_page *lower = sqlite3PcacheFetch(pc, pgno, 3);20 if( lower==NULL ){21 int rc = sqlite3PcacheFetchStress(pc, pgno, &lower);22 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheFetchStress failed");23 TEST_ASSERT_NOT_NULL_MESSAGE(lower, "xFetch returned NULL after stress");24 }25 PgHdr *p = sqlite3PcacheFetchFinish(pc, pgno, lower);26 TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3PcacheFetchFinish returned NULL");27 return p;28}29 30void setUp(void) {31 int rc;32 rc = sqlite3_initialize();33 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_initialize failed");34 rc = sqlite3PcacheInitialize();35 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3PcacheInitialize failed");36}37 38void tearDown(void) {39 sqlite3PcacheShutdown();40 sqlite3_shutdown();41}42 43/* Test 1: Clean page becomes DIRTY and is placed on the dirty list (single page) */44void test_sqlite3PcacheMakeDirty_sets_dirty_and_list_for_clean_page(void){45 PCache *pc = allocCache(1); /* purgeable */46 PgHdr *p1 = getPage(pc, 1);47 48 /* Precondition: new page is CLEAN */49 TEST_ASSERT_TRUE_MESSAGE((p1->flags & PGHDR_CLEAN)!=0, "New page not CLEAN");50 TEST_ASSERT_TRUE_MESSAGE((p1->flags & PGHDR_DIRTY)==0, "New page unexpectedly DIRTY");51 TEST_ASSERT_EQUAL_INT64(1, sqlite3PcachePageRefcount(p1));52 53 sqlite3PcacheMakeDirty(p1);54 55 TEST_ASSERT_TRUE_MESSAGE((p1->flags & PGHDR_DIRTY)!=0, "Page not marked DIRTY");56 TEST_ASSERT_TRUE_MESSAGE((p1->flags & PGHDR_CLEAN)==0, "CLEAN flag not cleared");57 58 /* As the only dirty page, it should be the head: prev==NULL and next==NULL */59 TEST_ASSERT_NULL(p1->pDirtyPrev);60 TEST_ASSERT_NULL(p1->pDirtyNext);61 62 /* Cleanup */63 sqlite3PcacheDrop(p1);64 sqlite3PcacheClose(pc);65 sqlite3_free(pc);66}67 68/* Test 2: Calling MakeDirty on an already DIRTY page is a no-op on list links */69void test_sqlite3PcacheMakeDirty_noop_when_already_dirty(void){70 PCache *pc = allocCache(1);71 PgHdr *p1 = getPage(pc, 1);72 73 sqlite3PcacheMakeDirty(p1);74 unsigned origFlags = p1->flags;75 PgHdr *origPrev = p1->pDirtyPrev;76 PgHdr *origNext = p1->pDirtyNext;77 78 sqlite3PcacheMakeDirty(p1);79 80 TEST_ASSERT_EQUAL_HEX32(origFlags, p1->flags);81 TEST_ASSERT_EQUAL_PTR(origPrev, p1->pDirtyPrev);82 TEST_ASSERT_EQUAL_PTR(origNext, p1->pDirtyNext);83 84 /* Cleanup */85 sqlite3PcacheDrop(p1);86 sqlite3PcacheClose(pc);87 sqlite3_free(pc);88}89 90/* Test 3: DONT_WRITE is cleared on a DIRTY page without changing list links */91void test_sqlite3PcacheMakeDirty_clears_DONT_WRITE_on_dirty(void){92 PCache *pc = allocCache(1);93 PgHdr *p1 = getPage(pc, 1);94 95 sqlite3PcacheMakeDirty(p1);96 PgHdr *origPrev = p1->pDirtyPrev;97 PgHdr *origNext = p1->pDirtyNext;98 99 p1->flags |= PGHDR_DONT_WRITE;100 TEST_ASSERT_TRUE((p1->flags & PGHDR_DONT_WRITE)!=0);101 102 sqlite3PcacheMakeDirty(p1);103 104 TEST_ASSERT_TRUE((p1->flags & PGHDR_DIRTY)!=0);105 TEST_ASSERT_TRUE((p1->flags & PGHDR_DONT_WRITE)==0);106 TEST_ASSERT_EQUAL_PTR(origPrev, p1->pDirtyPrev);107 TEST_ASSERT_EQUAL_PTR(origNext, p1->pDirtyNext);108 109 /* Cleanup */110 sqlite3PcacheDrop(p1);111 sqlite3PcacheClose(pc);112 sqlite3_free(pc);113}114 115/* Test 4: Clean page with DONT_WRITE becomes DIRTY and clears DONT_WRITE */116void test_sqlite3PcacheMakeDirty_clears_DONT_WRITE_and_adds_on_clean(void){117 PCache *pc = allocCache(1);118 PgHdr *p1 = getPage(pc, 1);119 120 /* Set DONT_WRITE while still clean */121 TEST_ASSERT_TRUE((p1->flags & PGHDR_CLEAN)!=0);122 p1->flags |= PGHDR_DONT_WRITE;123 124 sqlite3PcacheMakeDirty(p1);125 126 TEST_ASSERT_TRUE((p1->flags & PGHDR_DIRTY)!=0);127 TEST_ASSERT_TRUE((p1->flags & PGHDR_CLEAN)==0);128 TEST_ASSERT_TRUE((p1->flags & PGHDR_DONT_WRITE)==0);129 TEST_ASSERT_NULL(p1->pDirtyPrev);130 TEST_ASSERT_NULL(p1->pDirtyNext);131 132 /* Cleanup */133 sqlite3PcacheDrop(p1);134 sqlite3PcacheClose(pc);135 sqlite3_free(pc);136}137 138/* Test 5: Multiple pages get linked in LRU order (newest at front) */139void test_sqlite3PcacheMakeDirty_dirty_list_ordering_multiple_pages(void){140 PCache *pc = allocCache(1);141 PgHdr *p1 = getPage(pc, 1);142 PgHdr *p2 = getPage(pc, 2);143 PgHdr *p3 = getPage(pc, 3);144 145 /* Make dirty in order: 1, then 2, then 3 */146 sqlite3PcacheMakeDirty(p1);147 TEST_ASSERT_NULL(p1->pDirtyPrev);148 TEST_ASSERT_NULL(p1->pDirtyNext);149 150 sqlite3PcacheMakeDirty(p2);151 /* p2 becomes head; p2->next == p1; p1->prev == p2 */152 TEST_ASSERT_NULL(p2->pDirtyPrev);153 TEST_ASSERT_EQUAL_PTR(p1, p2->pDirtyNext);154 TEST_ASSERT_EQUAL_PTR(p2, p1->pDirtyPrev);155 TEST_ASSERT_NULL(p1->pDirtyNext);156 157 sqlite3PcacheMakeDirty(p3);158 /* p3 becomes head; chain: p3 -> p2 -> p1 */159 TEST_ASSERT_NULL(p3->pDirtyPrev);160 TEST_ASSERT_EQUAL_PTR(p2, p3->pDirtyNext);161 TEST_ASSERT_EQUAL_PTR(p3, p2->pDirtyPrev);162 TEST_ASSERT_EQUAL_PTR(p1, p2->pDirtyNext);163 TEST_ASSERT_EQUAL_PTR(p2, p1->pDirtyPrev);164 TEST_ASSERT_NULL(p1->pDirtyNext);165 166 /* Cleanup: drop in any order - drop removes from list as needed */167 sqlite3PcacheDrop(p1);168 sqlite3PcacheDrop(p2);169 sqlite3PcacheDrop(p3);170 sqlite3PcacheClose(pc);171 sqlite3_free(pc);172}173 174/* Test 6: If NEED_SYNC is set on a clean page, it remains set after MakeDirty175 and MakeDirty still succeeds (we cannot directly test pSynced here) */176void test_sqlite3PcacheMakeDirty_respects_NEED_SYNC_flag(void){177 PCache *pc = allocCache(1);178 PgHdr *p1 = getPage(pc, 1);179 180 /* Set NEED_SYNC while clean */181 p1->flags |= PGHDR_NEED_SYNC;182 sqlite3PcacheMakeDirty(p1);183 184 TEST_ASSERT_TRUE((p1->flags & PGHDR_DIRTY)!=0);185 TEST_ASSERT_TRUE((p1->flags & PGHDR_CLEAN)==0);186 TEST_ASSERT_TRUE((p1->flags & PGHDR_NEED_SYNC)!=0);187 188 /* Cleanup */189 sqlite3PcacheDrop(p1);190 sqlite3PcacheClose(pc);191 sqlite3_free(pc);192}193 194int main(void){195 UNITY_BEGIN();196 RUN_TEST(test_sqlite3PcacheMakeDirty_sets_dirty_and_list_for_clean_page);197 RUN_TEST(test_sqlite3PcacheMakeDirty_noop_when_already_dirty);198 RUN_TEST(test_sqlite3PcacheMakeDirty_clears_DONT_WRITE_on_dirty);199 RUN_TEST(test_sqlite3PcacheMakeDirty_clears_DONT_WRITE_and_adds_on_clean);200 RUN_TEST(test_sqlite3PcacheMakeDirty_dirty_list_ordering_multiple_pages);201 RUN_TEST(test_sqlite3PcacheMakeDirty_respects_NEED_SYNC_flag);202 return UNITY_END();203}