use std::fs;
use super::*;
use crate::pack::counters;
use crate::pack_git::Fixture;
#[test]
fn selection_fallback_hashes_each_copy_and_clones_skip_damage() {
let fixture = Fixture::new(ObjectFormat::Sha1, true, 4);
let original = fs::read(&fixture.index_path).unwrap();
let width = 20;
let count = fixture.records.len();
let names = &original[1032..1032 + count * width];
let position = names
.chunks_exact(width)
.position(|name| name == fixture.ordinary.as_bytes())
.unwrap();
let other = (position + 1) % count;
let crcs = 1032 + count * width;
let offsets = crcs + count * 4;
let first = fixture.repo.object_dir().join("pack/a.idx");
let second = fixture.repo.object_dir().join("pack/z.idx");
let pack = fs::read(fixture.index_path.with_extension("pack")).unwrap();
fs::write(first.with_extension("pack"), &pack).unwrap();
fs::write(second.with_extension("pack"), &pack).unwrap();
let mut crc_damage = original.clone();
crc_damage[crcs + position * 4] ^= 1;
rehash(&mut crc_damage);
fs::write(&first, crc_damage).unwrap();
let mut identity_damage = original;
for byte in 0..4 {
identity_damage.swap(crcs + position * 4 + byte, crcs + other * 4 + byte);
identity_damage.swap(offsets + position * 4 + byte, offsets + other * 4 + byte);
}
rehash(&mut identity_damage);
fs::write(&second, identity_damage).unwrap();
fs::remove_file(&fixture.index_path).unwrap();
fs::remove_file(fixture.index_path.with_extension("pack")).unwrap();
let objects = fixture.repo.objects(PackLimits::default()).unwrap();
let clone = objects.clone();
let cancel = AtomicBool::new(false);
assert!(
objects
.read_unverified(fixture.ordinary, ReadLimits::default(), &cancel)
.is_err()
);
counters::reset();
assert!(clone.read(fixture.ordinary, ReadLimits::default()).is_err());
assert_eq!(counters::OS_READS.with(|reads| reads.get()), 0);
assert_eq!(
objects
.read(fixture.delta, ReadLimits::default())
.unwrap()
.unwrap()
.id(),
fixture.delta
);
assert!(fixture.root.path().exists());
}
fn rehash(bytes: &mut [u8]) {
let end = bytes.len() - 20;
let checksum = ObjectFormat::Sha1.checksum(&bytes[..end]);
bytes[end..].copy_from_slice(checksum.as_bytes());
}
#[test]
fn independently_refreshed_clones_share_new_pack_damage() {
let fixture = Fixture::new(ObjectFormat::Sha1, true, 4);
let mut first = fixture.repo.objects(PackLimits::default()).unwrap();
let mut second = first.clone();
counters::reset();
assert_eq!(
first
.read(fixture.ordinary, ReadLimits::default())
.unwrap()
.unwrap()
.id(),
fixture.ordinary
);
let healthy_reads = counters::OS_READS.with(|reads| reads.get());
let new = fixture.repo.object_dir().join("pack/a-new.idx");
fs::copy(
fixture.index_path.with_extension("pack"),
new.with_extension("pack"),
)
.unwrap();
let mut bytes = fs::read(&fixture.index_path).unwrap();
let position = bytes[1032..1032 + fixture.records.len() * 20]
.as_chunks::<20>()
.0
.iter()
.position(|name| name == fixture.ordinary.as_bytes())
.unwrap();
bytes[1032 + fixture.records.len() * 20 + position * 4] ^= 1;
rehash(&mut bytes);
fs::write(new, bytes).unwrap();
first
.refresh(PackLimits::default(), AlternateLimits::default())
.unwrap();
second
.refresh(PackLimits::default(), AlternateLimits::default())
.unwrap();
assert_eq!(
first
.read(fixture.ordinary, ReadLimits::default())
.unwrap()
.unwrap()
.id(),
fixture.ordinary
);
counters::reset();
assert_eq!(
second
.read(fixture.ordinary, ReadLimits::default())
.unwrap()
.unwrap()
.id(),
fixture.ordinary
);
assert_eq!(counters::OS_READS.with(|reads| reads.get()), healthy_reads);
}