github.com/psiphon-inc/goarista@v0.0.0-20160825065156-d002785f4c67/pathmap/pathmap_test.go (about) 1 // Copyright (C) 2016 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package pathmap 6 7 import ( 8 "errors" 9 "testing" 10 11 "github.com/aristanetworks/goarista/test" 12 ) 13 14 func accumulator(counter map[int]int) VisitorFunc { 15 return func(val interface{}) error { 16 counter[val.(int)]++ 17 return nil 18 } 19 } 20 21 func TestVisit(t *testing.T) { 22 m := New() 23 m.Set([]string{"foo", "bar", "baz"}, 1) 24 m.Set([]string{"*", "bar", "baz"}, 2) 25 m.Set([]string{"*", "*", "baz"}, 3) 26 m.Set([]string{"*", "*", "*"}, 4) 27 m.Set([]string{"foo", "*", "*"}, 5) 28 m.Set([]string{"foo", "bar", "*"}, 6) 29 m.Set([]string{"foo", "*", "baz"}, 7) 30 m.Set([]string{"*", "bar", "*"}, 8) 31 32 m.Set([]string{}, 10) 33 34 m.Set([]string{"*"}, 20) 35 m.Set([]string{"foo"}, 21) 36 37 m.Set([]string{"zap", "zip"}, 30) 38 m.Set([]string{"zap", "zip"}, 31) 39 40 m.Set([]string{"zip", "*"}, 40) 41 m.Set([]string{"zip", "*"}, 41) 42 43 testCases := []struct { 44 path []string 45 expected map[int]int 46 }{{ 47 path: []string{"foo", "bar", "baz"}, 48 expected: map[int]int{1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1}, 49 }, { 50 path: []string{"qux", "bar", "baz"}, 51 expected: map[int]int{2: 1, 3: 1, 4: 1, 8: 1}, 52 }, { 53 path: []string{"foo", "qux", "baz"}, 54 expected: map[int]int{3: 1, 4: 1, 5: 1, 7: 1}, 55 }, { 56 path: []string{"foo", "bar", "qux"}, 57 expected: map[int]int{4: 1, 5: 1, 6: 1, 8: 1}, 58 }, { 59 path: []string{}, 60 expected: map[int]int{10: 1}, 61 }, { 62 path: []string{"foo"}, 63 expected: map[int]int{20: 1, 21: 1}, 64 }, { 65 path: []string{"foo", "bar"}, 66 expected: map[int]int{}, 67 }, { 68 path: []string{"zap", "zip"}, 69 expected: map[int]int{31: 1}, 70 }, { 71 path: []string{"zip", "zap"}, 72 expected: map[int]int{41: 1}, 73 }} 74 75 for _, tc := range testCases { 76 result := make(map[int]int, len(tc.expected)) 77 m.Visit(tc.path, accumulator(result)) 78 if diff := test.Diff(tc.expected, result); diff != "" { 79 t.Errorf("Test case %v: %s", tc.path, diff) 80 } 81 } 82 } 83 84 func TestVisitError(t *testing.T) { 85 m := New() 86 m.Set([]string{"foo", "bar"}, 1) 87 m.Set([]string{"*", "bar"}, 2) 88 89 errTest := errors.New("Test") 90 91 err := m.Visit([]string{"foo", "bar"}, func(v interface{}) error { return errTest }) 92 if err != errTest { 93 t.Errorf("Unexpected error. Expected: %v, Got: %v", errTest, err) 94 } 95 } 96 97 func TestGet(t *testing.T) { 98 m := New() 99 m.Set([]string{}, 0) 100 m.Set([]string{"foo", "bar"}, 1) 101 m.Set([]string{"foo", "*"}, 2) 102 m.Set([]string{"*", "bar"}, 3) 103 m.Set([]string{"zap", "zip"}, 4) 104 105 testCases := []struct { 106 path []string 107 expected interface{} 108 }{{ 109 path: []string{}, 110 expected: 0, 111 }, { 112 path: []string{"foo", "bar"}, 113 expected: 1, 114 }, { 115 path: []string{"foo", "*"}, 116 expected: 2, 117 }, { 118 path: []string{"*", "bar"}, 119 expected: 3, 120 }, { 121 path: []string{"bar", "foo"}, 122 expected: nil, 123 }, { 124 path: []string{"zap", "*"}, 125 expected: nil, 126 }} 127 128 for _, tc := range testCases { 129 got := m.Get(tc.path) 130 if got != tc.expected { 131 t.Errorf("Test case %v: Expected %v, Got %v", 132 tc.path, tc.expected, got) 133 } 134 } 135 } 136 137 func countNodes(n *node) int { 138 if n == nil { 139 return 0 140 } 141 count := 1 142 count += countNodes(n.wildcard) 143 for _, child := range n.children { 144 count += countNodes(child) 145 } 146 return count 147 } 148 149 func TestDelete(t *testing.T) { 150 m := New() 151 m.Set([]string{}, 0) 152 m.Set([]string{"*"}, 1) 153 m.Set([]string{"foo", "bar"}, 2) 154 m.Set([]string{"foo", "*"}, 3) 155 156 n := countNodes(m.(*node)) 157 if n != 5 { 158 t.Errorf("Initial count wrong. Expected: 5, Got: %d", n) 159 } 160 161 testCases := []struct { 162 del []string // Path to delete 163 expected bool // expected return value of Delete 164 visit []string // Path to Visit 165 before map[int]int // Expected to find items before deletion 166 after map[int]int // Expected to find items after deletion 167 count int // Count of nodes 168 }{{ 169 del: []string{"zap"}, // A no-op Delete 170 expected: false, 171 visit: []string{"foo", "bar"}, 172 before: map[int]int{2: 1, 3: 1}, 173 after: map[int]int{2: 1, 3: 1}, 174 count: 5, 175 }, { 176 del: []string{"foo", "bar"}, 177 expected: true, 178 visit: []string{"foo", "bar"}, 179 before: map[int]int{2: 1, 3: 1}, 180 after: map[int]int{3: 1}, 181 count: 4, 182 }, { 183 del: []string{"*"}, 184 expected: true, 185 visit: []string{"foo"}, 186 before: map[int]int{1: 1}, 187 after: map[int]int{}, 188 count: 3, 189 }, { 190 del: []string{"*"}, 191 expected: false, 192 visit: []string{"foo"}, 193 before: map[int]int{}, 194 after: map[int]int{}, 195 count: 3, 196 }, { 197 del: []string{"foo", "*"}, 198 expected: true, 199 visit: []string{"foo", "bar"}, 200 before: map[int]int{3: 1}, 201 after: map[int]int{}, 202 count: 1, // Should have deleted "foo" and "bar" nodes 203 }, { 204 del: []string{}, 205 expected: true, 206 visit: []string{}, 207 before: map[int]int{0: 1}, 208 after: map[int]int{}, 209 count: 1, // Root node can't be deleted 210 }} 211 212 for i, tc := range testCases { 213 beforeResult := make(map[int]int, len(tc.before)) 214 m.Visit(tc.visit, accumulator(beforeResult)) 215 if diff := test.Diff(tc.before, beforeResult); diff != "" { 216 t.Errorf("Test case %d (%v): %s", i, tc.del, diff) 217 } 218 219 if got := m.Delete(tc.del); got != tc.expected { 220 t.Errorf("Test case %d (%v): Unexpected return. Expected %t, Got: %t", 221 i, tc.del, tc.expected, got) 222 } 223 224 afterResult := make(map[int]int, len(tc.after)) 225 m.Visit(tc.visit, accumulator(afterResult)) 226 if diff := test.Diff(tc.after, afterResult); diff != "" { 227 t.Errorf("Test case %d (%v): %s", i, tc.del, diff) 228 } 229 } 230 } 231 232 func genWords(count, wordLength int) []string { 233 chars := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 234 if count+wordLength > len(chars) { 235 panic("need more chars") 236 } 237 result := make([]string, count) 238 for i := 0; i < count; i++ { 239 result[i] = string(chars[i : i+wordLength]) 240 } 241 return result 242 } 243 244 func benchmarkPathMap(pathLength, pathDepth int, b *testing.B) { 245 m := New() 246 247 // Push pathDepth paths, each of length pathLength 248 path := genWords(pathLength, 10) 249 words := genWords(pathDepth, 10) 250 n := m.(*node) 251 for _, element := range path { 252 n.children = map[string]*node{} 253 for _, word := range words { 254 n.children[word] = &node{} 255 } 256 n = n.children[element] 257 } 258 b.ResetTimer() 259 for i := 0; i < b.N; i++ { 260 m.Visit(path, func(v interface{}) error { return nil }) 261 } 262 } 263 264 func BenchmarkPathMap1x25(b *testing.B) { benchmarkPathMap(1, 25, b) } 265 func BenchmarkPathMap10x50(b *testing.B) { benchmarkPathMap(10, 25, b) } 266 func BenchmarkPathMap20x50(b *testing.B) { benchmarkPathMap(20, 25, b) }