use itertools::Itertools;
use jujutsu_lib::repo::Repo;
use jujutsu_lib::revset::revset_for_commits;
use jujutsu_lib::revset_graph_iterator::RevsetGraphEdge;
use test_case::test_case;
use testutils::{CommitGraphBuilder, TestRepo};
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_linearized(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_a]);
let commit_d = graph_builder.commit_with_parents(&[&commit_b, &commit_c]);
let repo = tx.commit();
let pos_root = repo
.index()
.commit_id_to_pos(repo.store().root_commit_id())
.unwrap();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let revset = revset_for_commits(&repo, &[&commit_a, &commit_d]);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 2);
assert_eq!(commits[0].0.commit_id(), *commit_d.id());
assert_eq!(commits[1].0.commit_id(), *commit_a.id());
assert_eq!(commits[0].1, vec![RevsetGraphEdge::indirect(pos_a)]);
assert_eq!(commits[1].1, vec![RevsetGraphEdge::missing(pos_root)]);
}
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_virtual_octopus(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.initial_commit();
let commit_c = graph_builder.initial_commit();
let commit_d = graph_builder.commit_with_parents(&[&commit_a, &commit_b]);
let commit_e = graph_builder.commit_with_parents(&[&commit_b, &commit_c]);
let commit_f = graph_builder.commit_with_parents(&[&commit_d, &commit_e]);
let repo = tx.commit();
let pos_root = repo
.index()
.commit_id_to_pos(repo.store().root_commit_id())
.unwrap();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let pos_b = repo.index().commit_id_to_pos(commit_b.id()).unwrap();
let pos_c = repo.index().commit_id_to_pos(commit_c.id()).unwrap();
let revset = revset_for_commits(&repo, &[&commit_a, &commit_b, &commit_c, &commit_f]);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 4);
assert_eq!(commits[0].0.commit_id(), *commit_f.id());
assert_eq!(commits[1].0.commit_id(), *commit_c.id());
assert_eq!(commits[2].0.commit_id(), *commit_b.id());
assert_eq!(commits[3].0.commit_id(), *commit_a.id());
assert_eq!(
commits[0].1,
vec![
RevsetGraphEdge::indirect(pos_c),
RevsetGraphEdge::indirect(pos_b),
RevsetGraphEdge::indirect(pos_a),
]
);
assert_eq!(commits[1].1, vec![RevsetGraphEdge::missing(pos_root)]);
assert_eq!(commits[2].1, vec![RevsetGraphEdge::missing(pos_root)]);
assert_eq!(commits[3].1, vec![RevsetGraphEdge::missing(pos_root)]);
}
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_simple_fork(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_b]);
let commit_d = graph_builder.commit_with_parents(&[&commit_b]);
let commit_e = graph_builder.commit_with_parents(&[&commit_d]);
let repo = tx.commit();
let pos_root = repo
.index()
.commit_id_to_pos(repo.store().root_commit_id())
.unwrap();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let revset = revset_for_commits(&repo, &[&commit_a, &commit_c, &commit_e]);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 3);
assert_eq!(commits[0].0.commit_id(), *commit_e.id());
assert_eq!(commits[1].0.commit_id(), *commit_c.id());
assert_eq!(commits[2].0.commit_id(), *commit_a.id());
assert_eq!(commits[0].1, vec![RevsetGraphEdge::indirect(pos_a)]);
assert_eq!(commits[1].1, vec![RevsetGraphEdge::indirect(pos_a)]);
assert_eq!(commits[2].1, vec![RevsetGraphEdge::missing(pos_root)]);
}
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_multiple_missing(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.initial_commit();
let commit_c = graph_builder.initial_commit();
let commit_d = graph_builder.commit_with_parents(&[&commit_a, &commit_b]);
let commit_e = graph_builder.commit_with_parents(&[&commit_b, &commit_c]);
let commit_f = graph_builder.commit_with_parents(&[&commit_d, &commit_e]);
let repo = tx.commit();
let pos_root = repo
.index()
.commit_id_to_pos(repo.store().root_commit_id())
.unwrap();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let pos_b = repo.index().commit_id_to_pos(commit_b.id()).unwrap();
let pos_c = repo.index().commit_id_to_pos(commit_c.id()).unwrap();
let revset = revset_for_commits(&repo, &[&commit_b, &commit_f]);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 2);
assert_eq!(commits[0].0.commit_id(), *commit_f.id());
assert_eq!(commits[1].0.commit_id(), *commit_b.id());
assert_eq!(
commits[0].1,
vec![
RevsetGraphEdge::missing(pos_c),
RevsetGraphEdge::indirect(pos_b),
RevsetGraphEdge::missing(pos_a),
]
);
assert_eq!(commits[1].1, vec![RevsetGraphEdge::missing(pos_root)]);
}
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_edge_to_ancestor(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.initial_commit();
let commit_c = graph_builder.commit_with_parents(&[&commit_a]);
let commit_d = graph_builder.commit_with_parents(&[&commit_b, &commit_c]);
let commit_e = graph_builder.commit_with_parents(&[&commit_c]);
let commit_f = graph_builder.commit_with_parents(&[&commit_d, &commit_e]);
let repo = tx.commit();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let pos_b = repo.index().commit_id_to_pos(commit_b.id()).unwrap();
let pos_c = repo.index().commit_id_to_pos(commit_c.id()).unwrap();
let pos_d = repo.index().commit_id_to_pos(commit_d.id()).unwrap();
let revset = revset_for_commits(&repo, &[&commit_c, &commit_d, &commit_f]);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 3);
assert_eq!(commits[0].0.commit_id(), *commit_f.id());
assert_eq!(commits[1].0.commit_id(), *commit_d.id());
assert_eq!(commits[2].0.commit_id(), *commit_c.id());
if skip_transitive_edges {
assert_eq!(commits[0].1, vec![RevsetGraphEdge::direct(pos_d)]);
} else {
assert_eq!(
commits[0].1,
vec![
RevsetGraphEdge::direct(pos_d),
RevsetGraphEdge::indirect(pos_c),
]
);
}
assert_eq!(
commits[1].1,
vec![
RevsetGraphEdge::direct(pos_c),
RevsetGraphEdge::missing(pos_b),
]
);
assert_eq!(commits[2].1, vec![RevsetGraphEdge::missing(pos_a)]);
}
#[test_case(false ; "keep transitive edges")]
#[test_case(true ; "skip transitive edges")]
fn test_graph_iterator_edge_escapes_from_(skip_transitive_edges: bool) {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_a]);
let commit_d = graph_builder.commit_with_parents(&[&commit_b]);
let commit_e = graph_builder.commit_with_parents(&[&commit_d]);
let commit_f = graph_builder.commit_with_parents(&[&commit_d, &commit_c]);
let commit_g = graph_builder.commit_with_parents(&[&commit_b]);
let commit_h = graph_builder.commit_with_parents(&[&commit_f]);
let commit_i = graph_builder.commit_with_parents(&[&commit_e, &commit_h]);
let commit_j = graph_builder.commit_with_parents(&[&commit_g, &commit_i]);
let repo = tx.commit();
let pos_root = repo
.index()
.commit_id_to_pos(repo.store().root_commit_id())
.unwrap();
let pos_a = repo.index().commit_id_to_pos(commit_a.id()).unwrap();
let pos_d = repo.index().commit_id_to_pos(commit_d.id()).unwrap();
let pos_g = repo.index().commit_id_to_pos(commit_g.id()).unwrap();
let pos_h = repo.index().commit_id_to_pos(commit_h.id()).unwrap();
let revset = revset_for_commits(
&repo,
&[&commit_a, &commit_d, &commit_g, &commit_h, &commit_j],
);
let commits = revset
.iter()
.graph()
.set_skip_transitive_edges(skip_transitive_edges)
.collect_vec();
assert_eq!(commits.len(), 5);
assert_eq!(commits[0].0.commit_id(), *commit_j.id());
assert_eq!(commits[1].0.commit_id(), *commit_h.id());
assert_eq!(commits[2].0.commit_id(), *commit_g.id());
assert_eq!(commits[3].0.commit_id(), *commit_d.id());
assert_eq!(commits[4].0.commit_id(), *commit_a.id());
if skip_transitive_edges {
assert_eq!(
commits[0].1,
vec![
RevsetGraphEdge::indirect(pos_h),
RevsetGraphEdge::direct(pos_g)
]
);
assert_eq!(commits[1].1, vec![RevsetGraphEdge::indirect(pos_d)]);
} else {
assert_eq!(
commits[0].1,
vec![
RevsetGraphEdge::indirect(pos_h),
RevsetGraphEdge::direct(pos_g),
RevsetGraphEdge::indirect(pos_d),
]
);
assert_eq!(
commits[1].1,
vec![
RevsetGraphEdge::indirect(pos_d),
RevsetGraphEdge::indirect(pos_a)
]
);
}
assert_eq!(commits[2].1, vec![RevsetGraphEdge::indirect(pos_a)]);
assert_eq!(commits[3].1, vec![RevsetGraphEdge::indirect(pos_a)]);
assert_eq!(commits[4].1, vec![RevsetGraphEdge::missing(pos_root)]);
}
#[test]
fn test_reverse_graph_iterator() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init(true);
let repo = &test_repo.repo;
let mut tx = repo.start_transaction(&settings, "test");
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_b]);
let commit_d = graph_builder.commit_with_parents(&[&commit_c]);
let commit_e = graph_builder.commit_with_parents(&[&commit_c]);
let commit_f = graph_builder.commit_with_parents(&[&commit_d, &commit_e]);
let repo = tx.commit();
let pos_c = repo.index().commit_id_to_pos(commit_c.id()).unwrap();
let pos_d = repo.index().commit_id_to_pos(commit_d.id()).unwrap();
let pos_e = repo.index().commit_id_to_pos(commit_e.id()).unwrap();
let pos_f = repo.index().commit_id_to_pos(commit_f.id()).unwrap();
let revset = revset_for_commits(
&repo,
&[&commit_a, &commit_c, &commit_d, &commit_e, &commit_f],
);
let commits = revset.iter().graph().reversed().collect_vec();
assert_eq!(commits.len(), 5);
assert_eq!(commits[0].0.commit_id(), *commit_a.id());
assert_eq!(commits[1].0.commit_id(), *commit_c.id());
assert_eq!(commits[2].0.commit_id(), *commit_d.id());
assert_eq!(commits[3].0.commit_id(), *commit_e.id());
assert_eq!(commits[4].0.commit_id(), *commit_f.id());
assert_eq!(commits[0].1, vec![RevsetGraphEdge::indirect(pos_c)]);
assert_eq!(
commits[1].1,
vec![
RevsetGraphEdge::direct(pos_e),
RevsetGraphEdge::direct(pos_d),
]
);
assert_eq!(commits[2].1, vec![RevsetGraphEdge::direct(pos_f)]);
assert_eq!(commits[3].1, vec![RevsetGraphEdge::direct(pos_f)]);
assert_eq!(commits[4].1, vec![]);
}