AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Wrapper provided in the module under test */7extern void test_groupConcatFinalize(sqlite3_context *context);8 9/* Unity fixtures */10void setUp(void) { }11void tearDown(void) { }12 13/* Helper: initialize sqlite3 Mems and context for finalize() */14static void init_context(sqlite3 *db, sqlite3_context *pCtx, Mem *pOut, Mem *pAgg){15 memset(pCtx, 0, sizeof(*pCtx));16 memset(pOut, 0, sizeof(*pOut));17 memset(pAgg, 0, sizeof(*pAgg));18 sqlite3VdbeMemInit(pOut, db, 0);19 sqlite3VdbeMemInit(pAgg, db, 0);20 pCtx->db = db;21 pCtx->pOut = pOut;22 pCtx->pMem = pAgg;23}24 25/* Helper: create and populate a GroupConcatCtx with given content */26static GroupConcatCtx* make_gcc_with_content(sqlite3 *db, const char *content27#ifndef SQLITE_OMIT_WINDOWFUNC28 , int withSepArray29#endif30){31 GroupConcatCtx *pGCC = (GroupConcatCtx*)sqlite3DbMallocZero(db, sizeof(GroupConcatCtx));32 TEST_ASSERT_NOT_NULL_MESSAGE(pGCC, "Failed to allocate GroupConcatCtx");33 34 /* Initialize StrAccum for dynamic accumulation with sane mxAlloc */35 sqlite3StrAccumInit(&pGCC->str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);36 37 if( content ){38 sqlite3_str_append(&pGCC->str, content, (int)strlen(content));39 }40 41#ifndef SQLITE_OMIT_WINDOWFUNC42 if( withSepArray ){43 /* Allocate a dummy pnSepLengths array to exercise free path in finalize */44 pGCC->pnSepLengths = (int*)sqlite3DbMallocZero(db, sizeof(int) * 3);45 TEST_ASSERT_NOT_NULL_MESSAGE(pGCC->pnSepLengths, "Failed to allocate pnSepLengths");46 pGCC->nAccum = 2; /* arbitrary positive to represent some accumulation */47 pGCC->nFirstSepLength = 1; /* arbitrary */48 pGCC->pnSepLengths[0] = 1;49 pGCC->pnSepLengths[1] = 2;50 }51#endif52 53 return pGCC;54}55 56/* Helper: attach an aggregate context (GroupConcatCtx*) to the context Mem */57static void attach_agg_context(sqlite3 *db, sqlite3_context *pCtx, Mem *pAgg, GroupConcatCtx *pGCC){58 (void)db;59 /* Mark the aggregate-context Mem as MEM_Agg and store pointer in z */60 pAgg->flags = MEM_Agg;61 pAgg->z = (char*)pGCC;62}63 64/* Helper: assert that Mem contains exactly expected string */65static void assert_mem_string_eq(Mem *pOut, const char *expected){66 TEST_ASSERT_TRUE_MESSAGE((pOut->flags & MEM_Str)!=0, "Output Mem does not contain a string");67 TEST_ASSERT_NOT_NULL_MESSAGE(pOut->z, "Output string pointer is NULL");68 TEST_ASSERT_EQUAL_INT((int)strlen(expected), pOut->n);69 TEST_ASSERT_EQUAL_STRING(expected, pOut->z);70}71 72/* Test: No aggregate context present -> finalize produces no result */73void test_groupConcatFinalize_no_agg_context_leaves_no_result(void){74 sqlite3 *db = 0;75 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));76 77 sqlite3_context ctx;78 Mem out, agg;79 init_context(db, &ctx, &out, &agg);80 81 /* Do NOT set MEM_Agg: simulates no aggregate-context allocation ever happened */82 test_groupConcatFinalize(&ctx);83 84 /* Expect no result set into pOut */85 TEST_ASSERT_EQUAL_UINT32(0, out.flags);86 TEST_ASSERT_NULL(out.z);87 88 sqlite3VdbeMemRelease(&out);89 sqlite3VdbeMemRelease(&agg);90 sqlite3_close(db);91}92 93/* Test: Aggregate context present with content "A,B" -> finalize returns "A,B" */94void test_groupConcatFinalize_returns_accumulated_text(void){95 sqlite3 *db = 0;96 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));97 98 sqlite3_context ctx;99 Mem out, agg;100 init_context(db, &ctx, &out, &agg);101 102 GroupConcatCtx *pGCC = make_gcc_with_content(db, "A,B"103#ifndef SQLITE_OMIT_WINDOWFUNC104 , 0105#endif106 );107 attach_agg_context(db, &ctx, &agg, pGCC);108 109 test_groupConcatFinalize(&ctx);110 111 assert_mem_string_eq(&out, "A,B");112 113 /* Cleanup: result string lives in out; release it, then free aggregate ctx */114 sqlite3VdbeMemRelease(&out);115 sqlite3VdbeMemRelease(&agg);116 sqlite3DbFree(db, pGCC);117 sqlite3_close(db);118}119 120/* Test: Aggregate context present, pnSepLengths allocated -> finalize still returns correct text */121void test_groupConcatFinalize_with_sep_lengths_allocated(void){122#ifndef SQLITE_OMIT_WINDOWFUNC123 sqlite3 *db = 0;124 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));125 126 sqlite3_context ctx;127 Mem out, agg;128 init_context(db, &ctx, &out, &agg);129 130 GroupConcatCtx *pGCC = make_gcc_with_content(db, "X,Y", 1);131 attach_agg_context(db, &ctx, &agg, pGCC);132 133 test_groupConcatFinalize(&ctx);134 135 assert_mem_string_eq(&out, "X,Y");136 137 /* Cleanup */138 sqlite3VdbeMemRelease(&out);139 sqlite3VdbeMemRelease(&agg);140 sqlite3DbFree(db, pGCC);141 sqlite3_close(db);142#else143 /* If window functions are omitted, just assert true to mark the test as pass */144 TEST_ASSERT_TRUE(1);145#endif146}147 148/* Optional: Aggregate context present but with empty accumulation -> finalize gives empty string */149void test_groupConcatFinalize_empty_accumulation_returns_empty_string(void){150 sqlite3 *db = 0;151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));152 153 sqlite3_context ctx;154 Mem out, agg;155 init_context(db, &ctx, &out, &agg);156 157 GroupConcatCtx *pGCC = make_gcc_with_content(db, "", 158#ifndef SQLITE_OMIT_WINDOWFUNC159 0160#endif161 );162 attach_agg_context(db, &ctx, &agg, pGCC);163 164 test_groupConcatFinalize(&ctx);165 166 /* Expect an empty string as result (not NULL), since pGCC existed */167 assert_mem_string_eq(&out, "");168 169 sqlite3VdbeMemRelease(&out);170 sqlite3VdbeMemRelease(&agg);171 sqlite3DbFree(db, pGCC);172 sqlite3_close(db);173}174 175/* Unity main */176int main(void){177 UNITY_BEGIN();178 RUN_TEST(test_groupConcatFinalize_no_agg_context_leaves_no_result);179 RUN_TEST(test_groupConcatFinalize_returns_accumulated_text);180 RUN_TEST(test_groupConcatFinalize_with_sep_lengths_allocated);181 RUN_TEST(test_groupConcatFinalize_empty_accumulation_returns_empty_string);182 return UNITY_END();183}