@ -1068,7 +1068,7 @@ mod graph {
assert! ( ids . contains ( & "bbbbbb" ) ) ;
assert! ( ids . contains ( & "bbbbbb" ) ) ;
}
}
/// When B depends on A, only A is a root .
/// When B depends on A, only B is a root (B is the goal; A is a prerequisite) .
#[ test ]
#[ test ]
fn roots_with_chain ( ) {
fn roots_with_chain ( ) {
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
@ -1077,7 +1077,7 @@ mod graph {
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let roots = graph . roots ( ) ;
let roots = graph . roots ( ) ;
assert_eq! ( roots . len ( ) , 1 ) ;
assert_eq! ( roots . len ( ) , 1 ) ;
assert_eq! ( roots [ 0 ] . id , " aaaaaa ") ;
assert_eq! ( roots [ 0 ] . id , " bbbbbb ") ;
}
}
/// Roots are sorted by priority descending.
/// Roots are sorted by priority descending.
@ -1095,7 +1095,7 @@ mod graph {
}
}
/// `subtree` on a linear chain A → B → C (B depends on A, C depends on B)
/// `subtree` on a linear chain A → B → C (B depends on A, C depends on B)
/// returns all three IDs when starting from A .
/// returns all three IDs when starting from C (the top-level goal) .
#[ test ]
#[ test ]
fn subtree_linear_chain ( ) {
fn subtree_linear_chain ( ) {
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
@ -1104,14 +1104,14 @@ mod graph {
let tickets = vec! [ a , b , c ] ;
let tickets = vec! [ a , b , c ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let sub = graph . subtree ( " aaaaaa ") ;
let sub = graph . subtree ( " cccccc ") ;
assert_eq! ( sub . len ( ) , 3 , "subtree should include all three tickets" ) ;
assert_eq! ( sub . len ( ) , 3 , "subtree should include all three tickets" ) ;
assert! ( sub . contains ( & "aaaaaa" ) ) ;
assert! ( sub . contains ( & "aaaaaa" ) ) ;
assert! ( sub . contains ( & "bbbbbb" ) ) ;
assert! ( sub . contains ( & "bbbbbb" ) ) ;
assert! ( sub . contains ( & "cccccc" ) ) ;
assert! ( sub . contains ( & "cccccc" ) ) ;
}
}
/// `subtree` on a leaf node (no dependen t s) returns just that ID.
/// `subtree` on a leaf node (no dependen cie s) returns just that ID.
#[ test ]
#[ test ]
fn subtree_leaf ( ) {
fn subtree_leaf ( ) {
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
@ -1119,8 +1119,8 @@ mod graph {
let tickets = vec! [ a , b ] ;
let tickets = vec! [ a , b ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let sub = graph . subtree ( " bbbbbb ") ;
let sub = graph . subtree ( " aaaaaa ") ;
assert_eq! ( sub , vec! [ " bbbbbb "] ) ;
assert_eq! ( sub , vec! [ " aaaaaa "] ) ;
}
}
/// `subtree` on an unknown ID returns an empty vec.
/// `subtree` on an unknown ID returns an empty vec.
@ -1169,9 +1169,9 @@ mod graph {
assert! ( node_ids . contains ( & "bbbbbb" ) ) ;
assert! ( node_ids . contains ( & "bbbbbb" ) ) ;
assert! ( node_ids . contains ( & "cccccc" ) ) ;
assert! ( node_ids . contains ( & "cccccc" ) ) ;
// Every edge should have " from" (blocker ) = "aaaaaa".
// Every edge should have " to" (dependency/prerequisite ) = "aaaaaa".
for edge in edges {
for edge in edges {
assert_eq! ( edge [ " from "] . as_str ( ) . unwrap ( ) , "aaaaaa" ) ;
assert_eq! ( edge [ " to "] . as_str ( ) . unwrap ( ) , "aaaaaa" ) ;
}
}
}
}
@ -1189,16 +1189,20 @@ mod graph {
fn to_subtree_json_value_scoped ( ) {
fn to_subtree_json_value_scoped ( ) {
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let b = make_ticket ( "bbbbbb" , & [ "aaaaaa" ] ) ; // depends on a
let b = make_ticket ( "bbbbbb" , & [ "aaaaaa" ] ) ; // depends on a
let c = make_ticket ( "cccccc" , & [ ] ) ; // unrelated root
let c = make_ticket ( "cccccc" , & [ ] ) ; // unrelated
let tickets = vec! [ a , b , c ] ;
let tickets = vec! [ a , b , c ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let json = graph . to_subtree_json_value ( "aaaaaa" ) ;
// Starting from bbbbbb (the goal): subtree includes bbbbbb + its dependency aaaaaa.
let json = graph . to_subtree_json_value ( "bbbbbb" ) ;
let nodes = json [ "nodes" ] . as_array ( ) . unwrap ( ) ;
let nodes = json [ "nodes" ] . as_array ( ) . unwrap ( ) ;
let node_ids : Vec < & str > = nodes . iter ( ) . map ( | n | n [ "id" ] . as_str ( ) . unwrap ( ) ) . collect ( ) ;
let node_ids : Vec < & str > = nodes . iter ( ) . map ( | n | n [ "id" ] . as_str ( ) . unwrap ( ) ) . collect ( ) ;
assert! ( node_ids . contains ( & "aaaaaa" ) , "root should be included" ) ;
assert! ( node_ids . contains ( & "bbbbbb" ) , "root should be included" ) ;
assert! ( node_ids . contains ( & "bbbbbb" ) , "dependent should be included" ) ;
assert! (
node_ids . contains ( & "aaaaaa" ) ,
"dependency should be included"
) ;
assert! (
assert! (
! node_ids . contains ( & "cccccc" ) ,
! node_ids . contains ( & "cccccc" ) ,
"unrelated ticket should be excluded"
"unrelated ticket should be excluded"
@ -1254,8 +1258,8 @@ mod display_graph {
assert! ( ! out . contains ( "└──" ) , "should have no branch connectors" ) ;
assert! ( ! out . contains ( "└──" ) , "should have no branch connectors" ) ;
}
}
/// A two-ticket chain (B depends on A) renders A at the top level and B
/// A two-ticket chain (B depends on A) renders B at the top level (the goal)
/// indented below it with `└──`.
/// and A indented below it with `└──` (the prerequisite) .
#[ test ]
#[ test ]
fn two_ticket_chain ( ) {
fn two_ticket_chain ( ) {
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
@ -1264,26 +1268,27 @@ mod display_graph {
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let out = format_graph ( & graph ) ;
let out = format_graph ( & graph ) ;
// A should appear before B .
// B should appear before A (B is the goal, A is the prerequisite) .
let pos_a = out . find ( "aaaaaa" ) . expect ( "aaaaaa should appear" ) ;
let pos_a = out . find ( "aaaaaa" ) . expect ( "aaaaaa should appear" ) ;
let pos_b = out . find ( "bbbbbb" ) . expect ( "bbbbbb should appear" ) ;
let pos_b = out . find ( "bbbbbb" ) . expect ( "bbbbbb should appear" ) ;
assert! (
assert! (
pos_ a < pos_b ,
pos_ b < pos_a ,
" root (aaaaaa) should appear before dependent (bbbbbb )"
" goal (bbbbbb) should appear before prerequisite (aaaaaa )"
) ;
) ;
// B 's line should use the └── connector.
// A 's line should use the └── connector.
assert! ( out . contains ( "└──" ) , "last (only) child should use └──" ) ;
assert! ( out . contains ( "└──" ) , "last (only) child should use └──" ) ;
assert! ( ! out . contains ( "├──" ) , "only child should not use ├──" ) ;
assert! ( ! out . contains ( "├──" ) , "only child should not use ├──" ) ;
}
}
/// When a root has two dependent s, the first uses `├──` and the last `└──`.
/// When a goal depends on two prerequisite s, the first uses `├──` and the last `└──`.
#[ test ]
#[ test ]
fn branching_parent ( ) {
fn branching_goal ( ) {
let root = make_ticket ( "aaaaaa" , & [ ] ) ;
let a = make_ticket ( "aaaaaa" , & [ ] ) ;
let b = make_ticket ( "bbbbbb" , & [ "aaaaaa" ] ) ;
let b = make_ticket ( "bbbbbb" , & [ ] ) ;
let c = make_ticket ( "cccccc" , & [ "aaaaaa" ] ) ;
// cccccc depends on both aaaaaa and bbbbbb, so it renders with two children.
let tickets = vec! [ root , b , c ] ;
let c = make_ticket ( "cccccc" , & [ "aaaaaa" , "bbbbbb" ] ) ;
let tickets = vec! [ a , b , c ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let out = format_graph ( & graph ) ;
let out = format_graph ( & graph ) ;
@ -1291,7 +1296,7 @@ mod display_graph {
assert! ( out . contains ( "└──" ) , "last child should use └──" ) ;
assert! ( out . contains ( "└──" ) , "last child should use └──" ) ;
}
}
/// `format_subtree` for a root only includes that root and its dependent s,
/// `format_subtree` for a goal includes that goal and its dependencie s,
/// not unrelated tickets.
/// not unrelated tickets.
#[ test ]
#[ test ]
fn subtree_excludes_unrelated ( ) {
fn subtree_excludes_unrelated ( ) {
@ -1301,9 +1306,10 @@ mod display_graph {
let tickets = vec! [ a , b , c ] ;
let tickets = vec! [ a , b , c ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
let out = format_subtree ( & graph , "aaaaaa" ) ;
// Starting from bbbbbb (the goal): renders bbbbbb and its dependency aaaaaa.
assert! ( out . contains ( "aaaaaa" ) , "root should be present" ) ;
let out = format_subtree ( & graph , "bbbbbb" ) ;
assert! ( out . contains ( "bbbbbb" ) , "dependent should be present" ) ;
assert! ( out . contains ( "bbbbbb" ) , "goal should be present" ) ;
assert! ( out . contains ( "aaaaaa" ) , "dependency should be present" ) ;
assert! ( ! out . contains ( "cccccc" ) , "unrelated ticket should be absent" ) ;
assert! ( ! out . contains ( "cccccc" ) , "unrelated ticket should be absent" ) ;
}
}
@ -1329,7 +1335,7 @@ mod display_graph {
let tickets = vec! [ a , b ] ;
let tickets = vec! [ a , b ] ;
let graph = TicketGraph ::build ( & tickets ) ;
let graph = TicketGraph ::build ( & tickets ) ;
// format_subtree drives rendering from "aaaaaa"; when it tries to
// format_subtree drives rendering from "aaaaaa"; when it tries to
// revisit "aaaaaa" via bbbbbb's dependen t edge, it should mark with *.
// revisit "aaaaaa" via bbbbbb's dependen cy edge, it should mark with *.
let out = format_subtree ( & graph , "aaaaaa" ) ;
let out = format_subtree ( & graph , "aaaaaa" ) ;
assert! (
assert! (
out . contains ( " *" ) ,
out . contains ( " *" ) ,