Always generate test repos on demand instead. This saves some repo space and keeps us from having to worry about fixture scripts being out of sync with the generated fixtures. It also lets us use git to inspect repos instead of hardcoding expected commit data. On the flip side, tests take ~400ms instead of ~20ms.
Showing the changes to git-commitgraph/tests/commitgraph.rs (1 of 41 files). Show all
Unified · Split
| @@ -1,8 +1,71 @@ | |||
| 1 | use git_object::owned; | 1 | use git_commitgraph::{Graph, GraphPosition}; |
| 2 | use std::path::PathBuf; | 2 | use git_object::{borrowed, owned}; |
| 3 | use std::collections::{HashMap, HashSet}; | ||
| 4 | use std::convert::{TryFrom, TryInto}; | ||
| 5 | use std::hash::BuildHasher; | ||
| 6 | use std::io::{BufRead, Cursor}; | ||
| 7 | use std::path::{Path, PathBuf}; | ||
| 8 | use std::process::Command; | ||
| 3 | 9 | ||
| 4 | mod access; | 10 | mod access; |
| 5 | 11 | ||
| 12 | pub fn check_common(cg: &Graph, expected: &HashMap<String, RefInfo, impl BuildHasher>) { | ||
| 13 | assert_eq!( | ||
| 14 | usize::try_from(cg.num_commits()).expect("an architecture able to hold 32 bits of integer"), | ||
| 15 | expected.len() | ||
| 16 | ); | ||
| 17 | for ref_info in expected.values() { | ||
| 18 | assert_eq!(cg.id_at(ref_info.pos()), ref_info.id(), "id_at({})", ref_info.pos()); | ||
| 19 | assert_eq!( | ||
| 20 | cg.lookup(ref_info.id()), | ||
| 21 | Some(ref_info.pos()), | ||
| 22 | "lookup({})", | ||
| 23 | ref_info.id() | ||
| 24 | ); | ||
| 25 | |||
| 26 | let expected_parents: Vec<_> = ref_info | ||
| 27 | .parent_ids() | ||
| 28 | .into_iter() | ||
| 29 | .map(|id| { | ||
| 30 | expected | ||
| 31 | .values() | ||
| 32 | .find(|item| item.id() == id) | ||
| 33 | .expect("find RefInfo by id") | ||
| 34 | }) | ||
| 35 | .collect(); | ||
| 36 | let commit = cg.commit_at(ref_info.pos()); | ||
| 37 | assert_eq!(commit.id(), ref_info.id()); | ||
| 38 | assert_eq!(commit.root_tree_id(), ref_info.root_tree_id()); | ||
| 39 | assert_eq!( | ||
| 40 | commit.parent1().expect("failed to access commit's parent1"), | ||
| 41 | expected_parents.iter().map(|x| x.pos()).next() | ||
| 42 | ); | ||
| 43 | assert_eq!( | ||
| 44 | commit | ||
| 45 | .iter_parents() | ||
| 46 | .collect::<Result<Vec<_>, _>>() | ||
| 47 | .expect("failed to access commit's parents"), | ||
| 48 | expected_parents.iter().map(|x| x.pos()).collect::<Vec<_>>() | ||
| 49 | ); | ||
| 50 | } | ||
| 51 | |||
| 52 | assert_eq!( | ||
| 53 | cg.iter_ids().collect::<HashSet<_>>(), | ||
| 54 | expected.values().map(|x| x.id()).collect::<HashSet<_>>() | ||
| 55 | ); | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn create_repo(script_path: &str) -> tempfile::TempDir { | ||
| 59 | let dir = tempfile::tempdir().expect("failed to create temp dir"); | ||
| 60 | let status = Command::new(fixture_path(script_path)) | ||
| 61 | .arg(dir.path()) | ||
| 62 | .env_remove("GIT_DIR") | ||
| 63 | .status() | ||
| 64 | .expect("failed to run repo script"); | ||
| 65 | assert!(status.success(), "repo script failed"); | ||
| 66 | dir | ||
| 67 | } | ||
| 68 | |||
| 6 | pub fn fixture_path(path: &str) -> PathBuf { | 69 | pub fn fixture_path(path: &str) -> PathBuf { |
| 7 | PathBuf::from("tests").join("fixtures").join(path) | 70 | PathBuf::from("tests").join("fixtures").join(path) |
| 8 | } | 71 | } |
| @@ -10,3 +73,84 @@ pub fn fixture_path(path: &str) -> PathBuf { · 1 unmodified line | |||
| 10 | pub fn hex_to_id(hex: &[u8]) -> owned::Id { | 73 | pub fn hex_to_id(hex: &[u8]) -> owned::Id { |
| 11 | owned::Id::from_40_bytes_in_hex(hex).expect("40 bytes hex") | 74 | owned::Id::from_40_bytes_in_hex(hex).expect("40 bytes hex") |
| 12 | } | 75 | } |
| 76 | |||
| 77 | pub struct RefInfo { | ||
| 78 | id: owned::Id, | ||
| 79 | parent_ids: Vec<owned::Id>, | ||
| 80 | pos: GraphPosition, | ||
| 81 | root_tree_id: owned::Id, | ||
| 82 | } | ||
| 83 | |||
| 84 | impl RefInfo { | ||
| 85 | pub fn id(&self) -> borrowed::Id { | ||
| 86 | self.id.to_borrowed() | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn pos(&self) -> GraphPosition { | ||
| 90 | self.pos | ||
| 91 | } | ||
| 92 | |||
| 93 | pub fn parent_ids(&self) -> impl IntoIterator<Item = borrowed::Id> { | ||
| 94 | self.parent_ids.iter().map(|x| x.to_borrowed()) | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn root_tree_id(&self) -> borrowed::Id { | ||
| 98 | self.root_tree_id.to_borrowed() | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn inspect_refs(repo_dir: impl AsRef<Path>, refs: &[&'static str]) -> HashMap<String, RefInfo> { | ||
| 103 | let output = Command::new("git") | ||
| 104 | .arg("-C") | ||
| 105 | .arg(repo_dir.as_ref()) | ||
| 106 | .arg("show") | ||
| 107 | .arg("--no-patch") | ||
| 108 | .arg("--pretty=format:%S %H %T %P") | ||
| 109 | .args(refs) | ||
| 110 | .arg("--") | ||
| 111 | .env_remove("GIT_DIR") | ||
| 112 | .output() | ||
| 113 | .expect("failed to execute `git show`"); | ||
| 114 | // Output format: <refname> <id> <tree_id> <parent_ids> | ||
| 115 | let mut infos: Vec<_> = Cursor::new(output.stdout) | ||
| 116 | .lines() | ||
| 117 | .map(|x| x.expect("failed to read `git show` output")) | ||
| 118 | .map(|x| { | ||
| 119 | let parts = x.trim_end().split(' ').collect::<Vec<_>>(); | ||
| 120 | ( | ||
| 121 | parts[0].to_string(), | ||
| 122 | owned::Id::from_40_bytes_in_hex(parts[1].as_bytes()).expect("40 bytes hex"), | ||
| 123 | owned::Id::from_40_bytes_in_hex(parts[2].as_bytes()).expect("40 bytes hex"), | ||
| 124 | parts[3..] | ||
| 125 | .iter() | ||
| 126 | .map(|x| owned::Id::from_40_bytes_in_hex(x.as_bytes()).expect("40 bytes hex")) | ||
| 127 | .collect(), | ||
| 128 | ) | ||
| 129 | }) | ||
| 130 | .collect(); | ||
| 131 | infos.sort_by_key(|x| x.1); | ||
| 132 | |||
| 133 | let get_pos = |id: borrowed::Id| -> GraphPosition { | ||
| 134 | let pos = infos | ||
| 135 | .binary_search_by_key(&id, |x| x.1.to_borrowed()) | ||
| 136 | .expect("sorted_ids to contain id"); | ||
| 137 | let pos: u32 = pos.try_into().expect("graph position to fit in u32"); | ||
| 138 | GraphPosition(pos) | ||
| 139 | }; | ||
| 140 | |||
| 141 | infos | ||
| 142 | .iter() | ||
| 143 | .cloned() | ||
| 144 | .map(|(name, id, root_tree_id, parent_ids)| { | ||
| 145 | ( | ||
| 146 | name, | ||
| 147 | RefInfo { | ||
| 148 | id, | ||
| 149 | parent_ids, | ||
| 150 | root_tree_id, | ||
| 151 | pos: get_pos(id.to_borrowed()), | ||
| 152 | }, | ||
| 153 | ) | ||
| 154 | }) | ||
| 155 | .collect() | ||
| 156 | } | ||