AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global snapshot of StrAccum state after calling sqlite3ResultStrAccum */7static StrAccum gSnap;8static int gSnapValid;9 10/* Helper to reset snapshot */11static void resetSnapshot(void){12 memset(&gSnap, 0, sizeof(gSnap));13 gSnapValid = 0;14}15 16static void snapshotAccum(StrAccum *p){17 gSnap = *p; /* Shallow copy is sufficient for field checks */18 gSnapValid = 1;19}20 21/* UDF: triggers error path (accError set), also ensures malloced buffer is present */22static void udf_accum_error(sqlite3_context *ctx, int argc, sqlite3_value **argv){23 (void)argc; (void)argv;24 StrAccum a;25 memset(&a, 0, sizeof(a));26 a.db = sqlite3_context_db_handle(ctx);27 a.accError = SQLITE_TOOBIG; /* Non-zero error */28 a.printfFlags = SQLITE_PRINTF_MALLOCED; /* Pretend we have malloced text to be cleaned */29 a.zText = sqlite3DbStrDup(a.db, "ShouldBeFreed");30 a.nChar = (int)strlen("ShouldBeFreed");31 /* Call target function */32 sqlite3ResultStrAccum(ctx, &a);33 /* Capture state after call */34 snapshotAccum(&a);35}36 37/* UDF: triggers malloced path (isMalloced true) and returns "Hello" */38static void udf_accum_malloced(sqlite3_context *ctx, int argc, sqlite3_value **argv){39 (void)argc; (void)argv;40 StrAccum a;41 memset(&a, 0, sizeof(a));42 a.db = sqlite3_context_db_handle(ctx);43 a.printfFlags = SQLITE_PRINTF_MALLOCED;44 a.zText = sqlite3DbStrDup(a.db, "Hello");45 a.nChar = 5;46 /* Call target function */47 sqlite3ResultStrAccum(ctx, &a);48 /* Capture state after call (should not be reset) */49 snapshotAccum(&a);50}51 52/* UDF: triggers non-malloced/no-error path -> empty string result + reset */53static void udf_accum_nomalloc(sqlite3_context *ctx, int argc, sqlite3_value **argv){54 (void)argc; (void)argv;55 StrAccum a;56 memset(&a, 0, sizeof(a));57 a.db = sqlite3_context_db_handle(ctx);58 /* Not malloced; even if we place some text here, function should ignore and reset */59 a.zText = (char*)"World"; /* Not malloced (string literal) */60 a.nChar = 5;61 a.printfFlags = 0; /* Not malloced */62 /* Call target function */63 sqlite3ResultStrAccum(ctx, &a);64 /* Capture state after call (should be reset) */65 snapshotAccum(&a);66}67 68void setUp(void) {69 /* Setup code here, or leave empty */70}71 72void tearDown(void) {73 /* Cleanup code here, or leave empty */74}75 76/* Utility: open in-memory DB */77static sqlite3* open_memory_db(void){78 sqlite3 *db = NULL;79 int rc = sqlite3_open(":memory:", &db);80 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");81 return db;82}83 84/* Utility: finalize and ensure rc is OK */85static void finalize_and_check(sqlite3_stmt *pStmt){86 int rc = sqlite3_finalize(pStmt);87 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_finalize failed");88}89 90/* Test 1: accError path results in SQLITE_ERROR with specific code and StrAccum reset */91void test_sqlite3ResultStrAccum_error_sets_error_and_resets(void){92 resetSnapshot();93 sqlite3 *db = open_memory_db();94 95 int rc = sqlite3_create_function_v2(db, "unit_err", 0, SQLITE_UTF8, NULL,96 udf_accum_error, NULL, NULL, NULL);97 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);98 99 sqlite3_stmt *pStmt = NULL;100 rc = sqlite3_prepare_v2(db, "SELECT unit_err()", -1, &pStmt, NULL);101 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);102 103 rc = sqlite3_step(pStmt);104 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);105 TEST_ASSERT_EQUAL_INT(SQLITE_TOOBIG, sqlite3_errcode(db));106 107 /* Snapshot should be valid and reflect reset state after call */108 TEST_ASSERT_TRUE(gSnapValid);109 TEST_ASSERT_EQUAL_PTR(NULL, gSnap.zText);110 TEST_ASSERT_EQUAL_INT(0, gSnap.nChar);111 TEST_ASSERT_EQUAL_INT(0, gSnap.nAlloc);112 TEST_ASSERT_EQUAL_INT(0, (gSnap.printfFlags & SQLITE_PRINTF_MALLOCED));113 114 finalize_and_check(pStmt);115 sqlite3_close(db);116}117 118/* Test 2: malloced path returns the text and does not reset StrAccum immediately */119void test_sqlite3ResultStrAccum_malloced_returns_text_dynamic(void){120 resetSnapshot();121 sqlite3 *db = open_memory_db();122 123 int rc = sqlite3_create_function_v2(db, "unit_malloc", 0, SQLITE_UTF8, NULL,124 udf_accum_malloced, NULL, NULL, NULL);125 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);126 127 sqlite3_stmt *pStmt = NULL;128 rc = sqlite3_prepare_v2(db, "SELECT unit_malloc()", -1, &pStmt, NULL);129 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);130 131 rc = sqlite3_step(pStmt);132 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);133 134 const unsigned char *txt = sqlite3_column_text(pStmt, 0);135 int n = sqlite3_column_bytes(pStmt, 0);136 TEST_ASSERT_NOT_NULL(txt);137 TEST_ASSERT_EQUAL_INT(5, n);138 TEST_ASSERT_EQUAL_STRING("Hello", (const char*)txt);139 140 /* StrAccum should NOT be reset in this branch; fields remain set */141 TEST_ASSERT_TRUE(gSnapValid);142 TEST_ASSERT_NOT_NULL(gSnap.zText);143 TEST_ASSERT_EQUAL_INT(5, gSnap.nChar);144 TEST_ASSERT_NOT_EQUAL(0, (gSnap.printfFlags & SQLITE_PRINTF_MALLOCED));145 146 finalize_and_check(pStmt);147 sqlite3_close(db);148}149 150/* Test 3: non-malloced/no-error path returns empty string and resets StrAccum */151void test_sqlite3ResultStrAccum_nonmalloced_returns_empty_and_resets(void){152 resetSnapshot();153 sqlite3 *db = open_memory_db();154 155 int rc = sqlite3_create_function_v2(db, "unit_nomalloc", 0, SQLITE_UTF8, NULL,156 udf_accum_nomalloc, NULL, NULL, NULL);157 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);158 159 sqlite3_stmt *pStmt = NULL;160 rc = sqlite3_prepare_v2(db, "SELECT unit_nomalloc()", -1, &pStmt, NULL);161 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);162 163 rc = sqlite3_step(pStmt);164 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);165 166 const unsigned char *txt = sqlite3_column_text(pStmt, 0);167 int n = sqlite3_column_bytes(pStmt, 0);168 TEST_ASSERT_NOT_NULL(txt);169 TEST_ASSERT_EQUAL_INT(0, n);170 TEST_ASSERT_EQUAL_STRING("", (const char*)txt);171 172 /* StrAccum should be reset */173 TEST_ASSERT_TRUE(gSnapValid);174 TEST_ASSERT_EQUAL_PTR(NULL, gSnap.zText);175 TEST_ASSERT_EQUAL_INT(0, gSnap.nChar);176 TEST_ASSERT_EQUAL_INT(0, gSnap.nAlloc);177 TEST_ASSERT_EQUAL_INT(0, (gSnap.printfFlags & SQLITE_PRINTF_MALLOCED));178 179 finalize_and_check(pStmt);180 sqlite3_close(db);181}182 183int main(void) {184 UNITY_BEGIN();185 RUN_TEST(test_sqlite3ResultStrAccum_error_sets_error_and_resets);186 RUN_TEST(test_sqlite3ResultStrAccum_malloced_returns_text_dynamic);187 RUN_TEST(test_sqlite3ResultStrAccum_nonmalloced_returns_empty_and_resets);188 return UNITY_END();189}