github.com/sunvim/utils@v0.1.0/radix/radix_test.go (about) 1 package radix 2 3 import ( 4 crand "crypto/rand" 5 "fmt" 6 "reflect" 7 "sort" 8 "testing" 9 ) 10 11 func TestRadix(t *testing.T) { 12 var min, max string 13 inp := make(map[string]interface{}) 14 for i := 0; i < 1000; i++ { 15 gen := generateUUID() 16 inp[gen] = i 17 if gen < min || i == 0 { 18 min = gen 19 } 20 if gen > max || i == 0 { 21 max = gen 22 } 23 } 24 25 r := NewFromMap(inp) 26 if r.Len() != len(inp) { 27 t.Fatalf("bad length: %v %v", r.Len(), len(inp)) 28 } 29 30 r.Walk(func(k string, v interface{}) bool { 31 println(k) 32 return false 33 }) 34 35 for k, v := range inp { 36 out, ok := r.Get(k) 37 if !ok { 38 t.Fatalf("missing key: %v", k) 39 } 40 if out != v { 41 t.Fatalf("value mis-match: %v %v", out, v) 42 } 43 } 44 45 // Check min and max 46 outMin, _, _ := r.Minimum() 47 if outMin != min { 48 t.Fatalf("bad minimum: %v %v", outMin, min) 49 } 50 outMax, _, _ := r.Maximum() 51 if outMax != max { 52 t.Fatalf("bad maximum: %v %v", outMax, max) 53 } 54 55 for k, v := range inp { 56 out, ok := r.Delete(k) 57 if !ok { 58 t.Fatalf("missing key: %v", k) 59 } 60 if out != v { 61 t.Fatalf("value mis-match: %v %v", out, v) 62 } 63 } 64 if r.Len() != 0 { 65 t.Fatalf("bad length: %v", r.Len()) 66 } 67 } 68 69 func TestRoot(t *testing.T) { 70 r := New() 71 _, ok := r.Delete("") 72 if ok { 73 t.Fatalf("bad") 74 } 75 _, ok = r.Insert("", true) 76 if ok { 77 t.Fatalf("bad") 78 } 79 val, ok := r.Get("") 80 if !ok || val != true { 81 t.Fatalf("bad: %v", val) 82 } 83 val, ok = r.Delete("") 84 if !ok || val != true { 85 t.Fatalf("bad: %v", val) 86 } 87 } 88 89 func TestDelete(t *testing.T) { 90 91 r := New() 92 93 s := []string{"", "A", "AB"} 94 95 for _, ss := range s { 96 r.Insert(ss, true) 97 } 98 99 for _, ss := range s { 100 _, ok := r.Delete(ss) 101 if !ok { 102 t.Fatalf("bad %q", ss) 103 } 104 } 105 } 106 107 func TestDeletePrefix(t *testing.T) { 108 type exp struct { 109 inp []string 110 prefix string 111 out []string 112 numDeleted int 113 } 114 115 cases := []exp{ 116 {[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3}, 117 {[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1}, 118 {[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6}, 119 {[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1}, 120 {[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0}, 121 } 122 123 for _, test := range cases { 124 r := New() 125 for _, ss := range test.inp { 126 r.Insert(ss, true) 127 } 128 129 deleted := r.DeletePrefix(test.prefix) 130 if deleted != test.numDeleted { 131 t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted) 132 } 133 134 out := []string{} 135 fn := func(s string, v interface{}) bool { 136 out = append(out, s) 137 return false 138 } 139 r.Walk(fn) 140 141 if !reflect.DeepEqual(out, test.out) { 142 t.Fatalf("mis-match: %v %v", out, test.out) 143 } 144 } 145 } 146 147 func TestLongestPrefix(t *testing.T) { 148 r := New() 149 150 keys := []string{ 151 "", 152 "foo", 153 "foobar", 154 "foobarbaz", 155 "foobarbazzip", 156 "foozip", 157 } 158 for _, k := range keys { 159 r.Insert(k, nil) 160 } 161 if r.Len() != len(keys) { 162 t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 163 } 164 165 type exp struct { 166 inp string 167 out string 168 } 169 cases := []exp{ 170 {"a", ""}, 171 {"abc", ""}, 172 {"fo", ""}, 173 {"foo", "foo"}, 174 {"foob", "foo"}, 175 {"foobar", "foobar"}, 176 {"foobarba", "foobar"}, 177 {"foobarbaz", "foobarbaz"}, 178 {"foobarbazzi", "foobarbaz"}, 179 {"foobarbazzip", "foobarbazzip"}, 180 {"foozi", "foo"}, 181 {"foozip", "foozip"}, 182 {"foozipzap", "foozip"}, 183 } 184 for _, test := range cases { 185 m, _, ok := r.LongestPrefix(test.inp) 186 if !ok { 187 t.Fatalf("no match: %v", test) 188 } 189 if m != test.out { 190 t.Fatalf("mis-match: %v %v", m, test) 191 } 192 } 193 } 194 195 func TestWalkPrefix(t *testing.T) { 196 r := New() 197 198 keys := []string{ 199 "foobar", 200 "foo/bar/baz", 201 "foo/baz/bar", 202 "foo/zip/zap", 203 "zipzap", 204 } 205 for _, k := range keys { 206 r.Insert(k, nil) 207 } 208 if r.Len() != len(keys) { 209 t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 210 } 211 212 type exp struct { 213 inp string 214 out []string 215 } 216 cases := []exp{ 217 { 218 "f", 219 []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 220 }, 221 { 222 "foo", 223 []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 224 }, 225 { 226 "foob", 227 []string{"foobar"}, 228 }, 229 { 230 "foo/", 231 []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 232 }, 233 { 234 "foo/b", 235 []string{"foo/bar/baz", "foo/baz/bar"}, 236 }, 237 { 238 "foo/ba", 239 []string{"foo/bar/baz", "foo/baz/bar"}, 240 }, 241 { 242 "foo/bar", 243 []string{"foo/bar/baz"}, 244 }, 245 { 246 "foo/bar/baz", 247 []string{"foo/bar/baz"}, 248 }, 249 { 250 "foo/bar/bazoo", 251 []string{}, 252 }, 253 { 254 "z", 255 []string{"zipzap"}, 256 }, 257 } 258 259 for _, test := range cases { 260 out := []string{} 261 fn := func(s string, v interface{}) bool { 262 out = append(out, s) 263 return false 264 } 265 r.WalkPrefix(test.inp, fn) 266 sort.Strings(out) 267 sort.Strings(test.out) 268 if !reflect.DeepEqual(out, test.out) { 269 t.Fatalf("mis-match: %v %v", out, test.out) 270 } 271 } 272 } 273 274 func TestWalkPath(t *testing.T) { 275 r := New() 276 277 keys := []string{ 278 "foo", 279 "foo/bar", 280 "foo/bar/baz", 281 "foo/baz/bar", 282 "foo/zip/zap", 283 "zipzap", 284 } 285 for _, k := range keys { 286 r.Insert(k, nil) 287 } 288 if r.Len() != len(keys) { 289 t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 290 } 291 292 type exp struct { 293 inp string 294 out []string 295 } 296 cases := []exp{ 297 { 298 "f", 299 []string{}, 300 }, 301 { 302 "foo", 303 []string{"foo"}, 304 }, 305 { 306 "foo/", 307 []string{"foo"}, 308 }, 309 { 310 "foo/ba", 311 []string{"foo"}, 312 }, 313 { 314 "foo/bar", 315 []string{"foo", "foo/bar"}, 316 }, 317 { 318 "foo/bar/baz", 319 []string{"foo", "foo/bar", "foo/bar/baz"}, 320 }, 321 { 322 "foo/bar/bazoo", 323 []string{"foo", "foo/bar", "foo/bar/baz"}, 324 }, 325 { 326 "z", 327 []string{}, 328 }, 329 } 330 331 for _, test := range cases { 332 out := []string{} 333 fn := func(s string, v interface{}) bool { 334 out = append(out, s) 335 return false 336 } 337 r.WalkPath(test.inp, fn) 338 sort.Strings(out) 339 sort.Strings(test.out) 340 if !reflect.DeepEqual(out, test.out) { 341 t.Fatalf("mis-match: %v %v", out, test.out) 342 } 343 } 344 } 345 346 // generateUUID is used to generate a random UUID 347 func generateUUID() string { 348 buf := make([]byte, 16) 349 if _, err := crand.Read(buf); err != nil { 350 panic(fmt.Errorf("failed to read random bytes: %v", err)) 351 } 352 353 return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 354 buf[0:4], 355 buf[4:6], 356 buf[6:8], 357 buf[8:10], 358 buf[10:16]) 359 }