sqlite / tests /tests_alter_errorMPrintf.c
AryaWu's picture
Upload folder using huggingface_hub
7510827 verified
#include "sqliteInt.h"
#include "unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Wrapper for the static function provided by the build harness */
extern void test_errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...);
/* Unity fixtures */
void setUp(void) {
/* no-op */
}
void tearDown(void) {
/* no-op */
}
/* Helpers to register UDFs that invoke test_errorMPrintf */
static void xErr_basic(sqlite3_context *ctx, int argc, sqlite3_value **argv){
/* Expect 4 args: text, int, int64, double */
const char *name = (const char*)sqlite3_value_text(argv[0]);
int a = sqlite3_value_int(argv[1]);
sqlite3_int64 b = sqlite3_value_int64(argv[2]);
double c = sqlite3_value_double(argv[3]);
test_errorMPrintf(ctx, "Name:%s Int:%d I64:%lld Dbl:%.2f", name, a, b, c);
}
static void xErr_percent(sqlite3_context *ctx, int argc, sqlite3_value **argv){
/* No args needed. Just a percent literal test. */
(void)argc; (void)argv;
test_errorMPrintf(ctx, "Rate: %d%%", 100);
}
static void xErr_null(sqlite3_context *ctx, int argc, sqlite3_value **argv){
/* 1 arg: possibly NULL text */
const char *s = (const char*)sqlite3_value_text(argv[0]); /* may be NULL */
test_errorMPrintf(ctx, "S=%s", s);
}
static void xErr_long(sqlite3_context *ctx, int argc, sqlite3_value **argv){
/* 1 arg: long text string */
const char *s = (const char*)sqlite3_value_text(argv[0]);
test_errorMPrintf(ctx, "Long:%s", s);
}
/* Utility: prepare, step and capture error message on failure.
Returns a heap-allocated copy of sqlite3_errmsg(db) when step returns SQLITE_ERROR.
Caller must sqlite3_free on the returned string (allocated with sqlite3_malloc). */
static char* step_and_capture_errmsg(sqlite3 *db, sqlite3_stmt *stmt, int *pRc){
int rc = sqlite3_step(stmt);
if( pRc ) *pRc = rc;
if( rc==SQLITE_ERROR ){
const char *z = sqlite3_errmsg(db);
size_t n = strlen(z);
char *copy = (char*)sqlite3_malloc((int)n+1);
if( copy ){
memcpy(copy, z, n+1);
}
return copy;
}
return NULL;
}
/* Test 1: Basic formatting with multiple specifiers */
void test_errorMPrintf_formats_basic(void){
sqlite3 *db = 0;
sqlite3_stmt *stmt = 0;
int rc;
rc = sqlite3_open(":memory:", &db);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_create_function_v2(db, "t_basic", 4, SQLITE_UTF8, NULL,
xErr_basic, NULL, NULL, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
const char *sql = "SELECT t_basic('Alice', 42, 1234567890123, 3.14159)";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
int stepRc = SQLITE_OK;
char *errmsg = step_and_capture_errmsg(db, stmt, &stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
/* Expect rounding to two decimals for %.2f */
const char *expected = "Name:Alice Int:42 I64:1234567890123 Dbl:3.14";
TEST_ASSERT_NOT_NULL(errmsg);
TEST_ASSERT_EQUAL_STRING(expected, errmsg);
sqlite3_free(errmsg);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
/* Test 2: Literal percent "%%" */
void test_errorMPrintf_handles_percent_literal(void){
sqlite3 *db = 0;
sqlite3_stmt *stmt = 0;
int rc;
rc = sqlite3_open(":memory:", &db);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_create_function_v2(db, "t_pct", 0, SQLITE_UTF8, NULL,
xErr_percent, NULL, NULL, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
const char *sql = "SELECT t_pct()";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
int stepRc = SQLITE_OK;
char *errmsg = step_and_capture_errmsg(db, stmt, &stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
const char *expected = "Rate: 100%";
TEST_ASSERT_NOT_NULL(errmsg);
TEST_ASSERT_EQUAL_STRING(expected, errmsg);
sqlite3_free(errmsg);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
/* Test 3: NULL string is rendered as "(NULL)" by sqlite3_mprintf family */
void test_errorMPrintf_null_string_becomes_NULL_literal(void){
sqlite3 *db = 0;
sqlite3_stmt *stmt = 0;
int rc;
rc = sqlite3_open(":memory:", &db);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_create_function_v2(db, "t_null", 1, SQLITE_UTF8, NULL,
xErr_null, NULL, NULL, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
const char *sql = "SELECT t_null(NULL)";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
int stepRc = SQLITE_OK;
char *errmsg = step_and_capture_errmsg(db, stmt, &stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
const char *expected = "S=(NULL)";
TEST_ASSERT_NOT_NULL(errmsg);
TEST_ASSERT_EQUAL_STRING(expected, errmsg);
sqlite3_free(errmsg);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
/* Test 4: Very long message to exercise allocation and correctness */
void test_errorMPrintf_long_message(void){
sqlite3 *db = 0;
sqlite3_stmt *stmt = 0;
int rc;
/* Build a long input string */
const int N = 50000;
char *longInput = (char*)malloc((size_t)N + 1);
TEST_ASSERT_NOT_NULL(longInput);
memset(longInput, 'A', (size_t)N);
longInput[N] = '\0';
/* Expected: "Long:" + longInput */
size_t prefixLen = 5; /* "Long:" */
char *expected = (char*)malloc(prefixLen + (size_t)N + 1);
TEST_ASSERT_NOT_NULL(expected);
memcpy(expected, "Long:", prefixLen);
memcpy(expected + prefixLen, longInput, (size_t)N + 1);
rc = sqlite3_open(":memory:", &db);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_create_function_v2(db, "t_long", 1, SQLITE_UTF8, NULL,
xErr_long, NULL, NULL, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_prepare_v2(db, "SELECT t_long(?)", -1, &stmt, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_bind_text(stmt, 1, longInput, -1, SQLITE_TRANSIENT);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
int stepRc = SQLITE_OK;
char *errmsg = step_and_capture_errmsg(db, stmt, &stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
TEST_ASSERT_NOT_NULL(errmsg);
TEST_ASSERT_EQUAL_STRING(expected, errmsg);
sqlite3_free(errmsg);
sqlite3_finalize(stmt);
sqlite3_close(db);
free(expected);
free(longInput);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_errorMPrintf_formats_basic);
RUN_TEST(test_errorMPrintf_handles_percent_literal);
RUN_TEST(test_errorMPrintf_null_string_becomes_NULL_literal);
RUN_TEST(test_errorMPrintf_long_message);
return UNITY_END();
}