AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Wrapper provided by the build per instructions */7int test_newDatabase(BtShared *pBt);8 9static sqlite3 *gDb = NULL;10 11static Btree* getMainBtree(sqlite3 *db){12 /* Access internal aDb[0].pBt (Btree*) from sqliteInt.h */13 return db->aDb[0].pBt;14}15 16static void beginWriteTrans(Btree *p){17 int rc = sqlite3BtreeBeginTrans(p, 1, 0);18 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);19}20 21static void commitTrans(Btree *p){22 int rc = sqlite3BtreeCommit(p);23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);24}25 26void setUp(void) {27 int rc = sqlite3_initialize();28 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);29 rc = sqlite3_open(":memory:", &gDb);30 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);31}32 33void tearDown(void) {34 if( gDb ){35 sqlite3_close(gDb);36 gDb = NULL;37 }38}39 40/* Helper to copy first 100 bytes of db header */41static void copy_header_100(BtShared *pBt, unsigned char *dst){42 MemPage *pP1 = pBt->pPage1;43 TEST_ASSERT_NOT_NULL(pP1);44 TEST_ASSERT_NOT_NULL(pP1->aData);45 memcpy(dst, pP1->aData, 100);46}47 48static void assert_header_100_equal(BtShared *pBt, const unsigned char *hdr){49 MemPage *pP1 = pBt->pPage1;50 TEST_ASSERT_NOT_NULL(pP1);51 TEST_ASSERT_EQUAL_INT8_ARRAY(hdr, pP1->aData, 100);52}53 54static void load_magic_header(unsigned char *dst16){55 /* SQLITE_FILE_HEADER is 16 bytes, available via btree.c static const,56 but not directly visible here. Instead, read current db -> it should57 already contain the correct file header after open().58 59 For tests that require an exact comparison, we can take the header60 from current page before forcing nPage=0, then after newDatabase()61 it should still match. For initialization test we'll validate specific62 bytes instead of full compare to avoid platform-dependent values. */63 (void)dst16;64}65 66/* TEST 1: When nPage > 0, newDatabase should be a no-op and return SQLITE_OK */67void test_newDatabase_noop_when_nPage_gt_zero(void){68 TEST_ASSERT_NOT_NULL(gDb);69 Btree *pBtree = getMainBtree(gDb);70 TEST_ASSERT_NOT_NULL(pBtree);71 BtShared *pBt = pBtree->pBt;72 TEST_ASSERT_NOT_NULL(pBt);73 74 beginWriteTrans(pBtree);75 76 /* Capture initial state */77 unsigned char hdrBefore[100];78 sqlite3BtreeEnter(pBtree);79 TEST_ASSERT_TRUE(pBt->nPage > 0);80 Pgno nPageBefore = pBt->nPage;81 copy_header_100(pBt, hdrBefore);82 83 /* Call target wrapper while holding the mutex */84 int rc = test_newDatabase(pBt);85 sqlite3BtreeLeave(pBtree);86 87 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);88 89 /* Verify no changes */90 sqlite3BtreeEnter(pBtree);91 TEST_ASSERT_EQUAL_INT(nPageBefore, (int)pBt->nPage);92 assert_header_100_equal(pBt, hdrBefore);93 sqlite3BtreeLeave(pBtree);94 95 commitTrans(pBtree);96}97 98/* TEST 2: When nPage == 0, newDatabase initializes header and root correctly */99void test_newDatabase_initializes_header_and_root(void){100 TEST_ASSERT_NOT_NULL(gDb);101 Btree *pBtree = getMainBtree(gDb);102 TEST_ASSERT_NOT_NULL(pBtree);103 BtShared *pBt = pBtree->pBt;104 TEST_ASSERT_NOT_NULL(pBt);105 106 beginWriteTrans(pBtree);107 108 sqlite3BtreeEnter(pBtree);109 110 /* Force nPage to 0 to trigger initialization */111 pBt->nPage = 0;112 113 /* Capture page1 and parameters */114 MemPage *pP1 = pBt->pPage1;115 TEST_ASSERT_NOT_NULL(pP1);116 TEST_ASSERT_NOT_NULL(pP1->aData);117 unsigned char *data = pP1->aData;118 u32 pageSize = pBt->pageSize;119 u32 usableSize = pBt->usableSize;120 121 int rc = test_newDatabase(pBt);122 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);123 124 /* Validate header bytes set by newDatabase */125 /* 16-23 as per code: endian parts of pageSize, read/write versions, reserved, fractions */126 TEST_ASSERT_EQUAL_HEX8((u8)((pageSize>>8)&0xff), data[16]);127 TEST_ASSERT_EQUAL_HEX8((u8)((pageSize>>16)&0xff), data[17]);128 TEST_ASSERT_EQUAL_HEX8(1, data[18]); /* read version */129 TEST_ASSERT_EQUAL_HEX8(1, data[19]); /* write version */130 TEST_ASSERT_EQUAL_HEX8((u8)(pageSize - usableSize), data[20]); /* reserved size */131 TEST_ASSERT_EQUAL_HEX8(64, data[21]); /* max fraction */132 TEST_ASSERT_EQUAL_HEX8(32, data[22]); /* min fraction */133 TEST_ASSERT_EQUAL_HEX8(32, data[23]); /* leaf fraction */134 /* data[31] set to 1 */135 TEST_ASSERT_EQUAL_HEX8(1, data[31]);136 137 /* Check that root page (page 1) header at offset 100 has correct flags */138 int hdr = pP1->hdrOffset; /* should be 100 */139 TEST_ASSERT_EQUAL_INT(100, hdr);140 /* Flags PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF */141 TEST_ASSERT_EQUAL_HEX8(PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF, data[hdr + 0]);142 143 /* nPage set to 1 and BTS_PAGESIZE_FIXED set */144 TEST_ASSERT_EQUAL_UINT32(1, pBt->nPage);145 TEST_ASSERT_TRUE((pBt->btsFlags & BTS_PAGESIZE_FIXED) != 0);146 147 sqlite3BtreeLeave(pBtree);148 149 commitTrans(pBtree);150}151 152#ifndef SQLITE_OMIT_AUTOVACUUM153/* TEST 3: Verify autovacuum meta fields are written according to flags */154void test_newDatabase_writes_autovacuum_meta(void){155 TEST_ASSERT_NOT_NULL(gDb);156 Btree *pBtree = getMainBtree(gDb);157 TEST_ASSERT_NOT_NULL(pBtree);158 BtShared *pBt = pBtree->pBt;159 TEST_ASSERT_NOT_NULL(pBt);160 161 beginWriteTrans(pBtree);162 163 sqlite3BtreeEnter(pBtree);164 165 /* Force fresh initialization */166 pBt->nPage = 0;167 pBt->autoVacuum = 1;168 pBt->incrVacuum = 1;169 170 int rc = test_newDatabase(pBt);171 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173 /* Verify meta fields set at offsets 36+4*4 and 36+7*4 */174 unsigned char *data = pBt->pPage1->aData;175 u32 av = get4byte(&data[36 + 4*4]);176 u32 iv = get4byte(&data[36 + 7*4]);177 TEST_ASSERT_EQUAL_UINT32(1u, av);178 TEST_ASSERT_EQUAL_UINT32(1u, iv);179 180 sqlite3BtreeLeave(pBtree);181 commitTrans(pBtree);182}183#endif184 185int main(void) {186 UNITY_BEGIN();187 188 RUN_TEST(test_newDatabase_noop_when_nPage_gt_zero);189 RUN_TEST(test_newDatabase_initializes_header_and_root);190#ifndef SQLITE_OMIT_AUTOVACUUM191 RUN_TEST(test_newDatabase_writes_autovacuum_meta);192#endif193 194 return UNITY_END();195}