mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-27 14:57:14 +00:00
3c7c4e9f5c
The `testutils` module should ideally not be part of the library dependencies. Since they're used by the integration tests (and the CLI tests), we need to move them to a separate crate to achieve that.
443 lines
16 KiB
Rust
443 lines
16 KiB
Rust
// Copyright 2021 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use itertools::Itertools;
|
|
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;
|
|
|
|
// Tests that a fork and a merge becomes a single edge:
|
|
// D
|
|
// |\ D
|
|
// b c => :
|
|
// |/ A
|
|
// A ~
|
|
// |
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(), &[&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;
|
|
|
|
// Tests that merges outside the set can result in more parent edges than there
|
|
// was in the input:
|
|
//
|
|
// F
|
|
// |\
|
|
// d e F
|
|
// |\|\ => /|\
|
|
// A B C A B C
|
|
// \|/ ~ ~ ~
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(),
|
|
&[&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;
|
|
|
|
// Tests that the branch with "C" gets emitted correctly:
|
|
// E
|
|
// |
|
|
// d
|
|
// | C E C
|
|
// |/ |/
|
|
// b => A
|
|
// | ~
|
|
// A
|
|
// |
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(), &[&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;
|
|
|
|
// Tests that we get missing edges to "a" and "c" and not just one missing edge
|
|
// to the root.
|
|
// F
|
|
// / \ F
|
|
// d e => /|\
|
|
// |\ /| ~ B ~
|
|
// a B c ~
|
|
// \|/
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(), &[&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;
|
|
|
|
// Tests that we get both an edge from F to D and to D's ancestor C if we keep
|
|
// transitive edges and only the edge from F to D if we skip transitive
|
|
// edges:
|
|
// F F
|
|
// |\ |\
|
|
// D e D :
|
|
// |\| => |\:
|
|
// b C ~ C
|
|
// | ~
|
|
// a
|
|
// |
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(), &[&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;
|
|
|
|
// Tests a more complex case for skipping transitive edges.
|
|
// J
|
|
// /|
|
|
// | i J
|
|
// | |\ /:
|
|
// | | H | H
|
|
// G | | G :
|
|
// | e f => : D
|
|
// | \|\ :/
|
|
// | D | A
|
|
// \ / c |
|
|
// b / root
|
|
// |/
|
|
// A
|
|
// |
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(),
|
|
&[&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;
|
|
|
|
// Tests that merges, forks, direct edges, indirect edges, and "missing" edges
|
|
// are correct in reversed graph. "Missing" edges (i.e. edges to commits not
|
|
// in the input set) won't be part of the reversed graph. Conversely, there
|
|
// won't be missing edges to children not in the input.
|
|
//
|
|
// F
|
|
// |\
|
|
// D E
|
|
// |/
|
|
// C
|
|
// |
|
|
// b
|
|
// |
|
|
// A
|
|
// |
|
|
// root
|
|
let mut tx = repo.start_transaction("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.as_repo_ref(),
|
|
&[&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![]);
|
|
}
|