#include "sqliteInt.h" #include "unity.h" #include /* Global database handle for allocations */ static sqlite3 *gDb = NULL; static Expr* make_id_expr(Parse *pParse, const char *zId){ Token t; t.z = (char*)zId; t.n = (int)strlen(zId); return sqlite3ExprAlloc(pParse->db, TK_ID, &t, 0); } static Expr* make_int_expr(Parse *pParse, const char *zNum){ Token t; t.z = (char*)zNum; t.n = (int)strlen(zNum); return sqlite3ExprAlloc(pParse->db, TK_INTEGER, &t, 0); } void setUp(void) { if (!gDb) { int rc = sqlite3_open(":memory:", &gDb); TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory DB"); } } void tearDown(void) { if (gDb) { int rc = sqlite3_close(gDb); TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to close in-memory DB"); gDb = NULL; } } /* Helper to initialize a Parse object */ static void initParse(Parse *pParse){ memset(pParse, 0, sizeof(Parse)); pParse->db = gDb; /* Set a non-UNMAP mode to verify restoration */ pParse->eParseMode = PARSE_MODE_RENAME; } /* Test: NULL expression must be safe and must restore eParseMode */ void test_sqlite3RenameExprUnmap_restores_mode_when_expr_null(void){ Parse sParse; initParse(&sParse); u8 orig = sParse.eParseMode; sqlite3RenameExprUnmap(&sParse, NULL); TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode); TEST_ASSERT_EQUAL_INT(0, sParse.nErr); } /* Test: Simple literal expression, mode restored after call */ void test_sqlite3RenameExprUnmap_restores_mode_when_expr_simple(void){ Parse sParse; initParse(&sParse); Expr *pInt = make_int_expr(&sParse, "123"); TEST_ASSERT_NOT_NULL(pInt); u8 orig = sParse.eParseMode; sqlite3RenameExprUnmap(&sParse, pInt); TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode); TEST_ASSERT_EQUAL_INT(0, sParse.nErr); sqlite3ExprDelete(sParse.db, pInt); } /* Test: Expression tree should not be modified by Unmap (structure intact) */ void test_sqlite3RenameExprUnmap_does_not_modify_tree(void){ Parse sParse; initParse(&sParse); Expr *pA = make_id_expr(&sParse, "a"); Expr *pB = make_id_expr(&sParse, "b"); TEST_ASSERT_NOT_NULL(pA); TEST_ASSERT_NOT_NULL(pB); Expr *pPlus = sqlite3PExpr(&sParse, TK_PLUS, pA, pB); TEST_ASSERT_NOT_NULL(pPlus); Expr *pLeftBefore = pPlus->pLeft; Expr *pRightBefore = pPlus->pRight; sqlite3RenameExprUnmap(&sParse, pPlus); /* Validate the expression shape is unchanged */ TEST_ASSERT_EQUAL_PTR(pLeftBefore, pPlus->pLeft); TEST_ASSERT_EQUAL_PTR(pRightBefore, pPlus->pRight); TEST_ASSERT_EQUAL_INT(0, sParse.nErr); sqlite3ExprDelete(sParse.db, pPlus); /* deletes pA and pB as well */ } /* Test: Nested expression trees traverse without errors and restore mode */ void test_sqlite3RenameExprUnmap_handles_nested_tree_no_side_effects(void){ Parse sParse; initParse(&sParse); /* Build (a + b) * 3 */ Expr *pA = make_id_expr(&sParse, "a"); Expr *pB = make_id_expr(&sParse, "b"); Expr *pPlus = sqlite3PExpr(&sParse, TK_PLUS, pA, pB); TEST_ASSERT_NOT_NULL(pA); TEST_ASSERT_NOT_NULL(pB); TEST_ASSERT_NOT_NULL(pPlus); Expr *pThree = make_int_expr(&sParse, "3"); TEST_ASSERT_NOT_NULL(pThree); /* Multiplication operator is TK_STAR in SQLite */ Expr *pMul = sqlite3PExpr(&sParse, TK_STAR, pPlus, pThree); TEST_ASSERT_NOT_NULL(pMul); u8 orig = sParse.eParseMode; sqlite3RenameExprUnmap(&sParse, pMul); /* No parse errors introduced and mode restored */ TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode); TEST_ASSERT_EQUAL_INT(0, sParse.nErr); /* Tree is intact */ TEST_ASSERT_EQUAL_PTR(pPlus, pMul->pLeft); TEST_ASSERT_EQUAL_PTR(pThree, pMul->pRight); sqlite3ExprDelete(sParse.db, pMul); /* deletes entire tree */ } int main(void) { UNITY_BEGIN(); RUN_TEST(test_sqlite3RenameExprUnmap_restores_mode_when_expr_null); RUN_TEST(test_sqlite3RenameExprUnmap_restores_mode_when_expr_simple); RUN_TEST(test_sqlite3RenameExprUnmap_does_not_modify_tree); RUN_TEST(test_sqlite3RenameExprUnmap_handles_nested_tree_no_side_effects); return UNITY_END(); }