test_window_manager.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #include "gtest/gtest.h"
  2. #include "lumacs/window_manager.hpp"
  3. #include "lumacs/editor_core.hpp"
  4. #include "lumacs/window.hpp"
  5. #include "lumacs/layout_node.hpp"
  6. #include <memory>
  7. #include <vector>
  8. #include <numeric> // For std::iota
  9. using namespace lumacs;
  10. // Fixture for WindowManager tests
  11. class WindowManagerTest : public ::testing::Test {
  12. protected:
  13. std::unique_ptr<EditorCore> core;
  14. WindowManager* wm; // Raw pointer for convenience, owned by core
  15. void SetUp() override {
  16. // Instantiate EditorCore which in turn initializes WindowManager and BufferManager
  17. core = std::make_unique<EditorCore>();
  18. wm = &core->window_manager(); // Get reference to the manager owned by core
  19. // Ensure a clean slate for some tests by explicitly creating a new scratch buffer
  20. // if the default one from init.lua is not suitable.
  21. // For these tests, we mostly care about window *layout*, not buffer content.
  22. core->buffer().clear();
  23. core->set_cursor({0,0});
  24. }
  25. void TearDown() override {
  26. core.reset();
  27. }
  28. // Helper to count windows in the layout tree
  29. size_t count_windows_in_layout() {
  30. std::vector<std::shared_ptr<Window>> windows;
  31. core->collect_windows(wm->root_layout().get(), windows);
  32. return windows.size();
  33. }
  34. };
  35. TEST_F(WindowManagerTest, InitialState) {
  36. // Initially, there should be one active window
  37. ASSERT_NE(wm->active_window(), nullptr);
  38. ASSERT_EQ(count_windows_in_layout(), 1);
  39. ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::Leaf);
  40. ASSERT_EQ(wm->root_layout()->window, wm->active_window());
  41. }
  42. TEST_F(WindowManagerTest, SplitHorizontally) {
  43. auto original_active_window = wm->active_window();
  44. wm->split_horizontally();
  45. // Should now have two windows
  46. ASSERT_EQ(count_windows_in_layout(), 2);
  47. ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::HorizontalSplit);
  48. ASSERT_NE(wm->root_layout()->child1, nullptr);
  49. ASSERT_NE(wm->root_layout()->child2, nullptr);
  50. // New window should be active
  51. ASSERT_NE(wm->active_window(), original_active_window);
  52. }
  53. TEST_F(WindowManagerTest, SplitVertically) {
  54. auto original_active_window = wm->active_window();
  55. wm->split_vertically();
  56. // Should now have two windows
  57. ASSERT_EQ(count_windows_in_layout(), 2);
  58. ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::VerticalSplit);
  59. ASSERT_NE(wm->root_layout()->child1, nullptr);
  60. ASSERT_NE(wm->root_layout()->child2, nullptr);
  61. // New window should be active
  62. ASSERT_NE(wm->active_window(), original_active_window);
  63. }
  64. TEST_F(WindowManagerTest, CloseActiveWindowMergesLayout) {
  65. wm->split_horizontally(); // Creates 2 windows
  66. ASSERT_EQ(count_windows_in_layout(), 2);
  67. auto window_before_close = wm->active_window();
  68. wm->close_active_window(); // Closes the newly created window
  69. ASSERT_EQ(count_windows_in_layout(), 1); // Should merge back to one window
  70. ASSERT_NE(wm->active_window(), window_before_close); // Active window should be the remaining one
  71. ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::Leaf);
  72. }
  73. TEST_F(WindowManagerTest, CloseLastWindowDoesNothing) {
  74. ASSERT_EQ(count_windows_in_layout(), 1);
  75. auto original_active = wm->active_window();
  76. wm->close_active_window(); // Attempt to close the only window
  77. ASSERT_EQ(count_windows_in_layout(), 1); // Should still be one window
  78. ASSERT_EQ(wm->active_window(), original_active); // Should still be the same window
  79. }
  80. TEST_F(WindowManagerTest, NextWindowCyclesFocus) {
  81. wm->split_horizontally(); // Window 1 (original), Window 2 (newly active)
  82. auto w1 = wm->root_layout()->child1->window; // Original window
  83. auto w2 = wm->root_layout()->child2->window; // New window, active
  84. ASSERT_EQ(wm->active_window(), w2);
  85. wm->next_window(); // Should cycle to w1
  86. ASSERT_EQ(wm->active_window(), w1);
  87. wm->next_window(); // Should cycle back to w2
  88. ASSERT_EQ(wm->active_window(), w2);
  89. }
  90. TEST_F(WindowManagerTest, ComplexSplitAndClose) {
  91. // Initial: W1
  92. wm->split_vertically(); // Root: |W1|W2| (W2 active)
  93. wm->split_horizontally(); // Root: |W1| [W3;W2] (W2 active remains W2, so W3 active)
  94. // The previous split makes the new window active. So if W2 was active, after split_horizontally on W2, W3 is active.
  95. // Layout: |W1|
  96. // ----
  97. // |W3|
  98. // ---- (W3 active)
  99. // |W2|
  100. ASSERT_EQ(count_windows_in_layout(), 3);
  101. auto w1 = wm->root_layout()->child1->window;
  102. auto w2_node = wm->root_layout()->child2; // This is a split node
  103. auto w3 = w2_node->child1->window;
  104. auto w2 = w2_node->child2->window;
  105. wm->next_window(); // Should cycle to W1 (or W2, depends on traversal order)
  106. wm->next_window(); // Cycle again
  107. wm->close_active_window(); // Close the active window (W2 or W3)
  108. ASSERT_EQ(count_windows_in_layout(), 2); // Should have 2 windows left
  109. }