AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7#include <time.h>8 9/* Helpers to generate unique db filenames per test */10static int g_file_counter = 0;11static void make_test_db_path(char *buf, size_t buflen, const char *suffix){12 snprintf(buf, buflen, "test_pager_%ld_%d%s", (long)time(NULL), g_file_counter++, suffix?suffix:"");13}14 15static void cleanup_db_files(const char *base){16 if( base==NULL ) return;17 char path[1024];18 /* base */19 snprintf(path, sizeof(path), "%s", base);20 remove(path);21 /* -journal */22 snprintf(path, sizeof(path), "%s-journal", base);23 remove(path);24 /* -wal */25 snprintf(path, sizeof(path), "%s-wal", base);26 remove(path);27 /* -shm */28 snprintf(path, sizeof(path), "%s-shm", base);29 remove(path);30}31 32static Pager* getPagerFromDb(sqlite3 *db){33 if( db==0 ) return 0;34 /* Ensure schema is initialized to guarantee pBt ready */35 (void)sqlite3_exec(db, "SELECT 1", 0, 0, 0);36 if( db->nDb<=0 || db->aDb[0].pBt==0 ) return 0;37 return sqlite3BtreePager(db->aDb[0].pBt);38}39 40static void ensure_db_nonempty(sqlite3 *db){41 char *zErr = 0;42 int rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS t(x); INSERT INTO t(x) VALUES(1);", 0, 0, &zErr);43 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, zErr ? zErr : "exec failed");44 if( zErr ) sqlite3_free(zErr);45}46 47void setUp(void) {48 /* Nothing for now */49}50void tearDown(void) {51 /* Nothing for now */52}53 54/* Test 1: Basic successful shared lock acquisition and idempotency */55void test_sqlite3PagerSharedLock_basic_and_idempotent(void){56 char dbpath[1024];57 sqlite3 *db = 0;58 Pager *pPager = 0;59 int rc;60 61 make_test_db_path(dbpath, sizeof(dbpath), ".sqlite");62 cleanup_db_files(dbpath);63 64 rc = sqlite3_open(dbpath, &db);65 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);66 TEST_ASSERT_NOT_NULL(db);67 68 /* Ensure the database file exists and has no WAL mode */69 ensure_db_nonempty(db);70 71 pPager = getPagerFromDb(db);72 TEST_ASSERT_NOT_NULL(pPager);73 74 /* First call - should acquire shared lock and transition to READER */75 rc = sqlite3PagerSharedLock(pPager);76 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);77 TEST_ASSERT_EQUAL_INT(PAGER_READER, pPager->eState);78 TEST_ASSERT_TRUE(pPager->hasHeldSharedLock!=0);79 80 /* Second call - idempotent/no-op behavior - remains READER and OK */81 rc = sqlite3PagerSharedLock(pPager);82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);83 TEST_ASSERT_EQUAL_INT(PAGER_READER, pPager->eState);84 85 sqlite3_close(db);86 cleanup_db_files(dbpath);87}88 89/* Test 2: Read-only connection detects hot journal and returns SQLITE_READONLY_ROLLBACK */90void test_sqlite3PagerSharedLock_readonly_hot_journal_error(void){91 char dbpath[1024];92 sqlite3 *db_rw = 0;93 sqlite3 *db_ro = 0;94 Pager *pPager = 0;95 int rc;96 97 make_test_db_path(dbpath, sizeof(dbpath), ".sqlite");98 cleanup_db_files(dbpath);99 100 /* Create non-empty DB using a read-write connection */101 rc = sqlite3_open(dbpath, &db_rw);102 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);103 TEST_ASSERT_NOT_NULL(db_rw);104 ensure_db_nonempty(db_rw);105 sqlite3_close(db_rw);106 107 /* Open a separate read-only connection */108 rc = sqlite3_open_v2(dbpath, &db_ro, SQLITE_OPEN_READONLY, NULL);109 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);110 TEST_ASSERT_NOT_NULL(db_ro);111 112 pPager = getPagerFromDb(db_ro);113 TEST_ASSERT_NOT_NULL(pPager);114 TEST_ASSERT_TRUE_MESSAGE(pPager->readOnly!=0, "Pager should reflect read-only connection");115 116 /* Create a hot-journal file: exists and first byte != 0 */117 {118 FILE *jf = fopen(pPager->zJournal, "wb");119 TEST_ASSERT_NOT_NULL_MESSAGE(jf, "Failed to create journal file");120 unsigned char b = 0x01;121 size_t nw = fwrite(&b, 1, 1, jf);122 TEST_ASSERT_EQUAL_size_t(1, nw);123 fclose(jf);124 }125 126 /* Call target: should detect hot journal and return SQLITE_READONLY_ROLLBACK */127 rc = sqlite3PagerSharedLock(pPager);128 TEST_ASSERT_EQUAL_INT(SQLITE_READONLY_ROLLBACK, rc);129 130 sqlite3_close(db_ro);131 cleanup_db_files(dbpath);132}133 134/* Test 3: Behavior in WAL mode - ensure function works and ends in READER state */135void test_sqlite3PagerSharedLock_wal_mode(void){136 char dbpath[1024];137 sqlite3 *db = 0;138 Pager *pPager = 0;139 int rc;140 141 make_test_db_path(dbpath, sizeof(dbpath), ".sqlite");142 cleanup_db_files(dbpath);143 144 rc = sqlite3_open(dbpath, &db);145 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);146 TEST_ASSERT_NOT_NULL(db);147 148 /* Switch to WAL mode */149 rc = sqlite3_exec(db, "PRAGMA journal_mode=WAL;", 0, 0, 0);150 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);151 152 pPager = getPagerFromDb(db);153 TEST_ASSERT_NOT_NULL(pPager);154 155 /* Call target: in WAL, should begin read transaction and become READER */156 rc = sqlite3PagerSharedLock(pPager);157 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);158 TEST_ASSERT_EQUAL_INT(PAGER_READER, pPager->eState);159#ifndef SQLITE_OMIT_WAL160 TEST_ASSERT_NOT_NULL_MESSAGE(pPager->pWal, "Pager should be in WAL mode with pWal opened");161#endif162 163 sqlite3_close(db);164 cleanup_db_files(dbpath);165}166 167int main(void){168 UNITY_BEGIN();169 RUN_TEST(test_sqlite3PagerSharedLock_basic_and_idempotent);170 RUN_TEST(test_sqlite3PagerSharedLock_readonly_hot_journal_error);171 RUN_TEST(test_sqlite3PagerSharedLock_wal_mode);172 return UNITY_END();173}