dag: detect self references, use multierror
This commit is contained in:
parent
de3d9fb9d9
commit
5b0004ffc7
20
dag/dag.go
20
dag/dag.go
|
@ -4,6 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AcyclicGraph is a specialization of Graph that cannot have cycles. With
|
// AcyclicGraph is a specialization of Graph that cannot have cycles. With
|
||||||
|
@ -45,6 +47,8 @@ func (g *AcyclicGraph) Validate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look for cycles of more than 1 component
|
||||||
|
var err error
|
||||||
var cycles [][]Vertex
|
var cycles [][]Vertex
|
||||||
for _, cycle := range StronglyConnected(&g.Graph) {
|
for _, cycle := range StronglyConnected(&g.Graph) {
|
||||||
if len(cycle) > 1 {
|
if len(cycle) > 1 {
|
||||||
|
@ -52,20 +56,26 @@ func (g *AcyclicGraph) Validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(cycles) > 0 {
|
if len(cycles) > 0 {
|
||||||
cyclesStr := make([]string, len(cycles))
|
for _, cycle := range cycles {
|
||||||
for i, cycle := range cycles {
|
|
||||||
cycleStr := make([]string, len(cycle))
|
cycleStr := make([]string, len(cycle))
|
||||||
for j, vertex := range cycle {
|
for j, vertex := range cycle {
|
||||||
cycleStr[j] = VertexName(vertex)
|
cycleStr[j] = VertexName(vertex)
|
||||||
}
|
}
|
||||||
|
|
||||||
cyclesStr[i] = strings.Join(cycleStr, ", ")
|
err = multierror.Append(err, fmt.Errorf(
|
||||||
|
"Cycle: %s", strings.Join(cycleStr, ", ")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("cycles: %s", cyclesStr)
|
// Look for cycles to self
|
||||||
|
for _, e := range g.Edges() {
|
||||||
|
if e.Source() == e.Target() {
|
||||||
|
err = multierror.Append(err, fmt.Errorf(
|
||||||
|
"Self reference: %s", VertexName(e.Source())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk walks the graph, calling your callback as each node is visited.
|
// Walk walks the graph, calling your callback as each node is visited.
|
||||||
|
|
|
@ -75,6 +75,17 @@ func TestAcyclicGraphValidate_cycle(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcyclicGraphValidate_cycleSelf(t *testing.T) {
|
||||||
|
var g AcyclicGraph
|
||||||
|
g.Add(1)
|
||||||
|
g.Add(2)
|
||||||
|
g.Connect(BasicEdge(1, 1))
|
||||||
|
|
||||||
|
if err := g.Validate(); err == nil {
|
||||||
|
t.Fatal("should error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAcyclicGraphWalk(t *testing.T) {
|
func TestAcyclicGraphWalk(t *testing.T) {
|
||||||
var g AcyclicGraph
|
var g AcyclicGraph
|
||||||
g.Add(1)
|
g.Add(1)
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (g *Graph) Vertices() []Vertex {
|
||||||
|
|
||||||
// Edges returns the list of all the edges in the graph.
|
// Edges returns the list of all the edges in the graph.
|
||||||
func (g *Graph) Edges() []Edge {
|
func (g *Graph) Edges() []Edge {
|
||||||
list := g.vertices.List()
|
list := g.edges.List()
|
||||||
result := make([]Edge, len(list))
|
result := make([]Edge, len(list))
|
||||||
for i, v := range list {
|
for i, v := range list {
|
||||||
result[i] = v.(Edge)
|
result[i] = v.(Edge)
|
||||||
|
|
Loading…
Reference in New Issue