You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.4 KiB
Rust
151 lines
3.4 KiB
Rust
use chronofold::{Chronofold, LogIndex, Op, Session};
|
|
|
|
#[test]
|
|
fn concurrent_insertions() {
|
|
// Both insert after the same character:
|
|
assert_concurrent_eq(
|
|
"012!",
|
|
"0",
|
|
|s| {
|
|
s.extend("!".chars());
|
|
},
|
|
|s| {
|
|
s.extend("12".chars());
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_deletions() {
|
|
// Both delete the same single character:
|
|
assert_concurrent_eq(
|
|
"fobar",
|
|
"foobar",
|
|
|s| {
|
|
s.remove(LogIndex(2));
|
|
},
|
|
|s| {
|
|
s.remove(LogIndex(2));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_replacements() {
|
|
// Both replace the same substring:
|
|
assert_concurrent_eq(
|
|
"foobaz123",
|
|
"foobar",
|
|
|s| {
|
|
s.splice(LogIndex(4).., "123".chars());
|
|
},
|
|
|s| {
|
|
s.splice(LogIndex(4).., "baz".chars());
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_insertion_deletion() {
|
|
// Alice inserts after a character that is concurrently deleted by Bob.
|
|
|
|
// Equal log indices for the conflicting edits:
|
|
assert_concurrent_eq(
|
|
"0!",
|
|
"01",
|
|
|s| {
|
|
s.insert_after(LogIndex(2), '!');
|
|
},
|
|
|s| {
|
|
s.remove(LogIndex(2));
|
|
},
|
|
);
|
|
|
|
// Insert's log index is greater:
|
|
assert_concurrent_eq(
|
|
"0!23",
|
|
"01",
|
|
|s| {
|
|
s.extend("23".chars());
|
|
s.insert_after(LogIndex(2), '!');
|
|
},
|
|
|s| {
|
|
s.remove(LogIndex(2));
|
|
},
|
|
);
|
|
|
|
// Delete's log index is greater:
|
|
assert_concurrent_eq(
|
|
"023!",
|
|
"01",
|
|
|s| {
|
|
s.insert_after(LogIndex(2), '!');
|
|
},
|
|
|s| {
|
|
s.extend("23".chars());
|
|
s.remove(LogIndex(2));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_inserts_referencing_deletions() {
|
|
let mutate = |s: &mut Session<u8, char>| {
|
|
s.remove(LogIndex(2));
|
|
let delete_idx = LogIndex(3);
|
|
s.insert_after(delete_idx, '3');
|
|
};
|
|
assert_concurrent_eq("133", "12", mutate, mutate);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_referencing_deleted_element() {
|
|
let mut cfold = Chronofold::<u8, char>::default();
|
|
let mut session = cfold.session(1);
|
|
let idx = session.push_back('!');
|
|
session.clear();
|
|
session.insert_after(idx, '?');
|
|
assert_eq!("?", format!("{cfold}"));
|
|
}
|
|
|
|
fn assert_concurrent_eq<F, G>(expected: &str, initial: &str, mutate_left: F, mutate_right: G)
|
|
where
|
|
F: FnOnce(&mut Session<u8, char>),
|
|
G: FnOnce(&mut Session<u8, char>),
|
|
{
|
|
let mut cfold_left = Chronofold::<u8, char>::default();
|
|
cfold_left.session(1).extend(initial.chars());
|
|
let mut cfold_right = cfold_left.clone();
|
|
|
|
let ops_left: Vec<_> = {
|
|
let mut session = cfold_left.session(1);
|
|
mutate_left(&mut session);
|
|
session.iter_ops().map(Op::cloned).collect()
|
|
};
|
|
let ops_right: Vec<_> = {
|
|
let mut session = cfold_right.session(2);
|
|
mutate_right(&mut session);
|
|
session.iter_ops().map(Op::cloned).collect()
|
|
};
|
|
|
|
for op in ops_left {
|
|
cfold_right.apply(op).unwrap();
|
|
}
|
|
for op in ops_right {
|
|
cfold_left.apply(op).unwrap();
|
|
}
|
|
|
|
assert_eq!(
|
|
expected,
|
|
format!("{cfold_left}"),
|
|
"\n{}",
|
|
cfold_left.formatted_log(),
|
|
);
|
|
assert_eq!(
|
|
expected,
|
|
format!("{cfold_right}"),
|
|
"\n{}",
|
|
cfold_right.formatted_log(),
|
|
);
|
|
}
|