AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdarg.h>4#include <string.h>5#include <stdio.h>6 7/* External wrapper for the static function under test */8extern void test_checkAppendMsg(IntegrityCk *pCheck, const char *zFormat, ...);9 10static sqlite3 *gDb = NULL;11 12static void initIntegrityCk(IntegrityCk *pCk, char *buf, int buflen){13 memset(pCk, 0, sizeof(*pCk));14 /* Initialize the sqlite3_str accumulator */15 sqlite3StrAccumInit(&pCk->errMsg, 0, buf, buflen, SQLITE_MAX_LENGTH);16 pCk->mxErr = 10;17 pCk->nErr = 0;18 pCk->rc = SQLITE_OK;19 pCk->db = gDb;20 pCk->zPfx = NULL;21 pCk->v0 = pCk->v1 = pCk->v2 = 0;22}23 24/* Unity setUp/tearDown */25void setUp(void) {26 if( gDb==NULL ){27 /* Create an SQLite connection for progress/interrupt contexts if needed */28 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &gDb));29 }30}31void tearDown(void) {32 if( gDb ){33 sqlite3_close(gDb);34 gDb = NULL;35 }36}37 38/* Helper to get current message as a C-string (non-owning if within base buf) */39static const char* finishString(sqlite3_str *pStr){40 /* Use sqlite3StrAccumFinish to retrieve current string content.41 It may return pointer to base buffer (no malloc) or malloc'd memory.42 Since we only read and then discard the IntegrityCk, and base buffer43 is our local stack buffer, we can rely on it without freeing. */44 return sqlite3StrAccumFinish(pStr);45}46 47/* Test: Basic append with empty error message */48void test_checkAppendMsg_basic_append(void){49 IntegrityCk ck;50 char buf[256];51 initIntegrityCk(&ck, buf, (int)sizeof(buf));52 53 test_checkAppendMsg(&ck, "Hello %s", "World");54 55 const char *msg = finishString(&ck.errMsg);56 TEST_ASSERT_NOT_NULL(msg);57 TEST_ASSERT_EQUAL_STRING("Hello World", msg);58 TEST_ASSERT_EQUAL_INT(1, ck.nErr);59 TEST_ASSERT_EQUAL_INT(9, ck.mxErr);60 TEST_ASSERT_EQUAL_INT(SQLITE_OK, ck.rc);61}62 63/* Test: Newline insertion on second append, and prefix applied */64void test_checkAppendMsg_newline_and_prefix(void){65 IntegrityCk ck;66 char buf[256];67 initIntegrityCk(&ck, buf, (int)sizeof(buf));68 69 test_checkAppendMsg(&ck, "First");70 /* Set a prefix and values for the next append */71 ck.zPfx = "Tree %u/%u: ";72 ck.v0 = 7;73 ck.v1 = 11;74 ck.v2 = 0;75 76 test_checkAppendMsg(&ck, "Second %s", "line");77 78 const char *msg = finishString(&ck.errMsg);79 TEST_ASSERT_NOT_NULL(msg);80 /* Expect: First\nTree 7/11: Second line */81 TEST_ASSERT_EQUAL_STRING("First\nTree 7/11: Second line", msg);82 TEST_ASSERT_EQUAL_INT(2, ck.nErr);83 TEST_ASSERT_EQUAL_INT(8, ck.mxErr);84 TEST_ASSERT_EQUAL_INT(SQLITE_OK, ck.rc);85}86 87/* Test: No append when mxErr is zero */88void test_checkAppendMsg_no_append_when_mxErr_zero(void){89 IntegrityCk ck;90 char buf[128];91 initIntegrityCk(&ck, buf, (int)sizeof(buf));92 ck.mxErr = 0; /* Force early return */93 /* Ensure no interrupt and no progress handler */94 if( ck.db ){95 /* Clear progress handler and interrupt flags */96 sqlite3_progress_handler(ck.db, 0, NULL, NULL);97 AtomicStore(&ck.db->u1.isInterrupted, 0);98 }99 100 test_checkAppendMsg(&ck, "ShouldNotAppear");101 102 const char *msg = finishString(&ck.errMsg);103 /* With no writes, nChar may be 0 and finish may return empty string ("") or NULL.104 Based on sqlite3StrAccumFinish semantics, it returns a valid pointer ("" or base).105 */106 TEST_ASSERT_NOT_NULL(msg);107 TEST_ASSERT_EQUAL_STRING("", msg);108 TEST_ASSERT_EQUAL_INT(0, ck.nErr);109 TEST_ASSERT_EQUAL_INT(0, ck.mxErr);110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, ck.rc);111}112 113/* Progress handler causes immediate interrupt (no append occurs) */114static int progress_cb(void *pArg){115 /* Return non-zero to signal interrupt */116 (void)pArg;117 return 1;118}119void test_checkAppendMsg_progress_interrupt(void){120 IntegrityCk ck;121 char buf[128];122 initIntegrityCk(&ck, buf, (int)sizeof(buf));123 /* Install a progress handler that triggers immediately */124 sqlite3_progress_handler(ck.db, 1, progress_cb, NULL);125 126 test_checkAppendMsg(&ck, "Any %s", "message");127 128 const char *msg = finishString(&ck.errMsg);129 TEST_ASSERT_NOT_NULL(msg);130 /* No message appended due to interrupt */131 TEST_ASSERT_EQUAL_STRING("", msg);132 /* checkProgress increments nErr and sets rc + mxErr=0 */133 TEST_ASSERT_EQUAL_INT(SQLITE_INTERRUPT, ck.rc);134 TEST_ASSERT_EQUAL_INT(1, ck.nErr);135 TEST_ASSERT_EQUAL_INT(0, ck.mxErr);136 137 /* Clear progress handler */138 sqlite3_progress_handler(ck.db, 0, NULL, NULL);139}140 141/* Test: OOM handling via errMsg.accError==SQLITE_NOMEM triggers checkOom() */142void test_checkAppendMsg_oom_sets_rc_and_mxErr(void){143 IntegrityCk ck;144 char buf[128];145 initIntegrityCk(&ck, buf, (int)sizeof(buf));146 ck.mxErr = 3;147 ck.nErr = 0;148 149 /* Simulate OOM in the string accumulator */150 ck.errMsg.accError = SQLITE_NOMEM;151 152 test_checkAppendMsg(&ck, "X");153 154 /* Expect OOM flags set */155 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, ck.rc);156 TEST_ASSERT_EQUAL_INT(0, ck.mxErr);157 /* At least one error recorded */158 TEST_ASSERT_TRUE(ck.nErr >= 1);159}160 161/* main runner */162int main(void){163 UNITY_BEGIN();164 RUN_TEST(test_checkAppendMsg_basic_append);165 RUN_TEST(test_checkAppendMsg_newline_and_prefix);166 RUN_TEST(test_checkAppendMsg_no_append_when_mxErr_zero);167 RUN_TEST(test_checkAppendMsg_progress_interrupt);168 RUN_TEST(test_checkAppendMsg_oom_sets_rc_and_mxErr);169 return UNITY_END();170}