From f4e9dda0eaaf9ce3f65f015c32ba68477205dbd7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 24 May 2014 12:51:22 -0700 Subject: [PATCH] depgraph: alphabetize/style --- depgraph/graph.go | 172 ++++++++++++++++++++--------------------- depgraph/graph_test.go | 101 ++++++++++++------------ 2 files changed, 136 insertions(+), 137 deletions(-) diff --git a/depgraph/graph.go b/depgraph/graph.go index 1cf4c15b9..7d30de1af 100644 --- a/depgraph/graph.go +++ b/depgraph/graph.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform/digraph" ) -// Graph is used to represent the entire dependency graph +// Graph is used to represent a dependency graph. type Graph struct { Name string Meta interface{} @@ -19,6 +19,91 @@ type Graph struct { Root *Noun } +// ValidateError implements the Error interface but provides +// additional information on a validation error. +type ValidateError struct { + // If set, then the graph is missing a single root, on which + // there are no depdendencies + MissingRoot bool + + // Unreachable are nodes that could not be reached from + // the root noun. + Unreachable []*Noun + + // Cycles are groups of strongly connected nodes, which + // form a cycle. This is disallowed. + Cycles [][]*Noun +} + +func (v *ValidateError) Error() string { + return "The depedency graph is not valid" +} + +// ConstraintError is used to return detailed violation +// information from CheckConstraints +type ConstraintError struct { + Violations []*Violation +} + +func (c *ConstraintError) Error() string { + return fmt.Sprintf("%d constraint violations", len(c.Violations)) +} + +// Violation is used to pass along information about +// a constraint violation +type Violation struct { + Source *Noun + Target *Noun + Dependency *Dependency + Constraint Constraint + Err error +} + +func (v *Violation) Error() string { + return fmt.Sprintf("Constraint %v between %v and %v violated: %v", + v.Constraint, v.Source, v.Target, v.Err) +} + +// CheckConstraints walks the graph and ensures that all +// user imposed constraints are satisfied. +func (g *Graph) CheckConstraints() error { + // Ensure we have a root + if g.Root == nil { + return fmt.Errorf("Graph must be validated before checking constraint violations") + } + + // Create a constraint error + cErr := &ConstraintError{} + + // Walk from the root + digraph.DepthFirstWalk(g.Root, func(n digraph.Node) bool { + noun := n.(*Noun) + for _, dep := range noun.Deps { + target := dep.Target + for _, constraint := range dep.Constraints { + ok, err := constraint.Satisfied(noun, target) + if ok { + continue + } + violation := &Violation{ + Source: noun, + Target: target, + Dependency: dep, + Constraint: constraint, + Err: err, + } + cErr.Violations = append(cErr.Violations, violation) + } + } + return true + }) + + if cErr.Violations != nil { + return cErr + } + return nil +} + // 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 @@ -68,88 +153,3 @@ CHECK_CYCLES: } return nil } - -// ValidateError implements the Error interface but provides -// additional information on a validation error -type ValidateError struct { - // If set, then the graph is missing a single root, on which - // there are no depdendencies - MissingRoot bool - - // Unreachable are nodes that could not be reached from - // the root noun. - Unreachable []*Noun - - // Cycles are groups of strongly connected nodes, which - // form a cycle. This is disallowed. - Cycles [][]*Noun -} - -func (v *ValidateError) Error() string { - return "The depedency graph is not valid" -} - -// CheckConstraints walks the graph and ensures that all -// user imposed constraints are satisfied. -func (g *Graph) CheckConstraints() error { - // Ensure we have a root - if g.Root == nil { - return fmt.Errorf("Graph must be validated before checking constraint violations") - } - - // Create a constraint error - cErr := &ConstraintError{} - - // Walk from the root - digraph.DepthFirstWalk(g.Root, func(n digraph.Node) bool { - noun := n.(*Noun) - for _, dep := range noun.Deps { - target := dep.Target - for _, constraint := range dep.Constraints { - ok, err := constraint.Satisfied(noun, target) - if ok { - continue - } - violation := &Violation{ - Source: noun, - Target: target, - Dependency: dep, - Constraint: constraint, - Err: err, - } - cErr.Violations = append(cErr.Violations, violation) - } - } - return true - }) - - if cErr.Violations != nil { - return cErr - } - return nil -} - -// ConstraintError is used to return detailed violation -// information from CheckConstraints -type ConstraintError struct { - Violations []*Violation -} - -func (c *ConstraintError) Error() string { - return fmt.Sprintf("%d constraint violations", len(c.Violations)) -} - -// Violation is used to pass along information about -// a constraint violation -type Violation struct { - Source *Noun - Target *Noun - Dependency *Dependency - Constraint Constraint - Err error -} - -func (v *Violation) Error() string { - return fmt.Sprintf("Constraint %v between %v and %v violated: %v", - v.Constraint, v.Source, v.Target, v.Err) -} diff --git a/depgraph/graph_test.go b/depgraph/graph_test.go index f0bbc8889..59d3e7281 100644 --- a/depgraph/graph_test.go +++ b/depgraph/graph_test.go @@ -53,9 +53,55 @@ func NounMapToList(m map[string]*Noun) []*Noun { return list } -func TestGraph_Validate_NoRoot(t *testing.T) { +func TestGraph_Validate(t *testing.T) { nodes := ParseNouns(`a -> b -b -> a`) +a -> c +b -> d +b -> e +c -> d +c -> e`) + list := NounMapToList(nodes) + + g := &Graph{Name: "Test", Nouns: list} + if err := g.Validate(); err != nil { + t.Fatalf("err: %v", err) + } +} + +func TestGraph_Validate_Cycle(t *testing.T) { + nodes := ParseNouns(`a -> b +a -> c +b -> d +d -> b`) + list := NounMapToList(nodes) + + g := &Graph{Name: "Test", Nouns: list} + err := g.Validate() + if err == nil { + t.Fatalf("expected err") + } + + vErr, ok := err.(*ValidateError) + if !ok { + t.Fatalf("expected validate error") + } + + if len(vErr.Cycles) != 1 { + t.Fatalf("expected cycles") + } + + cycle := vErr.Cycles[0] + if cycle[0].Name != "d" { + t.Fatalf("bad: %v", cycle) + } + if cycle[1].Name != "b" { + t.Fatalf("bad: %v", cycle) + } +} + +func TestGraph_Validate_MultiRoot(t *testing.T) { + nodes := ParseNouns(`a -> b +c -> d`) list := NounMapToList(nodes) g := &Graph{Name: "Test", Nouns: list} @@ -74,9 +120,9 @@ b -> a`) } } -func TestGraph_Validate_MultiRoot(t *testing.T) { +func TestGraph_Validate_NoRoot(t *testing.T) { nodes := ParseNouns(`a -> b -c -> d`) +b -> a`) list := NounMapToList(nodes) g := &Graph{Name: "Test", Nouns: list} @@ -122,53 +168,6 @@ x -> x`) } } -func TestGraph_Validate_Cycle(t *testing.T) { - nodes := ParseNouns(`a -> b -a -> c -b -> d -d -> b`) - list := NounMapToList(nodes) - - g := &Graph{Name: "Test", Nouns: list} - err := g.Validate() - if err == nil { - t.Fatalf("expected err") - } - - vErr, ok := err.(*ValidateError) - if !ok { - t.Fatalf("expected validate error") - } - - if len(vErr.Cycles) != 1 { - t.Fatalf("expected cycles") - } - - cycle := vErr.Cycles[0] - if cycle[0].Name != "d" { - t.Fatalf("bad: %v", cycle) - } - if cycle[1].Name != "b" { - t.Fatalf("bad: %v", cycle) - } -} - -func TestGraph_Validate(t *testing.T) { - nodes := ParseNouns(`a -> b -a -> c -b -> d -b -> e -c -> d -c -> e`) - list := NounMapToList(nodes) - - g := &Graph{Name: "Test", Nouns: list} - err := g.Validate() - if err != nil { - t.Fatalf("err: %v", err) - } -} - type VersionMeta int type VersionConstraint struct { Min int