mod ancestor { use gix_hash::{oid, ObjectId}; use gix_odb::pack::FindExt; use gix_traverse::commit; use crate::hex_to_id; struct TraversalAssertion<'a> { init_script: &'a str, repo_name: &'a str, tips: &'a [&'a str], expected: &'a [&'a str], mode: commit::Parents, sorting: commit::Sorting, } impl<'a> TraversalAssertion<'a> { fn new(init_script: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { Self::new_at(init_script, "", tips, expected) } fn new_at(init_script: &'a str, repo_name: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { TraversalAssertion { init_script, repo_name, tips, expected, mode: Default::default(), sorting: Default::default(), } } fn with_parents(&mut self, mode: commit::Parents) -> &mut Self { self.mode = mode; self } fn with_sorting(&mut self, sorting: commit::Sorting) -> &mut Self { self.sorting = sorting; self } } impl TraversalAssertion<'_> { fn setup(&self) -> crate::Result<(gix_odb::Handle, Vec<ObjectId>, Vec<ObjectId>)> { let dir = gix_testtools::scripted_fixture_read_only_standalone(self.init_script)?; let store = gix_odb::at(dir.join(self.repo_name).join(".git").join("objects"))?; let tips: Vec<_> = self.tips.iter().copied().map(hex_to_id).collect(); let expected: Vec<ObjectId> = tips .clone() .into_iter() .chain(self.expected.iter().map(|hex_id| hex_to_id(hex_id))) .collect(); Ok((store, tips, expected)) } fn setup_commitgraph(&self, store: &gix_odb::Store, use_graph: bool) -> Option<gix_commitgraph::Graph> { use_graph .then(|| gix_commitgraph::at(store.path().join("info"))) .transpose() .expect("graph can be loaded if it exists") } fn check_with_predicate(&mut self, predicate: impl FnMut(&oid) -> bool + Clone) -> crate::Result<()> { let (store, tips, expected) = self.setup()?; for use_commitgraph in [false, true] { let oids = commit::Ancestors::filtered( tips.clone(), commit::ancestors::State::default(), |oid, buf| store.find_commit_iter(oid, buf).map(|t| t.0), predicate.clone(), ) .sorting(self.sorting)? .parents(self.mode) .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) .map(|res| res.map(|info| info.id)) .collect::<Result<Vec<_>, _>>()?; assert_eq!(oids, expected); } Ok(()) } fn check(&self) -> crate::Result { let (store, tips, expected) = self.setup()?; for use_commitgraph in [false, true] { let oids = commit::Ancestors::new(tips.clone(), commit::ancestors::State::default(), |oid, buf| { store.find_commit_iter(oid, buf).map(|t| t.0) }) .sorting(self.sorting)? .parents(self.mode) .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) .map(|res| res.map(|info| info.id)) .collect::<Result<Vec<_>, _>>()?; assert_eq!(oids, expected); } Ok(()) } } mod different_date_intermixed { use crate::commit::ancestor::TraversalAssertion; use gix_traverse::commit::Sorting; #[test] fn head_breadth_first() -> crate::Result { TraversalAssertion::new_at( "make_repos.sh", "intermixed", &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ // This is very different from what git does as it keeps commits together, // whereas we spread them out breadth-first. &[ "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ ], ) .check() } #[test] fn head_date_order() -> crate::Result { TraversalAssertion::new_at( "make_repos.sh", "intermixed", &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ // This is exactly what git shows. &[ "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ ], ) .with_sorting(Sorting::ByCommitTimeNewestFirst) .check() } } mod different_date { use crate::commit::ancestor::TraversalAssertion; use gix_traverse::commit::Sorting; #[test] fn head_breadth_first() -> crate::Result { TraversalAssertion::new_at( "make_repos.sh", "simple", &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ // This is very different from what git does as it keeps commits together, // whereas we spread them out breadth-first. &[ "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ ], ) .check() } #[test] fn head_date_order() -> crate::Result { TraversalAssertion::new_at( "make_repos.sh", "simple", &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ // This is exactly what git shows. &[ "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ ], ) .with_sorting(Sorting::ByCommitTimeNewestFirst) .check() } } /// Same dates are somewhat special as they show how sorting-details on priority queues affects ordering mod same_date { use crate::commit::ancestor::TraversalAssertion; use crate::hex_to_id; use gix_traverse::commit::{Parents, Sorting}; #[test] fn c4_breadth_first() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["9556057aee5abb06912922e9f26c46386a816822"], /* c4 */ &[ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .check() } #[test] fn head_breadth_first() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ // We always take the first parent first, then the second, and so on. // Deviation: git for some reason displays b1c2 *before* c5, but I think it's better // to have a strict parent order. &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .check() } #[test] fn head_date_order() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .with_sorting(Sorting::ByCommitTimeNewestFirst) .check() } #[test] fn head_first_parent_only_breadth_first() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .with_parents(Parents::First) .check() } #[test] fn head_c4_breadth_first() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &[ "01ec18a3ebf2855708ad3c9d244306bc1fae3e9b", /* m1b1 */ "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ ], &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .check() } #[test] fn filtered_commit_does_not_block_ancestors_reachable_from_another_commit() -> crate::Result { // I don't see a use case for the predicate returning false for a commit but return true for // at least one of its ancestors, so this test is kind of dubious. But we do want // `Ancestors` to not eagerly blacklist all of a commit's ancestors when blacklisting that // one commit, and this test happens to check that. TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .check_with_predicate(|id| id != hex_to_id("9152eeee2328073cf23dcf8e90c949170b711659")) } #[test] fn predicate_only_called_once_even_if_fork_point() -> crate::Result { // The `self.seen` check should come before the `self.predicate` check, as we don't know how // expensive calling `self.predicate` may be. let mut seen = false; TraversalAssertion::new( "make_traversal_repo_for_commits_same_date.sh", &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ &[ "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ ], ) .check_with_predicate(move |id| { if id == hex_to_id("9556057aee5abb06912922e9f26c46386a816822") { assert!(!seen); seen = true; false } else { true } }) } } /// Some dates adjusted to be a year apart, but still 'c1' and 'c2' with the same date. mod adjusted_dates { use crate::commit::ancestor::TraversalAssertion; use crate::hex_to_id; use gix_odb::FindExt; use gix_traverse::commit::{ancestors, Ancestors, Parents, Sorting}; #[test] fn head_breadth_first() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_with_dates.sh", &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ // Here `git` also shows `b1c1` first, making topo-order similar to date order for some reason, // even though c2 *is* the first parent. &[ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .check() } #[test] fn head_date_order() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_with_dates.sh", &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ &[ "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .with_sorting(Sorting::ByCommitTimeNewestFirst) .check() } #[test] fn head_date_order_with_cutoff() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_with_dates.sh", &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ &["bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac"], /* b1c1 */ ) .with_sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { time_in_seconds_since_epoch: 978393600, // =2001-01-02 00:00:00 +0000 }) .check() } #[test] fn date_order_with_cutoff_is_applied_to_starting_position() -> crate::Result { let dir = gix_testtools::scripted_fixture_read_only_standalone("make_traversal_repo_for_commits_with_dates.sh")?; let store = gix_odb::at(dir.join(".git").join("objects"))?; let iter = Ancestors::new( Some(hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7" /* c2 */)), ancestors::State::default(), move |oid, buf| store.find_commit_iter(oid, buf), ) .sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { time_in_seconds_since_epoch: 978393600, // =2001-01-02 00:00:00 +0000 })?; assert_eq!( iter.count(), 0, "initial tips that don't pass cutoff value are not returned either" ); Ok(()) } #[test] fn head_date_order_first_parent_only() -> crate::Result { TraversalAssertion::new( "make_traversal_repo_for_commits_with_dates.sh", &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ &[ "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ ], ) .with_sorting(Sorting::ByCommitTimeNewestFirst) .with_parents(Parents::First) .check() } }}
mod ancestor {
use gix_hash::{oid, ObjectId};
use gix_odb::pack::FindExt;
use gix_traverse::commit;
use crate::hex_to_id;
struct TraversalAssertion<'a> {
init_script: &'a str,
repo_name: &'a str,
tips: &'a [&'a str],
expected: &'a [&'a str],
mode: commit::Parents,
sorting: commit::Sorting,
}
impl<'a> TraversalAssertion<'a> {
fn new(init_script: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self {
Self::new_at(init_script, "", tips, expected)
}
fn new_at(init_script: &'a str, repo_name: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self {
TraversalAssertion {
init_script,
repo_name,
tips,
expected,
mode: Default::default(),
sorting: Default::default(),
}
}
fn with_parents(&mut self, mode: commit::Parents) -> &mut Self {
self.mode = mode;
self
}
fn with_sorting(&mut self, sorting: commit::Sorting) -> &mut Self {
self.sorting = sorting;
self
}
}
impl TraversalAssertion<'_> {
fn setup(&self) -> crate::Result<(gix_odb::Handle, Vec<ObjectId>, Vec<ObjectId>)> {
let dir = gix_testtools::scripted_fixture_read_only_standalone(self.init_script)?;
let store = gix_odb::at(dir.join(self.repo_name).join(".git").join("objects"))?;
let tips: Vec<_> = self.tips.iter().copied().map(hex_to_id).collect();
let expected: Vec<ObjectId> = tips
.clone()
.into_iter()
.chain(self.expected.iter().map(|hex_id| hex_to_id(hex_id)))
.collect();
Ok((store, tips, expected))
}
fn setup_commitgraph(&self, store: &gix_odb::Store, use_graph: bool) -> Option<gix_commitgraph::Graph> {
use_graph
.then(|| gix_commitgraph::at(store.path().join("info")))
.transpose()
.expect("graph can be loaded if it exists")
}
fn check_with_predicate(&mut self, predicate: impl FnMut(&oid) -> bool + Clone) -> crate::Result<()> {
let (store, tips, expected) = self.setup()?;
for use_commitgraph in [false, true] {
let oids = commit::Ancestors::filtered(
tips.clone(),
commit::ancestors::State::default(),
|oid, buf| store.find_commit_iter(oid, buf).map(|t| t.0),
predicate.clone(),
)
.sorting(self.sorting)?
.parents(self.mode)
.commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph))
.map(|res| res.map(|info| info.id))
.collect::<Result<Vec<_>, _>>()?;
assert_eq!(oids, expected);
}
Ok(())
}
fn check(&self) -> crate::Result {
let (store, tips, expected) = self.setup()?;
for use_commitgraph in [false, true] {
let oids = commit::Ancestors::new(tips.clone(), commit::ancestors::State::default(), |oid, buf| {
store.find_commit_iter(oid, buf).map(|t| t.0)
})
.sorting(self.sorting)?
.parents(self.mode)
.commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph))
.map(|res| res.map(|info| info.id))
.collect::<Result<Vec<_>, _>>()?;
assert_eq!(oids, expected);
}
Ok(())
}
}
mod different_date_intermixed {
use crate::commit::ancestor::TraversalAssertion;
use gix_traverse::commit::Sorting;
#[test]
fn head_breadth_first() -> crate::Result {
TraversalAssertion::new_at(
"make_repos.sh",
"intermixed",
&["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */
// This is very different from what git does as it keeps commits together,
// whereas we spread them out breadth-first.
&[
"2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */
"0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */
"a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */
"ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */
"77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */
"b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */
"65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */
],
)
.check()
}
#[test]
fn head_date_order() -> crate::Result {
TraversalAssertion::new_at(
"make_repos.sh",
"intermixed",
&["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */
// This is exactly what git shows.
&[
"2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */
"0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */
"a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */
"77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */
"b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */
"ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */
"65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */
],
)
.with_sorting(Sorting::ByCommitTimeNewestFirst)
.check()
}
}
mod different_date {
use crate::commit::ancestor::TraversalAssertion;
use gix_traverse::commit::Sorting;
#[test]
fn head_breadth_first() -> crate::Result {
TraversalAssertion::new_at(
"make_repos.sh",
"simple",
&["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */
// This is very different from what git does as it keeps commits together,
// whereas we spread them out breadth-first.
&[
"0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */
"66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */
"48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */
"8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */
"80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */
"cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */
"33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */
"ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */
"65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */
],
)
.check()
}
#[test]
fn head_date_order() -> crate::Result {
TraversalAssertion::new_at(
"make_repos.sh",
"simple",
&["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */
// This is exactly what git shows.
&[
"0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */
"66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */
"80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */
"48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */
"cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */
"8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */
"33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */
"ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */
"65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */
],
)
.with_sorting(Sorting::ByCommitTimeNewestFirst)
.check()
}
}
/// Same dates are somewhat special as they show how sorting-details on priority queues affects ordering
mod same_date {
use crate::commit::ancestor::TraversalAssertion;
use crate::hex_to_id;
use gix_traverse::commit::{Parents, Sorting};
#[test]
fn c4_breadth_first() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["9556057aee5abb06912922e9f26c46386a816822"], /* c4 */
&[
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.check()
}
#[test]
fn head_breadth_first() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */
// We always take the first parent first, then the second, and so on.
// Deviation: git for some reason displays b1c2 *before* c5, but I think it's better
// to have a strict parent order.
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */
"9556057aee5abb06912922e9f26c46386a816822", /* c4 */
"9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.check()
}
#[test]
fn head_date_order() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */
"9556057aee5abb06912922e9f26c46386a816822", /* c4 */
"9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.with_sorting(Sorting::ByCommitTimeNewestFirst)
.check()
}
#[test]
fn head_first_parent_only_breadth_first() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"9556057aee5abb06912922e9f26c46386a816822", /* c4 */
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.with_parents(Parents::First)
.check()
}
#[test]
fn head_c4_breadth_first() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&[
"01ec18a3ebf2855708ad3c9d244306bc1fae3e9b", /* m1b1 */
"9556057aee5abb06912922e9f26c46386a816822", /* c4 */
],
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.check()
}
#[test]
fn filtered_commit_does_not_block_ancestors_reachable_from_another_commit() -> crate::Result {
// I don't see a use case for the predicate returning false for a commit but return true for
// at least one of its ancestors, so this test is kind of dubious. But we do want
// `Ancestors` to not eagerly blacklist all of a commit's ancestors when blacklisting that
// one commit, and this test happens to check that.
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */
"9556057aee5abb06912922e9f26c46386a816822", /* c4 */
"17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.check_with_predicate(|id| id != hex_to_id("9152eeee2328073cf23dcf8e90c949170b711659"))
}
#[test]
fn predicate_only_called_once_even_if_fork_point() -> crate::Result {
// The `self.seen` check should come before the `self.predicate` check, as we don't know how
// expensive calling `self.predicate` may be.
let mut seen = false;
TraversalAssertion::new(
"make_traversal_repo_for_commits_same_date.sh",
&["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */
&[
"efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */
"ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */
"9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */
],
)
.check_with_predicate(move |id| {
if id == hex_to_id("9556057aee5abb06912922e9f26c46386a816822") {
assert!(!seen);
seen = true;
false
} else {
true
}
})
}
}
/// Some dates adjusted to be a year apart, but still 'c1' and 'c2' with the same date.
mod adjusted_dates {
use crate::commit::ancestor::TraversalAssertion;
use crate::hex_to_id;
use gix_odb::FindExt;
use gix_traverse::commit::{ancestors, Ancestors, Parents, Sorting};
#[test]
fn head_breadth_first() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_with_dates.sh",
&["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */
// Here `git` also shows `b1c1` first, making topo-order similar to date order for some reason,
// even though c2 *is* the first parent.
&[
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.check()
}
#[test]
fn head_date_order() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_with_dates.sh",
&["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */
&[
"bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.with_sorting(Sorting::ByCommitTimeNewestFirst)
.check()
}
#[test]
fn head_date_order_with_cutoff() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_with_dates.sh",
&["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */
&["bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac"], /* b1c1 */
)
.with_sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
time_in_seconds_since_epoch: 978393600, // =2001-01-02 00:00:00 +0000
})
.check()
}
#[test]
fn date_order_with_cutoff_is_applied_to_starting_position() -> crate::Result {
let dir =
gix_testtools::scripted_fixture_read_only_standalone("make_traversal_repo_for_commits_with_dates.sh")?;
let store = gix_odb::at(dir.join(".git").join("objects"))?;
let iter = Ancestors::new(
Some(hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7" /* c2 */)),
ancestors::State::default(),
move |oid, buf| store.find_commit_iter(oid, buf),
)
.sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
time_in_seconds_since_epoch: 978393600, // =2001-01-02 00:00:00 +0000
})?;
assert_eq!(
iter.count(),
0,
"initial tips that don't pass cutoff value are not returned either"
);
Ok(())
}
#[test]
fn head_date_order_first_parent_only() -> crate::Result {
TraversalAssertion::new(
"make_traversal_repo_for_commits_with_dates.sh",
&["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */
&[
"9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */
"134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */
],
)
.with_sorting(Sorting::ByCommitTimeNewestFirst)
.with_parents(Parents::First)
.check()
}
}
}