AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Global counters to observe destructor behavior */7static int g_destroy_calls = 0;8static void *g_last_user_data = NULL;9 10/* The wrapper for the static function under test is provided by the module:11 void test_functionDestroy(sqlite3 *db, FuncDef *p); */12void test_functionDestroy(sqlite3 *db, FuncDef *p);13 14/* Test destructor callback */15static void myDestroy(void *p){16 g_destroy_calls++;17 g_last_user_data = p;18}19 20/* Helper to allocate and initialize a FuncDestructor using the SQLite db allocator */21static FuncDestructor* makeDestructor(sqlite3 *db, int nRef, void *userData){22 FuncDestructor *fd = (FuncDestructor*)sqlite3DbMallocZero(db, sizeof(FuncDestructor));23 TEST_ASSERT_NOT_NULL_MESSAGE(fd, "sqlite3DbMallocZero returned NULL for FuncDestructor");24 fd->nRef = nRef;25 fd->xDestroy = myDestroy;26 fd->pUserData = userData;27 return fd;28}29 30void setUp(void) {31 g_destroy_calls = 0;32 g_last_user_data = NULL;33}34 35void tearDown(void) {36 /* Nothing to clean up globally */37}38 39/* Test: No-op when p->u.pDestructor == NULL */40void test_functionDestroy_no_destructor_noop(void){41 sqlite3 *db = NULL;42 int rc = sqlite3_open(":memory:", &db);43 TEST_ASSERT_EQUAL_INT(0, rc);44 TEST_ASSERT_NOT_NULL(db);45 46 FuncDef f;47 memset(&f, 0, sizeof(f));48 /* Ensure not builtin to avoid assertion in functionDestroy */49 f.funcFlags = 0;50 f.u.pDestructor = NULL;51 52 int before_calls = g_destroy_calls;53 test_functionDestroy(db, &f);54 TEST_ASSERT_EQUAL_INT(before_calls, g_destroy_calls);55 56 sqlite3_close(db);57}58 59/* Test: nRef > 1 => decrement only, no xDestroy, then free with another call */60void test_functionDestroy_decrement_only_then_free(void){61 sqlite3 *db = NULL;62 int rc = sqlite3_open(":memory:", &db);63 TEST_ASSERT_EQUAL_INT(0, rc);64 TEST_ASSERT_NOT_NULL(db);65 66 FuncDef f;67 memset(&f, 0, sizeof(f));68 f.funcFlags = 0;69 70 int sentinel = 42;71 FuncDestructor *fd = makeDestructor(db, 2, &sentinel);72 f.u.pDestructor = fd;73 74 int before_calls = g_destroy_calls;75 76 /* First destroy: nRef 2 -> 1, should not call xDestroy */77 test_functionDestroy(db, &f);78 TEST_ASSERT_EQUAL_INT(before_calls, g_destroy_calls);79 TEST_ASSERT_EQUAL_INT(1, fd->nRef);80 81 /* Second destroy: nRef 1 -> 0, should call xDestroy and free the destructor.82 Do not dereference fd after this call. */83 test_functionDestroy(db, &f);84 TEST_ASSERT_EQUAL_INT(before_calls + 1, g_destroy_calls);85 TEST_ASSERT_EQUAL_PTR(&sentinel, g_last_user_data);86 87 sqlite3_close(db);88}89 90/* Test: nRef == 1 => immediate xDestroy call and free */91void test_functionDestroy_direct_free(void){92 sqlite3 *db = NULL;93 int rc = sqlite3_open(":memory:", &db);94 TEST_ASSERT_EQUAL_INT(0, rc);95 TEST_ASSERT_NOT_NULL(db);96 97 FuncDef f;98 memset(&f, 0, sizeof(f));99 f.funcFlags = 0;100 101 int sentinel = 777;102 FuncDestructor *fd = makeDestructor(db, 1, &sentinel);103 f.u.pDestructor = fd;104 105 int before_calls = g_destroy_calls;106 107 test_functionDestroy(db, &f);108 TEST_ASSERT_EQUAL_INT(before_calls + 1, g_destroy_calls);109 TEST_ASSERT_EQUAL_PTR(&sentinel, g_last_user_data);110 111 sqlite3_close(db);112}113 114/* Test: Shared destructor across two FuncDef instances */115void test_functionDestroy_shared_destructor_across_copies(void){116 sqlite3 *db = NULL;117 int rc = sqlite3_open(":memory:", &db);118 TEST_ASSERT_EQUAL_INT(0, rc);119 TEST_ASSERT_NOT_NULL(db);120 121 FuncDef f1, f2;122 memset(&f1, 0, sizeof(f1));123 memset(&f2, 0, sizeof(f2));124 f1.funcFlags = 0;125 f2.funcFlags = 0;126 127 int sentinel = 1234;128 FuncDestructor *fd = makeDestructor(db, 2, &sentinel);129 f1.u.pDestructor = fd;130 f2.u.pDestructor = fd;131 132 int before_calls = g_destroy_calls;133 134 /* First destroy from one copy: nRef 2 -> 1, no xDestroy */135 test_functionDestroy(db, &f1);136 TEST_ASSERT_EQUAL_INT(before_calls, g_destroy_calls);137 TEST_ASSERT_EQUAL_INT(1, fd->nRef);138 139 /* Second destroy from the other copy: nRef 1 -> 0, calls xDestroy */140 test_functionDestroy(db, &f2);141 TEST_ASSERT_EQUAL_INT(before_calls + 1, g_destroy_calls);142 TEST_ASSERT_EQUAL_PTR(&sentinel, g_last_user_data);143 144 sqlite3_close(db);145}146 147int main(void){148 UNITY_BEGIN();149 RUN_TEST(test_functionDestroy_no_destructor_noop);150 RUN_TEST(test_functionDestroy_decrement_only_then_free);151 RUN_TEST(test_functionDestroy_direct_free);152 RUN_TEST(test_functionDestroy_shared_destructor_across_copies);153 return UNITY_END();154}