AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <math.h>6#include <float.h>7 8/* Prototype of the test wrapper exposed by the module */9void test_sumInverse(sqlite3_context *context, int argc, sqlite3_value **argv);10 11/* Local dummy step function to satisfy sqlite3_aggregate_context() assertion */12static void dummyStep(sqlite3_context *ctx, int argc, sqlite3_value **argv){13 UNUSED_PARAMETER(ctx);14 UNUSED_PARAMETER(argc);15 UNUSED_PARAMETER(argv);16}17 18/* Helpers to create a valid sqlite3_context for aggregate usage */19static void makeContext(sqlite3 **pDb,20 sqlite3_context *pCtx,21 Mem **ppMem,22 FuncDef *pFuncDef){23 memset(pCtx, 0, sizeof(*pCtx));24 memset(pFuncDef, 0, sizeof(*pFuncDef));25 26 /* Open an in-memory database */27 int rc = sqlite3_open(":memory:", pDb);28 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);29 TEST_ASSERT_NOT_NULL(*pDb);30 31 /* Create a Mem (sqlite3_value) tied to db */32 *ppMem = sqlite3ValueNew(*pDb);33 TEST_ASSERT_NOT_NULL(*ppMem);34 35 /* Configure FuncDef with non-NULL xSFunc */36 pFuncDef->xSFunc = dummyStep;37 38 /* Initialize context */39 pCtx->db = *pDb;40 pCtx->pMem = *ppMem;41 pCtx->pFunc = pFuncDef;42}43 44/* Helper to destroy a context */45static void destroyContext(sqlite3 *db, sqlite3_context *pCtx){46 if( pCtx && pCtx->pMem ){47 sqlite3ValueFree(pCtx->pMem);48 pCtx->pMem = NULL;49 }50 if( db ){51 sqlite3_close(db);52 }53}54 55/* Helpers to create argument values */56static sqlite3_value *makeIntValue(sqlite3 *db, i64 v){57 Mem *p = sqlite3ValueNew(db);58 TEST_ASSERT_NOT_NULL(p);59 sqlite3VdbeMemSetInt64(p, v);60 return (sqlite3_value*)p;61}62static sqlite3_value *makeRealValue(sqlite3 *db, double v){63 Mem *p = sqlite3ValueNew(db);64 TEST_ASSERT_NOT_NULL(p);65 sqlite3VdbeMemSetDouble(p, v);66 return (sqlite3_value*)p;67}68static sqlite3_value *makeNullValue(sqlite3 *db){69 Mem *p = sqlite3ValueNew(db);70 TEST_ASSERT_NOT_NULL(p);71 sqlite3VdbeMemSetNull(p);72 return (sqlite3_value*)p;73}74 75void setUp(void) {76 /* no-op */77}78 79void tearDown(void) {80 /* no-op */81}82 83/* Test 1: Integer-mode inverse without overflow: approx==0, subtract a small integer */84void test_sumInverse_integer_simple(void){85 sqlite3 *db = NULL;86 sqlite3_context ctx;87 Mem *pMem = NULL;88 FuncDef funcDef;89 makeContext(&db, &ctx, &pMem, &funcDef);90 91 /* Allocate SumCtx and initialize fields */92 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));93 TEST_ASSERT_NOT_NULL(pSum);94 memset(pSum, 0, sizeof(*pSum));95 pSum->cnt = 1;96 pSum->approx = 0;97 pSum->ovrfl = 0;98 pSum->iSum = 10; /* starting integer sum */99 100 sqlite3_value *argv0 = makeIntValue(db, 3);101 sqlite3_value *argv[1] = { argv0 };102 103 test_sumInverse(&ctx, 1, argv);104 105 TEST_ASSERT_EQUAL_INT(0, pSum->cnt);106 TEST_ASSERT_EQUAL_INT(0, pSum->approx);107 TEST_ASSERT_EQUAL_INT(0, pSum->ovrfl);108 TEST_ASSERT_EQUAL_INT64(7, pSum->iSum);109 110 sqlite3ValueFree((Mem*)argv0);111 destroyContext(db, &ctx);112}113 114/* Test 2: Integer-mode inverse with overflow: forces approx=1 and ovrfl=1 */115void test_sumInverse_integer_overflow_to_approx(void){116 sqlite3 *db = NULL;117 sqlite3_context ctx;118 Mem *pMem = NULL;119 FuncDef funcDef;120 makeContext(&db, &ctx, &pMem, &funcDef);121 122 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));123 TEST_ASSERT_NOT_NULL(pSum);124 memset(pSum, 0, sizeof(*pSum));125 pSum->cnt = 1;126 pSum->approx = 0;127 pSum->ovrfl = 0;128 129 /* Choose values so that iSum - (-10) overflows (iSum near LARGEST_INT64) */130 pSum->iSum = LARGEST_INT64 - 1;131 sqlite3_value *argv0 = makeIntValue(db, -10);132 sqlite3_value *argv[1] = { argv0 };133 134 test_sumInverse(&ctx, 1, argv);135 136 TEST_ASSERT_EQUAL_INT(0, pSum->cnt);137 TEST_ASSERT_EQUAL_INT(1, pSum->approx);138 TEST_ASSERT_EQUAL_INT(1, pSum->ovrfl);139 140 sqlite3ValueFree((Mem*)argv0);141 destroyContext(db, &ctx);142}143 144/* Test 3: Approx-mode with integer input: KBN step with -iVal */145void test_sumInverse_approx_with_integer(void){146 sqlite3 *db = NULL;147 sqlite3_context ctx;148 Mem *pMem = NULL;149 FuncDef funcDef;150 makeContext(&db, &ctx, &pMem, &funcDef);151 152 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));153 TEST_ASSERT_NOT_NULL(pSum);154 memset(pSum, 0, sizeof(*pSum));155 pSum->cnt = 2;156 pSum->approx = 1; /* already in approximate mode */157 pSum->rSum = 0.0;158 pSum->rErr = 0.0;159 160 sqlite3_value *argv0 = makeIntValue(db, 5);161 sqlite3_value *argv[1] = { argv0 };162 163 test_sumInverse(&ctx, 1, argv);164 165 TEST_ASSERT_EQUAL_INT(1, pSum->cnt);166 TEST_ASSERT_EQUAL_INT(1, pSum->approx);167 TEST_ASSERT_DOUBLE_WITHIN(0.0, -5.0, pSum->rSum); /* exact -5 */168 TEST_ASSERT_DOUBLE_WITHIN(0.0, 0.0, pSum->rErr);169 170 sqlite3ValueFree((Mem*)argv0);171 destroyContext(db, &ctx);172}173 174/* Test 4: Approx-mode with SMALLEST_INT64: special-case path (two steps) */175void test_sumInverse_approx_with_smallest_int64(void){176 sqlite3 *db = NULL;177 sqlite3_context ctx;178 Mem *pMem = NULL;179 FuncDef funcDef;180 makeContext(&db, &ctx, &pMem, &funcDef);181 182 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));183 TEST_ASSERT_NOT_NULL(pSum);184 memset(pSum, 0, sizeof(*pSum));185 pSum->cnt = 1;186 pSum->approx = 1;187 pSum->rSum = 0.0;188 pSum->rErr = 0.0;189 190 sqlite3_value *argv0 = makeIntValue(db, SMALLEST_INT64);191 sqlite3_value *argv[1] = { argv0 };192 193 test_sumInverse(&ctx, 1, argv);194 195 /* Expect rSum == LARGEST_INT64 + 1 == 2^63, exactly representable in double */196 double expected = (double)LARGEST_INT64 + 1.0; /* 9223372036854775808.0 = 2^63 */197 TEST_ASSERT_EQUAL_INT(0, pSum->cnt);198 TEST_ASSERT_DOUBLE_WITHIN(0.0, expected, pSum->rSum);199 TEST_ASSERT_DOUBLE_WITHIN(0.0, 0.0, pSum->rErr);200 201 sqlite3ValueFree((Mem*)argv0);202 destroyContext(db, &ctx);203}204 205/* Test 5: Approx-mode with real input: KBN step with -double, and ovrfl cleared */206void test_sumInverse_approx_with_real(void){207 sqlite3 *db = NULL;208 sqlite3_context ctx;209 Mem *pMem = NULL;210 FuncDef funcDef;211 makeContext(&db, &ctx, &pMem, &funcDef);212 213 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));214 TEST_ASSERT_NOT_NULL(pSum);215 memset(pSum, 0, sizeof(*pSum));216 pSum->cnt = 3;217 pSum->approx = 1;218 pSum->ovrfl = 1; /* should be cleared by real inverse path */219 pSum->rSum = 0.0;220 pSum->rErr = 0.0;221 222 sqlite3_value *argv0 = makeRealValue(db, 2.5);223 sqlite3_value *argv[1] = { argv0 };224 225 test_sumInverse(&ctx, 1, argv);226 227 TEST_ASSERT_EQUAL_INT(2, pSum->cnt);228 TEST_ASSERT_EQUAL_INT(0, pSum->ovrfl);229 TEST_ASSERT_DOUBLE_WITHIN(0.0, -2.5, pSum->rSum);230 TEST_ASSERT_DOUBLE_WITHIN(0.0, 0.0, pSum->rErr);231 232 sqlite3ValueFree((Mem*)argv0);233 destroyContext(db, &ctx);234}235 236/* Test 6: NULL input should be ignored (no changes) */237void test_sumInverse_ignore_null(void){238 sqlite3 *db = NULL;239 sqlite3_context ctx;240 Mem *pMem = NULL;241 FuncDef funcDef;242 makeContext(&db, &ctx, &pMem, &funcDef);243 244 SumCtx *pSum = (SumCtx*)sqlite3_aggregate_context(&ctx, (int)sizeof(SumCtx));245 TEST_ASSERT_NOT_NULL(pSum);246 memset(pSum, 0, sizeof(*pSum));247 pSum->cnt = 5;248 pSum->approx = 0;249 pSum->iSum = 1234;250 pSum->ovrfl = 0;251 252 sqlite3_value *argv0 = makeNullValue(db);253 sqlite3_value *argv[1] = { argv0 };254 255 test_sumInverse(&ctx, 1, argv);256 257 /* No changes expected */258 TEST_ASSERT_EQUAL_INT(5, pSum->cnt);259 TEST_ASSERT_EQUAL_INT(0, pSum->approx);260 TEST_ASSERT_EQUAL_INT64(1234, pSum->iSum);261 TEST_ASSERT_EQUAL_INT(0, pSum->ovrfl);262 263 sqlite3ValueFree((Mem*)argv0);264 destroyContext(db, &ctx);265}266 267int main(void){268 UNITY_BEGIN();269 RUN_TEST(test_sumInverse_integer_simple);270 RUN_TEST(test_sumInverse_integer_overflow_to_approx);271 RUN_TEST(test_sumInverse_approx_with_integer);272 RUN_TEST(test_sumInverse_approx_with_smallest_int64);273 RUN_TEST(test_sumInverse_approx_with_real);274 RUN_TEST(test_sumInverse_ignore_null);275 return UNITY_END();276}