AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Prototype for the auto-generated test wrapper around static getPageNormal() */9int test_getPageNormal(Pager *pPager, Pgno pgno, DbPage **ppPage, int flags);10 11static int g_test_counter = 0;12 13static void make_temp_filename(char *zOut, int nOut){14 sqlite3_snprintf(nOut, zOut, "test_getPageNormal_%d.db", g_test_counter++);15}16 17static sqlite3* open_db_and_prepare(Btree **ppBtree, Pager **ppPager, int ensureData){18 int rc;19 sqlite3 *db = 0;20 char zFile[128];21 22 make_temp_filename(zFile, sizeof(zFile));23 rc = sqlite3_open(zFile, &db);24 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");25 26 /* Ensure journal mode is DELETE to keep readDbPage path simple */27 rc = sqlite3_exec(db, "PRAGMA journal_mode=DELETE;", 0, 0, 0);28 TEST_ASSERT(rc==SQLITE_OK || rc==SQLITE_DONE);29 30 if( ensureData ){31 /* Create some content to ensure dbSize >= 1 and file exists */32 rc = sqlite3_exec(db,33 "BEGIN;"34 "CREATE TABLE IF NOT EXISTS t(x);"35 "INSERT INTO t(x) VALUES(1);"36 "COMMIT;",37 0, 0, 038 );39 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to create test table/row");40 }else{41 /* Touch the database to ensure Btree is initialized */42 rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS t0(y);", 0, 0, 0);43 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);44 }45 46 /* Fetch internal Btree and Pager */47 TEST_ASSERT_NOT_NULL(db->aDb);48 TEST_ASSERT_NOT_NULL(db->aDb[0].pBt);49 *ppBtree = db->aDb[0].pBt;50 TEST_ASSERT_NOT_NULL((*ppBtree)->pBt);51 *ppPager = (*ppBtree)->pBt->pPager;52 TEST_ASSERT_NOT_NULL(*ppPager);53 54 /* Start a read transaction to satisfy pager asserts (eState>=PAGER_READER) */55 rc = sqlite3BtreeBeginTrans(*ppBtree, 0);56 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3BtreeBeginTrans failed");57 58 /* Ensure hasHeldSharedLock is set, as the code asserts this */59 (*ppPager)->hasHeldSharedLock = 1;60 61 return db;62}63 64static void close_db_and_cleanup(sqlite3 *db){65 /* End any transaction and close */66 /* If the Btree is still in a transaction, commit it. 67 Use sqlite3_exec for simplicity. */68 sqlite3_exec(db, "COMMIT;", 0, 0, 0);69 sqlite3_close(db);70}71 72void setUp(void) {73 /* no-op */74}75 76void tearDown(void) {77 /* no-op */78}79 80/* Test 1: pgno==0 should return SQLITE_CORRUPT and set *ppPage==NULL */81void test_getPageNormal_pgno_zero_returns_corrupt(void){82 Btree *pBtree = 0;83 Pager *pPager = 0;84 sqlite3 *db = open_db_and_prepare(&pBtree, &pPager, /*ensureData=*/1);85 86 DbPage *pPage = 0;87 int rc = test_getPageNormal(pPager, 0, &pPage, 0);88 TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, rc);89 TEST_ASSERT_NULL(pPage);90 91 close_db_and_cleanup(db);92}93 94/* Test 2: PAGER_GET_NOCONTENT returns a zeroed page and success */95void test_getPageNormal_nocontent_zero_page(void){96 Btree *pBtree = 0;97 Pager *pPager = 0;98 sqlite3 *db = open_db_and_prepare(&pBtree, &pPager, /*ensureData=*/1);99 100 /* Choose a pgno beyond current dbSize to force the NOCONTENT/zero path.101 Also ensure pgno <= mxPgno to avoid SQLITE_FULL in this test. */102 Pgno pgno = (pPager->dbSize>0) ? pPager->dbSize + 1 : 2;103 if( pgno > pPager->mxPgno ){104 /* Expand limit slightly for test purposes */105 pPager->mxPgno = pgno + 10;106 }107 108 DbPage *pPage = 0;109 int rc = test_getPageNormal(pPager, pgno, &pPage, PAGER_GET_NOCONTENT);110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);111 TEST_ASSERT_NOT_NULL(pPage);112 TEST_ASSERT_EQUAL_PTR(pPager, ((PgHdr*)pPage)->pPager);113 TEST_ASSERT_EQUAL_UINT32(pgno, ((PgHdr*)pPage)->pgno);114 115 /* Verify the page content is zeroed */116 unsigned char *p = (unsigned char *)((PgHdr*)pPage)->pData;117 int i;118 for(i=0; i<pPager->pageSize; i++){119 if( p[i]!=0 ){120 TEST_FAIL_MESSAGE("Page content not zeroed under PAGER_GET_NOCONTENT");121 }122 }123 124 sqlite3PagerUnref(pPage);125 close_db_and_cleanup(db);126}127 128/* Test 3: Cache hit when fetching a page already in cache increments hit stat */129void test_getPageNormal_cache_hit_increments_stat(void){130 Btree *pBtree = 0;131 Pager *pPager = 0;132 sqlite3 *db = open_db_and_prepare(&pBtree, &pPager, /*ensureData=*/1);133 134 /* Choose a pgno > dbSize; keep within mxPgno */135 Pgno pgno = (pPager->dbSize>0) ? pPager->dbSize + 1 : 2;136 if( pgno > pPager->mxPgno ){137 pPager->mxPgno = pgno + 10;138 }139 140 DbPage *pPage1 = 0;141 int rc = test_getPageNormal(pPager, pgno, &pPage1, PAGER_GET_NOCONTENT);142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);143 TEST_ASSERT_NOT_NULL(pPage1);144 145 /* Second fetch without NOCONTENT should be a cache hit */146 int hitsBefore = pPager->aStat[PAGER_STAT_HIT];147 DbPage *pPage2 = 0;148 rc = test_getPageNormal(pPager, pgno, &pPage2, 0);149 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);150 TEST_ASSERT_NOT_NULL(pPage2);151 TEST_ASSERT_EQUAL_INT(hitsBefore + 1, pPager->aStat[PAGER_STAT_HIT]);152 153 /* Should be the same page object */154 TEST_ASSERT_EQUAL_PTR(pPage1, pPage2);155 156 sqlite3PagerUnref(pPage2);157 sqlite3PagerUnref(pPage1);158 159 close_db_and_cleanup(db);160}161 162/* Test 4: pgno > mxPgno -> SQLITE_FULL and *ppPage==NULL */163void test_getPageNormal_pgno_gt_mxPgno_returns_full(void){164 Btree *pBtree = 0;165 Pager *pPager = 0;166 sqlite3 *db = open_db_and_prepare(&pBtree, &pPager, /*ensureData=*/1);167 168 /* Force a low mxPgno and request a larger pgno */169 pPager->mxPgno = 5;170 Pgno pgno = 6;171 172 DbPage *pPage = 0;173 int rc = test_getPageNormal(pPager, pgno, &pPage, 0);174 TEST_ASSERT_EQUAL_INT(SQLITE_FULL, rc);175 TEST_ASSERT_NULL(pPage);176 177 close_db_and_cleanup(db);178}179 180/* Test 5: Reading an existing page without NOCONTENT reads from disk181 and increments miss/nRead counters. We also sanity check header signature. */182void test_getPageNormal_reads_from_disk_and_updates_stats(void){183 Btree *pBtree = 0;184 Pager *pPager = 0;185 sqlite3 *db = open_db_and_prepare(&pBtree, &pPager, /*ensureData=*/1);186 187 /* Make sure page 1 exists (table created in prepare ensures this) */188 Pgno pgno = 1;189 190 int missBefore = pPager->aStat[PAGER_STAT_MISS];191 int nReadBefore = pPager->nRead;192 193 DbPage *pPage = 0;194 int rc = test_getPageNormal(pPager, pgno, &pPage, 0);195 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);196 TEST_ASSERT_NOT_NULL(pPage);197 198 /* Miss and nRead should have incremented when a new page is read from disk */199 TEST_ASSERT_EQUAL_INT(missBefore + 1, pPager->aStat[PAGER_STAT_MISS]);200 TEST_ASSERT_EQUAL_INT(nReadBefore + 1, pPager->nRead);201 202 /* For page 1, expect SQLite database header "SQLite format 3\000" */203 if( ((PgHdr*)pPage)->pgno==1 ){204 const unsigned char *z = (const unsigned char *)((PgHdr*)pPage)->pData;205 /* Only perform this check if the database is not empty and page 1 was read206 into cache. It should be after creating a table. */207 TEST_ASSERT_EQUAL_MEMORY("SQLite format 3\000", z, 16);208 }209 210 sqlite3PagerUnref(pPage);211 close_db_and_cleanup(db);212}213 214int main(void){215 UNITY_BEGIN();216 RUN_TEST(test_getPageNormal_pgno_zero_returns_corrupt);217 RUN_TEST(test_getPageNormal_nocontent_zero_page);218 RUN_TEST(test_getPageNormal_cache_hit_increments_stat);219 RUN_TEST(test_getPageNormal_pgno_gt_mxPgno_returns_full);220 RUN_TEST(test_getPageNormal_reads_from_disk_and_updates_stats);221 return UNITY_END();222}