depgraph: if a walk func errors, don't walk dependents
This commit is contained in:
parent
63c0ff9260
commit
e47498dc94
|
@ -259,6 +259,10 @@ func (g *Graph) Walk(fn WalkFunc) error {
|
||||||
seenMap := make(map[*Noun]chan struct{})
|
seenMap := make(map[*Noun]chan struct{})
|
||||||
seenMap[g.Root] = make(chan struct{})
|
seenMap[g.Root] = make(chan struct{})
|
||||||
|
|
||||||
|
// Keep track of what nodes errored.
|
||||||
|
var errMapL sync.RWMutex
|
||||||
|
errMap := make(map[*Noun]struct{})
|
||||||
|
|
||||||
// Build the list of things to visit
|
// Build the list of things to visit
|
||||||
tovisit := make([]*Noun, 1, len(g.Nouns))
|
tovisit := make([]*Noun, 1, len(g.Nouns))
|
||||||
tovisit[0] = g.Root
|
tovisit[0] = g.Root
|
||||||
|
@ -302,10 +306,23 @@ func (g *Graph) Walk(fn WalkFunc) error {
|
||||||
case <-quitCh:
|
case <-quitCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if any dependencies errored. If so,
|
||||||
|
// then return right away, we won't walk it.
|
||||||
|
errMapL.RLock()
|
||||||
|
_, errOk := errMap[dep.Target]
|
||||||
|
errMapL.RUnlock()
|
||||||
|
if errOk {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call our callback!
|
// Call our callback!
|
||||||
if err := fn(current); err != nil {
|
if err := fn(current); err != nil {
|
||||||
|
errMapL.Lock()
|
||||||
|
errMap[current] = struct{}{}
|
||||||
|
errMapL.Unlock()
|
||||||
|
|
||||||
errCh <- err
|
errCh <- err
|
||||||
}
|
}
|
||||||
}(current)
|
}(current)
|
||||||
|
|
|
@ -388,21 +388,40 @@ c -> e`)
|
||||||
|
|
||||||
func TestGraphWalk_error(t *testing.T) {
|
func TestGraphWalk_error(t *testing.T) {
|
||||||
nodes := ParseNouns(`a -> b
|
nodes := ParseNouns(`a -> b
|
||||||
a -> c
|
b -> c
|
||||||
b -> d
|
a -> d`)
|
||||||
b -> e
|
|
||||||
c -> d
|
|
||||||
c -> e`)
|
|
||||||
list := NounMapToList(nodes)
|
list := NounMapToList(nodes)
|
||||||
g := &Graph{Name: "Test", Nouns: list}
|
g := &Graph{Name: "Test", Nouns: list}
|
||||||
if err := g.Validate(); err != nil {
|
if err := g.Validate(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := g.Walk(func(n *Noun) error {
|
// We repeat this a lot because sometimes timing causes
|
||||||
return fmt.Errorf("foo")
|
// a false positive.
|
||||||
})
|
for i := 0; i < 100; i++ {
|
||||||
if err == nil {
|
var lock sync.Mutex
|
||||||
t.Fatal("should error")
|
var walked []string
|
||||||
|
err := g.Walk(func(n *Noun) error {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
walked = append(walked, n.Name)
|
||||||
|
|
||||||
|
if n.Name == "b" {
|
||||||
|
return fmt.Errorf("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should error")
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(walked)
|
||||||
|
|
||||||
|
expected := []string{"b", "c", "d"}
|
||||||
|
if !reflect.DeepEqual(walked, expected) {
|
||||||
|
t.Fatalf("bad: %#v", walked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue