use crate::change::*;
use crate::changestore::*;
use crate::pristine::*;
use crate::record::*;
use crate::working_copy::*;
use crate::*;
fn hash_mismatch(change: &Change) -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
use crate::change::*;
let mut buf = tempfile::NamedTempFile::new()?;
let mut h = change.serialize(&mut buf)?;
match h {
crate::pristine::Hash::Blake3(ref mut h) => h[0] = h[0].wrapping_add(1),
_ => unreachable!(),
}
match Change::deserialize(buf.path().to_str().unwrap(), Some(&h)) {
Err(ChangeError::ChangeHashMismatch { .. }) => {}
_ => unreachable!(),
}
let f = ChangeFile::open(h, buf.path().to_str().unwrap())?;
assert_eq!(f.hashed(), &change.hashed);
Ok(())
}
#[test]
fn hash_mism() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\nd\ne\nf\n";
let mut repo = working_copy::memory::Memory::new();
let store = changestore::memory::Memory::new();
repo.add_file("file", contents.to_vec());
repo.add_file("file2", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel = txn.open_or_create_channel("main")?;
txn.add_file("file")?;
txn.add_file("file2")?;
let mut state = Builder::new();
state
.record(
&mut txn,
Algorithm::Myers,
&mut channel.borrow_mut(),
&mut repo,
&store,
"",
)
.unwrap();
let rec = state.finish();
let changes: Vec<_> = rec
.actions
.into_iter()
.map(|rec| rec.globalize(&txn).unwrap())
.collect();
info!("changes = {:?}", changes);
let change0 = crate::change::Change::make_change(
&txn,
&channel,
changes,
rec.contents,
crate::change::ChangeHeader {
message: "test".to_string(),
authors: vec![],
description: None,
timestamp: chrono::Utc::now(),
},
Vec::new(),
)
.unwrap();
let hash0 = store.save_change(&change0)?;
apply::apply_local_change(&mut txn, &mut channel, &change0, hash0, &rec.updatables)?;
hash_mismatch(&change0)?;
debug_to_file(&txn, &channel.borrow(), "debug")?;
Ok(())
}
fn record_all<T: MutTxnT, R: WorkingCopy, P: ChangeStore>(
repo: &mut R,
store: &P,
txn: &mut T,
channel: &mut ChannelRef<T>,
prefix: &str,
) -> Result<(Hash, Change), anyhow::Error>
where
R::Error: Send + Sync + 'static,
{
let mut state = Builder::new();
state.record(
txn,
Algorithm::default(),
&mut channel.borrow_mut(),
repo,
store,
prefix,
)?;
let rec = state.finish();
let changes = rec
.actions
.into_iter()
.map(|rec| rec.globalize(txn).unwrap())
.collect();
let change0 = crate::change::Change::make_change(
txn,
&channel,
changes,
rec.contents,
crate::change::ChangeHeader {
message: "test".to_string(),
authors: vec![],
description: None,
timestamp: chrono::Utc::now(),
},
Vec::new(),
)
.unwrap();
let hash = store.save_change(&change0)?;
apply::apply_local_change(txn, channel, &change0, hash, &rec.updatables)?;
Ok((hash, change0))
}
#[cfg(feature = "text-changes")]
#[test]
fn text() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\nd\ne\nf\n";
let mut repo = working_copy::memory::Memory::new();
let store = changestore::memory::Memory::new();
repo.add_file("file", contents.to_vec());
repo.add_file("file2", contents.to_vec());
let env = pristine::sanakirja::Pristine::new_anon()?;
let mut txn = env.mut_txn_begin();
let mut channel = txn.open_or_create_channel("main")?;
txn.add_file("file")?;
txn.add_file("file2")?;
let (h0, change0) = record_all(&mut repo, &store, &mut txn, &mut channel, "")?;
text_test(&store, &change0, h0);
repo.write_file::<_, std::io::Error, _>("file", |w| {
write!(w, "a\nx\nc\ne\ny\nf\n")?;
Ok(())
})?;
let (h1, change1) = record_all(&mut repo, &store, &mut txn, &mut channel, "")?;
text_test(&store, &change1, h1);
repo.remove_path("file2")?;
let (h2, change2) = record_all(&mut repo, &store, &mut txn, &mut channel, "")?;
text_test(&store, &change2, h2);
repo.rename("file", "file3")?;
txn.move_file("file", "file3")?;
let (h3, change3) = record_all(&mut repo, &store, &mut txn, &mut channel, "")?;
text_test(&store, &change3, h3);
let env2 = pristine::sanakirja::Pristine::new_anon()?;
let mut txn2 = env2.mut_txn_begin();
let mut channel2 = txn2.open_or_create_channel("main")?;
let mut repo2 = working_copy::memory::Memory::new();
apply::apply_change(&store, &mut txn2, &mut channel2, h0)?;
apply::apply_change(&store, &mut txn2, &mut channel2, h1)?;
apply::apply_change(&store, &mut txn2, &mut channel2, h2)?;
output::output_repository_no_pending(&mut repo2, &store, &mut txn2, &mut channel2, "", true)?;
repo2.rename("file", "file4")?;
txn2.move_file("file", "file4")?;
let (_, _) = record_all(&mut repo2, &store, &mut txn2, &mut channel2, "")?;
apply::apply_change(&store, &mut txn2, &mut channel2, h3)?;
output::output_repository_no_pending(&mut repo2, &store, &mut txn2, &mut channel2, "", true)?;
let (h, solution) = record_all(&mut repo2, &store, &mut txn2, &mut channel2, "")?;
text_test(&store, &solution, h);
Ok(())
}
fn text_test<C: ChangeStore>(c: &C, change0: &Change, h: Hash) {
let mut v = Vec::new();
change0
.write(
c,
Some(h),
|l, _p| format!("{}:{}", l.path, l.line),
true,
&mut v,
)
.unwrap();
for i in std::str::from_utf8(&v).unwrap().lines() {
debug!("{}", i);
}
let change1 = Change::read(std::io::Cursor::new(&v[..]), &mut HashMap::new()).unwrap();
if change0.header != change1.header {
error!("header: {:#?} != {:#?}", change0.header, change1.header);
}
if change0.dependencies != change1.dependencies {
error!(
"deps: {:#?} != {:#?}",
change0.dependencies, change1.dependencies
);
}
if change0.extra_known != change1.extra_known {
error!(
"extra: {:#?} != {:#?}",
change0.extra_known, change1.extra_known
);
}
if change0.metadata != change1.metadata {
error!("meta: {:#?} != {:#?}", change0.metadata, change1.metadata);
}
if change0.changes != change1.changes {
if change0.changes.len() != change1.changes.len() {
error!("change0.changes = {:#?}", change0.changes);
error!("change1.changes = {:#?}", change1.changes);
} else {
for (a, b) in change0.changes.iter().zip(change1.changes.iter()) {
error!("change0: {:#?}", a);
error!("change1: {:#?}", b);
}
}
}
if change0.contents != change1.contents {
error!("change0.contents = {:?}", change0.contents);
error!("change1.contents = {:?}", change1.contents);
}
assert_eq!(change0, &change1);
}