AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdint.h>6 7/* External wrapper provided in the module under test */8extern void test_percentCompute(sqlite3_context *pCtx, int bIsFinal);9 10/*11** We construct the aggregate context memory expected by percentCompute.12** This local struct mirrors the fields that percentCompute uses:13** - a: array of doubles14** - nUsed: number of used elements in a15** - nAlloc: capacity (not strictly needed but set sensibly)16** - bSorted: whether a is already sorted17** - bKeepSorted: flag set when not final18** - rPct: percentile fraction in [0,1]19** The exact internal name/type is not referenced; we only write20** the expected fields into the aggregate context memory that21** percentCompute will read.22*/23typedef struct TestPercentile {24 double *a;25 unsigned int nUsed;26 unsigned int nAlloc;27 unsigned int bSorted;28 unsigned int bKeepSorted;29 double rPct;30} TestPercentile;31 32/* Helper to initialize a sqlite3_context and its Mems */33static void init_context(sqlite3 *db,34 sqlite3_context *pCtx,35 Mem *pOut,36 Mem *pAgg,37 FuncDef *pDef,38 void *pUserData,39 void *pAggBuf,40 int nAggBuf){41 memset(pCtx, 0, sizeof(*pCtx));42 memset(pOut, 0, sizeof(*pOut));43 memset(pAgg, 0, sizeof(*pAgg));44 memset(pDef, 0, sizeof(*pDef));45 46 /* Set up function user-data for settings (discrete/continuous) */47 pDef->pUserData = pUserData;48 pCtx->pFunc = pDef;49 50 /* Output register for sqlite3_result_* */51 pOut->db = db;52 pCtx->pOut = pOut;53 54 /* Aggregate context storage */55 pAgg->db = db;56 if( pAggBuf ){57 pAgg->flags = MEM_Agg; /* signal that aggregate context exists */58 pAgg->z = (char*)pAggBuf; /* sqlite3_aggregate_context(...,0) returns this */59 pAgg->n = nAggBuf;60 }61 pCtx->pMem = pAgg;62}63 64void setUp(void) {65 /* empty */66}67 68void tearDown(void) {69 /* empty */70}71 72static double get_result_double(Mem *pOut){73 /* Mem is compatible with sqlite3_value */74 return sqlite3_value_double((sqlite3_value*)pOut);75}76 77/* Test: continuous percentile with interpolation; array unsorted -> sort inside percentCompute */78void test_percentCompute_continuous_basic(void){79 sqlite3 *db = 0;80 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(0, &db));81 82 sqlite3_context ctx; Mem outMem; Mem aggMem; FuncDef def;83 /* Allocate ample buffer for aggregate context */84 int const aggBufSize = 256;85 void *aggBuf = sqlite3_malloc(aggBufSize);86 TEST_ASSERT_NOT_NULL(aggBuf);87 memset(aggBuf, 0, aggBufSize);88 89 init_context(db, &ctx, &outMem, &aggMem, &def, (void*)(intptr_t)0 /* continuous */, aggBuf, aggBufSize);90 91 /* Prepare unsorted data: [40,10,30,20] -> sorted [10,20,30,40] */92 int n = 4;93 double *arr = (double*)sqlite3_malloc(n * sizeof(double));94 TEST_ASSERT_NOT_NULL(arr);95 arr[0]=40.0; arr[1]=10.0; arr[2]=30.0; arr[3]=20.0;96 97 TestPercentile *P = (TestPercentile*)aggBuf;98 P->a = arr;99 P->nUsed = (unsigned)n;100 P->nAlloc = (unsigned)n;101 P->bSorted = 0; /* require sort */102 P->bKeepSorted = 0;103 P->rPct = 0.25; /* 25th percentile: ix=0.75 -> 17.5 */104 105 test_percentCompute(&ctx, 0 /* not final */);106 107 double got = get_result_double(&outMem);108 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 17.5, got);109 /* Non-final should set bKeepSorted */110 TEST_ASSERT_EQUAL_UINT(1U, P->bKeepSorted);111 112 /* Clean up; non-final did not free arr */113 sqlite3_free(arr);114 sqlite3_free(aggBuf);115 sqlite3_close(db);116}117 118/* Test: discrete setting (no interpolation) */119void test_percentCompute_discrete_basic(void){120 sqlite3 *db = 0;121 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(0, &db));122 123 sqlite3_context ctx; Mem outMem; Mem aggMem; FuncDef def;124 int const aggBufSize = 256;125 void *aggBuf = sqlite3_malloc(aggBufSize);126 TEST_ASSERT_NOT_NULL(aggBuf);127 memset(aggBuf, 0, aggBufSize);128 129 init_context(db, &ctx, &outMem, &aggMem, &def, (void*)(intptr_t)1 /* discrete flag */, aggBuf, aggBufSize);130 131 int n = 4;132 double *arr = (double*)sqlite3_malloc(n * sizeof(double));133 TEST_ASSERT_NOT_NULL(arr);134 arr[0]=40.0; arr[1]=10.0; arr[2]=30.0; arr[3]=20.0;135 136 TestPercentile *P = (TestPercentile*)aggBuf;137 P->a = arr;138 P->nUsed = (unsigned)n;139 P->nAlloc = (unsigned)n;140 P->bSorted = 0; /* will be sorted */141 P->bKeepSorted = 0;142 P->rPct = 0.25; /* i1=0 -> choose a[0] after sort -> 10 */143 144 test_percentCompute(&ctx, 0 /* not final */);145 146 double got = get_result_double(&outMem);147 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 10.0, got);148 149 sqlite3_free(arr);150 sqlite3_free(aggBuf);151 sqlite3_close(db);152}153 154/* Test: exact-index/last-element behavior (continuous should pick endpoint cleanly) */155void test_percentCompute_exact_last_value(void){156 sqlite3 *db = 0;157 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(0, &db));158 159 sqlite3_context ctx; Mem outMem; Mem aggMem; FuncDef def;160 int const aggBufSize = 256;161 void *aggBuf = sqlite3_malloc(aggBufSize);162 TEST_ASSERT_NOT_NULL(aggBuf);163 memset(aggBuf, 0, aggBufSize);164 165 init_context(db, &ctx, &outMem, &aggMem, &def, (void*)(intptr_t)0 /* continuous */, aggBuf, aggBufSize);166 167 int n = 4;168 double *arr = (double*)sqlite3_malloc(n * sizeof(double));169 TEST_ASSERT_NOT_NULL(arr);170 arr[0]=5.0; arr[1]=15.0; arr[2]=25.0; arr[3]=35.0;171 172 TestPercentile *P = (TestPercentile*)aggBuf;173 P->a = arr;174 P->nUsed = (unsigned)n;175 P->nAlloc = (unsigned)n;176 P->bSorted = 0; /* allow internal sort */177 P->bKeepSorted = 0;178 P->rPct = 1.0; /* ix = nUsed-1 => choose last value */179 180 test_percentCompute(&ctx, 0 /* not final */);181 182 double got = get_result_double(&outMem);183 TEST_ASSERT_DOUBLE_WITHIN(1e-12, 35.0, got);184 185 sqlite3_free(arr);186 sqlite3_free(aggBuf);187 sqlite3_close(db);188}189 190/* Test: finalization cleans up memory and zeroes the accumulator */191void test_percentCompute_finalizes_and_zeros(void){192 sqlite3 *db = 0;193 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(0, &db));194 195 sqlite3_context ctx; Mem outMem; Mem aggMem; FuncDef def;196 int const aggBufSize = 256;197 void *aggBuf = sqlite3_malloc(aggBufSize);198 TEST_ASSERT_NOT_NULL(aggBuf);199 memset(aggBuf, 0, aggBufSize);200 201 init_context(db, &ctx, &outMem, &aggMem, &def, (void*)(intptr_t)0 /* continuous */, aggBuf, aggBufSize);202 203 /* nUsed==0 to avoid reading a[]; but set a non-NULL so free() is exercised */204 double *arr = (double*)sqlite3_malloc(2*sizeof(double));205 TEST_ASSERT_NOT_NULL(arr);206 arr[0]=1.0; arr[1]=2.0;207 208 TestPercentile *P = (TestPercentile*)aggBuf;209 P->a = arr;210 P->nUsed = 0; /* no results, but finalization should still free a and zero struct */211 P->nAlloc = 2;212 P->bSorted = 0;213 P->bKeepSorted = 0;214 P->rPct = 0.5;215 216 test_percentCompute(&ctx, 1 /* final */);217 218 /* After finalization, the aggregate buffer should be zeroed */219 unsigned char zero[64]; memset(zero, 0, sizeof(zero));220 TEST_ASSERT_EQUAL_INT(0, memcmp(aggBuf, zero, sizeof(zero)));221 222 /* arr was freed by percentCompute; do not free arr here */223 sqlite3_free(aggBuf);224 sqlite3_close(db);225}226 227/* Test: handles missing aggregate context (p==NULL) gracefully */228void test_percentCompute_handles_no_context(void){229 sqlite3 *db = 0;230 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(0, &db));231 232 sqlite3_context ctx; Mem outMem; Mem aggMem; FuncDef def;233 /* No aggregate buffer provided: sqlite3_aggregate_context(ctx,0) -> NULL */234 init_context(db, &ctx, &outMem, &aggMem, &def, (void*)(intptr_t)0, NULL, 0);235 236 test_percentCompute(&ctx, 0 /* not final */);237 238 /* No result should be produced; type remains NULL */239 int t = sqlite3_value_type((sqlite3_value*)&outMem);240 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, t);241 242 sqlite3_close(db);243}244 245int main(void) {246 UNITY_BEGIN();247 RUN_TEST(test_percentCompute_continuous_basic);248 RUN_TEST(test_percentCompute_discrete_basic);249 RUN_TEST(test_percentCompute_exact_last_value);250 RUN_TEST(test_percentCompute_finalizes_and_zeros);251 RUN_TEST(test_percentCompute_handles_no_context);252 return UNITY_END();253}