AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* External wrapper for the static function under test */7extern void test_minMaxValueFinalize(sqlite3_context *context, int bValue);8 9/* Test harness state */10static sqlite3 *gDb = NULL;11 12/* Control structure passed to aggregate via sqlite3_user_data */13typedef struct AggCtl {14 int mode; /* 0: no agg ctx, 1: flags=0, 2: int value, 3: dyn string */15 int bValue; /* argument passed to test_minMaxValueFinalize */16} AggCtl;17 18/* Aggregate step function to set up the aggregate context in various ways */19static void test_step(sqlite3_context *ctx, int argc, sqlite3_value **argv){20 AggCtl *ctl = (AggCtl*)sqlite3_user_data(ctx);21 UNUSED_PARAMETER(argc);22 UNUSED_PARAMETER(argv);23 24 if( ctl==NULL ) return;25 26 if( ctl->mode==0 ){27 /* Do not call sqlite3_aggregate_context at all */28 return;29 }30 31 /* Allocate aggregate context memory sized for Mem/sqlite3_value */32 Mem *p = (Mem*)sqlite3_aggregate_context(ctx, sizeof(Mem));33 if( p==NULL ) return; /* Out of memory or error, let SQLite handle it */34 35 /* Ensure it's initialized (aggregate_context should zero memory) */36 /* Set db pointer for any potential releases in finalize */37 p->db = sqlite3_context_db_handle(ctx);38 39 switch( ctl->mode ){40 case 1: {41 /* Leave flags==0 meaning "no value stored" */42 p->flags = 0;43 break;44 }45 case 2: {46 /* Store an integer value */47 p->flags = MEM_Int;48 p->u.i = 12345;49 break;50 }51 case 3: {52 /* Store a dynamically allocated UTF-8 string to exercise release */53 const char *msg = "hello";54 int n = (int)strlen(msg);55 char *z = (char*)sqlite3DbMallocZero(p->db, n+1);56 if( z ){57 memcpy(z, msg, n+1);58 p->z = z;59 p->n = n;60 p->enc = SQLITE_UTF8;61 p->flags = MEM_Str | MEM_Term | MEM_Dyn;62 }else{63 /* If allocation failed, leave flags at 0 so result will be NULL */64 p->flags = 0;65 }66 break;67 }68 default:69 /* Unknown mode: do nothing */70 break;71 }72}73 74/* Aggregate finalizer that calls the target function wrapper */75static void test_final(sqlite3_context *ctx){76 AggCtl *ctl = (AggCtl*)sqlite3_user_data(ctx);77 int bValue = ctl ? ctl->bValue : 0;78 test_minMaxValueFinalize(ctx, bValue);79}80 81/* Helper to register an aggregate function with given control */82static void register_test_agg(sqlite3 *db, const char *zName, AggCtl *ctl){83 int rc = sqlite3_create_function(db, zName, 1, SQLITE_UTF8, ctl,84 NULL, test_step, test_final);85 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_create_function failed");86}87 88/* Run a query "SELECT f(1)" and capture the single result */89static void run_select_single(sqlite3 *db, const char *zFuncName,90 int *pType, sqlite3_int64 *pI64, const unsigned char **pText){91 char sql[128];92 sqlite3_stmt *pStmt = NULL;93 snprintf(sql, sizeof(sql), "SELECT %s(1)", zFuncName);94 int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);95 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");96 rc = sqlite3_step(pStmt);97 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "expected one row");98 int t = sqlite3_column_type(pStmt, 0);99 if( pType ) *pType = t;100 if( t==SQLITE_INTEGER && pI64 ){101 *pI64 = sqlite3_column_int64(pStmt, 0);102 }else if( t==SQLITE_TEXT && pText ){103 *pText = sqlite3_column_text(pStmt, 0);104 }105 rc = sqlite3_step(pStmt);106 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_DONE, rc, "expected exactly one row");107 sqlite3_finalize(pStmt);108}109 110void setUp(void) {111 /* Empty; open per-test to ensure isolation */112}113 114void tearDown(void) {115 if( gDb ){116 sqlite3_close(gDb);117 gDb = NULL;118 }119}120 121/* Test: pRes==NULL path (no aggregate context ever allocated) -> result NULL */122void test_minMaxValueFinalize_null_when_no_aggctx_bValue0(void){123 int rc = sqlite3_open(":memory:", &gDb);124 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);125 126 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));127 TEST_ASSERT_NOT_NULL(ctl);128 ctl->mode = 0; /* do not allocate aggregate context */129 ctl->bValue = 0;130 131 register_test_agg(gDb, "xf_none_b0", ctl);132 133 int type = 0;134 run_select_single(gDb, "xf_none_b0", &type, NULL, NULL);135 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, type);136 137 sqlite3_free(ctl);138}139 140/* Test: pRes exists but flags==0 -> result NULL (bValue doesn't matter) */141void test_minMaxValueFinalize_null_when_flags_zero_bValue1(void){142 int rc = sqlite3_open(":memory:", &gDb);143 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);144 145 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));146 TEST_ASSERT_NOT_NULL(ctl);147 ctl->mode = 1; /* aggregate context allocated, flags=0 */148 ctl->bValue = 1;149 150 register_test_agg(gDb, "xf_empty_b1", ctl);151 152 int type = 0;153 run_select_single(gDb, "xf_empty_b1", &type, NULL, NULL);154 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, type);155 156 sqlite3_free(ctl);157}158 159/* Test: integer value stored, bValue==0 -> returns integer (and may release) */160void test_minMaxValueFinalize_returns_int_bValue0(void){161 int rc = sqlite3_open(":memory:", &gDb);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 164 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));165 TEST_ASSERT_NOT_NULL(ctl);166 ctl->mode = 2; /* integer value */167 ctl->bValue = 0;168 169 register_test_agg(gDb, "xf_int_b0", ctl);170 171 int type = 0;172 sqlite3_int64 v = 0;173 run_select_single(gDb, "xf_int_b0", &type, &v, NULL);174 TEST_ASSERT_EQUAL_INT(SQLITE_INTEGER, type);175 TEST_ASSERT_EQUAL_INT64(12345, v);176 177 sqlite3_free(ctl);178}179 180/* Test: integer value stored, bValue==1 -> returns integer (no release) */181void test_minMaxValueFinalize_returns_int_bValue1(void){182 int rc = sqlite3_open(":memory:", &gDb);183 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);184 185 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));186 TEST_ASSERT_NOT_NULL(ctl);187 ctl->mode = 2; /* integer value */188 ctl->bValue = 1;189 190 register_test_agg(gDb, "xf_int_b1", ctl);191 192 int type = 0;193 sqlite3_int64 v = 0;194 run_select_single(gDb, "xf_int_b1", &type, &v, NULL);195 TEST_ASSERT_EQUAL_INT(SQLITE_INTEGER, type);196 TEST_ASSERT_EQUAL_INT64(12345, v);197 198 sqlite3_free(ctl);199}200 201/* Test: dynamic string stored, bValue==0 -> returns text and exercises release path */202void test_minMaxValueFinalize_returns_text_bValue0_release(void){203 int rc = sqlite3_open(":memory:", &gDb);204 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);205 206 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));207 TEST_ASSERT_NOT_NULL(ctl);208 ctl->mode = 3; /* dynamic string value */209 ctl->bValue = 0;210 211 register_test_agg(gDb, "xf_text_b0", ctl);212 213 int type = 0;214 const unsigned char *txt = NULL;215 run_select_single(gDb, "xf_text_b0", &type, NULL, &txt);216 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, type);217 TEST_ASSERT_NOT_NULL(txt);218 TEST_ASSERT_EQUAL_INT(0, strcmp((const char*)txt, "hello"));219 220 sqlite3_free(ctl);221}222 223/* Test: dynamic string stored, bValue==1 -> returns text (no release in finalize) */224void test_minMaxValueFinalize_returns_text_bValue1_no_release(void){225 int rc = sqlite3_open(":memory:", &gDb);226 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);227 228 AggCtl *ctl = (AggCtl*)sqlite3_malloc(sizeof(AggCtl));229 TEST_ASSERT_NOT_NULL(ctl);230 ctl->mode = 3; /* dynamic string value */231 ctl->bValue = 1;232 233 register_test_agg(gDb, "xf_text_b1", ctl);234 235 int type = 0;236 const unsigned char *txt = NULL;237 run_select_single(gDb, "xf_text_b1", &type, NULL, &txt);238 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, type);239 TEST_ASSERT_NOT_NULL(txt);240 TEST_ASSERT_EQUAL_INT(0, strcmp((const char*)txt, "hello"));241 242 sqlite3_free(ctl);243}244 245int main(void) {246 UNITY_BEGIN();247 RUN_TEST(test_minMaxValueFinalize_null_when_no_aggctx_bValue0);248 RUN_TEST(test_minMaxValueFinalize_null_when_flags_zero_bValue1);249 RUN_TEST(test_minMaxValueFinalize_returns_int_bValue0);250 RUN_TEST(test_minMaxValueFinalize_returns_int_bValue1);251 RUN_TEST(test_minMaxValueFinalize_returns_text_bValue0_release);252 RUN_TEST(test_minMaxValueFinalize_returns_text_bValue1_no_release);253 return UNITY_END();254}