/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "tc_catch2.h" #include "LuaEngineTestFixture.h" /** * @brief Phase 1 Tests: Core Lua Engine Functionality * * These tests verify: * 1. Lua state management (creation, cleanup) * 2. Script execution and loading * 3. Binding store management * 4. Event manager functionality */ TEST_CASE("Lua State Management - State Creation", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Eluna instance has valid Lua state") { lua_State* L = fixture.GetLuaState(eluna); REQUIRE(L != nullptr); } SECTION("HasLuaState returns true for valid instance") { REQUIRE(eluna != nullptr); // If we got here, Eluna was created successfully with Lua state REQUIRE(true); } SECTION("Multiple Eluna instances have independent Lua states") { Eluna* eluna2 = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna2 != nullptr); lua_State* L1 = fixture.GetLuaState(eluna); lua_State* L2 = fixture.GetLuaState(eluna2); // States should be different REQUIRE(L1 != L2); } } TEST_CASE("Lua State Management - Lua Libraries", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Standard Lua libraries are available") { // Test that standard library functions work REQUIRE(fixture.ExecuteScript(eluna, "x = math.floor(3.7)")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 3); } SECTION("String library is available") { REQUIRE(fixture.ExecuteScript(eluna, "x = string.upper('hello')")); REQUIRE(fixture.GetGlobalAsString(eluna, "x") == "HELLO"); } SECTION("Table library is available") { REQUIRE(fixture.ExecuteScript(eluna, "t = {1, 2, 3}; x = #t")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 3); } SECTION("Math library functions work") { REQUIRE(fixture.ExecuteScript(eluna, "x = math.max(5, 10, 3)")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 10); } } TEST_CASE("Script Execution - Basic Script Loading", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Simple script executes successfully") { REQUIRE(fixture.ExecuteScript(eluna, "x = 42")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 42); } SECTION("Multi-line scripts execute successfully") { std::string script = R"( x = 10 y = 20 z = x + y )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsInt(eluna, "z") == 30); } SECTION("Scripts with functions execute successfully") { std::string script = R"( function add(a, b) return a + b end result = add(5, 3) )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsInt(eluna, "result") == 8); } SECTION("Scripts with tables execute successfully") { std::string script = R"( data = { name = 'test', value = 100, items = {1, 2, 3} } )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsString(eluna, "data.name") == "test"); REQUIRE(fixture.GetGlobalAsInt(eluna, "data.value") == 100); } } TEST_CASE("Script Execution - Error Handling", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Syntax errors are caught") { REQUIRE(!fixture.ExecuteScript(eluna, "this is not valid lua syntax")); } SECTION("Runtime errors are caught") { REQUIRE(!fixture.ExecuteScript(eluna, "x = nil; y = x.foo()")); } SECTION("Undefined function calls are caught") { REQUIRE(!fixture.ExecuteScript(eluna, "nonexistent_function()")); } SECTION("Type errors are caught") { REQUIRE(!fixture.ExecuteScript(eluna, "x = 'string'; y = x + 5")); } } TEST_CASE("Script Execution - Script Isolation", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna1 = fixture.CreateGlobalElunaInstance(); Eluna* eluna2 = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna1 != nullptr); REQUIRE(eluna2 != nullptr); SECTION("Scripts in different Eluna instances don't share state") { REQUIRE(fixture.ExecuteScript(eluna1, "shared_var = 100")); REQUIRE(fixture.ExecuteScript(eluna2, "shared_var = 200")); REQUIRE(fixture.GetGlobalAsInt(eluna1, "shared_var") == 100); REQUIRE(fixture.GetGlobalAsInt(eluna2, "shared_var") == 200); } SECTION("Functions defined in one instance are not available in another") { REQUIRE(fixture.ExecuteScript(eluna1, "function test_func() return 42 end")); REQUIRE(!fixture.FunctionExists(eluna2, "test_func")); } } TEST_CASE("Binding Management - Binding Store Creation", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Eluna instance has binding stores") { // Verify by checking that we can access global functions REQUIRE(fixture.ExecuteScript(eluna, "x = 1")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 1); } SECTION("Multiple Eluna instances have independent bindings") { Eluna* eluna2 = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna2 != nullptr); // Both should have independent binding stores REQUIRE(fixture.ExecuteScript(eluna, "x = 1")); REQUIRE(fixture.ExecuteScript(eluna2, "x = 2")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 1); REQUIRE(fixture.GetGlobalAsInt(eluna2, "x") == 2); } } TEST_CASE("Binding Management - Standard Library Bindings", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Math library bindings are available") { REQUIRE(fixture.ExecuteScript(eluna, "x = math.floor(3.9)")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 3); } SECTION("String library bindings are available") { REQUIRE(fixture.ExecuteScript(eluna, "x = string.len('hello')")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 5); } SECTION("Table library bindings are available") { REQUIRE(fixture.ExecuteScript(eluna, "t = {3, 1, 2}; table.sort(t); x = t[1]")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 1); } } TEST_CASE("Event Management - Event Registration", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Global table can be accessed") { // Verify that we can access and manipulate the global table REQUIRE(fixture.ExecuteScript(eluna, "x = type(_G)")); REQUIRE(fixture.GetGlobalAsString(eluna, "x") == "table"); } SECTION("Global functions are accessible") { // Test that standard global functions exist REQUIRE(fixture.ExecuteScript(eluna, "x = type(print)")); REQUIRE(fixture.GetGlobalAsString(eluna, "x") == "function"); } } TEST_CASE("Event Management - Timed Events", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Timed event counter can be incremented") { std::string script = R"( counter = 0 function increment_counter() counter = counter + 1 end )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.FunctionExists(eluna, "increment_counter")); } SECTION("Event handlers can be defined") { std::string script = R"( event_called = false function on_event() event_called = true end )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.FunctionExists(eluna, "on_event")); } } TEST_CASE("Event Management - Event Handler Execution", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Event handler can be called and modify state") { std::string script = R"( state_value = 0 function update_state(new_value) state_value = new_value end )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.CallFunction(eluna, "update_state")); // Function was called successfully REQUIRE(true); } SECTION("Multiple event handlers can coexist") { std::string script = R"( function handler1() return 1 end function handler2() return 2 end function handler3() return 3 end )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.FunctionExists(eluna, "handler1")); REQUIRE(fixture.FunctionExists(eluna, "handler2")); REQUIRE(fixture.FunctionExists(eluna, "handler3")); } } TEST_CASE("Lua Stack Operations - Push Operations", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Integer values are correctly pushed and retrieved") { REQUIRE(fixture.ExecuteScript(eluna, "x = 42")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 42); } SECTION("Float values are correctly pushed and retrieved") { REQUIRE(fixture.ExecuteScript(eluna, "x = 3.14")); int value = fixture.GetGlobalAsInt(eluna, "x"); REQUIRE(value == 3); // Truncated to int } SECTION("String values are correctly pushed and retrieved") { REQUIRE(fixture.ExecuteScript(eluna, "x = 'hello world'")); REQUIRE(fixture.GetGlobalAsString(eluna, "x") == "hello world"); } SECTION("Boolean values are correctly pushed and retrieved") { REQUIRE(fixture.ExecuteScript(eluna, "x = true")); REQUIRE(fixture.GetGlobalAsBoolean(eluna, "x") == true); REQUIRE(fixture.ExecuteScript(eluna, "y = false")); REQUIRE(fixture.GetGlobalAsBoolean(eluna, "y") == false); } SECTION("Nil values are correctly handled") { REQUIRE(fixture.ExecuteScript(eluna, "x = nil")); REQUIRE(fixture.GetGlobalAsString(eluna, "x") == ""); } } TEST_CASE("Lua Stack Operations - Type Checking", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Integer type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "x = 42; t = type(x)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "number"); } SECTION("String type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "x = 'hello'; t = type(x)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "string"); } SECTION("Boolean type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "x = true; t = type(x)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "boolean"); } SECTION("Table type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "x = {}; t = type(x)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "table"); } SECTION("Function type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "function f() end; t = type(f)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "function"); } SECTION("Nil type is correctly identified") { REQUIRE(fixture.ExecuteScript(eluna, "x = nil; t = type(x)")); REQUIRE(fixture.GetGlobalAsString(eluna, "t") == "nil"); } } TEST_CASE("Lua Stack Operations - Complex Types", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Nested tables are correctly handled") { std::string script = R"( data = { level1 = { level2 = { value = 42 } } } )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsInt(eluna, "data.level1.level2.value") == 42); } SECTION("Mixed type tables are correctly handled") { std::string script = R"( mixed = { int_val = 42, str_val = 'hello', bool_val = true } table_val = {1, 2, 3} )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsInt(eluna, "mixed.int_val") == 42); REQUIRE(fixture.GetGlobalAsString(eluna, "mixed.str_val") == "hello"); // Verify the separate array table has correct size REQUIRE(fixture.GetTableSize(eluna, "table_val") == 3); } SECTION("Array tables are correctly sized") { REQUIRE(fixture.ExecuteScript(eluna, "arr = {10, 20, 30, 40, 50}")); REQUIRE(fixture.GetTableSize(eluna, "arr") == 5); } } TEST_CASE("Lua State Cleanup - State Destruction", "[LuaEngine][LuaEngineCore]") { SECTION("Eluna instances can be created and destroyed") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); // Scope ends, fixture is destroyed } SECTION("Multiple Eluna instances can be created and destroyed") { LuaEngineTestFixture fixture; Eluna* eluna1 = fixture.CreateGlobalElunaInstance(); Eluna* eluna2 = fixture.CreateGlobalElunaInstance(); Eluna* eluna3 = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna1 != nullptr); REQUIRE(eluna2 != nullptr); REQUIRE(eluna3 != nullptr); // All should be different REQUIRE(eluna1 != eluna2); REQUIRE(eluna2 != eluna3); REQUIRE(eluna1 != eluna3); } } TEST_CASE("Integration - Complete Workflow", "[LuaEngine][LuaEngineCore]") { LuaEngineTestFixture fixture; Eluna* eluna = fixture.CreateGlobalElunaInstance(); REQUIRE(eluna != nullptr); SECTION("Complex script with multiple features") { std::string script = R"( -- Define data game_state = { players = {}, npcs = {}, events = {} } -- Define functions function add_player(name, level) table.insert(game_state.players, {name = name, level = level}) return #game_state.players end function get_player_count() return #game_state.players end -- Execute logic add_player('Alice', 50) add_player('Bob', 45) add_player('Charlie', 55) player_count = get_player_count() )"; REQUIRE(fixture.ExecuteScript(eluna, script)); REQUIRE(fixture.GetGlobalAsInt(eluna, "player_count") == 3); REQUIRE(fixture.FunctionExists(eluna, "add_player")); REQUIRE(fixture.FunctionExists(eluna, "get_player_count")); } SECTION("Script with error recovery") { // First script succeeds REQUIRE(fixture.ExecuteScript(eluna, "x = 10")); REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 10); // Second script fails REQUIRE(!fixture.ExecuteScript(eluna, "this is invalid")); // Third script succeeds (state is still valid) REQUIRE(fixture.ExecuteScript(eluna, "y = 20")); REQUIRE(fixture.GetGlobalAsInt(eluna, "y") == 20); // Original value still exists REQUIRE(fixture.GetGlobalAsInt(eluna, "x") == 10); } }