AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* The wrapper provided in the module for the static function. */7int test_fillInCell(8 MemPage *pPage,9 unsigned char *pCell,10 const BtreePayload *pX,11 int *pnSize12);13 14/* Local stub for MemPage.xParseCell used by fillInCell() debug asserts.15 It parses the header we write in tests and fills CellInfo accordingly. */16static void test_xParseCell_stub(MemPage *pPage, u8 *pCell, CellInfo *pInfo){17 memset(pInfo, 0, sizeof(*pInfo));18 u8 *p = pCell + pPage->childPtrSize;19 /* parse varint payload-size (we only need small sizes for tests) */20 u32 nPayload = 0;21 u8 c = *p++;22 if( c<0x80 ){23 nPayload = c;24 }else{25 /* Support a few bytes; our tests only use small values. */26 nPayload = (c & 0x7f);27 while( (*p & 0x80) && (p - pCell) < 10 ){28 nPayload = (nPayload<<7) | (*p & 0x7f);29 p++;30 }31 nPayload = (nPayload<<7) | (*p & 0x7f);32 p++;33 }34 pInfo->nPayload = nPayload;35 36 if( pPage->intKeyLeaf ){37 /* parse rowid varint (small) */38 u64 key = 0;39 int i = 0;40 u8 b;41 do{42 b = *p++;43 key = (key<<7) | (b & 0x7f);44 i++;45 }while( (b & 0x80) && i<9 );46 pInfo->nKey = (i64)key;47 }else{48 /* For index pages, CellInfo.nKey is the record size (nPayload) */49 pInfo->nKey = (i64)nPayload;50 }51 52 pInfo->pPayload = p;53 /* In our tests, we ensure nPayload <= maxLocal (no overflow) */54 pInfo->nLocal = nPayload;55 56 int nHeader = (int)(pInfo->pPayload - pCell);57 int nSize = nHeader + (int)nPayload;58 if( nSize<4 ) nSize = 4;59 pInfo->nSize = (u16)nSize;60}61 62/* Helper to allocate and initialize a BtShared+MemPage with minimally63 required fields for local cell (no-overflow) use. */64typedef struct TestPage {65 BtShared *pBt;66 MemPage *pPage;67 u8 *aPage;68 sqlite3_mutex *mutex;69} TestPage;70 71static void freeTestPage(TestPage *tp){72 if( !tp ) return;73 if( tp->mutex ){74 sqlite3_mutex_leave(tp->mutex);75 sqlite3_mutex_free(tp->mutex);76 }77 if( tp->aPage ) sqlite3_free(tp->aPage);78 if( tp->pPage ) sqlite3_free(tp->pPage);79 if( tp->pBt ) sqlite3_free(tp->pBt);80 memset(tp, 0, sizeof(*tp));81}82 83static void initTestPage(TestPage *tp, int isIntKeyLeaf){84 memset(tp, 0, sizeof(*tp));85 tp->pBt = (BtShared*)sqlite3MallocZero(sizeof(BtShared));86 TEST_ASSERT_NOT_NULL(tp->pBt);87 tp->pBt->pageSize = 4096;88 tp->pBt->usableSize = 4096; /* reserved bytes = 0 for tests */89 90 tp->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);91 TEST_ASSERT_NOT_NULL(tp->mutex);92 tp->pBt->mutex = tp->mutex;93 94 tp->aPage = (u8*)sqlite3MallocZero(tp->pBt->pageSize);95 TEST_ASSERT_NOT_NULL(tp->aPage);96 97 tp->pPage = (MemPage*)sqlite3MallocZero(sizeof(MemPage));98 TEST_ASSERT_NOT_NULL(tp->pPage);99 tp->pPage->pBt = tp->pBt;100 tp->pPage->aData = tp->aPage;101 tp->pPage->aDataEnd = tp->aPage + tp->pBt->pageSize;102 tp->pPage->childPtrSize = 0; /* leaf */103 tp->pPage->leaf = 1;104 tp->pPage->xParseCell = test_xParseCell_stub;105 106 if( isIntKeyLeaf ){107 tp->pPage->intKey = 1;108 tp->pPage->intKeyLeaf = 1;109 /* Generous maxLocal/minLocal so tests stay local */110 tp->pPage->maxLocal = 4000;111 tp->pPage->minLocal = 1000;112 }else{113 tp->pPage->intKey = 0;114 tp->pPage->intKeyLeaf = 0;115 tp->pPage->maxLocal = 4000;116 tp->pPage->minLocal = 1000;117 }118 119 /* Hold the mutex as required by fillInCell assertions */120 sqlite3_mutex_enter(tp->mutex);121}122 123/* Helpers for varints used in validation */124static int writeVarint32(u8 *p, u32 v){125 /* For our small test values, this is enough: fits in one byte if <128 */126 if( v<128 ){ p[0] = (u8)v; return 1; }127 /* Fallback multi-byte (not expected to be used in tests) */128 int n = 0;129 u8 buf[5];130 do{131 buf[n++] = (u8)(v & 0x7f);132 v >>= 7;133 }while( v );134 int i, j=0;135 for(i=n-1; i>=0; i--){136 p[j++] = buf[i] | (i?0x80:0x00);137 }138 return j;139}140 141static int writeVarint64(u8 *p, u64 v){142 /* For small test rowids (<128) this is one byte */143 if( v<128 ){ p[0] = (u8)v; return 1; }144 /* Fallback simple implementation */145 u8 buf[10];146 int n=0, i, j=0;147 do{148 buf[n++] = (u8)(v & 0x7f);149 v >>= 7;150 }while( v );151 for(i=n-1; i>=0; i--){152 p[j++] = buf[i] | (i?0x80:0x00);153 }154 return j;155}156 157/* Unity fixtures */158void setUp(void){159 /* Ensure SQLite core is initialized for mutex subsystem */160 sqlite3_initialize();161}162 163void tearDown(void){164 /* nothing */165}166 167/* Test 1: intkey leaf page with small payload and rowid */168void test_fillInCell_intkey_basic(void){169 TestPage tp;170 initTestPage(&tp, 1);171 172 unsigned char cell[64];173 memset(cell, 0xCC, sizeof(cell));174 int cellSize = -1;175 176 /* Prepare payload: nData=3 ("abc"), nZero=0, nKey(rowid)=5 */177 BtreePayload x;178 memset(&x, 0, sizeof(x));179 x.pData = (void*)"abc";180 x.nData = 3;181 x.nZero = 0;182 x.nKey = 5;183 184 int rc = test_fillInCell(tp.pPage, cell, &x, &cellSize);185 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);186 187 /* Expect header: varint(3)=0x03, varint(5)=0x05; payload "abc" */188 int hdr = 0;189 u8 expect[64];190 hdr += writeVarint32(expect+hdr, 3);191 hdr += writeVarint64(expect+hdr, 5);192 int total = hdr + 3;193 if( total<4 ) total = 4;194 195 TEST_ASSERT_EQUAL_INT(total, cellSize);196 /* Check header bytes */197 TEST_ASSERT_EQUAL_UINT8_ARRAY(expect, cell, hdr);198 /* Check payload bytes */199 TEST_ASSERT_EQUAL_UINT8('a', cell[hdr+0]);200 TEST_ASSERT_EQUAL_UINT8('b', cell[hdr+1]);201 TEST_ASSERT_EQUAL_UINT8('c', cell[hdr+2]);202 203 freeTestPage(&tp);204}205 206/* Test 2: intkey leaf with header+payload < 4 triggers 4-byte minimum */207void test_fillInCell_intkey_min4(void){208 TestPage tp;209 initTestPage(&tp, 1);210 211 unsigned char cell[64];212 memset(cell, 0xEE, sizeof(cell));213 int cellSize = -1;214 215 /* payload size 1 ("X"), key 1 -> hdr 2 bytes + payload 1 = 3 -> padded to 4 */216 BtreePayload x;217 memset(&x, 0, sizeof(x));218 x.pData = (void*)"X";219 x.nData = 1;220 x.nZero = 0;221 x.nKey = 1;222 223 int rc = test_fillInCell(tp.pPage, cell, &x, &cellSize);224 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);225 226 int hdr = 0;227 u8 expect[64];228 hdr += writeVarint32(expect+hdr, 1);229 hdr += writeVarint64(expect+hdr, 1);230 int total = hdr + 1;231 if( total<4 ) total = 4;232 233 TEST_ASSERT_EQUAL_INT(4, cellSize);234 /* Check header */235 TEST_ASSERT_EQUAL_UINT8_ARRAY(expect, cell, hdr);236 /* First data byte */237 TEST_ASSERT_EQUAL_UINT8('X', cell[hdr+0]);238 /* The function pads to 4 by setting pPayload[nPayload] = 0 */239 if( hdr+1 < 4 ){240 TEST_ASSERT_EQUAL_UINT8(0x00, cell[hdr+1]);241 }242 243 freeTestPage(&tp);244}245 246/* Test 3: index leaf (non-intkey): header only includes varint(nKey), then key bytes */247void test_fillInCell_index_basic(void){248 TestPage tp;249 initTestPage(&tp, 0); /* non-intkey (index leaf) */250 251 unsigned char cell[64];252 memset(cell, 0xAA, sizeof(cell));253 int cellSize = -1;254 255 const char *key = "xyz";256 BtreePayload x;257 memset(&x, 0, sizeof(x));258 x.pKey = (void*)key;259 x.nKey = 3;260 x.pData = 0; x.nData = 0; x.nZero = 0;261 262 int rc = test_fillInCell(tp.pPage, cell, &x, &cellSize);263 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);264 265 int hdr = 0;266 u8 expect[64];267 hdr += writeVarint32(expect+hdr, 3);268 int total = hdr + 3;269 if( total<4 ) total = 4;270 271 TEST_ASSERT_EQUAL_INT(total, cellSize);272 TEST_ASSERT_EQUAL_UINT8_ARRAY(expect, cell, hdr);273 TEST_ASSERT_EQUAL_UINT8('x', cell[hdr+0]);274 TEST_ASSERT_EQUAL_UINT8('y', cell[hdr+1]);275 TEST_ASSERT_EQUAL_UINT8('z', cell[hdr+2]);276 277 freeTestPage(&tp);278}279 280/* Test 4: intkey leaf with nZero fills zero bytes after nData */281void test_fillInCell_intkey_with_zero_fill(void){282 TestPage tp;283 initTestPage(&tp, 1);284 285 unsigned char cell[128];286 memset(cell, 0xBB, sizeof(cell));287 int cellSize = -1;288 289 /* nData=2 ("hi"), nZero=3 => payload 5 bytes total */290 BtreePayload x;291 memset(&x, 0, sizeof(x));292 x.pData = (void*)"hi";293 x.nData = 2;294 x.nZero = 3;295 x.nKey = 42;296 297 int rc = test_fillInCell(tp.pPage, cell, &x, &cellSize);298 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);299 300 int hdr = 0;301 u8 expect_hdr[16];302 hdr += writeVarint32(expect_hdr+hdr, 5);303 hdr += writeVarint64(expect_hdr+hdr, 42);304 int total = hdr + 5;305 if( total<4 ) total = 4;306 307 TEST_ASSERT_EQUAL_INT(total, cellSize);308 TEST_ASSERT_EQUAL_UINT8_ARRAY(expect_hdr, cell, hdr);309 /* "hi" then three zeros */310 TEST_ASSERT_EQUAL_UINT8('h', cell[hdr+0]);311 TEST_ASSERT_EQUAL_UINT8('i', cell[hdr+1]);312 TEST_ASSERT_EQUAL_UINT8(0x00, cell[hdr+2]);313 TEST_ASSERT_EQUAL_UINT8(0x00, cell[hdr+3]);314 TEST_ASSERT_EQUAL_UINT8(0x00, cell[hdr+4]);315 316 freeTestPage(&tp);317}318 319int main(void){320 UNITY_BEGIN();321 RUN_TEST(test_fillInCell_intkey_basic);322 RUN_TEST(test_fillInCell_intkey_min4);323 RUN_TEST(test_fillInCell_index_basic);324 RUN_TEST(test_fillInCell_intkey_with_zero_fill);325 return UNITY_END();326}