AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* External wrapper provided in the module for the static function */8extern void test_concatwsFunc(sqlite3_context *context, int argc, sqlite3_value **argv);9 10static sqlite3 *gDb = NULL;11 12/* Bridge function to call the target function under test via SQLite */13static void x_t_concatws(sqlite3_context *ctx, int argc, sqlite3_value **argv){14 test_concatwsFunc(ctx, argc, argv);15}16 17/* Helper: register the bridge function */18static void register_concatws(sqlite3 *db){19 int rc = sqlite3_create_function(db, "t_concatws", -1, SQLITE_UTF8, 0,20 x_t_concatws, 0, 0);21 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);22}23 24/* Helper: run a query returning a single column, single row.25 Returns a malloc'd buffer (sqlite3_malloc) with a NUL terminator for convenience.26 Sets *pN to the number of bytes (not including NUL), and *pType to SQLITE_NULL/TEXT/etc.27 For NULL results, returns NULL and sets *pN=0. */28static char *query_single_text_dup(const char *sql, int *pN, int *pType){29 sqlite3_stmt *stmt = NULL;30 int rc = sqlite3_prepare_v2(gDb, sql, -1, &stmt, 0);31 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");32 rc = sqlite3_step(stmt);33 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "expected single row");34 int t = sqlite3_column_type(stmt, 0);35 if( pType ) *pType = t;36 37 char *out = NULL;38 int n = 0;39 if( t!=SQLITE_NULL ){40 const unsigned char *z = sqlite3_column_text(stmt, 0);41 n = sqlite3_column_bytes(stmt, 0);42 out = (char*)sqlite3_malloc(n+1);43 TEST_ASSERT_NOT_NULL(out);44 if( n>0 ) memcpy(out, z, n);45 out[n] = 0; /* NUL-terminate for convenience even if embedded NULs exist */46 }47 if( pN ) *pN = n;48 49 rc = sqlite3_step(stmt);50 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_DONE, rc, "expected only one row");51 sqlite3_finalize(stmt);52 return out;53}54 55void setUp(void) {56 int rc = sqlite3_open(":memory:", &gDb);57 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");58 register_concatws(gDb);59}60 61void tearDown(void) {62 if( gDb ){63 int rc = sqlite3_close(gDb);64 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_close failed");65 gDb = NULL;66 }67}68 69/* Basic concatenation: ASCII separator and simple strings */70void test_concatwsFunc_basic(void){71 int n=0, t=0;72 char *res = query_single_text_dup("SELECT t_concatws('-', 'a','b','c');", &n, &t);73 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);74 TEST_ASSERT_EQUAL_INT(5, n);75 TEST_ASSERT_EQUAL_STRING("a-b-c", res);76 sqlite3_free(res);77}78 79/* NULL separator must yield NULL result */80void test_concatwsFunc_null_separator_results_null(void){81 int n=0, t=0;82 char *res = query_single_text_dup("SELECT t_concatws(NULL, 'a','b');", &n, &t);83 TEST_ASSERT_NULL(res);84 TEST_ASSERT_EQUAL_INT(0, n);85 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, t);86}87 88/* Ignore NULL arguments and do not produce extra separators around them */89void test_concatwsFunc_ignores_null_args(void){90 int n=0, t=0;91 char *res = query_single_text_dup(92 "SELECT t_concatws('-', NULL, 'b', NULL, 'c', NULL);", &n, &t);93 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);94 TEST_ASSERT_EQUAL_INT(3, n);95 TEST_ASSERT_EQUAL_STRING("b-c", res);96 sqlite3_free(res);97}98 99/* If all non-separator args are NULL, result is empty string (not NULL) */100void test_concatwsFunc_all_null_args_yields_empty_string(void){101 int n=0, t=0;102 char *res = query_single_text_dup("SELECT t_concatws('-', NULL, NULL);", &n, &t);103 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);104 TEST_ASSERT_EQUAL_INT(0, n);105 TEST_ASSERT_NOT_NULL(res);106 TEST_ASSERT_EQUAL_STRING("", res);107 sqlite3_free(res);108}109 110/* Empty separator concatenates values without separators */111void test_concatwsFunc_empty_separator(void){112 int n=0, t=0;113 char *res = query_single_text_dup("SELECT t_concatws('', 'a','b','c');", &n, &t);114 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);115 TEST_ASSERT_EQUAL_INT(3, n);116 TEST_ASSERT_EQUAL_STRING("abc", res);117 sqlite3_free(res);118}119 120/* Numeric arguments are coerced to text */121void test_concatwsFunc_numeric_args(void){122 int n=0, t=0;123 char *res = query_single_text_dup("SELECT t_concatws('-', 1, 2.5, 3);", &n, &t);124 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);125 /* Expect "1-2.5-3" */126 TEST_ASSERT_EQUAL_INT(7, n);127 TEST_ASSERT_EQUAL_STRING("1-2.5-3", res);128 sqlite3_free(res);129}130 131/* UTF-8 multibyte separator and arguments */132void test_concatwsFunc_utf8_separator_and_args(void){133 int n=0, t=0;134 /* '★' = E2 98 85; 'あ' = E3 81 82; 'β' = CE B2 */135 const char *sql =136 "SELECT t_concatws('\xE2\x98\x85', '\xE3\x81\x82', '\xCE\xB2');";137 char *res = query_single_text_dup(sql, &n, &t);138 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);139 /* "あ★β" -> 3 + 3 + 2 = 8 bytes */140 TEST_ASSERT_EQUAL_INT(8, n);141 /* Build expected as explicit bytes to be safe */142 const unsigned char expected[] = {0xE3,0x81,0x82, 0xE2,0x98,0x85, 0xCE,0xB2};143 TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, (unsigned char*)res, 8);144 sqlite3_free(res);145}146 147/* Embedded NUL bytes in arguments should be preserved; verify by bytes */148void test_concatwsFunc_embedded_nul_argument(void){149 int n=0, t=0;150 /* First arg after separator is CAST(X'610062' AS TEXT) => "a\0b" */151 char *res = query_single_text_dup(152 "SELECT t_concatws('-', CAST(x'610062' AS TEXT), 'X');", &n, &t);153 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);154 TEST_ASSERT_EQUAL_INT(5, n);155 const unsigned char expected[5] = { 'a', 0x00, 'b', '-', 'X' };156 TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, (unsigned char*)res, 5);157 sqlite3_free(res);158}159 160/* Many arguments: check total length and number of separators */161void test_concatwsFunc_many_args(void){162 /* Build SQL: SELECT t_concatws('-', 'x','x',... 'x') with 20 'x' args */163 const int nArgs = 20;164 /* Rough buffer size */165 char sql[1024];166 strcpy(sql, "SELECT t_concatws('-', ");167 for(int i=0; i<nArgs; i++){168 strcat(sql, "'x'");169 if( i<nArgs-1 ) strcat(sql, ",");170 }171 strcat(sql, ");");172 173 int n=0, t=0;174 char *res = query_single_text_dup(sql, &n, &t);175 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);176 177 /* Expect nArgs 'x' plus nArgs-1 '-' separators */178 int expectedLen = nArgs + (nArgs-1);179 TEST_ASSERT_EQUAL_INT(expectedLen, n);180 181 /* Count '-' occurrences */182 int dashCount = 0;183 for(int i=0; i<n; i++){184 if( res[i]=='-' ) dashCount++;185 }186 TEST_ASSERT_EQUAL_INT(nArgs-1, dashCount);187 188 /* First and last should be 'x' */189 TEST_ASSERT_EQUAL_CHAR('x', res[0]);190 TEST_ASSERT_EQUAL_CHAR('x', res[n-1]);191 192 sqlite3_free(res);193}194 195/* Empty string arguments (non-NULL) should produce separators between them */196void test_concatwsFunc_empty_string_arguments(void){197 int n=0, t=0;198 char *res = query_single_text_dup("SELECT t_concatws('-', '', '', '');", &n, &t);199 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, t);200 /* Expect "--" (two separators, three empty strings) */201 TEST_ASSERT_EQUAL_INT(2, n);202 TEST_ASSERT_EQUAL_STRING("--", res);203 sqlite3_free(res);204}205 206int main(void) {207 UNITY_BEGIN();208 RUN_TEST(test_concatwsFunc_basic);209 RUN_TEST(test_concatwsFunc_null_separator_results_null);210 RUN_TEST(test_concatwsFunc_ignores_null_args);211 RUN_TEST(test_concatwsFunc_all_null_args_yields_empty_string);212 RUN_TEST(test_concatwsFunc_empty_separator);213 RUN_TEST(test_concatwsFunc_numeric_args);214 RUN_TEST(test_concatwsFunc_utf8_separator_and_args);215 RUN_TEST(test_concatwsFunc_embedded_nul_argument);216 RUN_TEST(test_concatwsFunc_many_args);217 RUN_TEST(test_concatwsFunc_empty_string_arguments);218 return UNITY_END();219}