123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // 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"
- "notabug.org/themusicgod1/goarista/key"
- "notabug.org/themusicgod1/goarista/test"
- )
- func accumulator(counter map[int]int) VisitorFunc {
- return func(val interface{}) error {
- counter[val.(int)]++
- return nil
- }
- }
- 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)
- 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{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) }
|