AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4 5/* Unity fixtures */6void setUp(void) {7 /* No global setup required */8}9void tearDown(void) {10 /* No global teardown required */11}12 13/* Helper: open an in-memory DB and enter its mutex */14static sqlite3* open_and_lock_db(void){15 sqlite3 *db = 0;16 int rc = sqlite3_open(":memory:", &db);17 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);18 TEST_ASSERT_NOT_NULL(db);19 /* Ensure we hold the mutex as required by sqlite3ApiExit */20 sqlite3_mutex_enter(db->mutex);21 return db;22}23 24/* Helper: leave mutex and close DB */25static void unlock_and_close_db(sqlite3 *db){26 /* Leave the mutex before closing */27 sqlite3_mutex_leave(db->mutex);28 int rc = sqlite3_close(db);29 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);30}31 32/* Test 1: rc==0 and no mallocFailed -> return 0 */33void test_sqlite3ApiExit_returns_zero_when_ok(void){34 sqlite3 *db = open_and_lock_db();35 36 /* Ensure clean state */37 db->mallocFailed = 0;38 39 int rcOut = sqlite3ApiExit(db, 0);40 TEST_ASSERT_EQUAL_INT(0, rcOut);41 42 unlock_and_close_db(db);43}44 45/* Test 2: rc non-zero (not SQLITE_IOERR_NOMEM), no mallocFailed -> rc & errMask */46void test_sqlite3ApiExit_masks_rc(void){47 sqlite3 *db = open_and_lock_db();48 49 db->mallocFailed = 0;50 /* Choose a mask that will alter the returned value to verify masking */51 db->errMask = 0x03; /* Only low 2 bits should pass through */52 int inputRc = SQLITE_BUSY; /* 5 -> 0b0101 */53 int expected = inputRc & db->errMask; /* 5 & 3 = 1 */54 55 int rcOut = sqlite3ApiExit(db, inputRc);56 TEST_ASSERT_EQUAL_INT(expected, rcOut);57 58 unlock_and_close_db(db);59}60 61/* Test 3: mallocFailed==1 and rc==0 -> clears OOM (if no VDBE running), sets error, returns SQLITE_NOMEM */62void test_sqlite3ApiExit_handles_mallocFailed_clears_state_and_sets_error(void){63 sqlite3 *db = open_and_lock_db();64 65 /* Put DB in a consistent OOM state (sets mallocFailed=1 and disables lookaside) */66 sqlite3OomFault(db);67 TEST_ASSERT_EQUAL_INT(1, db->mallocFailed);68 69 int rcOut = sqlite3ApiExit(db, 0);70 /* Should map to NOMEM and clear mallocFailed when no VDBE is executing */71 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, rcOut);72 TEST_ASSERT_EQUAL_INT(0, db->mallocFailed);73 74 sqlite3_mutex_leave(db->mutex); /* Use API to read error code without holding mutex */75 int err = sqlite3_errcode(db);76 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, err);77 /* Re-enter to match unlock_and_close_db */78 sqlite3_mutex_enter(db->mutex);79 80 unlock_and_close_db(db);81}82 83/* Test 4: rc==SQLITE_IOERR_NOMEM (no mallocFailed) -> sets error, returns SQLITE_NOMEM, mallocFailed unchanged */84void test_sqlite3ApiExit_handles_ioerr_nomem_sets_error(void){85 sqlite3 *db = open_and_lock_db();86 87 db->mallocFailed = 0;88 89 int rcOut = sqlite3ApiExit(db, SQLITE_IOERR_NOMEM);90 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, rcOut);91 TEST_ASSERT_EQUAL_INT(0, db->mallocFailed);92 93 sqlite3_mutex_leave(db->mutex);94 int err = sqlite3_errcode(db);95 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, err);96 sqlite3_mutex_enter(db->mutex);97 98 unlock_and_close_db(db);99}100 101/* Test 5: mallocFailed==1 but VDBE running (nVdbeExec>0) -> return NOMEM, do not clear mallocFailed */102void test_sqlite3ApiExit_does_not_clear_oom_if_vdbe_running(void){103 sqlite3 *db = open_and_lock_db();104 105 /* Simulate a running VDBE before fault so OomFault sets interrupt flag too */106 db->nVdbeExec = 1;107 sqlite3OomFault(db);108 TEST_ASSERT_EQUAL_INT(1, db->mallocFailed);109 110 int rcOut = sqlite3ApiExit(db, 0);111 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, rcOut);112 /* Since a VDBE is "running", sqlite3OomClear should not clear mallocFailed */113 TEST_ASSERT_EQUAL_INT(1, db->mallocFailed);114 115 /* Restore to avoid side-effects on close */116 db->nVdbeExec = 0;117 118 sqlite3_mutex_leave(db->mutex);119 int err = sqlite3_errcode(db);120 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, err);121 sqlite3_mutex_enter(db->mutex);122 123 unlock_and_close_db(db);124}125 126int main(void){127 UNITY_BEGIN();128 RUN_TEST(test_sqlite3ApiExit_returns_zero_when_ok);129 RUN_TEST(test_sqlite3ApiExit_masks_rc);130 RUN_TEST(test_sqlite3ApiExit_handles_mallocFailed_clears_state_and_sets_error);131 RUN_TEST(test_sqlite3ApiExit_handles_ioerr_nomem_sets_error);132 RUN_TEST(test_sqlite3ApiExit_does_not_clear_oom_if_vdbe_running);133 return UNITY_END();134}