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.
80 lines
2.6 KiB
Rust
80 lines
2.6 KiB
Rust
//! A simple smoke test with two authors synchronizing random edits.
|
|
//!
|
|
//! TODO: Replace by property based tests.
|
|
|
|
use chronofold::{Chronofold, Op};
|
|
use rand::{rngs::ThreadRng, Rng};
|
|
|
|
type AuthorId = &'static str;
|
|
|
|
#[test]
|
|
fn random_edits_by_two_authors() {
|
|
let mut rng = rand::thread_rng();
|
|
|
|
// Alice creates a chronofold and makes some edits before sending Bob a
|
|
// copy.
|
|
let mut cfold_alice = Chronofold::<AuthorId, char>::default();
|
|
random_edits(&mut rng, "alice", &mut cfold_alice);
|
|
let mut cfold_bob = cfold_alice.clone();
|
|
|
|
// Alice and Bob both work on an their own copy, sending each other their
|
|
// ops after they finish their edits each day. After ten days, they compare
|
|
// their results.
|
|
for _ in 0..10 {
|
|
let ops_alice = random_edits(&mut rng, "alice", &mut cfold_alice);
|
|
let ops_bob = random_edits(&mut rng, "bob", &mut cfold_bob);
|
|
for op in ops_alice {
|
|
cfold_bob.apply(op).unwrap();
|
|
}
|
|
for op in ops_bob {
|
|
cfold_alice.apply(op).unwrap();
|
|
}
|
|
}
|
|
assert_eq!(format!("{cfold_alice}"), format!("{cfold_bob}"));
|
|
}
|
|
|
|
fn random_edits(
|
|
rng: &mut ThreadRng,
|
|
author: AuthorId,
|
|
cfold: &mut Chronofold<AuthorId, char>,
|
|
) -> Vec<Op<AuthorId, char>> {
|
|
let mut session = cfold.session(author);
|
|
|
|
// 1 to 5 inserts of random words at random positions
|
|
for _ in 0..rng.gen_range(1, 6) {
|
|
let current = session.as_ref().iter().map(|(_, i)| i).collect::<Vec<_>>();
|
|
if !current.is_empty() {
|
|
let idx = current[rng.gen_range(0, current.len())];
|
|
session.splice(idx..idx, random_word(rng).chars());
|
|
} else {
|
|
session.extend(random_word(rng).chars());
|
|
}
|
|
}
|
|
|
|
// 1 to 2 deletions of 1 to 3 characters at random positions
|
|
for _ in 0..rng.gen_range(0, 2) {
|
|
let current = session.as_ref().iter().map(|(_, i)| i).collect::<Vec<_>>();
|
|
if !current.is_empty() {
|
|
let length = usize::min(rng.gen_range(1, 4), current.len());
|
|
let start = current[rng.gen_range(0, current.len() - length + 1)];
|
|
let (_, end) = session
|
|
.as_ref()
|
|
.iter_range(start..)
|
|
.take(length)
|
|
.last()
|
|
.unwrap();
|
|
session.splice(start..=end, "".chars());
|
|
}
|
|
}
|
|
|
|
session.iter_ops().map(Op::cloned).collect()
|
|
}
|
|
|
|
fn random_word(rng: &mut ThreadRng) -> String {
|
|
let alphabet: Vec<_> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
|
|
(0..rng.gen_range(1, 4))
|
|
.map(|_| alphabet[rng.gen_range(0, alphabet.len())])
|
|
.collect::<String>()
|
|
+ " "
|
|
}
|