AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global counters for destructor invocations */7static int gFuncDestroyedCount = 0;8static int gCollationDestroyedCount = 0;9 10/* Simple scalar function to register */11static void testScalarFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv){12 (void)argc;13 (void)argv;14 sqlite3_result_null(ctx);15}16 17/* Destructor for function user data: increments the counter pointed to by p */18static void xUserDataDestroy(void *p){19 if( p ) (*(int*)p)++;20}21 22/* Simple collation compare function */23static int tcolCompare(void *pArg, int aLen, const void *a, int bLen, const void *b){24 (void)pArg;25 int n = aLen < bLen ? aLen : bLen;26 int c = memcmp(a, b, n);27 if( c==0 ){28 if( aLen < bLen ) return -1;29 if( aLen > bLen ) return 1;30 return 0;31 }32 return c;33}34 35/* Destructor for collation user data: increments the counter pointed to by p */36static void xCollationDestroy(void *p){37 if( p ) (*(int*)p)++;38}39 40/* Helper: open an in-memory database and assert success */41static sqlite3* open_memory_db(void){42 sqlite3 *db = 0;43 int rc = sqlite3_open(":memory:", &db);44 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);45 TEST_ASSERT_NOT_NULL(db);46 return db;47}48 49void setUp(void) {50 gFuncDestroyedCount = 0;51 gCollationDestroyedCount = 0;52}53void tearDown(void) {54 /* nothing */55}56 57/* 1) Non-zombie: should just release the mutex and return; DB remains usable */58void test_sqlite3LeaveMutexAndCloseZombie_nonZombie_earlyReturn_doesNotClose(void){59 sqlite3 *db = open_memory_db();60 61 /* Ensure not zombie (normal open state). */62 TEST_ASSERT_NOT_EQUAL_INT(SQLITE_STATE_ZOMBIE, db->eOpenState);63 64 /* Call under mutex; function should only unlock and return. */65 sqlite3_mutex_enter(db->mutex);66 sqlite3LeaveMutexAndCloseZombie(db);67 68 /* DB should remain usable. Prepare/step/finalize a simple statement. */69 sqlite3_stmt *pStmt = 0;70 int rc = sqlite3_prepare_v2(db, "SELECT 1", -1, &pStmt, 0);71 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);72 TEST_ASSERT_NOT_NULL(pStmt);73 rc = sqlite3_step(pStmt);74 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);75 rc = sqlite3_finalize(pStmt);76 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);77 78 /* Close normally. */79 rc = sqlite3_close(db);80 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);81}82 83/* 2) Zombie but busy (unfinalized stmt): should only release mutex and return; DB not closed */84void test_sqlite3LeaveMutexAndCloseZombie_zombieBusy_stmt_earlyReturn(void){85 sqlite3 *db = open_memory_db();86 87 /* Create an unfinalized statement so connectionIsBusy(db) == true */88 sqlite3_stmt *pStmt = 0;89 int rc = sqlite3_prepare_v2(db, "SELECT 42", -1, &pStmt, 0);90 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);91 TEST_ASSERT_NOT_NULL(pStmt);92 93 /* Mark connection as zombie and call target under mutex */94 db->eOpenState = SQLITE_STATE_ZOMBIE;95 sqlite3_mutex_enter(db->mutex);96 sqlite3LeaveMutexAndCloseZombie(db);97 98 /* Verify DB not closed by finalizing the statement successfully */99 rc = sqlite3_finalize(pStmt);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 102 /* Now the connection is zombie and not busy; close it fully via target */103 sqlite3_mutex_enter(db->mutex);104 sqlite3LeaveMutexAndCloseZombie(db);105 106 /* db has been freed; do not access it further. Success is no crash. */107}108 109/* 3) Zombie and idle: should free connection and invoke user destructors */110void test_sqlite3LeaveMutexAndCloseZombie_zombieIdle_calls_destructors_and_frees(void){111 sqlite3 *db = open_memory_db();112 113 /* Register a user function with a destructor */114 int rc = sqlite3_create_function_v2(115 db, "tfunc", 1, SQLITE_UTF8, &gFuncDestroyedCount,116 testScalarFunc, 0, 0, xUserDataDestroy117 );118 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);119 120 /* Register a collation with a destructor */121 rc = sqlite3_create_collation_v2(122 db, "tcol", SQLITE_UTF8, &gCollationDestroyedCount,123 tcolCompare, xCollationDestroy124 );125 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);126 127 /* Ensure there are no outstanding statements/backups and mark as zombie */128 db->eOpenState = SQLITE_STATE_ZOMBIE;129 130 /* Call target under mutex to force full close */131 sqlite3_mutex_enter(db->mutex);132 sqlite3LeaveMutexAndCloseZombie(db);133 134 /* DB has been freed. Verify destructors were invoked exactly once each. */135 TEST_ASSERT_EQUAL_INT(1, gFuncDestroyedCount);136 TEST_ASSERT_EQUAL_INT(1, gCollationDestroyedCount);137}138 139int main(void){140 UNITY_BEGIN();141 RUN_TEST(test_sqlite3LeaveMutexAndCloseZombie_nonZombie_earlyReturn_doesNotClose);142 RUN_TEST(test_sqlite3LeaveMutexAndCloseZombie_zombieBusy_stmt_earlyReturn);143 RUN_TEST(test_sqlite3LeaveMutexAndCloseZombie_zombieIdle_calls_destructors_and_frees);144 return UNITY_END();145}