AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Prototype for the provided test wrapper around the static function */7extern int test_createFunctionApi(8 sqlite3 *db,9 const char *zFunc,10 int nArg,11 int enc,12 void *p,13 void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),14 void (*xStep)(sqlite3_context*,int,sqlite3_value**),15 void (*xFinal)(sqlite3_context*),16 void (*xValue)(sqlite3_context*),17 void (*xInverse)(sqlite3_context*,int,sqlite3_value**),18 void(*xDestroy)(void*)19);20 21/* Minimal scalar function: returns arg0 + 1 (or 1 if null) */22static void sfunc_add_one(sqlite3_context *ctx, int argc, sqlite3_value **argv){23 int v = 0;24 if( argc>0 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){25 v = sqlite3_value_int(argv[0]);26 }27 sqlite3_result_int(ctx, v+1);28}29 30/* No-op aggregate step and finalizers */31static void step_noop(sqlite3_context *ctx, int argc, sqlite3_value **argv){32 (void)ctx; (void)argc; (void)argv;33}34static void final_noop(sqlite3_context *ctx){35 (void)ctx;36}37 38/* No-op value/inverse (for window-only functions) */39static void value_noop(sqlite3_context *ctx){40 (void)ctx;41}42static void inverse_noop(sqlite3_context *ctx, int argc, sqlite3_value **argv){43 (void)ctx; (void)argc; (void)argv;44}45 46/* Destructor that increments an integer at the provided pointer */47static void destroy_incr(void *p){48 int *pi = (int*)p;49 if( pi ) (*pi)++;50}51 52void setUp(void) {53 /* No global setup needed */54}55void tearDown(void) {56 /* No global teardown needed */57}58 59/* Test: Deleting a non-existent function should be a no-op (SQLITE_OK),60 and xDestroy must be called once because the FuncDestructor is unused. */61void test_createFunctionApi_delete_noop_calls_destructor(void){62 sqlite3 *db = 0;63 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));64 65 int dcount = 0;66 int rc = test_createFunctionApi(67 db, "nonexistent_func", -1, SQLITE_UTF8,68 &dcount,69 0, 0, 0, 0, 0,70 destroy_incr71 );72 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);73 TEST_ASSERT_EQUAL_INT(1, dcount);74 75 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));76}77 78/* Test: Misuse of API (both xSFunc and xFinal set) should return error,79 and xDestroy must be called once because FuncDestructor is unused. */80void test_createFunctionApi_misuse_calls_destructor(void){81 sqlite3 *db = 0;82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));83 84 int dcount = 0;85 int rc = test_createFunctionApi(86 db, "bad_func", 1, SQLITE_UTF8,87 &dcount,88 sfunc_add_one, /* xSFunc != 0 */89 0,90 final_noop, /* xFinal != 0 -> misuse */91 0, 0,92 destroy_incr93 );94 TEST_ASSERT_NOT_EQUAL(SQLITE_OK, rc);95 TEST_ASSERT_EQUAL_INT(1, dcount);96 97 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));98}99 100/* Test: Successful scalar registration should not call xDestroy immediately.101 Upon later deletion, the destructor of the existing function must be called exactly once. */102void test_createFunctionApi_register_and_delete_scalar(void){103 sqlite3 *db = 0;104 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));105 106 int dcount = 0;107 int rc = test_createFunctionApi(108 db, "ok_func_scalar", 1, SQLITE_UTF8,109 &dcount,110 sfunc_add_one, /* scalar function */111 0, 0, /* xStep/xFinal both 0 -> fine */112 0, 0, /* xValue/xInverse both 0 */113 destroy_incr114 );115 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);116 TEST_ASSERT_EQUAL_INT(0, dcount); /* Not called yet */117 118 /* Now delete the function (no xDestroy passed to the delete call).119 The original destructor must be invoked now. */120 rc = test_createFunctionApi(121 db, "ok_func_scalar", 1, SQLITE_UTF8,122 0,123 0, 0, 0, 0, 0,124 0125 );126 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);127 TEST_ASSERT_EQUAL_INT(1, dcount);128 129 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));130}131 132/* Test: Attempt to modify/delete an existing function while there are active VMs.133 Emulate an active VM by setting db->nVdbeActive.134 Expect SQLITE_BUSY, input xDestroy called, and existing destructor NOT called. */135void test_createFunctionApi_busy_prevents_modify_and_calls_input_destructor_only(void){136 sqlite3 *db = 0;137 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));138 139 /* Register an existing function with a destructor */140 int existing_dcount = 0;141 int rc = test_createFunctionApi(142 db, "busy_func", 1, SQLITE_UTF8,143 &existing_dcount,144 sfunc_add_one,145 0, 0,146 0, 0,147 destroy_incr148 );149 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);150 TEST_ASSERT_EQUAL_INT(0, existing_dcount);151 152 /* Emulate active statements */153 db->nVdbeActive = 1;154 155 /* Attempt to delete/modify it with a new destructor payload */156 int input_dcount = 0;157 rc = test_createFunctionApi(158 db, "busy_func", 1, SQLITE_UTF8,159 &input_dcount,160 0, 0, 0, 0, 0,161 destroy_incr162 );163 TEST_ASSERT_EQUAL_INT(SQLITE_BUSY, rc);164 TEST_ASSERT_EQUAL_INT(1, input_dcount); /* input destructor called */165 TEST_ASSERT_EQUAL_INT(0, existing_dcount); /* existing destructor NOT called */166 167 /* Clear active VMs and delete for cleanup */168 db->nVdbeActive = 0;169 rc = test_createFunctionApi(170 db, "busy_func", 1, SQLITE_UTF8,171 0,172 0, 0, 0, 0, 0,173 0174 );175 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);176 TEST_ASSERT_EQUAL_INT(1, existing_dcount); /* now called */177 178 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));179}180 181/* Test: Successful window-only registration (xValue/xInverse) without xSFunc.182 Destructor should be called upon deletion only. */183void test_createFunctionApi_register_and_delete_window_only(void){184 sqlite3 *db = 0;185 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));186 187 int dcount = 0;188 int rc = test_createFunctionApi(189 db, "ok_func_window_only", 1, SQLITE_UTF8,190 &dcount,191 0, /* xSFunc must be 0 when xValue!=0 */192 0, 0,193 value_noop, /* window-only value */194 inverse_noop, /* window-only inverse */195 destroy_incr196 );197 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);198 TEST_ASSERT_EQUAL_INT(0, dcount);199 200 /* Delete and expect destructor call */201 rc = test_createFunctionApi(202 db, "ok_func_window_only", 1, SQLITE_UTF8,203 0,204 0, 0, 0, 0, 0,205 0206 );207 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);208 TEST_ASSERT_EQUAL_INT(1, dcount);209 210 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));211}212 213int main(void){214 UNITY_BEGIN();215 RUN_TEST(test_createFunctionApi_delete_noop_calls_destructor);216 RUN_TEST(test_createFunctionApi_misuse_calls_destructor);217 RUN_TEST(test_createFunctionApi_register_and_delete_scalar);218 RUN_TEST(test_createFunctionApi_busy_prevents_modify_and_calls_input_destructor_only);219 RUN_TEST(test_createFunctionApi_register_and_delete_window_only);220 return UNITY_END();221}