Skip to content

Commit dbf26af

Browse files
committed
Make Graph and Workspace related debugging tools available in but-testing
1 parent 8d22666 commit dbf26af

File tree

7 files changed

+241
-244
lines changed

7 files changed

+241
-244
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/but-graph/tests/graph/init/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::graph_tree;
21
use but_graph::Graph;
3-
use but_testsupport::visualize_commit_graph_all;
2+
use but_testsupport::{graph_tree, graph_workspace, visualize_commit_graph_all};
43

54
#[test]
65
fn unborn() -> anyhow::Result<()> {
@@ -710,7 +709,6 @@ mod with_workspace;
710709

711710
mod utils;
712711
use crate::init::utils::standard_options_with_extra_target;
713-
use crate::vis::utils::graph_workspace;
714712
pub use utils::{
715713
StackState, add_stack_with_segments, add_workspace, id_at, id_by_rev,
716714
read_only_in_memory_scenario, standard_options,

crates/but-graph/tests/graph/init/with_workspace.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
use crate::graph_tree;
21
use crate::init::utils::{add_workspace_without_target, standard_options_with_extra_target};
32
use crate::init::{StackState, add_stack_with_segments, add_workspace, id_at, id_by_rev};
43
use crate::init::{read_only_in_memory_scenario, standard_options};
5-
use crate::vis::utils::graph_workspace;
64
use but_graph::Graph;
7-
use but_testsupport::visualize_commit_graph_all;
5+
use but_testsupport::{graph_tree, graph_workspace, visualize_commit_graph_all};
86

97
#[test]
108
fn single_stack_ambigous() -> anyhow::Result<()> {
@@ -4253,11 +4251,9 @@ fn applied_stack_above_explicit_lower_bound() -> anyhow::Result<()> {
42534251
}
42544252

42554253
mod edit_commit {
4256-
use crate::graph_tree;
42574254
use crate::init::{add_workspace, id_at, read_only_in_memory_scenario, standard_options};
4258-
use crate::vis::utils::graph_workspace;
42594255
use but_graph::Graph;
4260-
use but_testsupport::visualize_commit_graph_all;
4256+
use but_testsupport::{graph_tree, graph_workspace, visualize_commit_graph_all};
42614257

42624258
#[test]
42634259
fn applied_stack_below_explicit_lower_bound() -> anyhow::Result<()> {
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
mod init;
22
mod vis;
3-
pub use vis::utils::graph_tree;
43

54
mod ref_metadata_legacy;
Lines changed: 31 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! Tests for visualizing the graph data structure.
2+
23
use but_core::ref_metadata;
3-
use but_graph::{CommitFlags, Graph, Segment, SegmentIndex, SegmentMetadata};
4+
use but_graph::{Commit, CommitFlags, Graph, Segment, SegmentIndex, SegmentMetadata};
5+
use but_testsupport::graph_tree;
6+
use gix::ObjectId;
7+
use std::str::FromStr;
48

59
/// Simulate a graph data structure after the first pass, i.e., right after the walk.
610
/// There is no pruning of 'empty' branches, just a perfect representation of the graph as is,
@@ -101,241 +105,34 @@ fn detached_head() {
101105
");
102106
}
103107

104-
#[test]
105-
fn unborn_head() {
106-
insta::assert_snapshot!(graph_tree(&Graph::default()), @"<UNBORN>");
107-
}
108-
109-
pub(crate) mod utils {
110-
use but_graph::{Commit, CommitFlags, SegmentMetadata};
111-
use but_graph::{EntryPoint, Graph, SegmentIndex};
112-
113-
use gix::ObjectId;
114-
use termtree::Tree;
115-
116-
use but_graph::projection::StackCommitDebugFlags;
117-
use std::collections::{BTreeMap, BTreeSet};
118-
use std::str::FromStr;
119-
120-
pub fn commit(
121-
id: ObjectId,
122-
parent_ids: impl IntoIterator<Item = ObjectId>,
123-
flags: CommitFlags,
124-
) -> Commit {
125-
Commit {
126-
id,
127-
parent_ids: parent_ids.into_iter().collect(),
128-
refs: Vec::new(),
129-
flags,
130-
}
131-
}
132-
133-
pub fn id(hex: &str) -> ObjectId {
134-
let hash_len = gix::hash::Kind::Sha1.len_in_hex();
135-
if hex.len() != hash_len {
136-
ObjectId::from_str(
137-
&std::iter::repeat_n(hex, hash_len / hex.len())
138-
.collect::<Vec<_>>()
139-
.join(""),
140-
)
141-
} else {
142-
ObjectId::from_str(hex)
143-
}
144-
.unwrap()
145-
}
146-
147-
type StringTree = Tree<String>;
148-
149-
/// Visualize `graph` as a tree.
150-
pub fn graph_workspace(workspace: &but_graph::projection::Workspace) -> StringTree {
151-
let commit_flags = workspace
152-
.graph
153-
.hard_limit_hit()
154-
.then_some(StackCommitDebugFlags::HardLimitReached)
155-
.unwrap_or_default();
156-
let mut root = Tree::new(workspace.debug_string());
157-
for stack in &workspace.stacks {
158-
root.push(tree_for_stack(stack, commit_flags));
159-
}
160-
root
161-
}
162-
163-
fn tree_for_stack(
164-
stack: &but_graph::projection::Stack,
165-
commit_flags: StackCommitDebugFlags,
166-
) -> StringTree {
167-
let mut root = Tree::new(stack.debug_string());
168-
for segment in &stack.segments {
169-
root.push(tree_for_stack_segment(segment, commit_flags));
170-
}
171-
root
172-
}
173-
174-
fn tree_for_stack_segment(
175-
segment: &but_graph::projection::StackSegment,
176-
commit_flags: StackCommitDebugFlags,
177-
) -> StringTree {
178-
let mut root = Tree::new(segment.debug_string());
179-
if let Some(outside) = &segment.commits_outside {
180-
for commit in outside {
181-
root.push(format!("{}*", commit.debug_string(commit_flags)));
182-
}
183-
}
184-
for commit in &segment.commits_on_remote {
185-
root.push(commit.debug_string(commit_flags | StackCommitDebugFlags::RemoteOnly));
186-
}
187-
for commit in &segment.commits {
188-
root.push(commit.debug_string(commit_flags));
189-
}
190-
root
191-
}
192-
193-
/// Visualize `graph` as a tree.
194-
pub fn graph_tree(graph: &Graph) -> StringTree {
195-
let mut root = Tree::new("".to_string());
196-
let mut seen = Default::default();
197-
for sidx in graph.tip_segments() {
198-
root.push(recurse_segment(graph, sidx, &mut seen));
199-
}
200-
let missing = graph.num_segments() - seen.len();
201-
if missing > 0 {
202-
let mut missing = Tree::new(format!(
203-
"ERROR: disconnected {missing} nodes unreachable through base"
204-
));
205-
let mut newly_seen = Default::default();
206-
for sidx in graph.segments().filter(|sidx| !seen.contains(sidx)) {
207-
missing.push(recurse_segment(graph, sidx, &mut newly_seen));
208-
}
209-
root.push(missing);
210-
seen.extend(newly_seen);
211-
}
212-
213-
if seen.is_empty() {
214-
"<UNBORN>".to_string().into()
215-
} else {
216-
root
217-
}
218-
}
219-
220-
fn no_first_commit_on_named_segments(mut ep: EntryPoint) -> EntryPoint {
221-
if ep.segment.ref_name.is_some() && ep.commit_index == Some(0) {
222-
ep.commit_index = None;
223-
}
224-
ep
108+
fn id(hex: &str) -> ObjectId {
109+
let hash_len = gix::hash::Kind::Sha1.len_in_hex();
110+
if hex.len() != hash_len {
111+
ObjectId::from_str(
112+
&std::iter::repeat_n(hex, hash_len / hex.len())
113+
.collect::<Vec<_>>()
114+
.join(""),
115+
)
116+
} else {
117+
ObjectId::from_str(hex)
225118
}
119+
.unwrap()
120+
}
226121

227-
fn tree_for_commit(
228-
commit: &but_graph::Commit,
229-
is_entrypoint: bool,
230-
is_early_end: bool,
231-
hard_limit_hit: bool,
232-
) -> StringTree {
233-
Graph::commit_debug_string(commit, is_entrypoint, is_early_end, hard_limit_hit).into()
122+
fn commit(
123+
id: ObjectId,
124+
parent_ids: impl IntoIterator<Item = ObjectId>,
125+
flags: CommitFlags,
126+
) -> Commit {
127+
Commit {
128+
id,
129+
parent_ids: parent_ids.into_iter().collect(),
130+
refs: Vec::new(),
131+
flags,
234132
}
235-
fn recurse_segment(
236-
graph: &but_graph::Graph,
237-
sidx: SegmentIndex,
238-
seen: &mut BTreeSet<SegmentIndex>,
239-
) -> StringTree {
240-
let segment = &graph[sidx];
241-
if seen.contains(&sidx) {
242-
return format!(
243-
"→:{sidx}:{name}",
244-
sidx = sidx.index(),
245-
name = graph[sidx]
246-
.ref_name
247-
.as_ref()
248-
.map(|n| format!(
249-
" ({}{maybe_sibling})",
250-
Graph::ref_debug_string(n),
251-
maybe_sibling = segment
252-
.sibling_segment_id
253-
.map_or_else(String::new, |sid| format!(" →:{}:", sid.index()))
254-
))
255-
.unwrap_or_default()
256-
)
257-
.into();
258-
}
259-
seen.insert(sidx);
260-
let ep = no_first_commit_on_named_segments(graph.lookup_entrypoint().unwrap());
261-
let segment_is_entrypoint = ep.segment_index == sidx;
262-
let mut show_segment_entrypoint = segment_is_entrypoint;
263-
if segment_is_entrypoint {
264-
// Reduce noise by preferring ref-based entry-points.
265-
if segment.ref_name.is_none() && ep.commit_index.is_some() {
266-
show_segment_entrypoint = false;
267-
}
268-
}
269-
let connected_segments = {
270-
let mut m = BTreeMap::<_, Vec<_>>::new();
271-
let below = graph.segments_below_in_order(sidx).collect::<Vec<_>>();
272-
for (cidx, sidx) in below {
273-
m.entry(cidx).or_default().push(sidx);
274-
}
275-
m
276-
};
277-
278-
let mut root = Tree::new(format!(
279-
"{entrypoint}{meta}{arrow}:{id}[{generation}]:{ref_name_and_remote}",
280-
meta = match segment.metadata {
281-
None => {
282-
""
283-
}
284-
Some(SegmentMetadata::Workspace(_)) => {
285-
"📕"
286-
}
287-
Some(SegmentMetadata::Branch(_)) => {
288-
"📙"
289-
}
290-
},
291-
id = segment.id.index(),
292-
generation = segment.generation,
293-
arrow = if segment.workspace_metadata().is_some() {
294-
"►►►"
295-
} else {
296-
"►"
297-
},
298-
entrypoint = if show_segment_entrypoint {
299-
if ep.commit.is_none() && ep.commit_index.is_some() {
300-
"🫱"
301-
} else {
302-
"👉"
303-
}
304-
} else {
305-
""
306-
},
307-
ref_name_and_remote = Graph::ref_and_remote_debug_string(
308-
segment.ref_name.as_ref(),
309-
segment.remote_tracking_ref_name.as_ref(),
310-
segment.sibling_segment_id
311-
),
312-
));
313-
for (cidx, commit) in segment.commits.iter().enumerate() {
314-
let mut commit_tree = tree_for_commit(
315-
commit,
316-
segment_is_entrypoint && Some(cidx) == ep.commit_index,
317-
if cidx + 1 != segment.commits.len() {
318-
false
319-
} else {
320-
graph.is_early_end_of_traversal(sidx)
321-
},
322-
graph.hard_limit_hit(),
323-
);
324-
if let Some(segment_indices) = connected_segments.get(&Some(cidx)) {
325-
for sidx in segment_indices {
326-
commit_tree.push(recurse_segment(graph, *sidx, seen));
327-
}
328-
}
329-
root.push(commit_tree);
330-
}
331-
// Get the segments that are directly connected.
332-
if let Some(segment_indices) = connected_segments.get(&None) {
333-
for sidx in segment_indices {
334-
root.push(recurse_segment(graph, *sidx, seen));
335-
}
336-
}
133+
}
337134

338-
root
339-
}
135+
#[test]
136+
fn unborn_head() {
137+
insta::assert_snapshot!(graph_tree(&Graph::default()), @"<UNBORN>");
340138
}
341-
use utils::{commit, graph_tree, id};

crates/but-testsupport/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ gix.workspace = true
1313
anyhow.workspace = true
1414
termtree = "0.5.1"
1515
gix-testtools.workspace = true
16+
but-graph.workspace = true
1617

1718
[dev-dependencies]

0 commit comments

Comments
 (0)