AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <math.h>5 6/* We will call the provided wrapper for the static function */7void test_math1Func(sqlite3_context *context, int argc, sqlite3_value **argv);8 9/* Global DB handle for tests */10static sqlite3 *gDb = NULL;11 12/* Simple math functions to use as sqlite3_user_data(context) */13static double identity_func(double x){ return x; }14static double square_func(double x){ return x*x; }15 16/* Helper: invoke math1Func with given input value and user function pointer */17static void invoke_math1(sqlite3 *db, double (*fptr)(double),18 sqlite3_value *in, Mem *out){19 sqlite3_context ctx;20 FuncDef def;21 memset(&ctx, 0, sizeof(ctx));22 memset(&def, 0, sizeof(def));23 def.pUserData = (void*)fptr;24 def.nArg = 1;25 ctx.pFunc = &def;26 ctx.pOut = out;27 ctx.db = db;28 29 sqlite3_value *argv[1];30 argv[0] = in;31 test_math1Func(&ctx, 1, argv);32}33 34void setUp(void) {35 int rc = sqlite3_open(":memory:", &gDb);36 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory DB");37}38 39void tearDown(void) {40 if( gDb ){41 sqlite3_close(gDb);42 gDb = NULL;43 }44}45 46/* Utility to allocate a fresh sqlite3_value (Mem) initialized as NULL */47static Mem* newMem(void){48 sqlite3_value *v = sqlite3ValueNew(gDb);49 TEST_ASSERT_NOT_NULL(v);50 return (Mem*)v;51}52 53static void freeMem(Mem *m){54 sqlite3ValueFree((sqlite3_value*)m);55}56 57/* Create various input Mem values */58static Mem* newIntVal(sqlite3_int64 x){59 Mem *m = newMem();60 int rc = sqlite3VdbeMemSetInt64(m, x);61 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);62 return m;63}64 65static Mem* newDoubleVal(double x){66 Mem *m = newMem();67 int rc = sqlite3VdbeMemSetDouble(m, x);68 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);69 return m;70}71 72static Mem* newTextVal(const char *z){73 Mem *m = newMem();74 int rc = sqlite3VdbeMemSetStr(m, z, (int)strlen(z), SQLITE_UTF8, SQLITE_TRANSIENT);75 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);76 return m;77}78 79static Mem* newBlobVal(const void *p, int n){80 Mem *m = newMem();81 int rc = sqlite3VdbeMemSetStr(m, (const char*)p, n, 0 /* enc=0 => BLOB */, SQLITE_TRANSIENT);82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);83 return m;84}85 86static Mem* newNullVal(void){87 return newMem(); /* Already NULL by sqlite3ValueNew */88}89 90/* Extract double from output Mem */91static double outAsDouble(Mem *out){92 return sqlite3_value_double((sqlite3_value*)out);93}94 95static int outType(Mem *out){96 return sqlite3_value_type((sqlite3_value*)out);97}98 99/* Tests */100 101void test_math1Func_integer_identity(void){102 Mem *in = newIntVal(7);103 Mem *out = newMem(); /* initialize result holder as NULL */104 105 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);106 107 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, outType(out));108 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 7.0, outAsDouble(out));109 110 freeMem(in);111 freeMem(out);112}113 114void test_math1Func_float_square(void){115 Mem *in = newDoubleVal(1.5);116 Mem *out = newMem();117 118 invoke_math1(gDb, square_func, (sqlite3_value*)in, out);119 120 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, outType(out));121 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 2.25, outAsDouble(out));122 123 freeMem(in);124 freeMem(out);125}126 127void test_math1Func_numeric_text_float_identity(void){128 Mem *in = newTextVal("2.5"); /* numeric text */129 Mem *out = newMem();130 131 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);132 133 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, outType(out));134 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 2.5, outAsDouble(out));135 136 freeMem(in);137 freeMem(out);138}139 140void test_math1Func_numeric_text_integer_identity(void){141 Mem *in = newTextVal("42"); /* numeric integer text */142 Mem *out = newMem();143 144 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);145 146 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, outType(out));147 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 42.0, outAsDouble(out));148 149 freeMem(in);150 freeMem(out);151}152 153void test_math1Func_non_numeric_text_leaves_null(void){154 Mem *in = newTextVal("abc"); /* non-numeric text */155 Mem *out = newNullVal(); /* remains NULL if function returns early */156 157 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);158 159 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, outType(out));160 161 freeMem(in);162 freeMem(out);163}164 165void test_math1Func_blob_leaves_null(void){166 const unsigned char blobData[3] = {0x01, 0x02, 0x03};167 Mem *in = newBlobVal(blobData, 3);168 Mem *out = newNullVal();169 170 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);171 172 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, outType(out));173 174 freeMem(in);175 freeMem(out);176}177 178void test_math1Func_null_leaves_null(void){179 Mem *in = newNullVal();180 Mem *out = newNullVal();181 182 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);183 184 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, outType(out));185 186 freeMem(in);187 freeMem(out);188}189 190void test_math1Func_nan_propagation_identity(void){191 /* Create a NaN value and ensure it propagates through */192 double nanv = NAN;193 Mem *in = newDoubleVal(nanv);194 Mem *out = newMem();195 196 invoke_math1(gDb, identity_func, (sqlite3_value*)in, out);197 198 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, outType(out));199 double got = outAsDouble(out);200 TEST_ASSERT_TRUE(isnan(got));201 202 freeMem(in);203 freeMem(out);204}205 206int main(void) {207 UNITY_BEGIN();208 RUN_TEST(test_math1Func_integer_identity);209 RUN_TEST(test_math1Func_float_square);210 RUN_TEST(test_math1Func_numeric_text_float_identity);211 RUN_TEST(test_math1Func_numeric_text_integer_identity);212 RUN_TEST(test_math1Func_non_numeric_text_leaves_null);213 RUN_TEST(test_math1Func_blob_leaves_null);214 RUN_TEST(test_math1Func_null_leaves_null);215 RUN_TEST(test_math1Func_nan_propagation_identity);216 return UNITY_END();217}