use super::*;
#[test]
fn solve_order_conflict() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\nx\ny\nz\nb\n";
let bob = b"a\nu\nv\nw\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
let check_conflict = |buf: &[u8]| -> Result<(), anyhow::Error> {
let conflict: Vec<_> = std::str::from_utf8(buf)?.lines().collect();
debug!("{:?}", conflict);
{
let mut conflict = conflict.clone();
(&mut conflict[2..9]).sort_unstable();
assert_eq!(
conflict,
vec![
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"================================",
"u",
"v",
"w",
"x",
"y",
"z",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b"
]
);
}
Ok(())
};
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for (n, l) in conflict.iter().enumerate() {
if n == 0 || n == 2 || n == 3 || n == 7 || n == 8 || n == 10 {
writeln!(w, "{}", l)?
} else if n == 4 {
writeln!(w, "{}\nbla!", l)?
} else if n == 6 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
info!("resolving");
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check_conflict(&buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert!(std::str::from_utf8(&buf)?.lines().all(|l| l.len() < 10));
crate::unrecord::unrecord(&mut txn, &mut channel_bob, &changes, &resolution).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob3").unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check_conflict(&buf)?;
Ok(())
}
#[test]
fn order_conflict_simple() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\nx\nb\n";
let bob = b"a\ny\nb\n";
let charlie = b"a\nz\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
let mut repo_charlie = working_copy::memory::Memory::new();
let mut channel_charlie = txn.open_or_create_channel("charlie")?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, init_h)?;
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_charlie.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(charlie).unwrap();
Ok(())
})?;
let charlie_h = record_all(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
let check_conflict = |buf: &[u8]| -> Result<(), anyhow::Error> {
let conflict: Vec<_> = std::str::from_utf8(buf)?.lines().collect();
debug!("{:?}", conflict);
{
let mut conflict = conflict.clone();
(&mut conflict[2..7]).sort_unstable();
assert_eq!(
conflict,
vec![
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"================================",
"================================",
"x",
"y",
"z",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b"
]
);
}
Ok(())
};
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter().filter(|l| l.len() == 1) {
writeln!(w, "{}", l)?
}
Ok(())
})?;
let mut alice_resolution = Vec::new();
repo_alice.read_file("file", &mut alice_resolution)?;
info!("resolving");
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, charlie_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check_conflict(&buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
{
let mut conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
(&mut conflict[2..6]).sort_unstable();
assert_eq!(
conflict,
vec![
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"================================",
"x",
"y",
"z",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b"
]
)
}
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter().filter(|l| l.len() == 1) {
writeln!(w, "{}", l)?
}
Ok(())
})?;
let mut bob_resolution = Vec::new();
repo_bob.read_file("file", &mut bob_resolution)?;
info!("resolving");
let resolution2 = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alice_h).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bob_h).unwrap();
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie1").unwrap();
buf.clear();
repo_charlie.read_file("file", &mut buf)?;
check_conflict(&buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, resolution).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, resolution2).unwrap();
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
buf.clear();
repo_charlie.read_file("file", &mut buf)?;
assert_eq!(
std::str::from_utf8(&bob_resolution),
std::str::from_utf8(&buf)
);
Ok(())
}
#[test]
fn order_conflict_edit() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\nx\ny\nb\n";
let bob = b"a\nu\nv\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
let mut is_conflict = 0;
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() == 1 {
if is_conflict < 2 {
writeln!(w, "{}", l)?
}
is_conflict += 1
} else if l.as_bytes()[0] == b'<' {
is_conflict = 0
} else {
is_conflict = 1
}
}
Ok(())
})?;
let mut alice_resolution = Vec::new();
repo_alice.read_file("file", &mut alice_resolution)?;
info!("resolving {:?}", std::str::from_utf8(&alice_resolution));
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(alice_resolution, buf);
Ok(())
}
#[test]
fn edit_conflict_sides() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\n";
let alice = b"a\nx\nb\nc\n";
let bob = b"a\ny\nb\nc\n";
let mut repo = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo.write_file::<_, std::io::Error, _>("file", |w| {
let mut ended = false;
let mut n = 0;
for l in conflict.iter() {
debug!("line: {:?}", l);
if l.len() > 5 {
if l.as_bytes()[0] == b'<' {
ended = true
}
if true {
writeln!(w, "pre{}\n{}\npost{}", n, l, n)?;
} else {
writeln!(w, "{}", l)?;
}
n += 1
} else if !ended {
writeln!(w, "{}", l)?
} else {
debug!("writing c: {:?}", l);
writeln!(w, "c")?
}
}
Ok(())
})?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
info!("resolving");
let resolution = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf2 = Vec::new();
repo.read_file("file", &mut buf2)?;
info!("{:?}", std::str::from_utf8(&buf2).unwrap());
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf2));
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
let mut buf3 = Vec::new();
repo.read_file("file", &mut buf3)?;
let mut lines2: Vec<_> = std::str::from_utf8(&buf2).unwrap().lines().collect();
lines2.sort_unstable();
let mut lines3: Vec<_> = std::str::from_utf8(&buf3).unwrap().lines().collect();
lines3.sort_unstable();
assert_eq!(lines2, lines3);
Ok(())
}
#[test]
fn edit_after_conflict() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\n";
let alice = b"a\nx\ny\nb\nc\n";
let bob = b"a\nx\ny\nb\nc\n";
let mut repo = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
debug!("line: {:?}", l);
if l.len() > 5 && l.as_bytes()[0] != b'<' {
writeln!(w, "pre\n{}\npost", l)?;
} else if *l != "b" && *l != "x" {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
info!("resolving");
let resolution = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf2 = Vec::new();
repo.read_file("file", &mut buf2)?;
info!("{:?}", std::str::from_utf8(&buf2).unwrap());
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf2));
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
let mut buf3 = Vec::new();
repo.read_file("file", &mut buf3)?;
let mut lines2: Vec<_> = std::str::from_utf8(&buf2)?.lines().collect();
lines2.sort_unstable();
let mut lines3: Vec<_> = std::str::from_utf8(&buf3)?.lines().collect();
lines3.sort_unstable();
assert_eq!(lines2, lines3);
Ok(())
}
#[test]
fn delete_before_marker() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\n";
let alice0 = b"a\nx\ny\nb\nc\n";
let alice1 = b"a\nx\ny\nz\nb\nc\n";
let bob0 = b"a\nu\nv\nb\nc\n";
let bob1 = b"a\nu\nv\nw\nb\nc\n";
let mut repo = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
let bob_edits: &[&[u8]] = &[bob0, bob1];
let bob_changes: Vec<_> = bob_edits
.iter()
.map(|bob| {
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})
.unwrap();
record_all(&mut repo, &changes, &mut txn, &mut channel_bob, "").unwrap()
})
.collect();
let alice_edits: &[&[u8]] = &[alice0, alice1];
let alice_changes: Vec<_> = alice_edits
.iter()
.map(|alice| {
repo.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})
.unwrap();
record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "").unwrap()
})
.collect();
for bob_h in bob_changes.iter() {
apply::apply_change(&changes, &mut txn, &mut channel_alice, *bob_h)?;
}
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo.write_file::<_, std::io::Error, _>("file", |w| {
let mut ended = false;
for l in conflict.iter() {
debug!("line: {:?}", l);
if *l == "z" || *l == "w" {
} else if l.starts_with("<<<") {
writeln!(w, "{}", l)?;
ended = true
} else if ended {
writeln!(w, "end\n{}", l)?;
ended = false
} else {
writeln!(w, "{}", l)?;
}
}
Ok(())
})?;
let mut buf = Vec::new();
repo.read_file("file", &mut buf)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
info!("resolving");
let conflict_edits = record_all(&mut repo, &changes, &mut txn, &mut channel_alice, "")?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf2 = Vec::new();
repo.read_file("file", &mut buf2)?;
info!("{:?}", std::str::from_utf8(&buf2).unwrap());
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf2));
for alice_h in alice_changes.iter() {
apply::apply_change(&changes, &mut txn, &mut channel_bob, *alice_h)?;
}
apply::apply_change(&changes, &mut txn, &mut channel_bob, conflict_edits)?;
output::output_repository_no_pending(
&mut repo,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
buf2.clear();
repo.read_file("file", &mut buf2)?;
let mut lines: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
lines.sort_unstable();
let mut lines2: Vec<_> = std::str::from_utf8(&buf2).unwrap().lines().collect();
lines2.sort_unstable();
assert_eq!(lines, lines2);
Ok(())
}
#[test]
fn conflict_last_line() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\n";
let alice = b"a\nx";
let bob = b"a\ny";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
let check_conflict = |buf: &[u8]| -> Result<(), anyhow::Error> {
let conflict: Vec<_> = std::str::from_utf8(buf)?.lines().collect();
debug!("{:?}", conflict);
{
let mut conflict = conflict.clone();
(&mut conflict[2..5]).sort_unstable();
assert_eq!(
conflict,
vec![
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"================================",
"x",
"y",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
]
);
}
Ok(())
};
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter().filter(|l| l.len() <= 2) {
writeln!(w, "{}", l)?
}
Ok(())
})?;
info!("resolving");
let mut buf_alice = Vec::new();
repo_alice.read_file("file", &mut buf_alice)?;
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check_conflict(&buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf_alice));
Ok(())
}
#[test]
fn zombie_last_line() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb";
let alice = b"a\nx";
let bob = b"";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
let check_conflict = |buf: &[u8]| -> Result<(), anyhow::Error> {
let conflict: Vec<_> = std::str::from_utf8(buf)?.lines().collect();
assert_eq!(
conflict,
vec![
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"x",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
]
);
Ok(())
};
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
{
let mut state = Builder::new();
state
.record(
&mut txn,
Algorithm::default(),
&mut channel_alice.borrow_mut(),
&mut repo_alice,
&changes,
"",
)
.unwrap();
let rec = state.finish();
assert!(rec.actions.is_empty())
}
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
write!(w, "x")?;
Ok(())
})?;
info!("resolving");
let mut buf_alice = Vec::new();
repo_alice.read_file("file", &mut buf_alice)?;
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check_conflict(&buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf_alice));
Ok(())
}
#[test]
fn edit_post_conflict() -> Result<(), anyhow::Error> {
edit_post_conflict_(
|buf| {
let buf: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
assert!(
buf == [
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"0",
"1",
"2",
"================================",
"3",
"4",
"5",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b",
] || buf
== [
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"3",
"4",
"5",
"================================",
"0",
"1",
"2",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b",
]
)
},
|buf, w| {
let conflict: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
for l in conflict.iter() {
if *l == "a" {
writeln!(w, "a\na'")?
} else if l.len() == 1 && *l != "0" && *l != "3" {
writeln!(w, "{}", l)?
}
}
Ok(())
},
)
}
#[test]
fn edit_around_conflict() -> Result<(), anyhow::Error> {
edit_post_conflict_(
|buf| {
let buf: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
assert!(
buf == [
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"0",
"1",
"2",
"================================",
"3",
"4",
"5",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b",
] || buf
== [
"a",
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"3",
"4",
"5",
"================================",
"0",
"1",
"2",
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
"b",
]
)
},
|buf, w| {
let conflict: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
for l in conflict.iter() {
if *l == "a" {
writeln!(w, "a\na'")?
} else if *l == "b" {
writeln!(w, "c")?
} else {
writeln!(w, "{}", l)?
}
}
Ok(())
},
)
}
fn edit_post_conflict_<
Check: FnMut(&[u8]),
Resolve: FnOnce(&[u8], &mut dyn std::io::Write) -> Result<(), std::io::Error>,
>(
mut check: Check,
resolve: Resolve,
) -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\n0\n1\n2\nb\n";
let bob = b"a\n3\n4\n5\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
check(&buf);
repo_alice.write_file::<_, std::io::Error, _>("file", |mut w| {
resolve(&buf, &mut w)?;
Ok(())
})?;
info!("resolving");
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
check(&buf);
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
let mut buf2 = Vec::new();
repo_bob.read_file("file", &mut buf2)?;
buf.clear();
repo_alice.read_file("file", &mut buf)?;
let mut lines: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
lines.sort_unstable();
let mut lines2: Vec<_> = std::str::from_utf8(&buf2).unwrap().lines().collect();
lines2.sort_unstable();
assert_eq!(lines, lines2);
Ok(())
}
#[test]
fn nested_conflict() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\nx\nb\n";
let bob = b"a\ny\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
debug!("Alice records");
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
debug!("Alice applies");
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
let buf = std::str::from_utf8(&buf).unwrap();
w.write_all(buf.replace("x\n", "u\nx\n").as_bytes())?;
Ok(())
})?;
info!("resolving");
let resolution_alice = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
let buf = std::str::from_utf8(&buf).unwrap();
w.write_all(buf.replace("x\n", "i\nx\n").as_bytes())?;
Ok(())
})?;
info!("resolving");
let resolution_bob = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
apply::apply_change(&changes, &mut txn, &mut channel_alice, resolution_bob).unwrap();
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice3").unwrap();
buf.clear();
repo_alice.read_file("file", &mut buf)?;
debug!("{}", std::str::from_utf8(&buf).unwrap());
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution_alice).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
let mut buf2 = Vec::new();
repo_bob.read_file("file", &mut buf2)?;
let mut lines: Vec<_> = std::str::from_utf8(&buf).unwrap().lines().collect();
lines.sort_unstable();
let mut lines2: Vec<_> = std::str::from_utf8(&buf2).unwrap().lines().collect();
lines2.sort_unstable();
assert_eq!(lines, lines2);
Ok(())
}
#[test]
fn zombie_context_resolution() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let mut repo_alice = working_copy::memory::Memory::new();
let mut repo_bob = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
let env_alice = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_alice = env_alice.mut_txn_begin();
let env_bob = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_bob = env_bob.mut_txn_begin();
let mut channel_alice = txn_alice.open_or_create_channel("alice").unwrap();
txn_alice.add_file("file").unwrap();
repo_alice.add_file("file", b"".to_vec());
let x: &[&[u8]] = &[b"c\n", b"a\nc\n", b"a\nb\nc\n", b"a\n", b""];
let p_alice: Vec<_> = x
.iter()
.map(|c| {
repo_alice
.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(c)?;
Ok(())
})
.unwrap();
record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)
.unwrap()
})
.collect();
let mut channel_bob = txn_bob.open_or_create_channel("bob").unwrap();
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, p_alice[0]).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob")?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(b"x\nc\n")?;
Ok(())
})?;
debug!("bob records conflict");
let p_bob = record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "").unwrap();
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob0")?;
for (n, p) in (&p_alice[1..]).iter().enumerate() {
info!("{}. Applying {:?}", n, p);
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, *p).unwrap();
debug_to_file(&txn_bob, &channel_bob.borrow(), &format!("debug_bob_{}", n))?;
}
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf)?;
debug!("file = {:?}", std::str::from_utf8(&buf));
assert_eq!(
std::str::from_utf8(&buf),
Ok(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n")
);
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(b"x\nc\n")?;
Ok(())
})?;
let resolution =
record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "").unwrap();
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob2")?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(buf, b"x\nc\n");
debug!("Alice applies Bob's change");
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, p_bob).unwrap();
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug_alice1")?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
true,
)?;
let mut buf2 = Vec::new();
repo_alice.read_file("file", &mut buf2)?;
assert_eq!(
std::str::from_utf8(&buf2),
Ok(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n")
);
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
true,
)?;
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug_alice2")?;
let mut buf2 = Vec::new();
repo_alice.read_file("file", &mut buf2)?;
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf2));
Ok(())
}
#[test]
fn zombie_half_survivor() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let mut repo_alice = working_copy::memory::Memory::new();
let mut repo_bob = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
let env_alice = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_alice = env_alice.mut_txn_begin();
let env_bob = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_bob = env_bob.mut_txn_begin();
let mut channel_alice = txn_alice.open_or_create_channel("alice").unwrap();
txn_alice.add_file("file").unwrap();
repo_alice.add_file("file", b"".to_vec());
let x: &[&[u8]] = &[b"a\nb\nc\nd\n", b""];
let p_alice: Vec<_> = x
.iter()
.map(|c| {
repo_alice
.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(c)?;
Ok(())
})
.unwrap();
record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)
.unwrap()
})
.collect();
let mut channel_bob = txn_bob.open_or_create_channel("bob").unwrap();
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, p_alice[0]).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(b"a\nb\nx\ny\nz\nc\nd\n")?;
Ok(())
})?;
let p_bob = record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "").unwrap();
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob0")?;
for p in &p_alice[1..] {
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, *p).unwrap();
}
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob1")?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob1_")?;
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(
std::str::from_utf8(&buf),
Ok(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\ny\nz\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n")
);
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(b"a\nz\nd\n")?;
Ok(())
})?;
let resolution =
record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "").unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob2")?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert_eq!(buf, b"a\nz\nd\n");
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, p_bob).unwrap();
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, resolution).unwrap();
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
true,
)?;
let mut buf2 = Vec::new();
repo_alice.read_file("file", &mut buf2)?;
assert_eq!(std::str::from_utf8(&buf), std::str::from_utf8(&buf2));
Ok(())
}
#[test]
fn three_way_zombie() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"u\na\nb\nc\nd\nv\n";
let alice = b"u\na\nb\nx\nc\nd\nv\n";
let bob = b"u\na\nd\nv\n";
let alice_bob = b"u\na\nx\nd\nv\n";
let charlie = b"u\nv\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
let mut repo_charlie = working_copy::memory::Memory::new();
let mut channel_charlie = txn.open_or_create_channel("charlie")?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, init_h)?;
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_charlie.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(charlie).unwrap();
Ok(())
})?;
record_all(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
debug!("alice = {:?}", std::str::from_utf8(&buf));
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| Ok(w.write_all(alice_bob)?))?;
let resolution = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, resolution)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bob_h)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie1").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alice_h)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, resolution)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie3").unwrap();
Ok(())
}
#[test]
fn cyclic_conflict_resolution() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let alice = b"a\nx\ny\nz\nb\n";
let bob = b"a\nu\nv\nw\nb\n";
let charlie = b"a\nU\nV\nW\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
let mut repo_charlie = working_copy::memory::Memory::new();
let mut channel_charlie = txn.open_or_create_channel("charlie")?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, init_h)?;
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
repo_charlie.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(charlie).unwrap();
Ok(())
})?;
let charlie_h = record_all(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
)?;
info!("Done outputting Charlie's working_copy");
{
let mut buf = Vec::new();
repo_charlie.read_file("file", &mut buf).unwrap();
info!("Charlie = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice").unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob").unwrap();
let bob_h = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
debug!("alice: {:?}", std::str::from_utf8(&buf));
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() < 10 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
info!("resolving");
let alices_resolution =
record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
debug!("bob: {:?}", std::str::from_utf8(&buf));
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() < 10 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
info!("resolving");
let _bobs_resolution = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, alices_resolution).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, charlie_h).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
debug!("outputting bob2");
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob3").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert!(std::str::from_utf8(&buf)?.lines().any(|l| l.len() >= 10));
debug!("{:?}", std::str::from_utf8(&buf));
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() < 10 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
debug!("resolving again");
let second_resolution = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
buf.clear();
repo_bob.read_file("file", &mut buf)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob4").unwrap();
assert!(std::str::from_utf8(&buf)?.lines().all(|l| l.len() < 10));
crate::unrecord::unrecord(&mut txn, &mut channel_bob, &changes, &second_resolution).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob5").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
assert!(std::str::from_utf8(&buf)?.lines().any(|l| l.len() >= 10));
Ok(())
}
#[test]
fn cyclic_zombies() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\n";
let alice = b"a\nx\ny\nz\nb\nc\n";
let alice2 = b"a\nx\nX\ny\nz\nb\nc\n";
let alice3 = b"a\nx\nX\nY\ny\nz\nb\nc\n";
let bob = b"a\nu\nv\nw\nb\nc\n";
let bob2 = b"a\nu\nU\nv\nw\nb\nc\n";
let bob3 = b"a\nu\nU\nV\nv\nw\nb\nc\n";
let charlie = b"a\nc\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("file", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel_alice = txn.open_or_create_channel("alice")?;
txn.add_file("file")?;
let init_h = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
let mut channel_bob = txn.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
{
let mut buf = Vec::new();
repo_bob.read_file("file", &mut buf).unwrap();
info!("Bob = {:?}", std::str::from_utf8(&buf));
}
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob).unwrap();
Ok(())
})?;
let bob_h1 = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob2).unwrap();
Ok(())
})?;
let bob_h2 = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(bob3).unwrap();
Ok(())
})?;
let bob_h3 = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob0").unwrap();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice).unwrap();
Ok(())
})?;
let alice_h1 = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice2).unwrap();
Ok(())
})?;
let alice_h2 = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(alice3).unwrap();
Ok(())
})?;
let alice_h3 = record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice0").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h1)?;
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h2)?;
apply::apply_change(&changes, &mut txn, &mut channel_alice, bob_h3)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn,
&mut channel_alice,
"",
true,
)?;
let mut buf = Vec::new();
repo_alice.read_file("file", &mut buf)?;
debug!("alice: {:?}", std::str::from_utf8(&buf));
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice1").unwrap();
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_alice.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() < 10 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
info!("resolving");
let alices_resolution =
record_all(&mut repo_alice, &changes, &mut txn, &mut channel_alice, "")?;
debug_to_file(&txn, &channel_alice.borrow(), "debug_alice2").unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h1).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h2).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_bob, alice_h3).unwrap();
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob1").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
debug!("bob: {:?}", std::str::from_utf8(&buf));
let conflict: Vec<_> = std::str::from_utf8(&buf)?.lines().collect();
repo_bob.write_file::<_, std::io::Error, _>("file", |w| {
for l in conflict.iter() {
if l.len() < 10 {
writeln!(w, "{}", l)?
}
}
Ok(())
})?;
info!("resolving");
let bobs_resolution = record_all(&mut repo_bob, &changes, &mut txn, &mut channel_bob, "")?;
let mut repo_charlie = working_copy::memory::Memory::new();
let mut channel_charlie = txn.open_or_create_channel("charlie")?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, init_h)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alice_h1)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alice_h2)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alice_h3)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bob_h1)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bob_h2)?;
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bob_h3)?;
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
repo_charlie.write_file::<_, std::io::Error, _>("file", |w| {
w.write_all(charlie).unwrap();
Ok(())
})?;
let charlie_h = record_all(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
)?;
apply::apply_change(&changes, &mut txn, &mut channel_bob, alices_resolution).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob2").unwrap();
debug!("outputting bob2");
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob3").unwrap();
buf.clear();
repo_bob.read_file("file", &mut buf)?;
debug!("applying charlie's patch");
apply::apply_change(&changes, &mut txn, &mut channel_bob, charlie_h).unwrap();
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob4").unwrap();
let (alive_, reachable_) = check_alive(&txn, &channel_bob.borrow().graph);
if !alive_.is_empty() {
error!("alive (bob0): {:?}", alive_);
}
if !reachable_.is_empty() {
error!("reachable (bob0): {:?}", reachable_);
}
debug!("outputting bob's repo");
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn,
&mut channel_bob,
"",
true,
)?;
debug_to_file(&txn, &channel_bob.borrow(), "debug_bob5").unwrap();
let (alive, reachable) = check_alive(&txn, &channel_bob.borrow().graph);
if !alive.is_empty() {
panic!("alive (bob1): {:?}", alive);
} else if !alive_.is_empty() {
panic!("alive_ (bob1): {:?}", alive_);
}
if !reachable.is_empty() {
panic!("reachable (bob1): {:?}", reachable);
} else if !reachable_.is_empty() {
panic!("reachable_ (bob1): {:?}", reachable_);
}
debug!("Charlie applies");
apply::apply_change(&changes, &mut txn, &mut channel_charlie, alices_resolution).unwrap();
apply::apply_change(&changes, &mut txn, &mut channel_charlie, bobs_resolution).unwrap();
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie").unwrap();
let (alive, reachable) = check_alive(&txn, &channel_charlie.borrow().graph);
if !alive.is_empty() {
panic!("alive (charlie0): {:?}", alive);
}
if !reachable.is_empty() {
panic!("reachable (charlie0): {:?}", reachable);
}
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn,
&mut channel_charlie,
"",
true,
)?;
debug_to_file(&txn, &channel_charlie.borrow(), "debug_charlie1").unwrap();
let (alive, reachable) = check_alive(&txn, &channel_charlie.borrow().graph);
if !alive.is_empty() {
panic!("alive (charlie1): {:?}", alive);
}
if !reachable.is_empty() {
panic!("reachable (charlie1): {:?}", reachable);
}
Ok(())
}
#[test]
fn cyclic_files() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let mut repo_bob = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("a/file", contents.to_vec());
repo_alice.add_file("b/file", contents.to_vec());
let env_alice = pristine::sanakirja::Pristine::new_anon()?;
let env_bob = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_alice = env_alice.mut_txn_begin();
let mut txn_bob = env_bob.mut_txn_begin();
let mut channel_alice = txn_alice.open_or_create_channel("alice")?;
txn_alice.add_file("a/file")?;
txn_alice.add_file("b/file")?;
let init_h = record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)?;
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug_init").unwrap();
let mut channel_bob = txn_bob.open_or_create_channel("bob")?;
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
txn_bob.move_file("a", "b/a").unwrap();
repo_bob.rename("a", "b/a").unwrap();
let ab = record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "")?;
txn_alice.move_file("b", "a/b").unwrap();
repo_alice.rename("b", "a/b").unwrap();
let _ba = record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)?;
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, ab)?;
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug").unwrap();
debug!("outputting cycle");
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
true,
)?;
let v: Vec<_> = txn_alice.iter_working_copy().collect();
println!("{:?}", v);
let (alive, reachable) = check_alive(&txn_alice, &channel_alice.borrow().graph);
if !alive.is_empty() {
panic!("alive: {:?}", alive);
}
if !reachable.is_empty() {
panic!("reachable: {:?}", reachable);
}
debug!("recording the resolution");
let _resolution = record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)?;
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug2").unwrap();
Ok(())
}
#[test]
fn tree_inodes_test() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\n";
let mut repo_alice = working_copy::memory::Memory::new();
let changes = changestore::memory::Memory::new();
repo_alice.add_file("a/b/file", contents.to_vec());
let env_alice = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_alice = env_alice.mut_txn_begin();
let mut channel_alice = txn_alice.open_or_create_channel("alice")?;
txn_alice.add_file("a/b/file")?;
let env_bob = pristine::sanakirja::Pristine::new_anon()?;
let mut txn_bob = env_bob.mut_txn_begin();
let mut channel_bob = txn_bob.open_or_create_channel("bob")?;
txn_bob.add_file("a/b/file")?;
let init_h = record_all(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
)?;
debug_to_file(&txn_alice, &channel_alice.borrow(), "debug_init").unwrap();
let mut repo_bob = working_copy::memory::Memory::new();
apply::apply_change(&changes, &mut txn_bob, &mut channel_bob, init_h)?;
output::output_repository_no_pending(
&mut repo_bob,
&changes,
&mut txn_bob,
&mut channel_bob,
"",
true,
)?;
info!("Done outputting Bob's working_copy");
repo_bob.rename("a/b/file", "c/d/file")?;
txn_bob.move_file("a/b/file", "c/d/file")?;
repo_bob.remove_path("a")?;
txn_bob.remove_file("a")?;
let bob_h = record_all(&mut repo_bob, &changes, &mut txn_bob, &mut channel_bob, "")?;
debug_to_file(&txn_bob, &channel_bob.borrow(), "debug_bob0").unwrap();
apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, bob_h)?;
output::output_repository_no_pending(
&mut repo_alice,
&changes,
&mut txn_alice,
&mut channel_alice,
"",
true,
)?;
check_tree_inodes(&txn_alice, &channel_alice.borrow());
Ok(())
}
fn check_tree_inodes<T: TxnT>(txn: &T, channel: &T::Channel) {
for x in txn.iter_inodes().unwrap() {
let (inode, vertex) = x.unwrap();
debug!("inode = {:?}, vertex = {:?}", inode, vertex);
let mut inode_ = inode;
while !inode_.is_root() {
if let Some(next) = txn.get_revtree(inode_, None).unwrap() {
debug!("next = {:?}", next);
inode_ = next.parent_inode;
} else {
panic!("inode = {:?}, inode_ = {:?}", inode, inode_);
}
}
if !is_alive(txn, T::graph(&channel), vertex.inode_vertex()).unwrap() {
for e in iter_adjacent(
txn,
T::graph(&channel),
vertex.inode_vertex(),
EdgeFlags::empty(),
EdgeFlags::all(),
)
.unwrap()
{
error!("{:?} {:?} {:?}", inode, vertex, e)
}
panic!(
"inode {:?}, vertex {:?}, is not alive, {:?}",
inode,
vertex,
tree_path(txn, vertex)
)
}
}
}