AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6#include <math.h>7 8/* The target function is static in the module; use the provided wrapper. */9void test_math2Func(sqlite3_context *context, int argc, sqlite3_value **argv);10 11/* Unity required hooks */12void setUp(void) {13 /* No global setup needed for these tests */14}15void tearDown(void) {16 /* No global teardown needed for these tests */17}18 19/* Wrapper xFunc that calls the target function's test wrapper */20static void xFuncWrapper(sqlite3_context *ctx, int argc, sqlite3_value **argv){21 test_math2Func(ctx, argc, argv);22}23 24/* Helper: register a scalar function "tm2" with given user data pointer */25static void register_tm2(sqlite3 *db, void *pUserData){26 int rc = sqlite3_create_function_v2(27 db, "tm2", 2, SQLITE_UTF8, pUserData, xFuncWrapper, 0, 0, 028 );29 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);30}31 32/* Helper: run a single-row, single-column SELECT and fetch a double or NULL */33static int query_single_double(sqlite3 *db, const char *sql, double *pVal, int *pIsNull){34 sqlite3_stmt *stmt = 0;35 int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);36 if( rc!=SQLITE_OK ) return rc;37 rc = sqlite3_step(stmt);38 if( rc==SQLITE_ROW ){39 int t = sqlite3_column_type(stmt, 0);40 if( pIsNull ) *pIsNull = (t==SQLITE_NULL);41 if( t==SQLITE_NULL ){42 if( pVal ) *pVal = 0.0;43 }else{44 if( pVal ) *pVal = sqlite3_column_double(stmt, 0);45 }46 rc = SQLITE_OK;47 }48 sqlite3_finalize(stmt);49 return rc;50}51 52/* Custom function pointers for user data tests */53static double add2(double a, double b){ return a + b; }54static double mul2(double a, double b){ return a * b; }55 56/* Test: basic power computations using pow as user data */57static void test_math2Func_pow_basic(void){58 sqlite3 *db = 0;59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));60 /* Pass pow as the user data function */61 register_tm2(db, (void*)pow);62 63 double v = 0.0; int isNull = 0;64 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(2,3);", &v, &isNull));65 TEST_ASSERT_FALSE(isNull);66 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 8.0, v);67 68 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(2,-1);", &v, &isNull));69 TEST_ASSERT_FALSE(isNull);70 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 0.5, v);71 72 sqlite3_close(db);73}74 75/* Test: TEXT that is numeric should coerce to numeric and be accepted */76static void test_math2Func_text_numeric_coercion(void){77 sqlite3 *db = 0;78 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));79 register_tm2(db, (void*)pow);80 81 double v = 0.0; int isNull = 0;82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2('2','3.0');", &v, &isNull));83 TEST_ASSERT_FALSE(isNull);84 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 8.0, v);85 86 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2('2.5','2');", &v, &isNull));87 TEST_ASSERT_FALSE(isNull);88 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 6.25, v);89 90 sqlite3_close(db);91}92 93/* Test: non-numeric arguments result in NULL (function early returns) */94static void test_math2Func_non_numeric_args(void){95 sqlite3 *db = 0;96 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));97 register_tm2(db, (void*)pow);98 99 double v = 0.0; int isNull = 0;100 /* First arg non-numeric TEXT */101 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2('abc', 2);", &v, &isNull));102 TEST_ASSERT_TRUE(isNull);103 104 /* Second arg BLOB */105 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(2, x'123456');", &v, &isNull));106 TEST_ASSERT_TRUE(isNull);107 108 sqlite3_close(db);109}110 111/* Test: NULL arguments propagate as NULL result */112static void test_math2Func_null_args(void){113 sqlite3 *db = 0;114 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));115 register_tm2(db, (void*)pow);116 117 double v = 0.0; int isNull = 0;118 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(NULL, 1);", &v, &isNull));119 TEST_ASSERT_TRUE(isNull);120 121 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(1, NULL);", &v, &isNull));122 TEST_ASSERT_TRUE(isNull);123 124 sqlite3_close(db);125}126 127/* Test: user data function pointer is honored (use multiplication) */128static void test_math2Func_user_data_custom(void){129 sqlite3 *db = 0;130 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));131 /* Register tm2 with our own function pointer: multiplication */132 register_tm2(db, (void*)mul2);133 134 double v = 0.0; int isNull = 0;135 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(3, 4);", &v, &isNull));136 TEST_ASSERT_FALSE(isNull);137 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 12.0, v);138 139 /* Re-register with addition to ensure pointer swap works */140 register_tm2(db, (void*)add2);141 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(3.5, 4.25);", &v, &isNull));142 TEST_ASSERT_FALSE(isNull);143 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 7.75, v);144 145 sqlite3_close(db);146}147 148/* Test: NaN propagation (e.g., pow(-2, 0.5)) */149static void test_math2Func_pow_nan(void){150 sqlite3 *db = 0;151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));152 register_tm2(db, (void*)pow);153 154 double v = 0.0; int isNull = 0;155 TEST_ASSERT_EQUAL_INT(SQLITE_OK, query_single_double(db, "SELECT tm2(-2.0, 0.5);", &v, &isNull));156 TEST_ASSERT_FALSE(isNull);157 /* v should be NaN; isnan() returns non-zero for NaN */158 TEST_ASSERT_TRUE(isnan(v));159 160 sqlite3_close(db);161}162 163int main(void){164 UNITY_BEGIN();165 RUN_TEST(test_math2Func_pow_basic);166 RUN_TEST(test_math2Func_text_numeric_coercion);167 RUN_TEST(test_math2Func_non_numeric_args);168 RUN_TEST(test_math2Func_null_args);169 RUN_TEST(test_math2Func_user_data_custom);170 RUN_TEST(test_math2Func_pow_nan);171 return UNITY_END();172}