#include "sqliteInt.h" #include "unity.h" #include #include /* Wrapper for the static function provided by the module under test */ extern void test_renameColumnElistNames( Parse *pParse, RenameCtx *pCtx, const ExprList *pEList, const char *zOld ); /* Helper to open a database and initialize a Parse object */ static void openDbAndInitParse(sqlite3 **ppDb, Parse *pParse){ sqlite3 *db = 0; int rc = sqlite3_open(":memory:", &db); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); memset(pParse, 0, sizeof(*pParse)); pParse->db = db; *ppDb = db; } /* Helper to create an identifier expression */ static Expr* makeIdExpr(sqlite3 *db, const char *z){ Token t; t.z = z; t.n = (int)strlen(z); return sqlite3ExprAlloc(db, TK_ID, &t, 0); } /* Helper to append an expression with a specific alias name (zAlias) to an ExprList. Returns the zEName pointer assigned inside the list (owned by db). */ static const char* appendExprWithAlias(Parse *pParse, ExprList **ppList, const char *zExpr, const char *zAlias){ ExprList *pList = *ppList; Expr *pExpr = makeIdExpr(pParse->db, zExpr ? zExpr : "x"); pList = sqlite3ExprListAppend(pParse, pList, pExpr); TEST_ASSERT_NOT_NULL(pList); Token aliasTok; aliasTok.z = zAlias; aliasTok.n = (int)strlen(zAlias); sqlite3ExprListSetName(pParse, pList, &aliasTok, 1); *ppList = pList; /* Return the actual stored alias pointer */ return pList->a[pList->nExpr-1].zEName; } /* Helper to map a zEName pointer to a token for rename machinery */ static void mapNameToken(Parse *pParse, const char *zName, const char *zTok){ Token t; t.z = zTok; t.n = (int)strlen(zTok); sqlite3RenameTokenMap(pParse, (void*)zName, &t); } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* Test: pEList is NULL -> no crash, no tokens added */ void test_renameColumnElistNames_null_list(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); /* Ensure ctx has pParse for any allocations */ ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, NULL, "x"); TEST_ASSERT_EQUAL_INT(0, ctx.nList); sqlite3_close(db); } /* Test: No names match zOld -> no tokens added */ void test_renameColumnElistNames_no_matches(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); ExprList *pList = NULL; const char *zA = appendExprWithAlias(&sParse, &pList, "col1", "A"); const char *zB = appendExprWithAlias(&sParse, &pList, "col2", "B"); const char *zC = appendExprWithAlias(&sParse, &pList, "col3", "C"); /* Map tokens (though they won't be used since names do not match) */ mapNameToken(&sParse, zA, "TOK_A"); mapNameToken(&sParse, zB, "TOK_B"); mapNameToken(&sParse, zC, "TOK_C"); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, pList, "Z"); /* no matches expected */ TEST_ASSERT_EQUAL_INT(0, ctx.nList); sqlite3ExprListDelete(sParse.db, pList); sqlite3_close(db); } /* Test: Single match; case-insensitive */ void test_renameColumnElistNames_single_match_case_insensitive(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); ExprList *pList = NULL; const char *zFoo = appendExprWithAlias(&sParse, &pList, "col1", "Foo"); const char *zBar = appendExprWithAlias(&sParse, &pList, "col2", "Bar"); mapNameToken(&sParse, zFoo, "TOK_FOO"); mapNameToken(&sParse, zBar, "TOK_BAR"); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, pList, "fOO"); /* should match "Foo" */ TEST_ASSERT_EQUAL_INT(1, ctx.nList); TEST_ASSERT_NOT_NULL(ctx.aList); TEST_ASSERT_TRUE(ctx.aList[0].n > 0); /* cleanup */ sqlite3DbFree(sParse.db, ctx.aList); sqlite3ExprListDelete(sParse.db, pList); sqlite3_close(db); } /* Test: Multiple matches, including different cases */ void test_renameColumnElistNames_multiple_matches(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); ExprList *pList = NULL; const char *zX1 = appendExprWithAlias(&sParse, &pList, "c1", "x"); const char *zX2 = appendExprWithAlias(&sParse, &pList, "c2", "X"); const char *zY = appendExprWithAlias(&sParse, &pList, "c3", "y"); const char *zX3 = appendExprWithAlias(&sParse, &pList, "c4", "x"); mapNameToken(&sParse, zX1, "TOK_X1"); mapNameToken(&sParse, zX2, "TOK_X2"); mapNameToken(&sParse, zY, "TOK_Y"); mapNameToken(&sParse, zX3, "TOK_X3"); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, pList, "x"); TEST_ASSERT_EQUAL_INT(3, ctx.nList); sqlite3DbFree(sParse.db, ctx.aList); sqlite3ExprListDelete(sParse.db, pList); sqlite3_close(db); } /* Test: Entries with eEName != ENAME_NAME are ignored */ void test_renameColumnElistNames_ignores_non_ENAME_NAME(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); ExprList *pList = NULL; const char *zTgtIgnored = appendExprWithAlias(&sParse, &pList, "c1", "tgt"); const char *zTgtMatch = appendExprWithAlias(&sParse, &pList, "c2", "tgt"); /* Force the first entry to have a non-name eEName (e.g. ENAME_SPAN) */ TEST_ASSERT_TRUE(pList->nExpr >= 2); pList->a[0].fg.eEName = ENAME_SPAN; /* ensure it is ignored */ /* The second entry remains ENAME_NAME as set by sqlite3ExprListSetName */ mapNameToken(&sParse, zTgtIgnored, "TOK_IGN"); mapNameToken(&sParse, zTgtMatch, "TOK_MATCH"); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, pList, "TGT"); TEST_ASSERT_EQUAL_INT(1, ctx.nList); sqlite3DbFree(sParse.db, ctx.aList); sqlite3ExprListDelete(sParse.db, pList); sqlite3_close(db); } /* Test: Entries with zEName == NULL are safely ignored (no crash, no addition) */ void test_renameColumnElistNames_ignores_null_zEName(void){ sqlite3 *db = 0; Parse sParse; openDbAndInitParse(&db, &sParse); ExprList *pList = NULL; const char *zGood = appendExprWithAlias(&sParse, &pList, "c2", "Alive"); /* Add another element and then nullify its zEName */ (void)appendExprWithAlias(&sParse, &pList, "cNULL", "NullName"); pList->a[pList->nExpr-1].zEName = NULL; /* simulate missing name */ /* Ensure eEName is ENAME_NAME for both; the NULL zEName should still be ignored */ pList->a[0].fg.eEName = ENAME_NAME; pList->a[1].fg.eEName = ENAME_NAME; mapNameToken(&sParse, zGood, "TOK_ALIVE"); RenameCtx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pParse = &sParse; test_renameColumnElistNames(&sParse, &ctx, pList, "Alive"); TEST_ASSERT_EQUAL_INT(1, ctx.nList); sqlite3DbFree(sParse.db, ctx.aList); sqlite3ExprListDelete(sParse.db, pList); sqlite3_close(db); } int main(void){ UNITY_BEGIN(); RUN_TEST(test_renameColumnElistNames_null_list); RUN_TEST(test_renameColumnElistNames_no_matches); RUN_TEST(test_renameColumnElistNames_single_match_case_insensitive); RUN_TEST(test_renameColumnElistNames_multiple_matches); RUN_TEST(test_renameColumnElistNames_ignores_non_ENAME_NAME); RUN_TEST(test_renameColumnElistNames_ignores_null_zEName); return UNITY_END(); }