mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 21:07:43 +00:00
310 lines
8.7 KiB
Rust
310 lines
8.7 KiB
Rust
|
use std::collections::HashMap;
|
||
|
|
||
|
use loro_delta::{
|
||
|
text_delta::{TextChunk, TextDelta},
|
||
|
DeltaRopeBuilder,
|
||
|
};
|
||
|
use tracing_subscriber::fmt::format::FmtSpan;
|
||
|
|
||
|
#[ctor::ctor]
|
||
|
fn init_color_backtrace() {
|
||
|
color_backtrace::install();
|
||
|
use tracing_subscriber::{prelude::*, registry::Registry};
|
||
|
if option_env!("DEBUG").is_some() {
|
||
|
tracing::subscriber::set_global_default(
|
||
|
Registry::default().with(
|
||
|
tracing_subscriber::fmt::Layer::default()
|
||
|
.with_file(true)
|
||
|
.with_line_number(true)
|
||
|
.with_span_events(FmtSpan::ACTIVE),
|
||
|
),
|
||
|
)
|
||
|
.unwrap();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn text_delta() {
|
||
|
let mut text = TextDelta::new();
|
||
|
text.push_str_insert("123456789");
|
||
|
assert_eq!(text.try_to_string().unwrap(), "123456789");
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta
|
||
|
.push_str_insert("abc")
|
||
|
.push_retain(3, ())
|
||
|
.push_delete(3);
|
||
|
text.compose(&delta);
|
||
|
assert_eq!(text.try_to_string().unwrap(), "abc123789");
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn delete_delta_compose() {
|
||
|
let mut a: TextDelta = DeltaRopeBuilder::new().delete(5).build();
|
||
|
let b: TextDelta = DeltaRopeBuilder::new().delete(5).build();
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, DeltaRopeBuilder::new().delete(10).build());
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn insert_long() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_str_insert("1234567890");
|
||
|
a.insert_str(3, &"abc".repeat(10));
|
||
|
assert_eq!(
|
||
|
a,
|
||
|
DeltaRopeBuilder::new()
|
||
|
.insert(TextChunk::try_from_str("123abc").unwrap(), ())
|
||
|
.insert(TextChunk::try_from_str("abcabcabc").unwrap(), ())
|
||
|
.insert(TextChunk::try_from_str("abcabcabc").unwrap(), ())
|
||
|
.insert(TextChunk::try_from_str("abcabcabc").unwrap(), ())
|
||
|
.insert(TextChunk::try_from_str("4567890").unwrap(), ())
|
||
|
.build()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_delete_delta_compose() {
|
||
|
let mut a: TextDelta = DeltaRopeBuilder::new().retain(5, ()).build();
|
||
|
let b: TextDelta = DeltaRopeBuilder::new().delete(5).build();
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, DeltaRopeBuilder::new().delete(5).build());
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_delete_delta_compose_1() {
|
||
|
let mut a: TextDelta = DeltaRopeBuilder::new().retain(10, ()).build();
|
||
|
let b: TextDelta = DeltaRopeBuilder::new().retain(2, ()).delete(5).build();
|
||
|
a.compose(&b);
|
||
|
assert_eq!(
|
||
|
a,
|
||
|
DeltaRopeBuilder::new()
|
||
|
.retain(2, ())
|
||
|
.delete(5)
|
||
|
.retain(3, ())
|
||
|
.build()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn compose_long_delete() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_retain(5, ());
|
||
|
a.push_str_insert("1234567890");
|
||
|
a.push_retain(5, ());
|
||
|
a.push_delete(1);
|
||
|
a.push_str_insert("1234567890");
|
||
|
let b: TextDelta = DeltaRopeBuilder::new().retain(2, ()).delete(20).build();
|
||
|
a.compose(&b);
|
||
|
assert_eq!(
|
||
|
a,
|
||
|
DeltaRopeBuilder::new()
|
||
|
.retain(2, ())
|
||
|
.replace(TextChunk::try_from_str("34567890").unwrap(), (), 9)
|
||
|
.build()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
type RichTextDelta = TextDelta<HashMap<String, bool>>;
|
||
|
#[test]
|
||
|
fn rich_text_delta() {
|
||
|
let mut text = RichTextDelta::new();
|
||
|
text.push_str_insert("123456789");
|
||
|
assert_eq!(text.try_to_string().unwrap(), "123456789");
|
||
|
let mut delta = RichTextDelta::new();
|
||
|
let mut styles = HashMap::new();
|
||
|
styles.insert("bold".to_string(), true);
|
||
|
delta
|
||
|
.push_str_insert("abc")
|
||
|
.push_retain(3, styles.clone())
|
||
|
.push_delete(3);
|
||
|
text.compose(&delta);
|
||
|
|
||
|
let mut expected = RichTextDelta::new();
|
||
|
expected
|
||
|
.push_str_insert("abc")
|
||
|
.push_insert(TextChunk::try_from_str("123").unwrap(), styles.clone())
|
||
|
.push_str_insert("789");
|
||
|
|
||
|
assert_eq!(text, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn insert_plus_insert() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_str_insert("A");
|
||
|
let mut b = TextDelta::new();
|
||
|
b.push_str_insert("B");
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta.push_str_insert("B").push_str_insert("A");
|
||
|
delta
|
||
|
};
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn insert_plus_retain() {
|
||
|
let mut a: RichTextDelta = TextDelta::new();
|
||
|
a.push_str_insert("A");
|
||
|
let mut b: RichTextDelta = TextDelta::new();
|
||
|
let mut attrs = HashMap::new();
|
||
|
attrs.insert("bold".to_string(), true);
|
||
|
attrs.insert("color".to_string(), true);
|
||
|
b.push_retain(1, attrs.clone());
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta.push_insert(TextChunk::try_from_str("A").unwrap(), attrs);
|
||
|
delta
|
||
|
};
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn insert_plus_delete() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_str_insert("A");
|
||
|
let mut b: TextDelta = TextDelta::new();
|
||
|
b.push_delete(1);
|
||
|
let expected = TextDelta::new();
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn delete_plus_delete() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_delete(1);
|
||
|
let mut b: TextDelta = TextDelta::new();
|
||
|
b.push_delete(1);
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta.push_delete(2);
|
||
|
delta
|
||
|
};
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_plus_insert() {
|
||
|
let mut a: RichTextDelta = TextDelta::new();
|
||
|
let mut attrs = HashMap::new();
|
||
|
attrs.insert("color".to_string(), true);
|
||
|
a.push_retain(1, attrs.clone());
|
||
|
let mut b = TextDelta::new();
|
||
|
b.push_str_insert("B");
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta.push_str_insert("B").push_retain(1, attrs);
|
||
|
delta
|
||
|
};
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_plus_retain() {
|
||
|
let mut a: RichTextDelta = TextDelta::new();
|
||
|
let mut attrs_a = HashMap::new();
|
||
|
attrs_a.insert("color".to_string(), true);
|
||
|
a.push_retain(1, attrs_a.clone());
|
||
|
let mut b = TextDelta::new();
|
||
|
let mut attrs_b = HashMap::new();
|
||
|
attrs_b.insert("bold".to_string(), true);
|
||
|
attrs_b.insert("color".to_string(), true);
|
||
|
b.push_retain(1, attrs_b.clone());
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
delta.push_retain(1, attrs_b);
|
||
|
delta
|
||
|
};
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_plus_delete() {
|
||
|
let mut a: RichTextDelta = TextDelta::new();
|
||
|
let mut attrs = HashMap::new();
|
||
|
attrs.insert("color".to_string(), true);
|
||
|
a.push_retain(1, attrs);
|
||
|
let mut b = TextDelta::new();
|
||
|
b.push_delete(1);
|
||
|
let mut expected: RichTextDelta = TextDelta::new();
|
||
|
expected.push_delete(1);
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
// Test for inserting in the middle of text
|
||
|
#[test]
|
||
|
fn insert_in_middle_of_text() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_str_insert("Hello");
|
||
|
let mut b: TextDelta = TextDelta::new();
|
||
|
b.push_retain(3, ()).push_str_insert("X");
|
||
|
let mut expected: TextDelta = TextDelta::new();
|
||
|
expected.push_str_insert("HelXlo");
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
// Test for insert and delete ordering
|
||
|
#[test]
|
||
|
fn insert_and_delete_ordering() {
|
||
|
let mut a: TextDelta = TextDelta::new();
|
||
|
a.push_str_insert("Hello");
|
||
|
let mut insert_first: TextDelta = TextDelta::new();
|
||
|
insert_first
|
||
|
.push_retain(3, ())
|
||
|
.push_str_insert("X")
|
||
|
.push_delete(1);
|
||
|
let mut delete_first = TextDelta::new();
|
||
|
delete_first
|
||
|
.push_retain(3, ())
|
||
|
.push_delete(1)
|
||
|
.push_str_insert("X");
|
||
|
let mut expected: TextDelta = TextDelta::new();
|
||
|
expected.push_str_insert("HelXo");
|
||
|
a.compose(&insert_first);
|
||
|
a.compose(&delete_first);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn retain_start_optimization_split() {
|
||
|
let mut a: RichTextDelta = TextDelta::new();
|
||
|
let mut attrs_bold = HashMap::new();
|
||
|
attrs_bold.insert("bold".to_string(), true);
|
||
|
|
||
|
a.push_insert(TextChunk::try_from_str("A").unwrap(), attrs_bold.clone())
|
||
|
.push_str_insert("B")
|
||
|
.push_insert(TextChunk::try_from_str("C").unwrap(), attrs_bold)
|
||
|
.push_retain(5, Default::default())
|
||
|
.push_delete(1);
|
||
|
|
||
|
let mut b: RichTextDelta = TextDelta::new();
|
||
|
b.push_retain(4, Default::default()).push_str_insert("D");
|
||
|
|
||
|
let expected = {
|
||
|
let mut delta = TextDelta::new();
|
||
|
let mut attrs_bold = HashMap::new();
|
||
|
attrs_bold.insert("bold".to_string(), true);
|
||
|
|
||
|
delta
|
||
|
.push_insert(TextChunk::try_from_str("A").unwrap(), attrs_bold.clone())
|
||
|
.push_str_insert("B")
|
||
|
.push_insert(TextChunk::try_from_str("C").unwrap(), attrs_bold)
|
||
|
.push_retain(1, Default::default())
|
||
|
.push_str_insert("D")
|
||
|
.push_retain(4, Default::default())
|
||
|
.push_delete(1);
|
||
|
delta
|
||
|
};
|
||
|
|
||
|
a.compose(&b);
|
||
|
assert_eq!(a, expected);
|
||
|
}
|