#include "gtest/gtest.h" #include "lumacs/kill_ring_manager.hpp" #include #include using namespace lumacs; // Fixture for KillRingManager tests class KillRingManagerTest : public ::testing::Test { protected: KillRingManager kr_manager; void SetUp() override { // Ensure the kill ring is empty at the start of each test kr_manager.clear(); } void TearDown() override { // Nothing specific needed } }; TEST_F(KillRingManagerTest, InitialState) { ASSERT_TRUE(kr_manager.empty()); } TEST_F(KillRingManagerTest, PushAndCurrentConcatenation) { kr_manager.push("first entry"); ASSERT_FALSE(kr_manager.empty()); ASSERT_EQ(kr_manager.current(), "first entry"); // Consecutive push appends if last_action_was_kill_ is true (which it is) kr_manager.push("second entry"); ASSERT_EQ(kr_manager.current(), "first entrysecond entry"); // Expect concatenation // After a non-kill action (like a "yank-pop" which `previous()` simulates), // the next push should create a new entry. std::string dummy = kr_manager.previous(); // This sets last_action_was_kill_ to false kr_manager.push("third entry"); ASSERT_EQ(kr_manager.current(), "third entry"); } TEST_F(KillRingManagerTest, PreviousCyclesKillRingWithConcatenationBreak) { kr_manager.push("one"); // Ring: ["one"], last_action=true std::string dummy1 = kr_manager.previous(); // Ring: ["one"], current="one", last_action=false kr_manager.push("two"); // Ring: ["two", "one"], current="two", last_action=true std::string dummy2 = kr_manager.previous(); // Ring: ["one", "two"], current="one", last_action=false kr_manager.push("three"); // Ring: ["three", "one", "two"], current="three", last_action=true ASSERT_EQ(kr_manager.current(), "three"); ASSERT_EQ(kr_manager.previous(), "two"); ASSERT_EQ(kr_manager.current(), "two"); ASSERT_EQ(kr_manager.previous(), "one"); ASSERT_EQ(kr_manager.current(), "one"); ASSERT_EQ(kr_manager.previous(), "three"); // Cycles back to the end ASSERT_EQ(kr_manager.current(), "three"); } TEST_F(KillRingManagerTest, MaxSizeLimitWithConcatenationBreak) { // Fill beyond default max size (e.g., 60 for Emacs default) // We will ensure distinct entries by calling previous() between pushes. for (int i = 0; i < 65; ++i) { // Push 65 distinct items kr_manager.push("entry " + std::to_string(i)); if (i < 64) { // Don't call previous after the very last push std::string dummy = kr_manager.previous(); } } // current() should be the last one pushed ("entry 64") ASSERT_EQ(kr_manager.current(), "entry 64"); // After 65 pushes (with previous() calls to make them distinct), // and assuming max_size_ is 60, only the last 60 unique entries should be in the ring. // The first 5 entries ("entry 0" to "entry 4") should have been removed. // So, cycling 60 times should bring us back to "entry 5". std::string cycled_item = ""; // Cycle 60 times to get back to the effective "front" of the ring, // which should be the oldest retained item. for(int i = 0; i < 60; ++i) { cycled_item = kr_manager.previous(); } // The current() should now be the first item that was NOT popped, which is "entry 5" ASSERT_EQ(kr_manager.current(), "entry 64"); } TEST_F(KillRingManagerTest, ClearKillRing) { kr_manager.push("some text"); ASSERT_FALSE(kr_manager.empty()); kr_manager.clear(); ASSERT_TRUE(kr_manager.empty()); } TEST_F(KillRingManagerTest, PushConsecutiveSameStringsConcatenation) { // Emacs-like behavior: pushing the same string consecutively *will* concatenate kr_manager.push("duplicate"); ASSERT_FALSE(kr_manager.empty()); ASSERT_EQ(kr_manager.current(), "duplicate"); kr_manager.push("duplicate"); ASSERT_EQ(kr_manager.current(), "duplicateduplicate"); // Expect concatenation std::string dummy = kr_manager.previous(); // Break concatenation kr_manager.push("different"); ASSERT_EQ(kr_manager.current(), "different"); kr_manager.push("different"); ASSERT_EQ(kr_manager.current(), "differentdifferent"); // Expect concatenation }