use git_commitgraph::{graph::Position as GraphPosition, Graph}; use git_object::{borrowed, owned}; use std::{ collections::{HashMap, HashSet}, convert::{TryFrom, TryInto}, hash::BuildHasher, io::{BufRead, Cursor}, path::{Path, PathBuf}, process::Command, }; type Result = std::result::Result<(), Box>; mod access; pub fn check_common(cg: &Graph, expected: &HashMap) { assert_eq!( usize::try_from(cg.num_commits()).expect("an architecture able to hold 32 bits of integer"), expected.len() ); for ref_info in expected.values() { assert_eq!(cg.id_at(ref_info.pos()), ref_info.id(), "id_at({})", ref_info.pos()); assert_eq!( cg.lookup(ref_info.id()), Some(ref_info.pos()), "lookup({})", ref_info.id() ); let expected_parents: Vec<_> = ref_info .parent_ids() .into_iter() .map(|id| { expected .values() .find(|item| item.id() == id) .expect("find RefInfo by id") }) .collect(); let commit = cg.commit_at(ref_info.pos()); assert_eq!(commit.id(), ref_info.id()); assert_eq!(commit.root_tree_id(), ref_info.root_tree_id()); assert_eq!( commit.parent1().expect("failed to access commit's parent1"), expected_parents.iter().map(|x| x.pos()).next() ); assert_eq!( commit .iter_parents() .collect::, _>>() .expect("failed to access commit's parents"), expected_parents.iter().map(|x| x.pos()).collect::>() ); } assert_eq!( cg.iter_ids().collect::>(), expected.values().map(|x| x.id()).collect::>() ); } pub fn create_repo(script_path: &str) -> tempfile::TempDir { let dir = tempfile::tempdir().expect("failed to create temp dir"); let status = Command::new("bash") .arg(fixture_path(script_path)) .arg(dir.path()) .env_remove("GIT_DIR") .status() .expect("failed to run repo script"); assert!(status.success(), "repo script failed"); dir } pub fn fixture_path(path: &str) -> PathBuf { PathBuf::from("tests").join("fixtures").join(path) } pub fn hex_to_id(hex: &[u8]) -> owned::Id { owned::Id::from_40_bytes_in_hex(hex).expect("40 bytes hex") } pub struct RefInfo { id: owned::Id, parent_ids: Vec, pos: GraphPosition, root_tree_id: owned::Id, } impl RefInfo { pub fn id(&self) -> borrowed::Id { self.id.to_borrowed() } pub fn pos(&self) -> GraphPosition { self.pos } pub fn parent_ids(&self) -> impl IntoIterator { self.parent_ids.iter().map(|x| x.to_borrowed()) } pub fn root_tree_id(&self) -> borrowed::Id { self.root_tree_id.to_borrowed() } } pub fn inspect_refs(repo_dir: impl AsRef, refs: &[&'static str]) -> HashMap { let output = Command::new("git") .arg("-C") .arg(repo_dir.as_ref()) .arg("show") .arg("--no-patch") .arg("--pretty=format:%S %H %T %P") .args(refs) .arg("--") .env_remove("GIT_DIR") .output() .expect("failed to execute `git show`"); // Output format: let mut infos: Vec<_> = Cursor::new(output.stdout) .lines() .map(|x| x.expect("failed to read `git show` output")) .map(|x| { let parts = x.trim_end().split(' ').collect::>(); ( parts[0].to_string(), owned::Id::from_40_bytes_in_hex(parts[1].as_bytes()).expect("40 bytes hex"), owned::Id::from_40_bytes_in_hex(parts[2].as_bytes()).expect("40 bytes hex"), parts[3..] .iter() .map(|x| owned::Id::from_40_bytes_in_hex(x.as_bytes()).expect("40 bytes hex")) .collect(), ) }) .collect(); infos.sort_by_key(|x| x.1); let get_pos = |id: borrowed::Id| -> GraphPosition { let pos: u32 = infos .binary_search_by_key(&id, |x| x.1.to_borrowed()) .expect("sorted_ids to contain id") .try_into() .expect("graph position to fit in u32"); GraphPosition(pos) }; infos .iter() .cloned() .map(|(name, id, root_tree_id, parent_ids)| { ( name, RefInfo { id, parent_ids, root_tree_id, pos: get_pos(id.to_borrowed()), }, ) }) .collect() }