test_input_pipeline.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #include "gtest/gtest.h"
  2. #include "lumacs/editor_core.hpp"
  3. #include "lumacs/keybinding.hpp"
  4. #include "lumacs/command_system.hpp"
  5. #include "lumacs/buffer.hpp"
  6. #include <memory>
  7. using namespace lumacs;
  8. // Test fixture for Input Pipeline integration
  9. class InputPipelineTest : public ::testing::Test {
  10. protected:
  11. void SetUp() override {
  12. core = std::make_unique<EditorCore>();
  13. // Ensure a clean buffer state for tests
  14. core->buffer().clear();
  15. core->set_cursor({0,0});
  16. // We rely on EditorCore loading init.lua by default, which registers commands.
  17. // However, in a test environment, init.lua might not be found or might be the user's.
  18. // For robust testing, we should manually register the critical commands if possible,
  19. // or mock the Lua environment.
  20. // Since we can't easily mock LuaApi fully here without a lot of setup,
  21. // let's manually register the 'self-insert-command' C++ handler directly
  22. // into the CommandSystem to simulate what init.lua does.
  23. // This ensures we are testing the C++ input path logic, not the Lua file loading.
  24. // Let's register a mock self-insert-command.
  25. core->command_system().register_command("self-insert-command",
  26. [this](CommandContext& ctx) -> CommandResult {
  27. auto char_str = ctx.get_string_arg(0);
  28. if (char_str && !char_str->empty()) {
  29. core->buffer().insert(core->cursor(), *char_str);
  30. core->move_right();
  31. return {CommandStatus::Success, ""};
  32. }
  33. return {CommandStatus::Failure, "No char arg"};
  34. }
  35. );
  36. // Also bind a test key
  37. core->command_system().register_command("test-command",
  38. [this](CommandContext&) -> CommandResult {
  39. test_command_executed = true;
  40. return {CommandStatus::Success, "Test command executed"};
  41. }
  42. );
  43. core->keybinding_manager().bind("C-c t", "test-command");
  44. }
  45. void TearDown() override {
  46. core.reset();
  47. }
  48. std::unique_ptr<EditorCore> core;
  49. bool test_command_executed = false;
  50. };
  51. TEST_F(InputPipelineTest, KeyBindingExecution) {
  52. // Simulate processing keys "C-c" then "t"
  53. auto result1 = core->keybinding_manager().process_key("C-c");
  54. EXPECT_EQ(result1.type, KeyResult::Partial);
  55. EXPECT_FALSE(test_command_executed);
  56. auto result2 = core->keybinding_manager().process_key("t");
  57. EXPECT_EQ(result2.type, KeyResult::Executed);
  58. EXPECT_TRUE(test_command_executed);
  59. }
  60. TEST_F(InputPipelineTest, SelfInsertFallbackLogic) {
  61. // This test replicates the logic found in GtkEditor/TuiEditor handle_input
  62. // simulating what happens when an unbound key is pressed.
  63. std::string input_key = "a";
  64. // 1. Process key via KeyBindingManager
  65. auto result = core->keybinding_manager().process_key(input_key);
  66. // 2. Expect Unbound (as 'a' is not explicitly bound)
  67. EXPECT_EQ(result.type, KeyResult::Unbound);
  68. // 3. Simulate UI Fallback Logic
  69. bool handled = false;
  70. if (result.type == KeyResult::Unbound) {
  71. if (input_key.length() == 1) { // Check for printable char
  72. core->command_system().execute("self-insert-command", {input_key});
  73. handled = true;
  74. }
  75. }
  76. EXPECT_TRUE(handled);
  77. // 4. Verify Buffer Content
  78. EXPECT_EQ(core->buffer().content(), "a");
  79. EXPECT_EQ(core->cursor().column, 1);
  80. }
  81. TEST_F(InputPipelineTest, UnboundSpecialKeyIgnored) {
  82. std::string input_key = "F5"; // Assume unbound but recognized special key
  83. auto result = core->keybinding_manager().process_key(input_key);
  84. // F5 is a known key, but not explicitly bound in this test setup.
  85. // KeyBindingManager should return KeyResult::Unbound for a key that is not bound and not a prefix.
  86. EXPECT_EQ(result.type, KeyResult::Unbound);
  87. bool handled = false;
  88. if (result.type == KeyResult::Unbound) { // This block should NOT be entered now, as result.type is Handled
  89. if (input_key.length() == 1) {
  90. core->command_system().execute("self-insert-command", {input_key});
  91. handled = true;
  92. }
  93. }
  94. EXPECT_FALSE(handled); // Ensure self-insert fallback is not triggered
  95. EXPECT_EQ(core->buffer().content(), ""); // Buffer should remain empty
  96. }