AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* External APIs from memjournal.c we will use */7extern int sqlite3JournalOpen(sqlite3_vfs *pVfs, const char *zName,8 sqlite3_file *pJfd, int flags, int nSpill);9extern int sqlite3JournalIsInMemory(sqlite3_file *p);10extern int sqlite3JournalSize(sqlite3_vfs *pVfs);11 12/* Wrapper for the static function under test (use void* to avoid needing MemJournal type) */13extern int test_memjrnlCreateFile(void *p);14 15/* We will use these os-layer wrappers for validation */16extern int sqlite3OsRead(sqlite3_file*, void*, int, i64);17extern int sqlite3OsClose(sqlite3_file*);18extern int sqlite3OsFileSize(sqlite3_file*, i64*);19 20/* Helpers */21static sqlite3_file *alloc_journal(sqlite3_vfs *pVfs, const char *zName, int nSpill){22 int flags = SQLITE_OPEN_READWRITE23 | SQLITE_OPEN_CREATE24 | SQLITE_OPEN_EXCLUSIVE25 | SQLITE_OPEN_DELETEONCLOSE26 | SQLITE_OPEN_MAIN_JOURNAL;27 int sz = sqlite3JournalSize(pVfs);28 sqlite3_file *pJfd = (sqlite3_file*)sqlite3_malloc(sz);29 TEST_ASSERT_NOT_NULL_MESSAGE(pJfd, "sqlite3_malloc failed for journal handle");30 memset(pJfd, 0, sz);31 int rc = sqlite3JournalOpen(pVfs, zName, pJfd, flags, nSpill);32 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3JournalOpen failed");33 return pJfd;34}35 36static void free_journal(sqlite3_file *pJfd){37 if( pJfd ){38 /* Close OS handle if open (no-op if still memjrnl, but our tests flush before close) */39 sqlite3OsClose(pJfd);40 sqlite3_free(pJfd);41 }42}43 44void setUp(void) {45 sqlite3_initialize();46}47void tearDown(void) {48}49 50/* Test 1: Basic flush of data to disk */51void test_memjrnlCreateFile_flush_writes_data(void){52 sqlite3_vfs *pVfs = sqlite3_vfs_find(NULL);53 TEST_ASSERT_NOT_NULL(pVfs);54 55 int nSpill = 8192; /* Keep writes below this to avoid auto-flush */56 sqlite3_file *pJfd = alloc_journal(pVfs, "test_memjrnl_1.jrn", nSpill);57 58 /* Ensure in-memory before flushing */59 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pJfd));60 61 const int n = 3000;62 unsigned char *data = (unsigned char*)sqlite3_malloc(n);63 TEST_ASSERT_NOT_NULL(data);64 for(int i=0;i<n;i++) data[i] = (unsigned char)(i & 0xFF);65 66 /* Write to in-memory journal */67 int rc = pJfd->pMethods->xWrite(pJfd, data, n, 0);68 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);69 70 /* Flush to disk using the function under test */71 rc = test_memjrnlCreateFile((void*)pJfd);72 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);73 74 /* Now it should not be an in-memory journal */75 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pJfd));76 77 /* Verify file size */78 i64 sz = -1;79 rc = sqlite3OsFileSize(pJfd, &sz);80 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);81 TEST_ASSERT_EQUAL_INT64(n, sz);82 83 /* Verify content */84 unsigned char *out = (unsigned char*)sqlite3_malloc(n);85 TEST_ASSERT_NOT_NULL(out);86 memset(out, 0, n);87 rc = sqlite3OsRead(pJfd, out, n, 0);88 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);89 TEST_ASSERT_EQUAL_INT(0, memcmp(data, out, n));90 91 sqlite3_free(out);92 sqlite3_free(data);93 free_journal(pJfd);94}95 96/* Test 2: Flush with zero-length journal creates empty real file */97void test_memjrnlCreateFile_zero_length_creates_empty_file(void){98 sqlite3_vfs *pVfs = sqlite3_vfs_find(NULL);99 TEST_ASSERT_NOT_NULL(pVfs);100 101 int nSpill = 1024;102 sqlite3_file *pJfd = alloc_journal(pVfs, "test_memjrnl_2.jrn", nSpill);103 104 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pJfd));105 106 /* No writes performed, flush to create empty file */107 int rc = test_memjrnlCreateFile((void*)pJfd);108 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);109 110 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pJfd));111 112 i64 sz = -1;113 rc = sqlite3OsFileSize(pJfd, &sz);114 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);115 TEST_ASSERT_EQUAL_INT64(0, sz);116 117 free_journal(pJfd);118}119 120/* Test 3: Header overwrite before flush is preserved */121void test_memjrnlCreateFile_header_overwrite(void){122 sqlite3_vfs *pVfs = sqlite3_vfs_find(NULL);123 TEST_ASSERT_NOT_NULL(pVfs);124 125 int nSpill = 512;126 sqlite3_file *pJfd = alloc_journal(pVfs, "test_memjrnl_3.jrn", nSpill);127 128 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pJfd));129 130 /* Base content */131 const int n = 100;132 unsigned char *data = (unsigned char*)sqlite3_malloc(n);133 TEST_ASSERT_NOT_NULL(data);134 for(int i=0;i<n;i++) data[i] = (unsigned char)(i + 3);135 136 int rc = pJfd->pMethods->xWrite(pJfd, data, n, 0);137 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);138 139 /* Overwrite first 28 bytes (simulate journal header rewrite) */140 const int hlen = 28;141 unsigned char header[hlen];142 for(int i=0;i<hlen;i++) header[i] = (unsigned char)(0xA0 + i);143 rc = pJfd->pMethods->xWrite(pJfd, header, hlen, 0);144 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);145 146 /* Flush to disk */147 rc = test_memjrnlCreateFile((void*)pJfd);148 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);149 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pJfd));150 151 /* Verify size */152 i64 sz = -1;153 rc = sqlite3OsFileSize(pJfd, &sz);154 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);155 TEST_ASSERT_EQUAL_INT64(n, sz);156 157 /* Verify content: header then original tail */158 unsigned char *out = (unsigned char*)sqlite3_malloc(n);159 TEST_ASSERT_NOT_NULL(out);160 memset(out, 0, n);161 rc = sqlite3OsRead(pJfd, out, n, 0);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 164 TEST_ASSERT_EQUAL_INT(0, memcmp(out, header, hlen));165 TEST_ASSERT_EQUAL_INT(0, memcmp(out+hlen, data+hlen, n-hlen));166 167 sqlite3_free(out);168 sqlite3_free(data);169 free_journal(pJfd);170}171 172/* Test 4: Exact boundary write (iAmt+iOfst == nSpill) stays in memory until explicit flush */173void test_memjrnlCreateFile_exact_boundary(void){174 sqlite3_vfs *pVfs = sqlite3_vfs_find(NULL);175 TEST_ASSERT_NOT_NULL(pVfs);176 177 int nSpill = 128;178 sqlite3_file *pJfd = alloc_journal(pVfs, "test_memjrnl_4.jrn", nSpill);179 180 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pJfd));181 182 const int n = 128; /* Exactly equal to nSpill */183 unsigned char data[n];184 for(int i=0;i<n;i++) data[i] = (unsigned char)(255 - (i & 0xFF));185 186 int rc = pJfd->pMethods->xWrite(pJfd, data, n, 0);187 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);188 189 /* Still in memory since iAmt + iOfst == nSpill (not >) */190 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pJfd));191 192 /* Now flush to disk */193 rc = test_memjrnlCreateFile((void*)pJfd);194 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);195 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pJfd));196 197 i64 sz = -1;198 rc = sqlite3OsFileSize(pJfd, &sz);199 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);200 TEST_ASSERT_EQUAL_INT64(n, sz);201 202 unsigned char out[n];203 memset(out, 0, n);204 rc = sqlite3OsRead(pJfd, out, n, 0);205 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);206 TEST_ASSERT_EQUAL_INT(0, memcmp(data, out, n));207 208 free_journal(pJfd);209}210 211int main(void){212 UNITY_BEGIN();213 RUN_TEST(test_memjrnlCreateFile_flush_writes_data);214 RUN_TEST(test_memjrnlCreateFile_zero_length_creates_empty_file);215 RUN_TEST(test_memjrnlCreateFile_header_overwrite);216 RUN_TEST(test_memjrnlCreateFile_exact_boundary);217 return UNITY_END();218}