AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Forward declaration of the test wrapper for the static function */7int test_writeJournalHdr(Pager *pPager);8 9/* A simple in-memory sqlite3_file to capture writes from writeJournalHdr */10typedef struct TestMemFile TestMemFile;11struct TestMemFile {12 sqlite3_file base; /* Must be first */13 unsigned char *data;14 sqlite3_int64 sz;15 sqlite3_int64 cap;16 int devchar; /* Device characteristics to report */17 int write_count; /* Count writes for verification */18};19 20static void memfileEnsureCap(TestMemFile *p, sqlite3_int64 newCap){21 if( newCap <= p->cap ) return;22 sqlite3_int64 ncap = p->cap ? p->cap : 1024;23 while( ncap < newCap ) ncap *= 2;24 unsigned char *nd = (unsigned char*)sqlite3_realloc(p->data, (int)ncap);25 if( nd==0 ){26 /* OOM shouldn't happen in tests; if it does, keep old buffer to avoid UB */27 return;28 }29 if( ncap > p->cap ){30 memset(nd + p->cap, 0, (size_t)(ncap - p->cap));31 }32 p->data = nd;33 p->cap = ncap;34}35 36static int memClose(sqlite3_file *pFile){37 TestMemFile *p = (TestMemFile*)pFile;38 if( p->data ) sqlite3_free(p->data);39 p->data = 0;40 p->sz = 0;41 p->cap = 0;42 p->devchar = 0;43 p->write_count = 0;44 p->base.pMethods = 0;45 return SQLITE_OK;46}47static int memRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite3_int64 iOfst){48 TestMemFile *p = (TestMemFile*)pFile;49 if( iOfst + iAmt > p->sz ){50 /* As per VFS contract, read beyond EOF returns SQLITE_IOERR_SHORT_READ51 but also fills available bytes and zero-fills the rest. For tests,52 just zero-fill fully. */53 memset(zBuf, 0, iAmt);54 return SQLITE_OK;55 }56 memcpy(zBuf, p->data + iOfst, (size_t)iAmt);57 return SQLITE_OK;58}59static int memWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite3_int64 iOfst){60 TestMemFile *p = (TestMemFile*)pFile;61 sqlite3_int64 end = iOfst + iAmt;62 memfileEnsureCap(p, end);63 if( iOfst > p->sz ){64 memset(p->data + p->sz, 0, (size_t)(iOfst - p->sz));65 }66 memcpy(p->data + iOfst, zBuf, (size_t)iAmt);67 if( end > p->sz ) p->sz = end;68 p->write_count++;69 return SQLITE_OK;70}71static int memTruncate(sqlite3_file *pFile, sqlite3_int64 size){72 TestMemFile *p = (TestMemFile*)pFile;73 if( size < p->sz ){74 p->sz = size;75 }else if( size > p->sz ){76 memfileEnsureCap(p, size);77 memset(p->data + p->sz, 0, (size_t)(size - p->sz));78 p->sz = size;79 }80 return SQLITE_OK;81}82static int memSync(sqlite3_file *pFile, int flags){83 (void)pFile; (void)flags;84 return SQLITE_OK;85}86static int memFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){87 TestMemFile *p = (TestMemFile*)pFile;88 *pSize = p->sz;89 return SQLITE_OK;90}91static int memLock(sqlite3_file *pFile, int eLock){ (void)pFile; (void)eLock; return SQLITE_OK; }92static int memUnlock(sqlite3_file *pFile, int eLock){ (void)pFile; (void)eLock; return SQLITE_OK; }93static int memCheckReservedLock(sqlite3_file *pFile, int *pResOut){ (void)pFile; *pResOut = 0; return SQLITE_OK; }94static int memFileControl(sqlite3_file *pFile, int op, void *pArg){ (void)pFile; (void)op; (void)pArg; return SQLITE_NOTFOUND; }95static int memSectorSize(sqlite3_file *pFile){ (void)pFile; return 512; }96static int memDeviceCharacteristics(sqlite3_file *pFile){97 TestMemFile *p = (TestMemFile*)pFile;98 return p->devchar;99}100 101static const sqlite3_io_methods TestMemFileMethods = {102 1, /* iVersion */103 memClose,104 memRead,105 memWrite,106 memTruncate,107 memSync,108 memFileSize,109 memLock,110 memUnlock,111 memCheckReservedLock,112 memFileControl,113 memSectorSize,114 memDeviceCharacteristics115};116 117/* Helper to initialize a TestMemFile (open file) */118static void memfileInit(TestMemFile *p, int devchar){119 memset(p, 0, sizeof(*p));120 p->base.pMethods = &TestMemFileMethods;121 p->devchar = devchar;122 p->data = 0;123 p->sz = 0;124 p->cap = 0;125 p->write_count = 0;126}127 128/* Unity hooks */129void setUp(void) { }130void tearDown(void) { }131 132/* Utility to allocate and zero a Pager */133static Pager *allocZeroPager(void){134 Pager *p = (Pager*)sqlite3_malloc((int)sizeof(Pager));135 TEST_ASSERT_NOT_NULL(p);136 memset(p, 0, sizeof(Pager));137 return p;138}139 140/* Read big-endian 32-bit from buffer using SQLite macro */141static u32 be32_at(const unsigned char *p){142 return get4byte((u8*)p);143}144 145/* Test 1: Basic header write at offset 0, noSync=1 path */146void test_writeJournalHdr_basic_zero_offset(void){147 Pager *p = allocZeroPager();148 149 /* Set up journal file (jfd) */150 TestMemFile jfd;151 memfileInit(&jfd, 0);152 153 /* Pager fields */154 p->jfd = &jfd.base;155 p->fd = 0; /* NULL - allowed because noSync=1 */156 p->noSync = 1; /* Triggers 0xFFFFFFFF in nRec and short-circuits fd usage */157 p->journalMode = 0; /* Not MEMORY */158 p->journalOff = 0; /* Start at 0 */159 p->dbOrigSize = 12345;160 p->sectorSize = 512;161 p->pageSize = 1024;162 163 p->pTmpSpace = (char*)sqlite3_malloc((int)p->pageSize);164 TEST_ASSERT_NOT_NULL(p->pTmpSpace);165 166 int rc = test_writeJournalHdr(p);167 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);168 169 /* Verify offsets */170 TEST_ASSERT_EQUAL_INT64(0, p->journalHdr);171 TEST_ASSERT_EQUAL_INT64(512, p->journalOff);172 TEST_ASSERT_EQUAL_INT64(512, jfd.sz);173 174 /* Verify header fields at offset 0 */175 const unsigned char *hdr = jfd.data;176 /* nRec field should be 0xFFFFFFFF when noSync=1 */177 TEST_ASSERT_EQUAL_UINT32(0xFFFFFFFFu, be32_at(hdr + 8));178 /* dbOrigSize, sectorSize, pageSize */179 TEST_ASSERT_EQUAL_UINT32(p->dbOrigSize, be32_at(hdr + 16));180 TEST_ASSERT_EQUAL_UINT32(p->sectorSize, be32_at(hdr + 20));181 TEST_ASSERT_EQUAL_UINT32(p->pageSize, be32_at(hdr + 24));182 183 sqlite3_free(p->pTmpSpace);184 sqlite3_free(p);185 memClose(&jfd.base);186}187 188/* Test 2: Alignment from non-zero journalOff and savepoint update */189void test_writeJournalHdr_alignment_and_savepoint(void){190 Pager *p = allocZeroPager();191 192 TestMemFile jfd;193 memfileInit(&jfd, 0);194 195 p->jfd = &jfd.base;196 p->fd = 0;197 p->noSync = 1;198 p->journalMode = 0;199 p->journalOff = 100; /* Unaligned - should align up to 512 */200 p->dbOrigSize = 7;201 p->sectorSize = 512;202 p->pageSize = 1024;203 204 /* Savepoints: first has iHdrOffset==0 -> should become old journalOff (100).205 Second already set -> unchanged. */206 p->nSavepoint = 2;207 p->aSavepoint = (PagerSavepoint*)sqlite3_malloc(sizeof(PagerSavepoint)*2);208 TEST_ASSERT_NOT_NULL(p->aSavepoint);209 memset(p->aSavepoint, 0, sizeof(PagerSavepoint)*2);210 p->aSavepoint[0].iHdrOffset = 0;211 p->aSavepoint[1].iHdrOffset = 123;212 213 p->pTmpSpace = (char*)sqlite3_malloc((int)p->pageSize);214 TEST_ASSERT_NOT_NULL(p->pTmpSpace);215 216 int rc = test_writeJournalHdr(p);217 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);218 219 TEST_ASSERT_EQUAL_INT64(512, p->journalHdr);220 TEST_ASSERT_EQUAL_INT64(1024, p->journalOff);221 TEST_ASSERT_EQUAL_INT64(1024, jfd.sz);222 223 /* Savepoint expectations */224 TEST_ASSERT_EQUAL_INT64(100, p->aSavepoint[0].iHdrOffset);225 TEST_ASSERT_EQUAL_INT64(123, p->aSavepoint[1].iHdrOffset);226 227 /* Verify header fields at offset 512 */228 const unsigned char *hdr = jfd.data + 512;229 TEST_ASSERT_EQUAL_UINT32(0xFFFFFFFFu, be32_at(hdr + 8));230 TEST_ASSERT_EQUAL_UINT32(p->dbOrigSize, be32_at(hdr + 16));231 TEST_ASSERT_EQUAL_UINT32(p->sectorSize, be32_at(hdr + 20));232 TEST_ASSERT_EQUAL_UINT32(p->pageSize, be32_at(hdr + 24));233 234 sqlite3_free(p->aSavepoint);235 sqlite3_free(p->pTmpSpace);236 sqlite3_free(p);237 memClose(&jfd.base);238}239 240/* Test 3: Multiple writes when pageSize < sectorSize */241void test_writeJournalHdr_multiple_writes_when_pagesize_lt_sector(void){242 Pager *p = allocZeroPager();243 244 TestMemFile jfd;245 memfileInit(&jfd, 0);246 247 p->jfd = &jfd.base;248 p->fd = 0;249 p->noSync = 1;250 p->journalMode = 0;251 p->journalOff = 0;252 p->dbOrigSize = 999;253 p->sectorSize = 512; /* Header sector */254 p->pageSize = 256; /* Smaller than sector -> expects 2 writes of 256 */255 256 p->pTmpSpace = (char*)sqlite3_malloc((int)p->pageSize);257 TEST_ASSERT_NOT_NULL(p->pTmpSpace);258 259 int rc = test_writeJournalHdr(p);260 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);261 262 TEST_ASSERT_EQUAL_INT64(0, p->journalHdr);263 TEST_ASSERT_EQUAL_INT64(512, p->journalOff);264 TEST_ASSERT_EQUAL_INT64(512, jfd.sz);265 TEST_ASSERT_EQUAL_INT(2, jfd.write_count);266 267 const unsigned char *hdr = jfd.data;268 TEST_ASSERT_EQUAL_UINT32(0xFFFFFFFFu, be32_at(hdr + 8));269 TEST_ASSERT_EQUAL_UINT32(p->dbOrigSize, be32_at(hdr + 16));270 TEST_ASSERT_EQUAL_UINT32(p->sectorSize, be32_at(hdr + 20));271 TEST_ASSERT_EQUAL_UINT32(p->pageSize, be32_at(hdr + 24));272 273 sqlite3_free(p->pTmpSpace);274 sqlite3_free(p);275 memClose(&jfd.base);276}277 278/* Test 4: SAFE_APPEND path (noSync=0, fd has SAFE_APPEND) -> nRec=0xFFFFFFFF */279void test_writeJournalHdr_safe_append_sets_nRec_FFFFFFFF(void){280 Pager *p = allocZeroPager();281 282 TestMemFile jfd, fd;283 memfileInit(&jfd, 0);284 285 /* fd device with SAFE_APPEND characteristic */286 memfileInit(&fd, SQLITE_IOCAP_SAFE_APPEND);287 288 p->jfd = &jfd.base;289 p->fd = &fd.base;290 p->noSync = 0; /* Must be 0 to test SAFE_APPEND branch */291 p->journalMode = 0; /* Not MEMORY */292 p->journalOff = 0;293 p->dbOrigSize = 42;294 p->sectorSize = 512;295 p->pageSize = 4096;296 297 p->pTmpSpace = (char*)sqlite3_malloc((int)p->pageSize);298 TEST_ASSERT_NOT_NULL(p->pTmpSpace);299 300 int rc = test_writeJournalHdr(p);301 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);302 303 TEST_ASSERT_EQUAL_INT64(0, p->journalHdr);304 TEST_ASSERT_EQUAL_INT64(512, p->journalOff);305 TEST_ASSERT_EQUAL_INT64(512, jfd.sz);306 307 const unsigned char *hdr = jfd.data;308 TEST_ASSERT_EQUAL_UINT32(0xFFFFFFFFu, be32_at(hdr + 8));309 TEST_ASSERT_EQUAL_UINT32(p->dbOrigSize, be32_at(hdr + 16));310 TEST_ASSERT_EQUAL_UINT32(p->sectorSize, be32_at(hdr + 20));311 TEST_ASSERT_EQUAL_UINT32(p->pageSize, be32_at(hdr + 24));312 313 sqlite3_free(p->pTmpSpace);314 sqlite3_free(p);315 memClose(&jfd.base);316 memClose(&fd.base);317}318 319/* Test 5: Non-safe path (noSync=0, fd has no SAFE_APPEND) -> nRec==0 */320void test_writeJournalHdr_no_safe_append_sets_nRec_zero(void){321 Pager *p = allocZeroPager();322 323 TestMemFile jfd, fd;324 memfileInit(&jfd, 0);325 memfileInit(&fd, 0); /* No special device characteristics */326 327 p->jfd = &jfd.base;328 p->fd = &fd.base;329 p->noSync = 0;330 p->journalMode = 0; /* Not MEMORY */331 p->journalOff = 0;332 p->dbOrigSize = 31415;333 p->sectorSize = 512;334 p->pageSize = 2048;335 336 p->pTmpSpace = (char*)sqlite3_malloc((int)p->pageSize);337 TEST_ASSERT_NOT_NULL(p->pTmpSpace);338 339 int rc = test_writeJournalHdr(p);340 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);341 342 TEST_ASSERT_EQUAL_INT64(0, p->journalHdr);343 TEST_ASSERT_EQUAL_INT64(512, p->journalOff);344 TEST_ASSERT_EQUAL_INT64(512, jfd.sz);345 346 const unsigned char *hdr = jfd.data;347 /* In this branch, first 12 bytes were zeroed; nRec at +8 is 0. */348 TEST_ASSERT_EQUAL_UINT32(0u, be32_at(hdr + 8));349 TEST_ASSERT_EQUAL_UINT32(p->dbOrigSize, be32_at(hdr + 16));350 TEST_ASSERT_EQUAL_UINT32(p->sectorSize, be32_at(hdr + 20));351 TEST_ASSERT_EQUAL_UINT32(p->pageSize, be32_at(hdr + 24));352 353 sqlite3_free(p->pTmpSpace);354 sqlite3_free(p);355 memClose(&jfd.base);356 memClose(&fd.base);357}358 359int main(void){360 UNITY_BEGIN();361 RUN_TEST(test_writeJournalHdr_basic_zero_offset);362 RUN_TEST(test_writeJournalHdr_alignment_and_savepoint);363 RUN_TEST(test_writeJournalHdr_multiple_writes_when_pagesize_lt_sector);364 RUN_TEST(test_writeJournalHdr_safe_append_sets_nRec_FFFFFFFF);365 RUN_TEST(test_writeJournalHdr_no_safe_append_sets_nRec_zero);366 return UNITY_END();367}