diff --git a/depgraph/graph.go b/depgraph/graph.go index 7d30de1af..81c1abcee 100644 --- a/depgraph/graph.go +++ b/depgraph/graph.go @@ -6,7 +6,9 @@ package depgraph import ( + "bytes" "fmt" + "strings" "github.com/hashicorp/terraform/digraph" ) @@ -104,6 +106,44 @@ func (g *Graph) CheckConstraints() error { return nil } +// String generates a little ASCII string of the graph, useful in +// debugging output. +func (g *Graph) String() string { + var buf bytes.Buffer + + cb := func(n *Noun, depth int) { + buf.WriteString(fmt.Sprintf( + "%s%s\n", + strings.Repeat(" ", depth), + n.Name)) + } + + type listItem struct { + n *Noun + d int + } + nodes := []listItem{{g.Root, 0}} + for len(nodes) > 0 { + // Pop current node + n := len(nodes) + current := nodes[n-1] + nodes = nodes[:n-1] + + // Visit + cb(current.n, current.d) + + // Traverse + for _, dep := range current.n.Deps { + nodes = append(nodes, listItem{ + n: dep.Target, + d: current.d + 1, + }) + } + } + + return buf.String() +} + // Validate is used to ensure that a few properties of the graph are not violated: // 1) There must be a single "root", or source on which nothing depends. // 2) All nouns in the graph must be reachable from the root diff --git a/depgraph/graph_test.go b/depgraph/graph_test.go index 59d3e7281..d80f37646 100644 --- a/depgraph/graph_test.go +++ b/depgraph/graph_test.go @@ -53,6 +53,38 @@ func NounMapToList(m map[string]*Noun) []*Noun { return list } +func TestGraph_String(t *testing.T) { + nodes := ParseNouns(`a -> b +a -> c +b -> d +b -> e +c -> d +c -> e`) + + g := &Graph{ + Name: "Test", + Nouns: NounMapToList(nodes), + Root: nodes["a"], + } + actual := g.String() + + expected := ` +a + c + e + d + b + e + d +` + + actual = strings.TrimSpace(actual) + expected = strings.TrimSpace(expected) + if actual != expected { + t.Fatalf("bad:\n%s\n!=\n%s", actual, expected) + } +} + func TestGraph_Validate(t *testing.T) { nodes := ParseNouns(`a -> b a -> c