jes/test/src/test_Buffer.cc

468 lines
13 KiB
C++

#include "gtest/gtest.h"
#include "Buffer.h"
#include "TestSupport.h"
#include "Path.h"
#include <string>
#define C(x) ((uint32_t)(x))
#define ss(s) std::string(s)
TEST(BufferTest, writes_an_identical_file_to_what_is_loaded_if_no_changes_were_performed)
{
const char * files_to_test[] = {
"test/files/empty.txt",
"test/files/one_eol.txt",
"test/files/two_eol.txt",
"test/files/line_endings/cr_format.txt",
"test/files/line_endings/crlf_format.txt",
"test/files/line_endings/lf_format.txt",
"test/files/no_eol_at_eof.txt",
"test/files/no_eol_at_eof_crlf.txt",
};
for (auto e : files_to_test)
{
Buffer b(e);
ASSERT_TRUE(b.write_to_file("test/tmp/f"));
ASSERT_TRUE(Path::is_file("test/tmp/f"));
TestSupport::compare_files(e, "test/tmp/f");
}
}
TEST(BufferTest, writes_an_empty_file_for_an_empty_buffer)
{
Buffer b;
ASSERT_TRUE(b.write_to_file("test/tmp/f"));
TestSupport::compare_files("test/files/empty.txt", "test/tmp/f");
}
TEST(BufferTest, allows_navigating_using_iterators)
{
Buffer b("test/files/line_endings/lf_format.txt");
auto iterator = b.add_cursor();
EXPECT_EQ(0u, iterator->line());
ASSERT_EQ(C('H'), **iterator);
EXPECT_FALSE(iterator->go_previous_line());
EXPECT_EQ(0u, iterator->line());
ASSERT_EQ(C('H'), **iterator);
EXPECT_TRUE(iterator->go_right_in_line(false));
EXPECT_EQ(0u, iterator->line());
ASSERT_EQ(C('e'), **iterator);
EXPECT_TRUE(iterator->go_next_line());
EXPECT_EQ(1u, iterator->line());
ASSERT_EQ(C('T'), **iterator);
EXPECT_FALSE(iterator->go_left_in_line());
EXPECT_EQ(1u, iterator->line());
ASSERT_EQ(C('T'), **iterator);
EXPECT_TRUE(iterator->go_right_in_line(true));
EXPECT_EQ(1u, iterator->line());
ASSERT_EQ(C('h'), **iterator);
EXPECT_FALSE(iterator->go_next_line());
EXPECT_EQ(1u, iterator->line());
ASSERT_EQ(C('h'), **iterator);
EXPECT_TRUE(iterator->go_previous_line());
EXPECT_EQ(0u, iterator->line());
ASSERT_EQ(C('H'), **iterator);
}
TEST(BufferTest, allows_navigating_using_iterators2)
{
Buffer b("test/files/blank_lines.txt");
auto iterator = b.add_cursor();
EXPECT_EQ(0u, iterator->line());
EXPECT_EQ(C('a'), **iterator);
EXPECT_TRUE(iterator->go_next_line());
EXPECT_EQ(1u, iterator->line());
EXPECT_EQ(C('\n'), **iterator);
EXPECT_FALSE(iterator->go_start_of_line());
EXPECT_EQ(1u, iterator->line());
EXPECT_EQ(C('\n'), **iterator);
EXPECT_FALSE(iterator->go_end_of_line(false));
EXPECT_EQ(1u, iterator->line());
EXPECT_EQ(C('\n'), **iterator);
EXPECT_FALSE(iterator->go_end_of_line(true));
EXPECT_EQ(1u, iterator->line());
EXPECT_EQ(C('\n'), **iterator);
EXPECT_TRUE(iterator->go_next_line());
EXPECT_EQ(2u, iterator->line());
EXPECT_EQ(C('d'), **iterator);
EXPECT_TRUE(iterator->go_previous_line());
EXPECT_EQ(1u, iterator->line());
EXPECT_EQ(C('\n'), **iterator);
EXPECT_TRUE(iterator->go_previous_line());
EXPECT_EQ(0u, iterator->line());
EXPECT_EQ(C('a'), **iterator);
}
TEST(BufferTest, allows_inserting_and_erasing_characters)
{
std::string s("abc\ndef\nghi\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.insert_code_point(*it, C('1'));
EXPECT_EQ(ss("1abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('a'), **it);
b.enter_insert_mode();
it->go_end_of_line(true);
b.insert_code_point(*it, C('$'));
EXPECT_EQ(ss("1abc$\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('\n'), **it);
b.erase_code_point(*it);
EXPECT_EQ(ss("1abc$def\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.exit_insert_mode();
}
TEST(BufferTest, adds_a_newline_after_inserted_character_when_inserting_in_an_empty_buffer)
{
Buffer b;
auto it = b.add_cursor();
EXPECT_EQ(0u, it->offset());
b.enter_insert_mode();
b.insert_code_point(*it, C('J'));
EXPECT_EQ(ss("J\n"), b.get_string());
EXPECT_EQ(C('\n'), **it);
b.exit_insert_mode();
}
TEST(BufferTest, allows_undo_and_redo_of_inserts)
{
std::string s("abc\ndef\nghi\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
b.insert_code_point(*it, C('2'));
b.exit_insert_mode();
EXPECT_EQ(ss("12abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('a'), **it);
EXPECT_EQ(1u, b.m_change_operations.size());
it->go_next_line();
it->go_right_in_line(false);
b.enter_insert_mode();
b.insert_code_point(*it, C('3'));
b.exit_insert_mode();
EXPECT_EQ(ss("12abc\nd3ef\nghi\n"), b.get_string());
EXPECT_EQ(C('e'), **it);
EXPECT_EQ(2u, b.m_change_operations.size());
it->go_end_of_line(true);
b.insert_code_point(*it, C('$'));
EXPECT_EQ(ss("12abc\nd3ef$\nghi\n"), b.get_string());
EXPECT_EQ(C('$'), **it); /* cursor moved back because not in insert mode */
EXPECT_EQ(3u, b.m_change_operations.size());
it->go_start_of_line();
EXPECT_EQ(C('d'), **it);
b.undo();
EXPECT_EQ(ss("12abc\nd3ef\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.undo();
EXPECT_EQ(ss("12abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.undo();
EXPECT_EQ(ss("abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.redo();
EXPECT_EQ(ss("12abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.redo();
EXPECT_EQ(ss("12abc\nd3ef\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
b.redo();
EXPECT_EQ(ss("12abc\nd3ef$\nghi\n"), b.get_string());
EXPECT_EQ(C('d'), **it);
/* undo/redo do not create new operations */
EXPECT_FALSE(b.m_current_change_operation);
EXPECT_EQ(3u, b.m_change_operations.size());
}
TEST(BufferTest, allows_undo_and_redo_of_erases)
{
std::string s("abc\ndef\nghi\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.erase_code_point(*it);
EXPECT_EQ(ss("bc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('b'), **it);
EXPECT_EQ(1u, b.m_change_operations.size());
it->go_next_line();
it->go_right_in_line(false);
b.erase_code_point(*it);
EXPECT_EQ(ss("bc\ndf\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
EXPECT_EQ(2u, b.m_change_operations.size());
it->go_end_of_line(true);
b.erase_code_point(*it);
EXPECT_EQ(ss("bc\ndfghi\n"), b.get_string());
EXPECT_EQ(C('g'), **it);
EXPECT_EQ(3u, b.m_change_operations.size());
it->go_left_in_line();
EXPECT_EQ(C('f'), **it);
b.undo();
EXPECT_EQ(ss("bc\ndf\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
b.undo();
EXPECT_EQ(ss("bc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
b.undo();
EXPECT_EQ(ss("abc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
b.redo();
EXPECT_EQ(ss("bc\ndef\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
b.redo();
EXPECT_EQ(ss("bc\ndf\nghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
b.redo();
EXPECT_EQ(ss("bc\ndfghi\n"), b.get_string());
EXPECT_EQ(C('f'), **it);
/* undo/redo do not create new operations */
EXPECT_FALSE(b.m_current_change_operation);
EXPECT_EQ(3u, b.m_change_operations.size());
}
TEST(BufferTest, erase_chunks_are_coalesced_within_a_change_operation)
{
std::string s("abcdef\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
it->go_right_in_line(false);
it->go_right_in_line(false); /* erase just after previous erase */
b.erase_code_point(*it);
b.erase_code_point(*it);
it->go_left_in_line();
b.erase_code_point(*it); /* erase just before previous erase */
b.exit_insert_mode();
EXPECT_EQ(C('e'), **it);
EXPECT_EQ(ss("aef\n"), b.get_string());
ASSERT_EQ(1u, b.m_change_operations.size());
ASSERT_EQ(1u, b.m_change_operations[0]->changes.size());
b.undo();
EXPECT_EQ(C('e'), **it);
EXPECT_EQ(ss("abcdef\n"), b.get_string());
}
TEST(BufferTest, erases_in_uncommitted_insert_units_modify_the_insert_units)
{
std::string s("abcdef\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
it->go_right_in_line(false);
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
b.insert_code_point(*it, C('2'));
b.insert_code_point(*it, C('3'));
it->go_left_in_line();
b.erase_code_point(*it);
b.exit_insert_mode();
EXPECT_EQ(1u, b.m_change_operations.size());
EXPECT_EQ(ss("a12bcdef\n"), b.get_string());
EXPECT_EQ(C('b'), **it);
b.undo();
EXPECT_EQ(ss("abcdef\n"), b.get_string());
EXPECT_EQ(C('b'), **it);
b.redo();
EXPECT_EQ(ss("a12bcdef\n"), b.get_string());
EXPECT_EQ(C('b'), **it);
}
TEST(BufferTest, erases_outside_uncommitted_insert_units_start_new_units_within_the_same_operation)
{
std::string s("abcdef\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
it->go_right_in_line(false);
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
b.insert_code_point(*it, C('2'));
b.erase_code_point(*it);
b.exit_insert_mode();
EXPECT_EQ(1u, b.m_change_operations.size());
EXPECT_EQ(ss("a12cdef\n"), b.get_string());
EXPECT_EQ(C('c'), **it);
b.undo();
EXPECT_EQ(ss("abcdef\n"), b.get_string());
EXPECT_EQ(C('c'), **it);
b.redo();
EXPECT_EQ(ss("a12cdef\n"), b.get_string());
EXPECT_EQ(C('c'), **it);
b.enter_insert_mode();
b.insert_code_point(*it, C('3'));
it->go_start_of_line();
b.erase_code_point(*it);
b.exit_insert_mode();
EXPECT_EQ("123cdef\n", b.get_string());
EXPECT_EQ(C('1'), **it);
b.undo();
EXPECT_EQ("a12cdef\n", b.get_string());
EXPECT_EQ(C('1'), **it);
b.undo();
EXPECT_EQ("abcdef\n", b.get_string());
EXPECT_EQ(C('b'), **it);
b.redo();
EXPECT_EQ("a12cdef\n", b.get_string());
EXPECT_EQ(C('c'), **it);
b.redo();
EXPECT_EQ("123cdef\n", b.get_string());
EXPECT_EQ(C('c'), **it);
}
TEST(BufferTest, a_insert_change_unit_is_dropped_if_everything_in_it_is_erased_before_committing)
{
std::string s("abc\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
it->go_right_in_line(false);
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
b.insert_code_point(*it, C('2'));
it->go_left_in_line();
b.erase_code_point(*it); /* backspace */
it->go_left_in_line();
b.erase_code_point(*it); /* backspace */
b.exit_insert_mode();
EXPECT_EQ(ss("abc\n"), b.get_string());
EXPECT_EQ(0u, b.m_change_operations.size());
EXPECT_EQ(C('b'), **it);
}
TEST(BufferTest, non_adjacent_inserts_in_insert_mode_create_a_single_change_operation)
{
std::string s("abc\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
it->go_right_in_line(false);
b.insert_code_point(*it, C('2'));
b.exit_insert_mode();
EXPECT_EQ(ss("1a2bc\n"), b.get_string());
b.undo();
EXPECT_EQ(ss("abc\n"), b.get_string());
it->go_right_in_line(false);
b.enter_insert_mode();
b.insert_code_point(*it, C('3'));
it->go_start_of_line();
b.insert_code_point(*it, C('4'));
b.exit_insert_mode();
EXPECT_EQ(ss("4ab3c\n"), b.get_string());
b.undo();
EXPECT_EQ("abc\n", b.get_string());
}
TEST(BufferTest, erase_then_insert_creates_single_change_operation)
{
std::string s("abc\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
b.erase_code_point(*it);
b.insert_code_point(*it, C('1'));
b.exit_insert_mode();
EXPECT_EQ("1bc\n", b.get_string());
b.undo();
EXPECT_EQ("abc\n", b.get_string());
}
TEST(BufferTest, non_adjacent_erases_create_a_single_change_operation)
{
std::string s("abc\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
b.erase_code_point(*it);
it->go_right_in_line(false);
b.erase_code_point(*it);
b.exit_insert_mode();
EXPECT_EQ("b\n", b.get_string());
b.undo();
EXPECT_EQ(ss("abc\n"), b.get_string());
}
TEST(BufferTest, inserting_at_beginning_and_end_of_line_in_insert_mode_creates_proper_undo_operation)
{
std::string s("abc\ndef\n");
Buffer b((const uint8_t *)&s[0], s.size());
auto it = b.add_cursor();
b.enter_insert_mode();
b.insert_code_point(*it, C('1'));
b.insert_code_point(*it, C('2'));
it->go_end_of_line(true);
b.insert_code_point(*it, C('3'));
b.insert_code_point(*it, C('4'));
b.exit_insert_mode();
EXPECT_EQ("12abc34\ndef\n", b.get_string());
b.undo();
EXPECT_EQ("abc\ndef\n", b.get_string());
b.redo();
EXPECT_EQ("12abc34\ndef\n", b.get_string());
}