use crate::change::*;
use crate::changestore::*;
use crate::pristine::*;
use crate::record::*;
use crate::working_copy::*;
use crate::*;
use std::io::Write;
use super::*;
fn hash_mismatch(change: &mut 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, |_, _| Ok::<_, anyhow::Error>(()))?;
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 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 txn = env.arc_txn_begin().unwrap();
let mut channel = txn.write().open_or_create_channel("main")?;
txn.write().add_file("file", 0)?;
txn.write().add_file("file2", 0)?;
let mut state = Builder::new();
state
.record(
txn.clone(),
Algorithm::Myers,
false,
&crate::DEFAULT_SEPARATOR,
channel.clone(),
&repo,
&store,
"",
0,
)
.unwrap();
let rec = state.finish();
let changes: Vec<_> = rec
.actions
.into_iter()
.map(|rec| rec.globalize(&*txn.read()).unwrap())
.collect();
info!("changes = {:?}", changes);
let mut change0 = crate::change::Change::make_change(
&*txn.read(),
&channel,
changes,
std::mem::take(&mut *rec.contents.lock()),
crate::change::ChangeHeader {
message: "test".to_string(),
authors: vec![],
description: None,
timestamp: chrono::Utc::now(),
},
Vec::new(),
)
.unwrap();
let hash0 = store.save_change(&mut change0, |_, _| Ok::<_, anyhow::Error>(()))?;
apply::apply_local_change(
&mut *txn.write(),
&mut channel,
&change0,
&hash0,
&rec.updatables,
)?;
hash_mismatch(&mut change0)?;
Ok(())
}
#[cfg(feature = "text-changes")]
#[test]
#[ignore]
fn text() -> Result<(), anyhow::Error> {
env_logger::try_init().unwrap_or(());
let contents = b"a\nb\nc\nd\ne\nf\n";
let 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 txn = env.arc_txn_begin().unwrap();
let channel = txn.write().open_or_create_channel("main")?;
txn.write().add_file("file", 0)?;
txn.write().add_file("file2", 0)?;
let h0 = record_all(&repo, &store, &txn, &channel, "")?;
let change0 = store.get_change(&h0).unwrap();
text_test(&store, &change0, h0);
write!(repo.write_file("file", Inode::ROOT)?, "a\nx\nc\ne\ny\nf\n")?;
let h1 = record_all(&repo, &store, &txn, &channel, "")?;
let change1 = store.get_change(&h1).unwrap();
text_test(&store, &change1, h1);
repo.remove_path("file2", false)?;
let h2 = record_all(&repo, &store, &txn, &channel, "")?;
let change2 = store.get_change(&h2).unwrap();
text_test(&store, &change2, h2);
repo.rename("file", "file3")?;
txn.write().move_file("file", "file3", 0)?;
let h3 = record_all(&repo, &store, &txn, &channel, "")?;
let change3 = store.get_change(&h3).unwrap();
text_test(&store, &change3, h3);
let env2 = pristine::sanakirja::Pristine::new_anon()?;
let txn2 = env2.arc_txn_begin().unwrap();
let channel2 = txn2.write().open_or_create_channel("main")?;
let repo2 = working_copy::memory::Memory::new();
apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h0)?;
apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h1)?;
apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h2)?;
output::output_repository_no_pending(&repo2, &store, &txn2, &channel2, "", true, None, 1, 0)?;
repo2.rename("file", "file4")?;
txn2.write().move_file("file", "file4", 0)?;
record_all(&repo2, &store, &txn2, &channel2, "")?;
apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h3)?;
output::output_repository_no_pending(&repo2, &store, &txn2, &channel2, "", true, None, 1, 0)?;
let h = record_all(&repo2, &store, &txn2, &channel2, "")?;
let solution = store.get_change(&h).unwrap();
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), 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::default()).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() {
trace!("change0.changes = {:#?}", change0.changes);
trace!("change1.changes = {:#?}", change1.changes);
} else {
for (a, b) in change0.changes.iter().zip(change1.changes.iter()) {
trace!("change0: {:#?}", a);
trace!("change1: {:#?}", b);
for (a, b) in a.iter().zip(b.iter()) {
if a != b {
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);
}