github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/path/path_test.go (about) 1 // Copyright (c) 2017 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 path 6 7 import ( 8 "fmt" 9 "testing" 10 11 "github.com/aristanetworks/goarista/key" 12 "github.com/aristanetworks/goarista/value" 13 ) 14 15 func TestNew(t *testing.T) { 16 tcases := []struct { 17 in []interface{} 18 out key.Path 19 }{ 20 { 21 in: nil, 22 out: key.Path{}, 23 }, { 24 in: []interface{}{}, 25 out: key.Path{}, 26 }, { 27 in: []interface{}{"foo", key.New("bar"), true}, 28 out: key.Path{key.New("foo"), key.New("bar"), key.New(true)}, 29 }, { 30 in: []interface{}{int8(5), int16(5), int32(5), int64(5)}, 31 out: key.Path{key.New(int8(5)), key.New(int16(5)), key.New(int32(5)), 32 key.New(int64(5))}, 33 }, { 34 in: []interface{}{uint8(5), uint16(5), uint32(5), uint64(5)}, 35 out: key.Path{key.New(uint8(5)), key.New(uint16(5)), key.New(uint32(5)), 36 key.New(uint64(5))}, 37 }, { 38 in: []interface{}{float32(5), float64(5)}, 39 out: key.Path{key.New(float32(5)), key.New(float64(5))}, 40 }, { 41 in: []interface{}{customKey{i: &a}, map[string]interface{}{}}, 42 out: key.Path{key.New(customKey{i: &a}), key.New(map[string]interface{}{})}, 43 }, { 44 in: []interface{}{[]string{"A", "B"}, []string{"C"}, "D"}, 45 out: key.Path{key.New("A"), key.New("B"), key.New("C"), key.New("D")}, 46 }, { 47 in: []interface{}{[]string{"A", "B"}, []interface{}{"1", "2"}, "D"}, 48 out: key.Path{key.New("A"), key.New("B"), key.New([]interface{}{"1", "2"}), 49 key.New("D")}, 50 }, { 51 in: []interface{}{[]string{"A", "B"}, 52 []key.Key{key.New("C"), key.New("D")}, 53 key.Path{key.New("E"), key.New("F")}, 54 []key.Path{ 55 key.Path{key.New("G"), key.New("H")}, 56 key.Path{key.New("I"), key.New("J")}, 57 }}, 58 out: key.Path{key.New("A"), key.New("B"), key.New("C"), key.New("D"), 59 key.New("E"), key.New("F"), key.New("G"), key.New("H"), 60 key.New("I"), key.New("J")}, 61 }, 62 } 63 for i, tcase := range tcases { 64 if p := New(tcase.in...); !Equal(p, tcase.out) { 65 t.Fatalf("Test %d failed: %#v != %#v", i, p, tcase.out) 66 } 67 } 68 } 69 70 func TestClone(t *testing.T) { 71 if !Equal(Clone(key.Path{}), key.Path{}) { 72 t.Error("Clone(key.Path{}) != key.Path{}") 73 } 74 a := key.Path{key.New("foo"), key.New("bar")} 75 b, c := Clone(a), Clone(a) 76 b[1] = key.New("baz") 77 if Equal(a, b) || !Equal(a, c) { 78 t.Error("Clone is not making a copied path") 79 } 80 } 81 82 func TestAppend(t *testing.T) { 83 tcases := []struct { 84 a key.Path 85 b []interface{} 86 result key.Path 87 }{ 88 { 89 a: key.Path{}, 90 b: []interface{}{}, 91 result: key.Path{}, 92 }, { 93 a: key.Path{key.New("foo")}, 94 b: []interface{}{}, 95 result: key.Path{key.New("foo")}, 96 }, { 97 a: key.Path{}, 98 b: []interface{}{"foo", key.New("bar")}, 99 result: key.Path{key.New("foo"), key.New("bar")}, 100 }, { 101 a: key.Path{key.New("foo")}, 102 b: []interface{}{int64(0), key.New("bar")}, 103 result: key.Path{key.New("foo"), key.New(int64(0)), key.New("bar")}, 104 }, 105 } 106 for i, tcase := range tcases { 107 if p := Append(tcase.a, tcase.b...); !Equal(p, tcase.result) { 108 t.Fatalf("Test %d failed: %#v != %#v", i, p, tcase.result) 109 } 110 } 111 } 112 113 func TestJoin(t *testing.T) { 114 tcases := []struct { 115 paths []key.Path 116 result key.Path 117 }{ 118 { 119 paths: nil, 120 result: nil, 121 }, { 122 paths: []key.Path{}, 123 result: nil, 124 }, { 125 paths: []key.Path{key.Path{}}, 126 result: nil, 127 }, { 128 paths: []key.Path{key.Path{key.New(true)}, key.Path{}}, 129 result: key.Path{key.New(true)}, 130 }, { 131 paths: []key.Path{key.Path{}, key.Path{key.New(true)}}, 132 result: key.Path{key.New(true)}, 133 }, { 134 paths: []key.Path{key.Path{key.New("foo")}, key.Path{key.New("bar")}}, 135 result: key.Path{key.New("foo"), key.New("bar")}, 136 }, { 137 paths: []key.Path{key.Path{key.New("bar")}, key.Path{key.New("foo")}}, 138 result: key.Path{key.New("bar"), key.New("foo")}, 139 }, { 140 paths: []key.Path{ 141 key.Path{key.New(uint32(0)), key.New(uint64(0))}, 142 key.Path{key.New(int8(0))}, 143 key.Path{key.New(int16(0)), key.New(int32(0))}, 144 key.Path{key.New(int64(0)), key.New(uint8(0)), key.New(uint16(0))}, 145 }, 146 result: key.Path{ 147 key.New(uint32(0)), key.New(uint64(0)), 148 key.New(int8(0)), key.New(int16(0)), 149 key.New(int32(0)), key.New(int64(0)), 150 key.New(uint8(0)), key.New(uint16(0)), 151 }, 152 }, 153 } 154 for i, tcase := range tcases { 155 if p := Join(tcase.paths...); !Equal(p, tcase.result) { 156 t.Fatalf("Test %d failed: %#v != %#v", i, p, tcase.result) 157 } 158 } 159 } 160 161 func TestParent(t *testing.T) { 162 if Parent(key.Path{}) != nil { 163 t.Fatal("Parent of empty key.Path should be nil") 164 } 165 tcases := []struct { 166 in key.Path 167 out key.Path 168 }{ 169 { 170 in: key.Path{key.New("foo")}, 171 out: key.Path{}, 172 }, { 173 in: key.Path{key.New("foo"), key.New("bar")}, 174 out: key.Path{key.New("foo")}, 175 }, { 176 in: key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 177 out: key.Path{key.New("foo"), key.New("bar")}, 178 }, 179 } 180 for _, tcase := range tcases { 181 if !Equal(Parent(tcase.in), tcase.out) { 182 t.Fatalf("Parent of %#v != %#v", tcase.in, tcase.out) 183 } 184 } 185 } 186 187 func TestBase(t *testing.T) { 188 if Base(key.Path{}) != nil { 189 t.Fatal("Base of empty key.Path should be nil") 190 } 191 tcases := []struct { 192 in key.Path 193 out key.Key 194 }{ 195 { 196 in: key.Path{key.New("foo")}, 197 out: key.New("foo"), 198 }, { 199 in: key.Path{key.New("foo"), key.New("bar")}, 200 out: key.New("bar"), 201 }, 202 } 203 for _, tcase := range tcases { 204 if !Base(tcase.in).Equal(tcase.out) { 205 t.Fatalf("Base of %#v != %#v", tcase.in, tcase.out) 206 } 207 } 208 } 209 210 type customKey struct { 211 i *int 212 } 213 214 func (c customKey) String() string { 215 return fmt.Sprintf("customKey=%d", *c.i) 216 } 217 218 func (c customKey) MarshalJSON() ([]byte, error) { 219 return nil, nil 220 } 221 222 func (c customKey) ToBuiltin() interface{} { 223 return nil 224 } 225 226 func (c customKey) Equal(other interface{}) bool { 227 o, ok := other.(customKey) 228 return ok && *c.i == *o.i 229 } 230 231 var ( 232 _ value.Value = customKey{} 233 _ key.Comparable = customKey{} 234 a = 1 235 b = 1 236 ) 237 238 func TestEqual(t *testing.T) { 239 tcases := []struct { 240 a key.Path 241 b key.Path 242 result bool 243 }{ 244 { 245 a: nil, 246 b: nil, 247 result: true, 248 }, { 249 a: nil, 250 b: key.Path{}, 251 result: true, 252 }, { 253 a: key.Path{}, 254 b: nil, 255 result: true, 256 }, { 257 a: key.Path{}, 258 b: key.Path{}, 259 result: true, 260 }, { 261 a: key.Path{}, 262 b: key.Path{key.New("")}, 263 result: false, 264 }, { 265 a: key.Path{Wildcard}, 266 b: key.Path{key.New("foo")}, 267 result: false, 268 }, { 269 a: key.Path{Wildcard}, 270 b: key.Path{Wildcard}, 271 result: true, 272 }, { 273 a: key.Path{key.New("foo")}, 274 b: key.Path{key.New("foo")}, 275 result: true, 276 }, { 277 a: key.Path{key.New(true)}, 278 b: key.Path{key.New(false)}, 279 result: false, 280 }, { 281 a: key.Path{key.New(int32(5))}, 282 b: key.Path{key.New(int64(5))}, 283 result: false, 284 }, { 285 a: key.Path{key.New("foo")}, 286 b: key.Path{key.New("foo"), key.New("bar")}, 287 result: false, 288 }, { 289 a: key.Path{key.New("foo"), key.New("bar")}, 290 b: key.Path{key.New("foo")}, 291 result: false, 292 }, { 293 a: key.Path{key.New(uint8(0)), key.New(int8(0))}, 294 b: key.Path{key.New(int8(0)), key.New(uint8(0))}, 295 result: false, 296 }, 297 // Ensure that we check deep equality. 298 { 299 a: key.Path{key.New(map[string]interface{}{})}, 300 b: key.Path{key.New(map[string]interface{}{})}, 301 result: true, 302 }, { 303 a: key.Path{key.New(customKey{i: &a})}, 304 b: key.Path{key.New(customKey{i: &b})}, 305 result: true, 306 }, 307 } 308 for i, tcase := range tcases { 309 if result := Equal(tcase.a, tcase.b); result != tcase.result { 310 t.Fatalf("Test %d failed: a: %#v; b: %#v, result: %t", 311 i, tcase.a, tcase.b, tcase.result) 312 } 313 } 314 } 315 316 func TestMatch(t *testing.T) { 317 tcases := []struct { 318 a key.Path 319 b key.Path 320 result bool 321 }{ 322 { 323 a: nil, 324 b: nil, 325 result: true, 326 }, { 327 a: nil, 328 b: key.Path{}, 329 result: true, 330 }, { 331 a: key.Path{}, 332 b: nil, 333 result: true, 334 }, { 335 a: key.Path{}, 336 b: key.Path{}, 337 result: true, 338 }, { 339 a: key.Path{}, 340 b: key.Path{key.New("foo")}, 341 result: false, 342 }, { 343 a: key.Path{Wildcard}, 344 b: key.Path{key.New("foo")}, 345 result: true, 346 }, { 347 a: key.Path{key.New("foo")}, 348 b: key.Path{Wildcard}, 349 result: false, 350 }, { 351 a: key.Path{Wildcard}, 352 b: key.Path{key.New("foo"), key.New("bar")}, 353 result: false, 354 }, { 355 a: key.Path{Wildcard, Wildcard}, 356 b: key.Path{key.New(int64(0))}, 357 result: false, 358 }, { 359 a: key.Path{Wildcard, Wildcard}, 360 b: key.Path{key.New(int64(0)), key.New(int32(0))}, 361 result: true, 362 }, { 363 a: key.Path{Wildcard, key.New(false)}, 364 b: key.Path{key.New(true), Wildcard}, 365 result: false, 366 }, 367 } 368 for i, tcase := range tcases { 369 if result := Match(tcase.a, tcase.b); result != tcase.result { 370 t.Fatalf("Test %d failed: a: %#v; b: %#v, result: %t", 371 i, tcase.a, tcase.b, tcase.result) 372 } 373 } 374 } 375 376 func TestHasElement(t *testing.T) { 377 tcases := []struct { 378 a key.Path 379 b key.Key 380 result bool 381 }{ 382 { 383 a: nil, 384 b: nil, 385 result: false, 386 }, { 387 a: nil, 388 b: key.New("foo"), 389 result: false, 390 }, { 391 a: key.Path{}, 392 b: nil, 393 result: false, 394 }, { 395 a: key.Path{key.New("foo")}, 396 b: nil, 397 result: false, 398 }, { 399 a: key.Path{key.New("foo")}, 400 b: key.New("foo"), 401 result: true, 402 }, { 403 a: key.Path{key.New(true)}, 404 b: key.New("true"), 405 result: false, 406 }, { 407 a: key.Path{key.New("foo"), key.New("bar")}, 408 b: key.New("bar"), 409 result: true, 410 }, { 411 a: key.Path{key.New(map[string]interface{}{})}, 412 b: key.New(map[string]interface{}{}), 413 result: true, 414 }, { 415 a: key.Path{key.New(map[string]interface{}{"foo": "a"})}, 416 b: key.New(map[string]interface{}{"bar": "a"}), 417 result: false, 418 }, 419 } 420 for i, tcase := range tcases { 421 if result := HasElement(tcase.a, tcase.b); result != tcase.result { 422 t.Errorf("Test %d failed: a: %#v; b: %#v, result: %t, expected: %t", 423 i, tcase.a, tcase.b, result, tcase.result) 424 } 425 } 426 } 427 428 func TestHasPrefix(t *testing.T) { 429 tcases := []struct { 430 a key.Path 431 b key.Path 432 result bool 433 }{ 434 { 435 a: nil, 436 b: nil, 437 result: true, 438 }, { 439 a: nil, 440 b: key.Path{}, 441 result: true, 442 }, { 443 a: key.Path{}, 444 b: nil, 445 result: true, 446 }, { 447 a: key.Path{}, 448 b: key.Path{}, 449 result: true, 450 }, { 451 a: key.Path{}, 452 b: key.Path{key.New("foo")}, 453 result: false, 454 }, { 455 a: key.Path{key.New("foo")}, 456 b: key.Path{}, 457 result: true, 458 }, { 459 a: key.Path{key.New(true)}, 460 b: key.Path{key.New(false)}, 461 result: false, 462 }, { 463 a: key.Path{key.New("foo"), key.New("bar")}, 464 b: key.Path{key.New("bar"), key.New("foo")}, 465 result: false, 466 }, { 467 a: key.Path{key.New(int8(0)), key.New(uint8(0))}, 468 b: key.Path{key.New(uint8(0)), key.New(uint8(0))}, 469 result: false, 470 }, { 471 a: key.Path{key.New(true), key.New(true)}, 472 b: key.Path{key.New(true), key.New(true), key.New(true)}, 473 result: false, 474 }, { 475 a: key.Path{key.New(true), key.New(true), key.New(true)}, 476 b: key.Path{key.New(true), key.New(true)}, 477 result: true, 478 }, { 479 a: key.Path{Wildcard, key.New(int32(0)), Wildcard}, 480 b: key.Path{key.New(int64(0)), Wildcard}, 481 result: false, 482 }, 483 } 484 for i, tcase := range tcases { 485 if result := HasPrefix(tcase.a, tcase.b); result != tcase.result { 486 t.Fatalf("Test %d failed: a: %#v; b: %#v, result: %t", 487 i, tcase.a, tcase.b, tcase.result) 488 } 489 } 490 } 491 492 func TestMatchPrefix(t *testing.T) { 493 tcases := []struct { 494 a key.Path 495 b key.Path 496 result bool 497 }{ 498 { 499 a: nil, 500 b: nil, 501 result: true, 502 }, { 503 a: nil, 504 b: key.Path{}, 505 result: true, 506 }, { 507 a: key.Path{}, 508 b: nil, 509 result: true, 510 }, { 511 a: key.Path{}, 512 b: key.Path{}, 513 result: true, 514 }, { 515 a: key.Path{}, 516 b: key.Path{key.New("foo")}, 517 result: false, 518 }, { 519 a: key.Path{key.New("foo")}, 520 b: key.Path{}, 521 result: true, 522 }, { 523 a: key.Path{key.New("foo")}, 524 b: key.Path{Wildcard}, 525 result: false, 526 }, { 527 a: key.Path{Wildcard}, 528 b: key.Path{key.New("foo")}, 529 result: true, 530 }, { 531 a: key.Path{Wildcard}, 532 b: key.Path{key.New("foo"), key.New("bar")}, 533 result: false, 534 }, { 535 a: key.Path{Wildcard, key.New(true)}, 536 b: key.Path{key.New(false), Wildcard}, 537 result: false, 538 }, { 539 a: key.Path{Wildcard, key.New(int32(0)), key.New(int16(0))}, 540 b: key.Path{key.New(int64(0)), key.New(int32(0))}, 541 result: true, 542 }, 543 } 544 for i, tcase := range tcases { 545 if result := MatchPrefix(tcase.a, tcase.b); result != tcase.result { 546 t.Fatalf("Test %d failed: a: %#v; b: %#v, result: %t", 547 i, tcase.a, tcase.b, tcase.result) 548 } 549 } 550 } 551 552 func TestFromString(t *testing.T) { 553 tcases := []struct { 554 in string 555 out key.Path 556 }{ 557 { 558 in: "", 559 out: key.Path{}, 560 }, { 561 in: "/", 562 out: key.Path{}, 563 }, { 564 in: "//", 565 out: key.Path{key.New(""), key.New("")}, 566 }, { 567 in: "foo", 568 out: key.Path{key.New("foo")}, 569 }, { 570 in: "/foo", 571 out: key.Path{key.New("foo")}, 572 }, { 573 in: "foo/bar", 574 out: key.Path{key.New("foo"), key.New("bar")}, 575 }, { 576 in: "/foo/bar", 577 out: key.Path{key.New("foo"), key.New("bar")}, 578 }, { 579 in: "foo/bar/baz", 580 out: key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 581 }, { 582 in: "/foo/bar/baz", 583 out: key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 584 }, { 585 in: "0/123/456/789", 586 out: key.Path{key.New("0"), key.New("123"), key.New("456"), key.New("789")}, 587 }, { 588 in: "/0/123/456/789", 589 out: key.Path{key.New("0"), key.New("123"), key.New("456"), key.New("789")}, 590 }, { 591 in: "`~!@#$%^&*()_+{}\\/|[];':\"<>?,./", 592 out: key.Path{key.New("`~!@#$%^&*()_+{}\\"), key.New("|[];':\"<>?,."), key.New("")}, 593 }, { 594 in: "/`~!@#$%^&*()_+{}\\/|[];':\"<>?,./", 595 out: key.Path{key.New("`~!@#$%^&*()_+{}\\"), key.New("|[];':\"<>?,."), key.New("")}, 596 }, 597 } 598 for i, tcase := range tcases { 599 if p := FromString(tcase.in); !Equal(p, tcase.out) { 600 t.Fatalf("Test %d failed: %#v != %#v", i, p, tcase.out) 601 } 602 } 603 } 604 605 func TestString(t *testing.T) { 606 tcases := []struct { 607 in key.Path 608 out string 609 }{ 610 { 611 in: key.Path{}, 612 out: "/", 613 }, { 614 in: key.Path{key.New("")}, 615 out: "/", 616 }, { 617 in: key.Path{key.New("foo")}, 618 out: "/foo", 619 }, { 620 in: key.Path{key.New("foo"), key.New("bar")}, 621 out: "/foo/bar", 622 }, { 623 in: key.Path{key.New("/foo"), key.New("bar")}, 624 out: "//foo/bar", 625 }, { 626 in: key.Path{key.New("foo"), key.New("bar/")}, 627 out: "/foo/bar/", 628 }, { 629 in: key.Path{key.New(""), key.New("foo"), key.New("bar")}, 630 out: "//foo/bar", 631 }, { 632 in: key.Path{key.New("foo"), key.New("bar"), key.New("")}, 633 out: "/foo/bar/", 634 }, { 635 in: key.Path{key.New("/"), key.New("foo"), key.New("bar")}, 636 out: "///foo/bar", 637 }, { 638 in: key.Path{key.New("foo"), key.New("bar"), key.New("/")}, 639 out: "/foo/bar//", 640 }, 641 } 642 for i, tcase := range tcases { 643 if s := tcase.in.String(); s != tcase.out { 644 t.Fatalf("Test %d failed: %s != %s", i, s, tcase.out) 645 } 646 } 647 } 648 649 func BenchmarkJoin(b *testing.B) { 650 generate := func(n int) []key.Path { 651 paths := make([]key.Path, 0, n) 652 for i := 0; i < n; i++ { 653 paths = append(paths, key.Path{key.New("foo")}) 654 } 655 return paths 656 } 657 benchmarks := map[string][]key.Path{ 658 "10 key.Paths": generate(10), 659 "100 key.Paths": generate(100), 660 "1000 key.Paths": generate(1000), 661 "10000 key.Paths": generate(10000), 662 } 663 for name, benchmark := range benchmarks { 664 b.Run(name, func(b *testing.B) { 665 for i := 0; i < b.N; i++ { 666 Join(benchmark...) 667 } 668 }) 669 } 670 } 671 672 func BenchmarkHasElement(b *testing.B) { 673 element := key.New("waldo") 674 generate := func(n, loc int) key.Path { 675 path := make(key.Path, n) 676 for i := 0; i < n; i++ { 677 if i == loc { 678 path[i] = element 679 } else { 680 path[i] = key.New(int8(0)) 681 } 682 } 683 return path 684 } 685 benchmarks := map[string]key.Path{ 686 "10 Elements Index 0": generate(10, 0), 687 "10 Elements Index 4": generate(10, 4), 688 "10 Elements Index 9": generate(10, 9), 689 "100 Elements Index 0": generate(100, 0), 690 "100 Elements Index 49": generate(100, 49), 691 "100 Elements Index 99": generate(100, 99), 692 "1000 Elements Index 0": generate(1000, 0), 693 "1000 Elements Index 499": generate(1000, 499), 694 "1000 Elements Index 999": generate(1000, 999), 695 } 696 for name, benchmark := range benchmarks { 697 b.Run(name, func(b *testing.B) { 698 for i := 0; i < b.N; i++ { 699 HasElement(benchmark, element) 700 } 701 }) 702 } 703 } 704 705 func TestAppendShouldCopy(t *testing.T) { 706 p := New("a", "b") 707 p = Append(p, "c") 708 d := Append(p, "d") 709 e := Append(p, "e") 710 711 if Equal(d, e) { 712 t.Errorf("paths d and e should not be equal: %s", e) 713 } 714 }