use std::{ path::{Path, PathBuf}, sync::atomic::AtomicBool, }; use gix_features::progress; use gix_testtools::fixture_path; use crate::hex_to_id; /// Writes a multi-index from the static SHA-1 pack indices, with pinned SHA-1 expectations. /// The SHA-256 counterpart lives in [`from_a_hash_parameterized_pack`] below. #[test] fn from_paths() -> crate::Result { let pack_dir = fixture_path("objects/pack"); let written = write_multi_index_from_pack_dir(&pack_dir, gix_hash::Kind::Sha1)?; assert_eq!(written.input_indices.len(), 3); assert_eq!( written.outcome.multi_index_checksum, hex_to_id("d34d327039a3554f8a644b29e07b903fa71ef269") ); assert_eq!(written.file.num_indices(), 3); assert_eq!( written.file.index_names(), vec![ PathBuf::from("pack-11fdfa9e156ab73caae3b6da867192221f2089c2.idx"), PathBuf::from("pack-a2bf8e71d8c18879e499335762dd95119d93d9f1.idx"), PathBuf::from("pack-c0438c19fb16422b6bbcce24387b3264416d485b.idx"), ] ); assert_eq!(written.file.num_objects(), 139); assert_eq!(written.file.checksum(), written.outcome.multi_index_checksum); written.verify_integrity_with_referenced_packs()?; Ok(()) } /// Like [`from_paths`], but sources its input index from the hash-parameterized fixture so the /// writer runs under both SHA-1 and SHA-256. The fixture's gc leaves one pack, hence one index. #[test] fn from_a_hash_parameterized_pack() -> crate::Result { let object_hash = crate::object_hash(); let pack_dir = crate::scripted_fixture_read_only("make_pack_gen_repo_multi_index.sh")?.join(".git/objects/pack"); let written = write_multi_index_from_pack_dir(&pack_dir, object_hash)?; assert_eq!(written.input_indices.len(), 1, "the aggressive gc leaves a single pack"); assert_eq!( written.file.object_hash(), object_hash, "the writer records the requested object hash" ); assert_eq!(written.file.num_indices(), 1); assert_eq!(written.file.num_objects(), 868); assert_eq!(written.file.checksum(), written.outcome.multi_index_checksum); written.verify_integrity_with_referenced_packs()?; Ok(()) } struct WrittenMultiIndex { file: gix_pack::multi_index::File, dir: gix_testtools::tempfile::TempDir, input_indices: Vec, outcome: gix_pack::multi_index::write::Outcome, } impl WrittenMultiIndex { fn verify_integrity_with_referenced_packs(&self) -> crate::Result { // Place the referenced pack and index next to the multi-index so integrity can resolve them. for ro_index in &self.input_indices { std::fs::copy( ro_index, self.dir .path() .join(ro_index.file_name().expect("index paths have file names")), )?; let ro_pack = ro_index.with_extension("pack"); std::fs::copy( &ro_pack, self.dir .path() .join(ro_pack.file_name().expect("pack paths have file names")), )?; } assert_eq!( self.file .verify_integrity(&mut progress::Discard, &AtomicBool::new(false), Default::default())? .actual_index_checksum, self.outcome.multi_index_checksum, "full integrity verification returns the written multi-index checksum" ); assert_eq!( self.file .verify_integrity_fast(&mut progress::Discard, &AtomicBool::new(false))?, self.file.checksum(), "fast integrity verification returns the checksum read from the file" ); Ok(()) } } fn write_multi_index_from_pack_dir(pack_dir: &Path, object_hash: gix_hash::Kind) -> crate::Result { let input_indices = std::fs::read_dir(pack_dir)? .filter_map(|r| { let idx_path = r.ok()?.path(); (idx_path.extension()? == "idx").then_some(idx_path) }) .collect::>(); let dir = gix_testtools::tempfile::TempDir::new()?; let output_path = dir.path().join("multi-pack-index"); let mut out = std::fs::OpenOptions::new() .write(true) .create_new(true) .open(&output_path)?; let outcome = gix_pack::multi_index::write_from_index_paths( input_indices.clone(), &mut out, &mut progress::Discard, &AtomicBool::new(false), gix_pack::multi_index::write::Options { object_hash }, )?; let file = gix_pack::multi_index::File::at(output_path, None)?; Ok(WrittenMultiIndex { file, dir, input_indices, outcome, }) }