#include "gtest/gtest.h" #include "lumacs/editor_core.hpp" #include "lumacs/keybinding.hpp" #include "lumacs/command_system.hpp" #include "lumacs/buffer.hpp" #include using namespace lumacs; // Test fixture for Input Pipeline integration class InputPipelineTest : public ::testing::Test { protected: void SetUp() override { core = std::make_unique(); // Ensure a clean buffer state for tests core->buffer().clear(); core->set_cursor({0,0}); // We rely on EditorCore loading init.lua by default, which registers commands. // However, in a test environment, init.lua might not be found or might be the user's. // For robust testing, we should manually register the critical commands if possible, // or mock the Lua environment. // Since we can't easily mock LuaApi fully here without a lot of setup, // let's manually register the 'self-insert-command' C++ handler directly // into the CommandSystem to simulate what init.lua does. // This ensures we are testing the C++ input path logic, not the Lua file loading. // Let's register a mock self-insert-command. core->command_system().register_command("self-insert-command", [this](CommandContext& ctx) -> CommandResult { auto char_str = ctx.get_string_arg(0); if (char_str && !char_str->empty()) { core->buffer().insert(core->cursor(), *char_str); core->move_right(); return {CommandStatus::Success, ""}; } return {CommandStatus::Failure, "No char arg"}; } ); // Also bind a test key core->command_system().register_command("test-command", [this](CommandContext&) -> CommandResult { test_command_executed = true; return {CommandStatus::Success, "Test command executed"}; } ); core->keybinding_manager().bind("C-c t", "test-command"); } void TearDown() override { core.reset(); } std::unique_ptr core; bool test_command_executed = false; }; TEST_F(InputPipelineTest, KeyBindingExecution) { // Simulate processing keys "C-c" then "t" auto result1 = core->keybinding_manager().process_key("C-c"); EXPECT_EQ(result1.type, KeyResult::Partial); EXPECT_FALSE(test_command_executed); auto result2 = core->keybinding_manager().process_key("t"); EXPECT_EQ(result2.type, KeyResult::Executed); EXPECT_TRUE(test_command_executed); } TEST_F(InputPipelineTest, SelfInsertFallbackLogic) { // This test replicates the logic found in GtkEditor/TuiEditor handle_input // simulating what happens when an unbound key is pressed. std::string input_key = "a"; // 1. Process key via KeyBindingManager auto result = core->keybinding_manager().process_key(input_key); // 2. Expect Unbound (as 'a' is not explicitly bound) EXPECT_EQ(result.type, KeyResult::Unbound); // 3. Simulate UI Fallback Logic bool handled = false; if (result.type == KeyResult::Unbound) { if (input_key.length() == 1) { // Check for printable char core->command_system().execute("self-insert-command", {input_key}); handled = true; } } EXPECT_TRUE(handled); // 4. Verify Buffer Content EXPECT_EQ(core->buffer().content(), "a"); EXPECT_EQ(core->cursor().column, 1); } TEST_F(InputPipelineTest, UnboundSpecialKeyIgnored) { std::string input_key = "F5"; // Assume unbound but recognized special key auto result = core->keybinding_manager().process_key(input_key); // F5 is a known key, but not explicitly bound in this test setup. // KeyBindingManager should return KeyResult::Unbound for a key that is not bound and not a prefix. EXPECT_EQ(result.type, KeyResult::Unbound); bool handled = false; if (result.type == KeyResult::Unbound) { // This block should NOT be entered now, as result.type is Handled if (input_key.length() == 1) { core->command_system().execute("self-insert-command", {input_key}); handled = true; } } EXPECT_FALSE(handled); // Ensure self-insert fallback is not triggered EXPECT_EQ(core->buffer().content(), ""); // Buffer should remain empty }