depgraph: alphabetize/style

This commit is contained in:
Mitchell Hashimoto 2014-05-24 12:51:22 -07:00
parent c0a7e5b98b
commit f4e9dda0ea
2 changed files with 136 additions and 137 deletions

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/digraph" "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 { type Graph struct {
Name string Name string
Meta interface{} Meta interface{}
@ -19,6 +19,91 @@ type Graph struct {
Root *Noun 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: // 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. // 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 // 2) All nouns in the graph must be reachable from the root
@ -68,88 +153,3 @@ CHECK_CYCLES:
} }
return nil 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)
}

View File

@ -53,9 +53,55 @@ func NounMapToList(m map[string]*Noun) []*Noun {
return list return list
} }
func TestGraph_Validate_NoRoot(t *testing.T) { func TestGraph_Validate(t *testing.T) {
nodes := ParseNouns(`a -> b 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) list := NounMapToList(nodes)
g := &Graph{Name: "Test", Nouns: list} 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 nodes := ParseNouns(`a -> b
c -> d`) b -> a`)
list := NounMapToList(nodes) list := NounMapToList(nodes)
g := &Graph{Name: "Test", Nouns: list} 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 VersionMeta int
type VersionConstraint struct { type VersionConstraint struct {
Min int Min int