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