AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdint.h>6 7/* Wrapper provided in the module for the static function under test */8extern void test_countStep(sqlite3_context *context, int argc, sqlite3_value **argv);9 10/* Dummy finalize function to satisfy potential asserts on pFunc->xFinalize */11static void dummyFinalize(sqlite3_context *pCtx){12 (void)pCtx;13}14 15/* Helper: initialize an aggregate sqlite3_context with a MEM_Agg Mem cell */16static void initAggSqlite3Context(sqlite3 *db, sqlite3_context *pCtx, Mem *pMem, FuncDef *pDef){17 memset(pCtx, 0, sizeof(*pCtx));18 memset(pMem, 0, sizeof(*pMem));19 memset(pDef, 0, sizeof(*pDef));20 21 /* Prepare the aggregate memory cell: mark as aggregate and attach db */22 pMem->flags = MEM_Agg;23 pMem->db = db;24 pMem->z = 0; /* Force sqlite3_aggregate_context to allocate on first use */25 pMem->n = 0;26 pMem->szMalloc = 0;27 28 /* Provide a FuncDef with a finalize pointer (helps with asserts in debug builds) */29 pDef->xFinalize = dummyFinalize;30 31 /* Wire up the context */32 pCtx->pMem = pMem;33 pCtx->pFunc = pDef;34}35 36/* Helper: create a sqlite3_value representing an integer */37static void setIntValue(sqlite3 *db, sqlite3_value *pVal, sqlite3_int64 v){38 Mem *m = (Mem*)pVal;39 memset(m, 0, sizeof(*m));40 m->db = db;41 m->flags = MEM_Int;42 m->u.i = v;43}44 45/* Helper: create a sqlite3_value representing NULL */46static void setNullValue(sqlite3_value *pVal){47 Mem *m = (Mem*)pVal;48 memset(m, 0, sizeof(*m));49 m->flags = MEM_Null;50}51 52/* Helper: read the current CountCtx.n from the aggregate context memory.53 Assumes CountCtx.n is the first 8 bytes of the allocated buffer. */54static sqlite3_int64 readCountFromCtx(sqlite3_context *pCtx){55 TEST_ASSERT_NOT_NULL(pCtx);56 TEST_ASSERT_NOT_NULL(pCtx->pMem);57 TEST_ASSERT_NOT_NULL(pCtx->pMem->z);58 sqlite3_int64 *pn = (sqlite3_int64*)(void*)pCtx->pMem->z;59 return *pn;60}61 62/* Unity fixtures */63void setUp(void) {64 /* nothing */65}66void tearDown(void) {67 /* nothing */68}69 70/* Test: COUNT(x) increments when x is non-NULL */71void test_countStep_increments_on_non_null(void){72 sqlite3 *db = 0;73 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));74 75 sqlite3_context ctx;76 Mem ctxMem;77 FuncDef def;78 initAggSqlite3Context(db, &ctx, &ctxMem, &def);79 80 sqlite3_value val;81 setIntValue(db, &val, 123);82 sqlite3_value *argv[1] = { &val };83 84 /* Before any call, aggregate memory should be allocated on first call.85 After one non-NULL step, count should be 1. */86 test_countStep(&ctx, 1, argv);87 TEST_ASSERT_NOT_NULL(ctx.pMem->z);88 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));89 90 /* Another non-NULL step increments again */91 setIntValue(db, &val, 456);92 test_countStep(&ctx, 1, argv);93 TEST_ASSERT_EQUAL_INT64(2, readCountFromCtx(&ctx));94 95 /* Cleanup */96 if( ctxMem.z ) sqlite3DbFree(db, ctxMem.z);97 sqlite3_close(db);98}99 100/* Test: COUNT(x) does not increment when x is NULL */101void test_countStep_skips_on_null(void){102 sqlite3 *db = 0;103 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));104 105 sqlite3_context ctx;106 Mem ctxMem;107 FuncDef def;108 initAggSqlite3Context(db, &ctx, &ctxMem, &def);109 110 /* Prime with one non-NULL so we can see no change on NULL */111 sqlite3_value nonnull;112 setIntValue(db, &nonnull, 1);113 sqlite3_value *argv1[1] = { &nonnull };114 test_countStep(&ctx, 1, argv1);115 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));116 117 /* Now pass NULL and ensure count does not change */118 sqlite3_value nullv;119 setNullValue(&nullv);120 sqlite3_value *argv2[1] = { &nullv };121 test_countStep(&ctx, 1, argv2);122 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));123 124 /* Cleanup */125 if( ctxMem.z ) sqlite3DbFree(db, ctxMem.z);126 sqlite3_close(db);127}128 129/* Test: COUNT(*) behavior (argc==0) increments regardless of arguments */130void test_countStep_increments_on_count_star(void){131 sqlite3 *db = 0;132 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));133 134 sqlite3_context ctx;135 Mem ctxMem;136 FuncDef def;137 initAggSqlite3Context(db, &ctx, &ctxMem, &def);138 139 /* First COUNT(*) step increments to 1 */140 test_countStep(&ctx, 0, 0);141 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));142 143 /* Second COUNT(*) step increments to 2 */144 test_countStep(&ctx, 0, 0);145 TEST_ASSERT_EQUAL_INT64(2, readCountFromCtx(&ctx));146 147 /* Cleanup */148 if( ctxMem.z ) sqlite3DbFree(db, ctxMem.z);149 sqlite3_close(db);150}151 152/* Test: Mixed sequence of NULL/non-NULL/COUNT(*) accumulates correctly */153void test_countStep_mixed_sequence_accumulates(void){154 sqlite3 *db = 0;155 TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));156 157 sqlite3_context ctx;158 Mem ctxMem;159 FuncDef def;160 initAggSqlite3Context(db, &ctx, &ctxMem, &def);161 162 sqlite3_value vInt, vNull;163 setIntValue(db, &vInt, 10);164 setNullValue(&vNull);165 sqlite3_value *argvInt[1] = { &vInt };166 sqlite3_value *argvNull[1] = { &vNull };167 168 /* Start at 0, apply sequence:169 non-NULL (+1) -> NULL (+0) -> COUNT(*) (+1) -> non-NULL (+1) -> NULL (+0) */170 test_countStep(&ctx, 1, argvInt); /* 1 */171 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));172 173 test_countStep(&ctx, 1, argvNull); /* 1 */174 TEST_ASSERT_EQUAL_INT64(1, readCountFromCtx(&ctx));175 176 test_countStep(&ctx, 0, 0); /* 2 */177 TEST_ASSERT_EQUAL_INT64(2, readCountFromCtx(&ctx));178 179 test_countStep(&ctx, 1, argvInt); /* 3 */180 TEST_ASSERT_EQUAL_INT64(3, readCountFromCtx(&ctx));181 182 test_countStep(&ctx, 1, argvNull); /* 3 */183 TEST_ASSERT_EQUAL_INT64(3, readCountFromCtx(&ctx));184 185 /* Cleanup */186 if( ctxMem.z ) sqlite3DbFree(db, ctxMem.z);187 sqlite3_close(db);188}189 190int main(void){191 UNITY_BEGIN();192 RUN_TEST(test_countStep_increments_on_non_null);193 RUN_TEST(test_countStep_skips_on_null);194 RUN_TEST(test_countStep_increments_on_count_star);195 RUN_TEST(test_countStep_mixed_sequence_accumulates);196 return UNITY_END();197}