560305f601
- uses newer version of go-ethereum required for go1.11
420 lines
11 KiB
Go
420 lines
11 KiB
Go
// Copyright (c) 2017 Arista Networks, Inc.
|
|
// Use of this source code is governed by the Apache License 2.0
|
|
// that can be found in the COPYING file.
|
|
|
|
package path
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/aristanetworks/goarista/key"
|
|
"github.com/aristanetworks/goarista/test"
|
|
)
|
|
|
|
func accumulator(counter map[int]int) VisitorFunc {
|
|
return func(val interface{}) error {
|
|
counter[val.(int)]++
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func TestMapSet(t *testing.T) {
|
|
m := Map{}
|
|
a := m.Set(key.Path{key.New("foo")}, 0)
|
|
b := m.Set(key.Path{key.New("foo")}, 1)
|
|
if !a || b {
|
|
t.Fatal("Map.Set not working properly")
|
|
}
|
|
}
|
|
|
|
func TestMapVisit(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 1)
|
|
m.Set(key.Path{Wildcard, key.New("bar"), key.New("baz")}, 2)
|
|
m.Set(key.Path{Wildcard, Wildcard, key.New("baz")}, 3)
|
|
m.Set(key.Path{Wildcard, Wildcard, Wildcard}, 4)
|
|
m.Set(key.Path{key.New("foo"), Wildcard, Wildcard}, 5)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar"), Wildcard}, 6)
|
|
m.Set(key.Path{key.New("foo"), Wildcard, key.New("baz")}, 7)
|
|
m.Set(key.Path{Wildcard, key.New("bar"), Wildcard}, 8)
|
|
|
|
m.Set(key.Path{}, 10)
|
|
|
|
m.Set(key.Path{Wildcard}, 20)
|
|
m.Set(key.Path{key.New("foo")}, 21)
|
|
|
|
m.Set(key.Path{key.New("zap"), key.New("zip")}, 30)
|
|
m.Set(key.Path{key.New("zap"), key.New("zip")}, 31)
|
|
|
|
m.Set(key.Path{key.New("zip"), Wildcard}, 40)
|
|
m.Set(key.Path{key.New("zip"), Wildcard}, 41)
|
|
|
|
testCases := []struct {
|
|
path key.Path
|
|
expected map[int]int
|
|
}{{
|
|
path: key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
|
|
expected: map[int]int{1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1},
|
|
}, {
|
|
path: key.Path{key.New("qux"), key.New("bar"), key.New("baz")},
|
|
expected: map[int]int{2: 1, 3: 1, 4: 1, 8: 1},
|
|
}, {
|
|
path: key.Path{key.New("foo"), key.New("qux"), key.New("baz")},
|
|
expected: map[int]int{3: 1, 4: 1, 5: 1, 7: 1},
|
|
}, {
|
|
path: key.Path{key.New("foo"), key.New("bar"), key.New("qux")},
|
|
expected: map[int]int{4: 1, 5: 1, 6: 1, 8: 1},
|
|
}, {
|
|
path: key.Path{},
|
|
expected: map[int]int{10: 1},
|
|
}, {
|
|
path: key.Path{key.New("foo")},
|
|
expected: map[int]int{20: 1, 21: 1},
|
|
}, {
|
|
path: key.Path{key.New("foo"), key.New("bar")},
|
|
expected: map[int]int{},
|
|
}, {
|
|
path: key.Path{key.New("zap"), key.New("zip")},
|
|
expected: map[int]int{31: 1},
|
|
}, {
|
|
path: key.Path{key.New("zip"), key.New("zap")},
|
|
expected: map[int]int{41: 1},
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
result := make(map[int]int, len(tc.expected))
|
|
m.Visit(tc.path, accumulator(result))
|
|
if diff := test.Diff(tc.expected, result); diff != "" {
|
|
t.Errorf("Test case %v: %s", tc.path, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMapVisitError(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
|
|
m.Set(key.Path{Wildcard, key.New("bar")}, 2)
|
|
|
|
errTest := errors.New("Test")
|
|
|
|
err := m.Visit(key.Path{key.New("foo"), key.New("bar")},
|
|
func(v interface{}) error { return errTest })
|
|
if err != errTest {
|
|
t.Errorf("Unexpected error. Expected: %v, Got: %v", errTest, err)
|
|
}
|
|
err = m.VisitPrefixes(key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
|
|
func(v interface{}) error { return errTest })
|
|
if err != errTest {
|
|
t.Errorf("Unexpected error. Expected: %v, Got: %v", errTest, err)
|
|
}
|
|
}
|
|
|
|
func TestMapGet(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{}, 0)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
|
|
m.Set(key.Path{key.New("foo"), Wildcard}, 2)
|
|
m.Set(key.Path{Wildcard, key.New("bar")}, 3)
|
|
m.Set(key.Path{key.New("zap"), key.New("zip")}, 4)
|
|
m.Set(key.Path{key.New("baz"), key.New("qux")}, nil)
|
|
|
|
testCases := []struct {
|
|
path key.Path
|
|
v interface{}
|
|
ok bool
|
|
}{{
|
|
path: key.Path{},
|
|
v: 0,
|
|
ok: true,
|
|
}, {
|
|
path: key.Path{key.New("foo"), key.New("bar")},
|
|
v: 1,
|
|
ok: true,
|
|
}, {
|
|
path: key.Path{key.New("foo"), Wildcard},
|
|
v: 2,
|
|
ok: true,
|
|
}, {
|
|
path: key.Path{Wildcard, key.New("bar")},
|
|
v: 3,
|
|
ok: true,
|
|
}, {
|
|
path: key.Path{key.New("baz"), key.New("qux")},
|
|
v: nil,
|
|
ok: true,
|
|
}, {
|
|
path: key.Path{key.New("bar"), key.New("foo")},
|
|
v: nil,
|
|
}, {
|
|
path: key.Path{key.New("zap"), Wildcard},
|
|
v: nil,
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
v, ok := m.Get(tc.path)
|
|
if v != tc.v || ok != tc.ok {
|
|
t.Errorf("Test case %v: Expected (v: %v, ok: %t), Got (v: %v, ok: %t)",
|
|
tc.path, tc.v, tc.ok, v, ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func countNodes(m *Map) int {
|
|
if m == nil {
|
|
return 0
|
|
}
|
|
count := 1
|
|
count += countNodes(m.wildcard)
|
|
for _, child := range m.children {
|
|
count += countNodes(child)
|
|
}
|
|
return count
|
|
}
|
|
|
|
func TestMapDelete(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{}, 0)
|
|
m.Set(key.Path{Wildcard}, 1)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 2)
|
|
m.Set(key.Path{key.New("foo"), Wildcard}, 3)
|
|
m.Set(key.Path{key.New("foo")}, 4)
|
|
|
|
n := countNodes(&m)
|
|
if n != 5 {
|
|
t.Errorf("Initial count wrong. Expected: 5, Got: %d", n)
|
|
}
|
|
|
|
testCases := []struct {
|
|
del key.Path // key.Path to delete
|
|
expected bool // expected return value of Delete
|
|
visit key.Path // key.Path to Visit
|
|
before map[int]int // Expected to find items before deletion
|
|
after map[int]int // Expected to find items after deletion
|
|
count int // Count of nodes
|
|
}{{
|
|
del: key.Path{key.New("zap")}, // A no-op Delete
|
|
expected: false,
|
|
visit: key.Path{key.New("foo"), key.New("bar")},
|
|
before: map[int]int{2: 1, 3: 1},
|
|
after: map[int]int{2: 1, 3: 1},
|
|
count: 5,
|
|
}, {
|
|
del: key.Path{key.New("foo"), key.New("bar")},
|
|
expected: true,
|
|
visit: key.Path{key.New("foo"), key.New("bar")},
|
|
before: map[int]int{2: 1, 3: 1},
|
|
after: map[int]int{3: 1},
|
|
count: 4,
|
|
}, {
|
|
del: key.Path{key.New("foo")},
|
|
expected: true,
|
|
visit: key.Path{key.New("foo")},
|
|
before: map[int]int{1: 1, 4: 1},
|
|
after: map[int]int{1: 1},
|
|
count: 4,
|
|
}, {
|
|
del: key.Path{key.New("foo")},
|
|
expected: false,
|
|
visit: key.Path{key.New("foo")},
|
|
before: map[int]int{1: 1},
|
|
after: map[int]int{1: 1},
|
|
count: 4,
|
|
}, {
|
|
del: key.Path{Wildcard},
|
|
expected: true,
|
|
visit: key.Path{key.New("foo")},
|
|
before: map[int]int{1: 1},
|
|
after: map[int]int{},
|
|
count: 3,
|
|
}, {
|
|
del: key.Path{Wildcard},
|
|
expected: false,
|
|
visit: key.Path{key.New("foo")},
|
|
before: map[int]int{},
|
|
after: map[int]int{},
|
|
count: 3,
|
|
}, {
|
|
del: key.Path{key.New("foo"), Wildcard},
|
|
expected: true,
|
|
visit: key.Path{key.New("foo"), key.New("bar")},
|
|
before: map[int]int{3: 1},
|
|
after: map[int]int{},
|
|
count: 1, // Should have deleted "foo" and "bar" nodes
|
|
}, {
|
|
del: key.Path{},
|
|
expected: true,
|
|
visit: key.Path{},
|
|
before: map[int]int{0: 1},
|
|
after: map[int]int{},
|
|
count: 1, // Root node can't be deleted
|
|
}}
|
|
|
|
for i, tc := range testCases {
|
|
beforeResult := make(map[int]int, len(tc.before))
|
|
m.Visit(tc.visit, accumulator(beforeResult))
|
|
if diff := test.Diff(tc.before, beforeResult); diff != "" {
|
|
t.Errorf("Test case %d (%v): %s", i, tc.del, diff)
|
|
}
|
|
|
|
if got := m.Delete(tc.del); got != tc.expected {
|
|
t.Errorf("Test case %d (%v): Unexpected return. Expected %t, Got: %t",
|
|
i, tc.del, tc.expected, got)
|
|
}
|
|
|
|
afterResult := make(map[int]int, len(tc.after))
|
|
m.Visit(tc.visit, accumulator(afterResult))
|
|
if diff := test.Diff(tc.after, afterResult); diff != "" {
|
|
t.Errorf("Test case %d (%v): %s", i, tc.del, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMapVisitPrefixes(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{}, 0)
|
|
m.Set(key.Path{key.New("foo")}, 1)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 2)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 3)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz"), key.New("quux")}, 4)
|
|
m.Set(key.Path{key.New("quux"), key.New("bar")}, 5)
|
|
m.Set(key.Path{key.New("foo"), key.New("quux")}, 6)
|
|
m.Set(key.Path{Wildcard}, 7)
|
|
m.Set(key.Path{key.New("foo"), Wildcard}, 8)
|
|
m.Set(key.Path{Wildcard, key.New("bar")}, 9)
|
|
m.Set(key.Path{Wildcard, key.New("quux")}, 10)
|
|
m.Set(key.Path{key.New("quux"), key.New("quux"), key.New("quux"), key.New("quux")}, 11)
|
|
|
|
testCases := []struct {
|
|
path key.Path
|
|
expected map[int]int
|
|
}{{
|
|
path: key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
|
|
expected: map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 7: 1, 8: 1, 9: 1},
|
|
}, {
|
|
path: key.Path{key.New("zip"), key.New("zap")},
|
|
expected: map[int]int{0: 1, 7: 1},
|
|
}, {
|
|
path: key.Path{key.New("foo"), key.New("zap")},
|
|
expected: map[int]int{0: 1, 1: 1, 8: 1, 7: 1},
|
|
}, {
|
|
path: key.Path{key.New("quux"), key.New("quux"), key.New("quux")},
|
|
expected: map[int]int{0: 1, 7: 1, 10: 1},
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
result := make(map[int]int, len(tc.expected))
|
|
m.VisitPrefixes(tc.path, accumulator(result))
|
|
if diff := test.Diff(tc.expected, result); diff != "" {
|
|
t.Errorf("Test case %v: %s", tc.path, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMapVisitPrefixed(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{}, 0)
|
|
m.Set(key.Path{key.New("qux")}, 1)
|
|
m.Set(key.Path{key.New("foo")}, 2)
|
|
m.Set(key.Path{key.New("foo"), key.New("qux")}, 3)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 4)
|
|
m.Set(key.Path{Wildcard, key.New("bar")}, 5)
|
|
m.Set(key.Path{key.New("foo"), Wildcard}, 6)
|
|
m.Set(key.Path{key.New("qux"), key.New("foo"), key.New("bar")}, 7)
|
|
|
|
testCases := []struct {
|
|
in key.Path
|
|
out map[int]int
|
|
}{{
|
|
in: key.Path{},
|
|
out: map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1},
|
|
}, {
|
|
in: key.Path{key.New("qux")},
|
|
out: map[int]int{1: 1, 5: 1, 7: 1},
|
|
}, {
|
|
in: key.Path{key.New("foo")},
|
|
out: map[int]int{2: 1, 3: 1, 4: 1, 5: 1, 6: 1},
|
|
}, {
|
|
in: key.Path{key.New("foo"), key.New("qux")},
|
|
out: map[int]int{3: 1, 6: 1},
|
|
}, {
|
|
in: key.Path{key.New("foo"), key.New("bar")},
|
|
out: map[int]int{4: 1, 5: 1, 6: 1},
|
|
}, {
|
|
in: key.Path{key.New(int64(0))},
|
|
out: map[int]int{5: 1},
|
|
}, {
|
|
in: key.Path{Wildcard},
|
|
out: map[int]int{5: 1},
|
|
}, {
|
|
in: key.Path{Wildcard, Wildcard},
|
|
out: map[int]int{},
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
out := make(map[int]int, len(tc.out))
|
|
m.VisitPrefixed(tc.in, accumulator(out))
|
|
if diff := test.Diff(tc.out, out); diff != "" {
|
|
t.Errorf("Test case %v: %s", tc.out, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMapString(t *testing.T) {
|
|
m := Map{}
|
|
m.Set(key.Path{}, 0)
|
|
m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
|
|
m.Set(key.Path{key.New("foo"), key.New("quux")}, 2)
|
|
m.Set(key.Path{key.New("foo"), Wildcard}, 3)
|
|
|
|
expected := `Val: 0
|
|
Child "foo":
|
|
Child "*":
|
|
Val: 3
|
|
Child "bar":
|
|
Val: 1
|
|
Child "quux":
|
|
Val: 2
|
|
`
|
|
got := fmt.Sprint(&m)
|
|
|
|
if expected != got {
|
|
t.Errorf("Unexpected string. Expected:\n\n%s\n\nGot:\n\n%s", expected, got)
|
|
}
|
|
}
|
|
|
|
func genWords(count, wordLength int) key.Path {
|
|
chars := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
|
if count+wordLength > len(chars) {
|
|
panic("need more chars")
|
|
}
|
|
result := make(key.Path, count)
|
|
for i := 0; i < count; i++ {
|
|
result[i] = key.New(string(chars[i : i+wordLength]))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func benchmarkPathMap(pathLength, pathDepth int, b *testing.B) {
|
|
// Push pathDepth paths, each of length pathLength
|
|
path := genWords(pathLength, 10)
|
|
words := genWords(pathDepth, 10)
|
|
m := &Map{}
|
|
for _, element := range path {
|
|
m.children = map[key.Key]*Map{}
|
|
for _, word := range words {
|
|
m.children[word] = &Map{}
|
|
}
|
|
m = m.children[element]
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
m.Visit(path, func(v interface{}) error { return nil })
|
|
}
|
|
}
|
|
|
|
func BenchmarkPathMap1x25(b *testing.B) { benchmarkPathMap(1, 25, b) }
|
|
func BenchmarkPathMap10x50(b *testing.B) { benchmarkPathMap(10, 25, b) }
|
|
func BenchmarkPathMap20x50(b *testing.B) { benchmarkPathMap(20, 25, b) }
|