1package reflectwalk 2 3import ( 4 "fmt" 5 "reflect" 6 "testing" 7) 8 9type TestEnterExitWalker struct { 10 Locs []Location 11} 12 13func (t *TestEnterExitWalker) Enter(l Location) error { 14 if t.Locs == nil { 15 t.Locs = make([]Location, 0, 5) 16 } 17 18 t.Locs = append(t.Locs, l) 19 return nil 20} 21 22func (t *TestEnterExitWalker) Exit(l Location) error { 23 t.Locs = append(t.Locs, l) 24 return nil 25} 26 27type TestPointerWalker struct { 28 pointers []bool 29 count int 30 enters int 31 exits int 32} 33 34func (t *TestPointerWalker) PointerEnter(v bool) error { 35 t.pointers = append(t.pointers, v) 36 t.enters++ 37 if v { 38 t.count++ 39 } 40 return nil 41} 42 43func (t *TestPointerWalker) PointerExit(v bool) error { 44 t.exits++ 45 if t.pointers[len(t.pointers)-1] != v { 46 return fmt.Errorf("bad pointer exit '%t' at exit %d", v, t.exits) 47 } 48 t.pointers = t.pointers[:len(t.pointers)-1] 49 return nil 50} 51 52type TestPrimitiveWalker struct { 53 Value reflect.Value 54} 55 56func (t *TestPrimitiveWalker) Primitive(v reflect.Value) error { 57 t.Value = v 58 return nil 59} 60 61type TestPrimitiveCountWalker struct { 62 Count int 63} 64 65func (t *TestPrimitiveCountWalker) Primitive(v reflect.Value) error { 66 t.Count += 1 67 return nil 68} 69 70type TestPrimitiveReplaceWalker struct { 71 Value reflect.Value 72} 73 74func (t *TestPrimitiveReplaceWalker) Primitive(v reflect.Value) error { 75 v.Set(reflect.ValueOf("bar")) 76 return nil 77} 78 79type TestMapWalker struct { 80 MapVal reflect.Value 81 Keys map[string]bool 82 Values map[string]bool 83} 84 85func (t *TestMapWalker) Map(m reflect.Value) error { 86 t.MapVal = m 87 return nil 88} 89 90func (t *TestMapWalker) MapElem(m, k, v reflect.Value) error { 91 if t.Keys == nil { 92 t.Keys = make(map[string]bool) 93 t.Values = make(map[string]bool) 94 } 95 96 t.Keys[k.Interface().(string)] = true 97 t.Values[v.Interface().(string)] = true 98 return nil 99} 100 101type TestMapElemReplaceWalker struct { 102 ValueFn func(v reflect.Value) reflect.Value 103} 104 105func (t *TestMapElemReplaceWalker) Map(m reflect.Value) error { 106 return nil 107} 108 109func (t *TestMapElemReplaceWalker) MapElem(m, k, v reflect.Value) error { 110 m.SetMapIndex(k, t.ValueFn(v)) 111 return nil 112} 113 114type TestSliceWalker struct { 115 Count int 116 SliceVal reflect.Value 117} 118 119func (t *TestSliceWalker) Slice(v reflect.Value) error { 120 t.SliceVal = v 121 return nil 122} 123 124func (t *TestSliceWalker) SliceElem(int, reflect.Value) error { 125 t.Count++ 126 return nil 127} 128 129type TestArrayWalker struct { 130 Count int 131 ArrayVal reflect.Value 132} 133 134func (t *TestArrayWalker) Array(v reflect.Value) error { 135 t.ArrayVal = v 136 return nil 137} 138 139func (t *TestArrayWalker) ArrayElem(int, reflect.Value) error { 140 t.Count++ 141 return nil 142} 143 144type TestStructWalker struct { 145 Fields []string 146} 147 148func (t *TestStructWalker) Struct(v reflect.Value) error { 149 return nil 150} 151 152func (t *TestStructWalker) StructField(sf reflect.StructField, v reflect.Value) error { 153 if t.Fields == nil { 154 t.Fields = make([]string, 0, 1) 155 } 156 157 t.Fields = append(t.Fields, sf.Name) 158 return nil 159} 160 161func TestTestStructs(t *testing.T) { 162 var raw interface{} 163 raw = new(TestEnterExitWalker) 164 if _, ok := raw.(EnterExitWalker); !ok { 165 t.Fatal("EnterExitWalker is bad") 166 } 167 168 raw = new(TestPrimitiveWalker) 169 if _, ok := raw.(PrimitiveWalker); !ok { 170 t.Fatal("PrimitiveWalker is bad") 171 } 172 173 raw = new(TestMapWalker) 174 if _, ok := raw.(MapWalker); !ok { 175 t.Fatal("MapWalker is bad") 176 } 177 178 raw = new(TestSliceWalker) 179 if _, ok := raw.(SliceWalker); !ok { 180 t.Fatal("SliceWalker is bad") 181 } 182 183 raw = new(TestArrayWalker) 184 if _, ok := raw.(ArrayWalker); !ok { 185 t.Fatal("ArrayWalker is bad") 186 } 187 188 raw = new(TestStructWalker) 189 if _, ok := raw.(StructWalker); !ok { 190 t.Fatal("StructWalker is bad") 191 } 192} 193 194func TestWalk_Basic(t *testing.T) { 195 w := new(TestPrimitiveWalker) 196 197 type S struct { 198 Foo string 199 } 200 201 data := &S{ 202 Foo: "foo", 203 } 204 205 err := Walk(data, w) 206 if err != nil { 207 t.Fatalf("err: %s", err) 208 } 209 210 if w.Value.Kind() != reflect.String { 211 t.Fatalf("bad: %#v", w.Value) 212 } 213} 214 215func TestWalk_Basic_Replace(t *testing.T) { 216 w := new(TestPrimitiveReplaceWalker) 217 218 type S struct { 219 Foo string 220 Bar []interface{} 221 } 222 223 data := &S{ 224 Foo: "foo", 225 Bar: []interface{}{[]string{"what"}}, 226 } 227 228 err := Walk(data, w) 229 if err != nil { 230 t.Fatalf("err: %s", err) 231 } 232 233 if data.Foo != "bar" { 234 t.Fatalf("bad: %#v", data.Foo) 235 } 236 if data.Bar[0].([]string)[0] != "bar" { 237 t.Fatalf("bad: %#v", data.Bar) 238 } 239} 240 241func TestWalk_Basic_ReplaceInterface(t *testing.T) { 242 w := new(TestPrimitiveReplaceWalker) 243 244 type S struct { 245 Foo []interface{} 246 } 247 248 data := &S{ 249 Foo: []interface{}{"foo"}, 250 } 251 252 err := Walk(data, w) 253 if err != nil { 254 t.Fatalf("err: %s", err) 255 } 256} 257 258func TestWalk_EnterExit(t *testing.T) { 259 w := new(TestEnterExitWalker) 260 261 type S struct { 262 A string 263 M map[string]string 264 } 265 266 data := &S{ 267 A: "foo", 268 M: map[string]string{ 269 "a": "b", 270 }, 271 } 272 273 err := Walk(data, w) 274 if err != nil { 275 t.Fatalf("err: %s", err) 276 } 277 278 expected := []Location{ 279 WalkLoc, 280 Struct, 281 StructField, 282 StructField, 283 StructField, 284 Map, 285 MapKey, 286 MapKey, 287 MapValue, 288 MapValue, 289 Map, 290 StructField, 291 Struct, 292 WalkLoc, 293 } 294 if !reflect.DeepEqual(w.Locs, expected) { 295 t.Fatalf("Bad: %#v", w.Locs) 296 } 297} 298 299func TestWalk_Interface(t *testing.T) { 300 w := new(TestPrimitiveCountWalker) 301 302 type S struct { 303 Foo string 304 Bar []interface{} 305 } 306 307 var data interface{} = &S{ 308 Foo: "foo", 309 Bar: []interface{}{[]string{"bar", "what"}, "baz"}, 310 } 311 312 err := Walk(data, w) 313 if err != nil { 314 t.Fatalf("err: %s", err) 315 } 316 317 if w.Count != 4 { 318 t.Fatalf("bad: %#v", w.Count) 319 } 320} 321 322func TestWalk_Interface_nil(t *testing.T) { 323 w := new(TestPrimitiveCountWalker) 324 325 type S struct { 326 Bar interface{} 327 } 328 329 var data interface{} = &S{} 330 331 err := Walk(data, w) 332 if err != nil { 333 t.Fatalf("err: %s", err) 334 } 335} 336 337func TestWalk_Map(t *testing.T) { 338 w := new(TestMapWalker) 339 340 type S struct { 341 Foo map[string]string 342 } 343 344 data := &S{ 345 Foo: map[string]string{ 346 "foo": "foov", 347 "bar": "barv", 348 }, 349 } 350 351 err := Walk(data, w) 352 if err != nil { 353 t.Fatalf("err: %s", err) 354 } 355 356 if !reflect.DeepEqual(w.MapVal.Interface(), data.Foo) { 357 t.Fatalf("Bad: %#v", w.MapVal.Interface()) 358 } 359 360 expectedK := map[string]bool{"foo": true, "bar": true} 361 if !reflect.DeepEqual(w.Keys, expectedK) { 362 t.Fatalf("Bad keys: %#v", w.Keys) 363 } 364 365 expectedV := map[string]bool{"foov": true, "barv": true} 366 if !reflect.DeepEqual(w.Values, expectedV) { 367 t.Fatalf("Bad values: %#v", w.Values) 368 } 369} 370 371func TestWalk_Map_ReplaceValue(t *testing.T) { 372 w := &TestMapElemReplaceWalker{ 373 ValueFn: func(v reflect.Value) reflect.Value { 374 if v.Type().Kind() == reflect.String { 375 return reflect.ValueOf("replaced") 376 } 377 378 if v.Type().Kind() == reflect.Interface { 379 if elem := v.Elem(); elem.Type() == reflect.TypeOf(map[string]interface{}{}) { 380 newMap := make(map[string]interface{}) 381 for _, k := range elem.MapKeys() { 382 newMap[k.String()] = elem.MapIndex(k).Interface() 383 } 384 newMap["extra-replaced"] = "not-replaced" 385 return reflect.ValueOf(newMap) 386 } else if elem.Type().Kind() == reflect.String { 387 return reflect.ValueOf("replaced") 388 } 389 } 390 391 return v 392 }, 393 } 394 395 type S struct { 396 Foo map[string]interface{} 397 } 398 399 data := &S{ 400 Foo: map[string]interface{}{ 401 "foo": map[string]interface{}{ 402 "bar": map[string]string{"baz": "should-get-replaced"}, 403 }, 404 }, 405 } 406 407 expected := &S{ 408 Foo: map[string]interface{}{ 409 "foo": map[string]interface{}{ 410 "bar": map[string]string{"baz": "replaced"}, 411 "extra-replaced": "replaced", 412 }, 413 }, 414 } 415 416 err := Walk(data, w) 417 if err != nil { 418 t.Fatalf("err: %v", err) 419 } 420 421 if !reflect.DeepEqual(data, expected) { 422 t.Fatalf("Values not equal: %#v", data) 423 } 424} 425 426func TestWalk_Pointer(t *testing.T) { 427 w := new(TestPointerWalker) 428 429 type S struct { 430 Foo string 431 Bar *string 432 Baz **string 433 } 434 435 s := "" 436 sp := &s 437 438 data := &S{ 439 Baz: &sp, 440 } 441 442 err := Walk(data, w) 443 if err != nil { 444 t.Fatalf("err: %s", err) 445 } 446 447 if w.enters != 5 { 448 t.Fatal("expected 4 values, saw", w.enters) 449 } 450 451 if w.count != 4 { 452 t.Fatal("exptec 3 pointers, saw", w.count) 453 } 454 455 if w.exits != w.enters { 456 t.Fatalf("number of enters (%d) and exits (%d) don't match", w.enters, w.exits) 457 } 458} 459 460func TestWalk_PointerPointer(t *testing.T) { 461 w := new(TestPointerWalker) 462 463 s := "" 464 sp := &s 465 pp := &sp 466 467 err := Walk(pp, w) 468 if err != nil { 469 t.Fatalf("err: %s", err) 470 } 471 472 if w.enters != 2 { 473 t.Fatal("expected 2 values, saw", w.enters) 474 } 475 476 if w.count != 2 { 477 t.Fatal("expected 2 pointers, saw", w.count) 478 } 479 480 if w.exits != w.enters { 481 t.Fatalf("number of enters (%d) and exits (%d) don't match", w.enters, w.exits) 482 } 483} 484 485func TestWalk_Slice(t *testing.T) { 486 w := new(TestSliceWalker) 487 488 type S struct { 489 Foo []string 490 } 491 492 data := &S{ 493 Foo: []string{"a", "b", "c"}, 494 } 495 496 err := Walk(data, w) 497 if err != nil { 498 t.Fatalf("err: %s", err) 499 } 500 501 if !reflect.DeepEqual(w.SliceVal.Interface(), data.Foo) { 502 t.Fatalf("bad: %#v", w.SliceVal.Interface()) 503 } 504 505 if w.Count != 3 { 506 t.Fatalf("Bad count: %d", w.Count) 507 } 508} 509 510func TestWalk_SliceWithPtr(t *testing.T) { 511 w := new(TestSliceWalker) 512 513 // This is key, the panic only happened when the slice field was 514 // an interface! 515 type I interface{} 516 517 type S struct { 518 Foo []I 519 } 520 521 type Empty struct{} 522 523 data := &S{ 524 Foo: []I{&Empty{}}, 525 } 526 527 err := Walk(data, w) 528 if err != nil { 529 t.Fatalf("err: %s", err) 530 } 531 532 if !reflect.DeepEqual(w.SliceVal.Interface(), data.Foo) { 533 t.Fatalf("bad: %#v", w.SliceVal.Interface()) 534 } 535 536 if w.Count != 1 { 537 t.Fatalf("Bad count: %d", w.Count) 538 } 539} 540 541func TestWalk_Array(t *testing.T) { 542 w := new(TestArrayWalker) 543 544 type S struct { 545 Foo [3]string 546 } 547 548 data := &S{ 549 Foo: [3]string{"a", "b", "c"}, 550 } 551 552 err := Walk(data, w) 553 if err != nil { 554 t.Fatalf("err: %s", err) 555 } 556 557 if !reflect.DeepEqual(w.ArrayVal.Interface(), data.Foo) { 558 t.Fatalf("bad: %#v", w.ArrayVal.Interface()) 559 } 560 561 if w.Count != 3 { 562 t.Fatalf("Bad count: %d", w.Count) 563 } 564} 565 566func TestWalk_ArrayWithPtr(t *testing.T) { 567 w := new(TestArrayWalker) 568 569 // based on similar slice test 570 type I interface{} 571 572 type S struct { 573 Foo [1]I 574 } 575 576 type Empty struct{} 577 578 data := &S{ 579 Foo: [1]I{&Empty{}}, 580 } 581 582 err := Walk(data, w) 583 if err != nil { 584 t.Fatalf("err: %s", err) 585 } 586 587 if !reflect.DeepEqual(w.ArrayVal.Interface(), data.Foo) { 588 t.Fatalf("bad: %#v", w.ArrayVal.Interface()) 589 } 590 591 if w.Count != 1 { 592 t.Fatalf("Bad count: %d", w.Count) 593 } 594} 595 596type testErr struct{} 597 598func (t *testErr) Error() string { 599 return "test error" 600} 601 602func TestWalk_Struct(t *testing.T) { 603 w := new(TestStructWalker) 604 605 // This makes sure we can also walk over pointer-to-pointers, and the ever 606 // so rare pointer-to-interface 607 type S struct { 608 Foo string 609 Bar *string 610 Baz **string 611 Err *error 612 } 613 614 bar := "ptr" 615 baz := &bar 616 e := error(&testErr{}) 617 618 data := &S{ 619 Foo: "foo", 620 Bar: &bar, 621 Baz: &baz, 622 Err: &e, 623 } 624 625 err := Walk(data, w) 626 if err != nil { 627 t.Fatalf("err: %s", err) 628 } 629 630 expected := []string{"Foo", "Bar", "Baz", "Err"} 631 if !reflect.DeepEqual(w.Fields, expected) { 632 t.Fatalf("bad: %#v", w.Fields) 633 } 634} 635 636// Very similar to above test but used to fail for #2, copied here for 637// regression testing 638func TestWalk_StructWithPtr(t *testing.T) { 639 w := new(TestStructWalker) 640 641 type S struct { 642 Foo string 643 Bar string 644 Baz *int 645 } 646 647 data := &S{ 648 Foo: "foo", 649 Bar: "bar", 650 } 651 652 err := Walk(data, w) 653 if err != nil { 654 t.Fatalf("err: %s", err) 655 } 656 657 expected := []string{"Foo", "Bar", "Baz"} 658 if !reflect.DeepEqual(w.Fields, expected) { 659 t.Fatalf("bad: %#v", w.Fields) 660 } 661} 662 663type TestInterfaceMapWalker struct { 664 MapVal reflect.Value 665 Keys map[string]bool 666 Values map[interface{}]bool 667} 668 669func (t *TestInterfaceMapWalker) Map(m reflect.Value) error { 670 t.MapVal = m 671 return nil 672} 673 674func (t *TestInterfaceMapWalker) MapElem(m, k, v reflect.Value) error { 675 if t.Keys == nil { 676 t.Keys = make(map[string]bool) 677 t.Values = make(map[interface{}]bool) 678 } 679 680 t.Keys[k.Interface().(string)] = true 681 t.Values[v.Interface()] = true 682 return nil 683} 684 685func TestWalk_MapWithPointers(t *testing.T) { 686 w := new(TestInterfaceMapWalker) 687 688 type S struct { 689 Foo map[string]interface{} 690 } 691 692 a := "a" 693 b := "b" 694 695 data := &S{ 696 Foo: map[string]interface{}{ 697 "foo": &a, 698 "bar": &b, 699 "baz": 11, 700 "zab": (*int)(nil), 701 }, 702 } 703 704 err := Walk(data, w) 705 if err != nil { 706 t.Fatalf("err: %s", err) 707 } 708 709 if !reflect.DeepEqual(w.MapVal.Interface(), data.Foo) { 710 t.Fatalf("Bad: %#v", w.MapVal.Interface()) 711 } 712 713 expectedK := map[string]bool{"foo": true, "bar": true, "baz": true, "zab": true} 714 if !reflect.DeepEqual(w.Keys, expectedK) { 715 t.Fatalf("Bad keys: %#v", w.Keys) 716 } 717 718 expectedV := map[interface{}]bool{&a: true, &b: true, 11: true, (*int)(nil): true} 719 if !reflect.DeepEqual(w.Values, expectedV) { 720 t.Fatalf("Bad values: %#v", w.Values) 721 } 722} 723 724type TestStructWalker_fieldSkip struct { 725 Skip bool 726 Fields int 727} 728 729func (t *TestStructWalker_fieldSkip) Enter(l Location) error { 730 if l == StructField { 731 t.Fields++ 732 } 733 734 return nil 735} 736 737func (t *TestStructWalker_fieldSkip) Exit(Location) error { 738 return nil 739} 740 741func (t *TestStructWalker_fieldSkip) Struct(v reflect.Value) error { 742 return nil 743} 744 745func (t *TestStructWalker_fieldSkip) StructField(sf reflect.StructField, v reflect.Value) error { 746 if t.Skip && sf.Name[0] == '_' { 747 return SkipEntry 748 } 749 750 return nil 751} 752 753func TestWalk_StructWithSkipEntry(t *testing.T) { 754 data := &struct { 755 Foo, _Bar int 756 }{ 757 Foo: 1, 758 _Bar: 2, 759 } 760 761 { 762 var s TestStructWalker_fieldSkip 763 if err := Walk(data, &s); err != nil { 764 t.Fatalf("err: %s", err) 765 } 766 767 if s.Fields != 2 { 768 t.Fatalf("bad: %d", s.Fields) 769 } 770 } 771 772 { 773 var s TestStructWalker_fieldSkip 774 s.Skip = true 775 if err := Walk(data, &s); err != nil { 776 t.Fatalf("err: %s", err) 777 } 778 779 if s.Fields != 1 { 780 t.Fatalf("bad: %d", s.Fields) 781 } 782 } 783} 784 785type TestStructWalker_valueSkip struct { 786 Skip bool 787 Fields int 788} 789 790func (t *TestStructWalker_valueSkip) Enter(l Location) error { 791 if l == StructField { 792 t.Fields++ 793 } 794 795 return nil 796} 797 798func (t *TestStructWalker_valueSkip) Exit(Location) error { 799 return nil 800} 801 802func (t *TestStructWalker_valueSkip) Struct(v reflect.Value) error { 803 if t.Skip { 804 return SkipEntry 805 } 806 807 return nil 808} 809 810func (t *TestStructWalker_valueSkip) StructField(sf reflect.StructField, v reflect.Value) error { 811 return nil 812} 813 814func TestWalk_StructParentWithSkipEntry(t *testing.T) { 815 data := &struct { 816 Foo, _Bar int 817 }{ 818 Foo: 1, 819 _Bar: 2, 820 } 821 822 { 823 var s TestStructWalker_valueSkip 824 if err := Walk(data, &s); err != nil { 825 t.Fatalf("err: %s", err) 826 } 827 828 if s.Fields != 2 { 829 t.Fatalf("bad: %d", s.Fields) 830 } 831 } 832 833 { 834 var s TestStructWalker_valueSkip 835 s.Skip = true 836 if err := Walk(data, &s); err != nil { 837 t.Fatalf("err: %s", err) 838 } 839 840 if s.Fields != 0 { 841 t.Fatalf("bad: %d", s.Fields) 842 } 843 } 844} 845