567 lines
15 KiB
Rust
567 lines
15 KiB
Rust
use super::*;
|
|
use ntest::timeout;
|
|
|
|
mod command_list;
|
|
|
|
fn small_test_rope() -> Rc<Rope> {
|
|
Rope::join(
|
|
Rope::join(
|
|
Rope::join(Rope::new("abc"), Some(Rope::new("def"))),
|
|
Some(Rope::join(Rope::new("g"), Some(Rope::new("hi")))),
|
|
),
|
|
Some(Rope::join(
|
|
Rope::join(
|
|
Rope::join(Rope::new("jklm"), Some(Rope::new("n"))),
|
|
Some(Rope::join(Rope::new("op"), Some(Rope::new("qr")))),
|
|
),
|
|
Some(Rope::join(Rope::new("stuv"), Some(Rope::new("wxyz")))),
|
|
)),
|
|
)
|
|
}
|
|
|
|
fn small_test_rope_leaf_strings() -> Vec<&'static str> {
|
|
vec![
|
|
"abc", "def", "g", "hi", "jklm", "n", "op", "qr", "stuv", "wxyz",
|
|
]
|
|
}
|
|
|
|
fn small_test_rope_full_string() -> &'static str {
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
}
|
|
|
|
fn small_test_rope_with_multibyte_chars() -> Rc<Rope> {
|
|
Rope::join(
|
|
Rope::join(
|
|
Rope::join(Rope::new("aあbc"), Some(Rope::new("deえf"))),
|
|
Some(Rope::join(Rope::new("g"), Some(Rope::new("hiい")))),
|
|
),
|
|
Some(Rope::join(
|
|
Rope::join(
|
|
Rope::join(Rope::new("jklm"), Some(Rope::new("n"))),
|
|
Some(Rope::join(Rope::new("おop"), Some(Rope::new("qr")))),
|
|
),
|
|
Some(Rope::join(Rope::new("stuうv"), Some(Rope::new("wxyz")))),
|
|
)),
|
|
)
|
|
}
|
|
|
|
fn small_test_rope_with_multibyte_chars_leaf_strings() -> Vec<&'static str> {
|
|
vec![
|
|
"aあbc", "deえf", "g", "hiい", "jklm", "n", "おop", "qr", "stuうv", "wxyz",
|
|
]
|
|
}
|
|
|
|
fn small_test_rope_with_multibyte_chars_full_string() -> &'static str {
|
|
"aあbcdeえfghiいjklmnおopqrstuうvwxyz"
|
|
}
|
|
|
|
impl Rope {
|
|
fn unwrap_branch_left(&self) -> &Rc<Rope> {
|
|
match self {
|
|
Rope::Branch { left, .. } => left,
|
|
Rope::Leaf { .. } => panic!("Expected branch node but found leaf"),
|
|
}
|
|
}
|
|
|
|
fn unwrap_branch_right(&self) -> Option<&Rc<Rope>> {
|
|
match self {
|
|
Rope::Branch { right, .. } => right.as_ref(),
|
|
Rope::Leaf { .. } => panic!("Expected branch node but found leaf"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[timeout(100)]
|
|
fn node_iterator_for_single_node_returns_node_and_only_node() {
|
|
let target = Rope::new("The");
|
|
|
|
let result: Vec<_> = target.iter_nodes().collect();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].total_bytes(), 3);
|
|
assert_eq!(result[0].total_chars(), 3);
|
|
assert_eq!(result[0].total_lines(), 1);
|
|
match result[0].as_ref() {
|
|
Rope::Leaf { text, .. } => assert_eq!(text, "The"),
|
|
_ => panic!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[timeout(100)]
|
|
fn node_iterator_returns_nodes_in_correct_order() {
|
|
let strings = small_test_rope_leaf_strings();
|
|
let target = small_test_rope();
|
|
|
|
let result: Vec<_> = target.iter_nodes().take(26).collect();
|
|
assert_eq!(result.len(), 10);
|
|
for (node, string) in result.iter().zip(strings) {
|
|
match &node.as_ref() {
|
|
Rope::Leaf { text } => assert_eq!(text, string),
|
|
_ => panic!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[timeout(100)]
|
|
fn char_iterator_returns_chars_in_correct_order() {
|
|
let target = small_test_rope();
|
|
|
|
let result: Vec<_> = target.iter_chars().collect();
|
|
assert_eq!(result.len(), 26);
|
|
for (&target_char, expected_char) in result.iter().zip(small_test_rope_full_string().chars()) {
|
|
assert_eq!(target_char, expected_char);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[timeout(100)]
|
|
fn split_splits_at_correct_location() {
|
|
let target = small_test_rope();
|
|
let full_string = small_test_rope_full_string();
|
|
for i in 0..(full_string.chars().count()) {
|
|
let (first, second) = target.split_at_char_index(i);
|
|
let first_string: String = first.iter_chars().collect();
|
|
let second_string: String = second.iter_chars().collect();
|
|
assert_eq!(first_string, full_string[0..i]);
|
|
assert_eq!(second_string, full_string[i..]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[timeout(100)]
|
|
fn split_splits_at_correct_location_with_multibyte_chars() {
|
|
let target = small_test_rope_with_multibyte_chars();
|
|
let expected_chars: Vec<_> = small_test_rope_with_multibyte_chars_full_string()
|
|
.chars()
|
|
.collect();
|
|
for i in 0..(expected_chars.len()) {
|
|
let (first, second) = target.split_at_char_index(i);
|
|
let string: String = first.iter_chars().collect();
|
|
let expected: String = expected_chars[0..i].iter().collect();
|
|
assert_eq!(string, expected);
|
|
let string: String = second.iter_chars().collect();
|
|
let expected: String = expected_chars[i..].iter().collect();
|
|
assert_eq!(string, expected);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn depths_have_correct_values() {
|
|
let target = small_test_rope_with_multibyte_chars();
|
|
assert_eq!(target.depth(), 4);
|
|
assert_eq!(target.unwrap_branch_left().depth(), 2);
|
|
assert_eq!(target.unwrap_branch_left().unwrap_branch_left().depth(), 1);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_left()
|
|
.depth(),
|
|
0
|
|
);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.depth(),
|
|
0
|
|
);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.depth(),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.unwrap_branch_left()
|
|
.depth(),
|
|
0
|
|
);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_left()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.depth(),
|
|
0
|
|
);
|
|
assert_eq!(target.unwrap_branch_right().unwrap().depth(), 3);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.unwrap_branch_left()
|
|
.depth(),
|
|
2
|
|
);
|
|
assert_eq!(
|
|
target
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.unwrap_branch_right()
|
|
.unwrap()
|
|
.depth(),
|
|
1
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_at_char_index() {
|
|
let target = Rope::new("The brown dog");
|
|
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
|
|
let rope1 = target.insert_at_char_index(4, "quick");
|
|
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
|
|
assert_eq!(rope1.iter_chars().collect::<String>(), "The quickbrown dog");
|
|
let rope2 = rope1.insert_at_char_index(9, " ");
|
|
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
|
|
assert_eq!(rope1.iter_chars().collect::<String>(), "The quickbrown dog");
|
|
assert_eq!(
|
|
rope2.iter_chars().collect::<String>(),
|
|
"The quick brown dog"
|
|
);
|
|
let rope3 =
|
|
rope2.insert_at_char_index("The quick brown dog".len(), " jumps over the lazy fox.");
|
|
assert_eq!(
|
|
rope3.iter_chars().collect::<String>(),
|
|
"The quick brown dog jumps over the lazy fox."
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn delete_at_char_index() {
|
|
let target = Rope::new("The quick brown fox jumps over the lazy dog.");
|
|
let test = target.delete_at_char_index(10, 6);
|
|
assert_eq!(
|
|
target.iter_chars().collect::<String>(),
|
|
"The quick brown fox jumps over the lazy dog."
|
|
);
|
|
assert_eq!(
|
|
test.iter_chars().collect::<String>(),
|
|
"The quick fox jumps over the lazy dog."
|
|
);
|
|
let test = target.delete_at_char_index(0, 4);
|
|
assert_eq!(
|
|
target.iter_chars().collect::<String>(),
|
|
"The quick brown fox jumps over the lazy dog."
|
|
);
|
|
assert_eq!(
|
|
test.iter_chars().collect::<String>(),
|
|
"quick brown fox jumps over the lazy dog."
|
|
);
|
|
let test =
|
|
target.delete_at_char_index("The quick brown fox jumps over the lazy dog.".len() - 5, 5);
|
|
assert_eq!(
|
|
target.iter_chars().collect::<String>(),
|
|
"The quick brown fox jumps over the lazy dog."
|
|
);
|
|
assert_eq!(
|
|
test.iter_chars().collect::<String>(),
|
|
"The quick brown fox jumps over the lazy"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn char_iterator_reports_correct_point() {
|
|
// Build a rope from fragments so we get a proper tree structure.
|
|
let target = Rope::new("This")
|
|
.concat(Rope::new(" is the first "))
|
|
.concat(Rope::new("line.\n"))
|
|
.concat(Rope::new("This is the "))
|
|
.concat(Rope::new("second line.\nThis is the third line."))
|
|
.concat(Rope::new("\n"))
|
|
.concat(Rope::new(
|
|
"This is the fourth line.\nThis is the fifth line.\nThis is ",
|
|
))
|
|
.concat(Rope::new("the"))
|
|
.concat(Rope::new(" sixth"))
|
|
.concat(Rope::new(" line."))
|
|
.concat(Rope::new("\nThis"))
|
|
.concat(Rope::new(" is the seventh line."));
|
|
let expected_values = vec![
|
|
('T', 0, 1),
|
|
('h', 0, 2),
|
|
('i', 0, 3),
|
|
('s', 0, 4),
|
|
(' ', 0, 5),
|
|
('i', 0, 6),
|
|
('s', 0, 7),
|
|
(' ', 0, 8),
|
|
('t', 0, 9),
|
|
('h', 0, 10),
|
|
('e', 0, 11),
|
|
(' ', 0, 12),
|
|
('f', 0, 13),
|
|
('i', 0, 14),
|
|
('r', 0, 15),
|
|
('s', 0, 16),
|
|
('t', 0, 17),
|
|
(' ', 0, 18),
|
|
('l', 0, 19),
|
|
('i', 0, 20),
|
|
('n', 0, 21),
|
|
('e', 0, 22),
|
|
('.', 0, 23),
|
|
('\n', 0, 24),
|
|
('T', 1, 1),
|
|
('h', 1, 2),
|
|
('i', 1, 3),
|
|
('s', 1, 4),
|
|
(' ', 1, 5),
|
|
('i', 1, 6),
|
|
('s', 1, 7),
|
|
(' ', 1, 8),
|
|
('t', 1, 9),
|
|
('h', 1, 10),
|
|
('e', 1, 11),
|
|
(' ', 1, 12),
|
|
('s', 1, 13),
|
|
('e', 1, 14),
|
|
('c', 1, 15),
|
|
('o', 1, 16),
|
|
('n', 1, 17),
|
|
('d', 1, 18),
|
|
(' ', 1, 19),
|
|
('l', 1, 20),
|
|
('i', 1, 21),
|
|
('n', 1, 22),
|
|
('e', 1, 23),
|
|
('.', 1, 24),
|
|
('\n', 1, 25),
|
|
('T', 2, 1),
|
|
('h', 2, 2),
|
|
('i', 2, 3),
|
|
('s', 2, 4),
|
|
(' ', 2, 5),
|
|
('i', 2, 6),
|
|
('s', 2, 7),
|
|
(' ', 2, 8),
|
|
('t', 2, 9),
|
|
('h', 2, 10),
|
|
('e', 2, 11),
|
|
(' ', 2, 12),
|
|
('t', 2, 13),
|
|
('h', 2, 14),
|
|
('i', 2, 15),
|
|
('r', 2, 16),
|
|
('d', 2, 17),
|
|
(' ', 2, 18),
|
|
('l', 2, 19),
|
|
('i', 2, 20),
|
|
('n', 2, 21),
|
|
('e', 2, 22),
|
|
('.', 2, 23),
|
|
('\n', 2, 24),
|
|
('T', 3, 1),
|
|
('h', 3, 2),
|
|
('i', 3, 3),
|
|
('s', 3, 4),
|
|
(' ', 3, 5),
|
|
('i', 3, 6),
|
|
('s', 3, 7),
|
|
(' ', 3, 8),
|
|
('t', 3, 9),
|
|
('h', 3, 10),
|
|
('e', 3, 11),
|
|
(' ', 3, 12),
|
|
('f', 3, 13),
|
|
('o', 3, 14),
|
|
('u', 3, 15),
|
|
('r', 3, 16),
|
|
('t', 3, 17),
|
|
('h', 3, 18),
|
|
(' ', 3, 19),
|
|
('l', 3, 20),
|
|
('i', 3, 21),
|
|
('n', 3, 22),
|
|
('e', 3, 23),
|
|
('.', 3, 24),
|
|
('\n', 3, 25),
|
|
('T', 4, 1),
|
|
('h', 4, 2),
|
|
('i', 4, 3),
|
|
('s', 4, 4),
|
|
(' ', 4, 5),
|
|
('i', 4, 6),
|
|
('s', 4, 7),
|
|
(' ', 4, 8),
|
|
('t', 4, 9),
|
|
('h', 4, 10),
|
|
('e', 4, 11),
|
|
(' ', 4, 12),
|
|
('f', 4, 13),
|
|
('i', 4, 14),
|
|
('f', 4, 15),
|
|
('t', 4, 16),
|
|
('h', 4, 17),
|
|
(' ', 4, 18),
|
|
('l', 4, 19),
|
|
('i', 4, 20),
|
|
('n', 4, 21),
|
|
('e', 4, 22),
|
|
('.', 4, 23),
|
|
('\n', 4, 24),
|
|
('T', 5, 1),
|
|
('h', 5, 2),
|
|
('i', 5, 3),
|
|
('s', 5, 4),
|
|
(' ', 5, 5),
|
|
('i', 5, 6),
|
|
('s', 5, 7),
|
|
(' ', 5, 8),
|
|
('t', 5, 9),
|
|
('h', 5, 10),
|
|
('e', 5, 11),
|
|
(' ', 5, 12),
|
|
('s', 5, 13),
|
|
('i', 5, 14),
|
|
('x', 5, 15),
|
|
('t', 5, 16),
|
|
('h', 5, 17),
|
|
(' ', 5, 18),
|
|
('l', 5, 19),
|
|
('i', 5, 20),
|
|
('n', 5, 21),
|
|
('e', 5, 22),
|
|
('.', 5, 23),
|
|
('\n', 5, 24),
|
|
('T', 6, 1),
|
|
('h', 6, 2),
|
|
('i', 6, 3),
|
|
('s', 6, 4),
|
|
(' ', 6, 5),
|
|
('i', 6, 6),
|
|
('s', 6, 7),
|
|
(' ', 6, 8),
|
|
('t', 6, 9),
|
|
('h', 6, 10),
|
|
('e', 6, 11),
|
|
(' ', 6, 12),
|
|
('s', 6, 13),
|
|
('e', 6, 14),
|
|
('v', 6, 15),
|
|
('e', 6, 16),
|
|
('n', 6, 17),
|
|
('t', 6, 18),
|
|
('h', 6, 19),
|
|
(' ', 6, 20),
|
|
('l', 6, 21),
|
|
('i', 6, 22),
|
|
('n', 6, 23),
|
|
('e', 6, 24),
|
|
('.', 6, 25),
|
|
('\n', 7, 26),
|
|
];
|
|
for (found, (expected_char, expected_line, expected_column)) in
|
|
target.iter_chars_with_point().zip(expected_values)
|
|
{
|
|
assert_eq!(found.character, expected_char);
|
|
assert_eq!(found.line, expected_line);
|
|
assert_eq!(found.column, expected_column);
|
|
}
|
|
}
|
|
|
|
// Run multiple tests with different seeds so that `cargo test` will run them in
|
|
// parallel.
|
|
// TODO: This could use a macro to generate the list of tests.
|
|
fn random_insertions_and_deletions_with_seed(seed: u64) {
|
|
let (start_text, edits) = command_list::generate_random_edit_sequence_with_seed(1000, seed);
|
|
let mut target = Rope::new(start_text);
|
|
for (command, expected_text) in edits {
|
|
println!("{:?}", command);
|
|
target = command.run(&target);
|
|
assert_eq!(expected_text, target.iter_chars().collect::<String>());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_1() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0001);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_2() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0002);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_3() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0003);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_4() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0004);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_5() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0005);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_6() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0006);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_7() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0007);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_8() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0008);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_9() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0009);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_10() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0010);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_11() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0011);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_12() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0012);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_13() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0013);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_14() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0014);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_15() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0015);
|
|
}
|
|
|
|
#[test]
|
|
fn random_insertions_and_deletions_16() {
|
|
random_insertions_and_deletions_with_seed(0x1234567890ab0016);
|
|
}
|