AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <time.h>6#include <stdio.h>7 8static sqlite3_vfs *gVfs = NULL;9 10static void unique_name(char *zBuf, size_t nBuf, const char *prefix){11 /* Create a name likely unique for the process/run */12 unsigned long long t = (unsigned long long)time(NULL);13 unsigned long long r = 0;14 sqlite3_randomness(sizeof(r), &r);15 sqlite3_snprintf((int)nBuf, zBuf, "%s_%llu_%llu.jrnl",16 prefix ? prefix : "testjrnl", t, r);17}18 19void setUp(void) {20 int rc = sqlite3_initialize();21 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);22 gVfs = sqlite3_vfs_find(0);23 TEST_ASSERT_NOT_NULL(gVfs);24}25 26void tearDown(void) {27 /* Nothing to do here */28}29 30#if defined(SQLITE_ENABLE_ATOMIC_WRITE) || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)31 32static sqlite3_file* allocJournalFile(sqlite3_vfs *pVfs){33 int n = sqlite3JournalSize(pVfs);34 sqlite3_file *p = (sqlite3_file*)sqlite3_malloc(n);35 TEST_ASSERT_NOT_NULL_MESSAGE(p, "Out of memory allocating sqlite3_file");36 memset(p, 0, n);37 return p;38}39 40void test_sqlite3JournalCreate_memory_only_noop(void) {41 /* Open a pure in-memory MemJournal and ensure sqlite3JournalCreate is a no-op */42 sqlite3_file *pFile = allocJournalFile(gVfs);43 sqlite3MemJournalOpen(pFile);44 45 /* Write some content */46 const char payload[] = "hello, memjournal";47 int rc = pFile->pMethods->xWrite(pFile, payload, (int)sizeof(payload), 0);48 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);49 50 /* Call sqlite3JournalCreate - should be a no-op for memory-only journal */51 rc = sqlite3JournalCreate(pFile);52 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);53 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pFile));54 55 /* Read back to verify content intact */56 char readback[sizeof(payload)];57 memset(readback, 0, sizeof(readback));58 rc = pFile->pMethods->xRead(pFile, readback, (int)sizeof(readback), 0);59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);60 TEST_ASSERT_EQUAL_INT(0, memcmp(payload, readback, sizeof(payload)));61 62 /* Cleanup */63 rc = pFile->pMethods->xClose(pFile);64 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);65 sqlite3_free(pFile);66}67 68void test_sqlite3JournalCreate_flush_to_disk(void) {69 /* Open a MemJournal configured to spill (nSpill>0) and force creation */70 char zName[256];71 unique_name(zName, sizeof(zName), "jrnl_create_ok");72 73 sqlite3_file *pFile = allocJournalFile(gVfs);74 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |75 SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_JOURNAL;76 int nSpill = 64; /* Keep individual writes <=64 to avoid auto-spill in xWrite */77 int rc = sqlite3JournalOpen(gVfs, zName, pFile, flags, nSpill);78 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);79 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pFile));80 81 /* Write two chunks that exactly fill spill threshold but do not exceed it per write */82 unsigned char buf1[32];83 unsigned char buf2[32];84 for(int i=0;i<32;i++){ buf1[i] = (unsigned char)(i+1); buf2[i] = (unsigned char)(i+101); }85 86 rc = pFile->pMethods->xWrite(pFile, buf1, 32, 0);87 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);88 rc = pFile->pMethods->xWrite(pFile, buf2, 32, 32);89 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);90 91 /* Now request creation of the real file */92 rc = sqlite3JournalCreate(pFile);93 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);94 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pFile));95 96 /* Verify that data was flushed to disk by reading back using new OS methods */97 unsigned char rb[64];98 memset(rb, 0, sizeof(rb));99 rc = pFile->pMethods->xRead(pFile, rb, 64, 0);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 TEST_ASSERT_EQUAL_INT(0, memcmp(rb, buf1, 32));102 TEST_ASSERT_EQUAL_INT(0, memcmp(rb+32, buf2, 32));103 104 /* Cleanup: close and delete the created file */105 rc = pFile->pMethods->xClose(pFile);106 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);107 rc = sqlite3OsDelete(gVfs, zName, 0);108 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);109 sqlite3_free(pFile);110}111 112void test_sqlite3JournalCreate_failure_restores_memjournal(void) {113 /* Use an invalid path to force sqlite3OsOpen failure */114 const char *zBad = "this_dir_does_not_exist__/cannot_create.jrnl";115 sqlite3_file *pFile = allocJournalFile(gVfs);116 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |117 SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_JOURNAL;118 int nSpill = 64;119 int rc = sqlite3JournalOpen(gVfs, zBad, pFile, flags, nSpill);120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);121 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pFile));122 123 const char payload[] = "will stay in memory";124 rc = pFile->pMethods->xWrite(pFile, payload, (int)sizeof(payload), 0);125 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);126 127 rc = sqlite3JournalCreate(pFile);128 TEST_ASSERT_NOT_EQUAL_INT(SQLITE_OK, rc);129 TEST_ASSERT_TRUE(sqlite3JournalIsInMemory(pFile));130 131 char readback[sizeof(payload)];132 memset(readback, 0, sizeof(readback));133 rc = pFile->pMethods->xRead(pFile, readback, (int)sizeof(readback), 0);134 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);135 TEST_ASSERT_EQUAL_INT(0, memcmp(payload, readback, sizeof(payload)));136 137 rc = pFile->pMethods->xClose(pFile);138 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);139 sqlite3_free(pFile);140}141 142void test_sqlite3JournalCreate_noop_on_real_file(void) {143 /* Open a real OS file (nSpill=0) and ensure sqlite3JournalCreate is a no-op */144 char zName[256];145 unique_name(zName, sizeof(zName), "jrnl_realfile");146 sqlite3_file *pFile = allocJournalFile(gVfs);147 148 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |149 SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_JOURNAL;150 int rc = sqlite3JournalOpen(gVfs, zName, pFile, flags, 0);151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);152 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pFile));153 154 rc = sqlite3JournalCreate(pFile);155 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);156 TEST_ASSERT_FALSE(sqlite3JournalIsInMemory(pFile));157 158 /* Cleanup */159 rc = pFile->pMethods->xClose(pFile);160 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);161 rc = sqlite3OsDelete(gVfs, zName, 0);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 sqlite3_free(pFile);164}165 166#else167 168void test_sqlite3JournalCreate_skipped(void) {169 TEST_IGNORE_MESSAGE("sqlite3JournalCreate not available: build without SQLITE_ENABLE_ATOMIC_WRITE or SQLITE_ENABLE_BATCH_ATOMIC_WRITE");170}171 172#endif173 174int main(void) {175 UNITY_BEGIN();176#if defined(SQLITE_ENABLE_ATOMIC_WRITE) || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)177 RUN_TEST(test_sqlite3JournalCreate_memory_only_noop);178 RUN_TEST(test_sqlite3JournalCreate_flush_to_disk);179 RUN_TEST(test_sqlite3JournalCreate_failure_restores_memjournal);180 RUN_TEST(test_sqlite3JournalCreate_noop_on_real_file);181#else182 RUN_TEST(test_sqlite3JournalCreate_skipped);183#endif184 return UNITY_END();185}