3
0

Add base graph tests; node set improvement

This commit is contained in:
Tomaž Jerman 2020-10-22 14:00:17 +02:00
parent ee2183c36f
commit 4893f4ccd8
3 changed files with 231 additions and 11 deletions

View File

@ -13,7 +13,7 @@ type (
// based on the node properties.
// Refer to the documentation for additional details.
Graph struct {
nodes []Node
nodes NodeSet
// Since it's calculated on the fly, this is all we need
invert bool
@ -61,18 +61,16 @@ func (g *Graph) Remove(nn ...Node) {
return
}
mm := make([]Node, 0, len(g.nodes))
for _, m := range g.nodes {
for _, n := range nn {
if g.nodesMatch(m, n) && g.canRemove(n) {
goto skip
}
rn := make(NodeSet, 0, len(nn))
for _, n := range nn {
if g.canRemove(n) {
rn = append(rn, n)
}
mm = append(mm, m)
skip:
}
g.nodes = mm
g.nodes = g.nodes.Remove(rn...)
g.processed = g.processed.Remove(rn...)
g.conflicts = g.conflicts.Remove(rn...)
}
// FindNode returns all nodes that match the given resource and identifiers

201
pkg/envoy/graph_test.go Normal file
View File

@ -0,0 +1,201 @@
package envoy
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
type (
TestNode struct {
rr NodeRelationships
ii NodeIdentifiers
}
)
func (n *TestNode) Identifiers() NodeIdentifiers {
return n.ii
}
func (n *TestNode) Matches(resource string, identifiers ...string) bool {
if resource != n.Resource() {
return false
}
return n.Identifiers().HasAny(identifiers...)
}
func (n *TestNode) Resource() string {
return "envoy:test:"
}
func (n *TestNode) Relations() NodeRelationships {
return n.rr
}
func TestEnvoyGraph_Rel(t *testing.T) {
req := require.New(t)
t.Run("simple node, no rels", func(t *testing.T) {
g := NewGraph()
rr := NodeRelationships{}
ii := NodeIdentifiers{"p1"}
n := &TestNode{rr: rr, ii: ii}
g.Add(n)
cc := g.Children(n)
req.Empty(cc)
pp := g.Parents(n)
req.Empty(pp)
})
t.Run("node with child and parent nodes", func(t *testing.T) {
g := NewGraph()
// The child node
rr1 := NodeRelationships{}
ii1 := NodeIdentifiers{"c1"}
n1 := &TestNode{rr: rr1, ii: ii1}
g.Add(n1)
// The middle node
rr2 := NodeRelationships{"envoy:test:": NodeIdentifiers{"c1"}}
ii2 := NodeIdentifiers{"n"}
n := &TestNode{rr: rr2, ii: ii2}
g.Add(n)
// The parent node
rr3 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n"}}
ii3 := NodeIdentifiers{"p1"}
n3 := &TestNode{rr: rr3, ii: ii3}
g.Add(n3)
cc := g.Children(n)
req.Len(cc, 1)
req.Equal(n1, cc[0])
pp := g.Parents(n)
req.Len(pp, 1)
req.Equal(n3, pp[0])
})
t.Run("(inverted) node with child and parent nodes", func(t *testing.T) {
g := NewGraph()
g.Invert()
// The child node
rr1 := NodeRelationships{}
ii1 := NodeIdentifiers{"c1"}
n1 := &TestNode{rr: rr1, ii: ii1}
g.Add(n1)
// The middle node
rr2 := NodeRelationships{"envoy:test:": NodeIdentifiers{"c1"}}
ii2 := NodeIdentifiers{"n"}
n := &TestNode{rr: rr2, ii: ii2}
g.Add(n)
// The parent node
rr3 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n"}}
ii3 := NodeIdentifiers{"p1"}
n3 := &TestNode{rr: rr3, ii: ii3}
g.Add(n3)
cc := g.Children(n)
req.Len(cc, 1)
req.Equal(n3, cc[0])
pp := g.Parents(n)
req.Len(pp, 1)
req.Equal(n1, pp[0])
})
}
func TestEnvoyGraph_DepResolution(t *testing.T) {
req := require.New(t)
ctx := context.Background()
t.Run("simple acyclic linear graph; (n1) => (n2) => (n3)", func(t *testing.T) {
g := NewGraph()
rr1 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n2"}}
ii1 := NodeIdentifiers{"n1"}
n1 := &TestNode{rr: rr1, ii: ii1}
g.Add(n1)
rr2 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n3"}}
ii2 := NodeIdentifiers{"n2"}
n2 := &TestNode{rr: rr2, ii: ii2}
g.Add(n2)
rr3 := NodeRelationships{}
ii3 := NodeIdentifiers{"n3"}
n3 := &TestNode{rr: rr3, ii: ii3}
g.Add(n3)
// 1. n1 since it has no parent nodes
n, pp, cc, err := g.Next(ctx)
req.Equal(n1, n)
req.NoError(err)
req.NotEmpty(cc)
req.Empty(pp)
// 2. n2 since its parents are resolved
n, pp, cc, err = g.Next(ctx)
req.Equal(n2, n)
req.NoError(err)
req.NotEmpty(cc)
req.NotEmpty(pp)
// 2. n3 since its parents are resolved
n, pp, cc, err = g.Next(ctx)
req.Equal(n3, n)
req.NoError(err)
req.Empty(cc)
req.NotEmpty(pp)
})
}
func TestEnvoyGraph_GarbageCollector(t *testing.T) {
req := require.New(t)
ctx := context.Background()
t.Run("simple acyclic linear graph; (n1) => (n2) => (n3)", func(t *testing.T) {
g := NewGraph()
rr1 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n2"}}
ii1 := NodeIdentifiers{"n1"}
n1 := &TestNode{rr: rr1, ii: ii1}
g.Add(n1)
rr2 := NodeRelationships{"envoy:test:": NodeIdentifiers{"n3"}}
ii2 := NodeIdentifiers{"n2"}
n2 := &TestNode{rr: rr2, ii: ii2}
g.Add(n2)
rr3 := NodeRelationships{}
ii3 := NodeIdentifiers{"n3"}
n3 := &TestNode{rr: rr3, ii: ii3}
g.Add(n3)
// n1 marked as processed; no garbage
g.Next(ctx)
req.Len(g.nodes, 3)
req.Len(g.processed, 1)
req.Equal(g.processed[0], n1)
// n2 marked as processed; garbage in nodes, processed
g.Next(ctx)
req.Len(g.nodes, 2)
req.Len(g.processed, 1)
req.Equal(g.processed[0], n2)
// all nodes processed; resetting graph
g.Next(ctx)
req.Len(g.nodes, 0)
req.Len(g.processed, 0)
})
}

View File

@ -92,3 +92,24 @@ func (ss NodeSet) Has(n Node) bool {
}
return has
}
func (ss NodeSet) Remove(nn ...Node) NodeSet {
mm := make(NodeSet, 0, len(ss))
if len(nn) <= 0 {
return ss
}
for _, s := range ss {
for _, n := range nn {
if s.Matches(n.Resource(), n.Identifiers()...) {
goto skip
}
}
mm = append(mm, s)
skip:
}
return mm
}