0 | 0 |
package reflectwalk
|
1 | 1 |
|
2 | 2 |
import (
|
|
3 |
"fmt"
|
3 | 4 |
"reflect"
|
4 | 5 |
"testing"
|
5 | 6 |
)
|
|
23 | 24 |
}
|
24 | 25 |
|
25 | 26 |
type TestPointerWalker struct {
|
26 | |
Ps []bool
|
|
27 |
pointers []bool
|
|
28 |
count int
|
|
29 |
enters int
|
|
30 |
exits int
|
27 | 31 |
}
|
28 | 32 |
|
29 | 33 |
func (t *TestPointerWalker) PointerEnter(v bool) error {
|
30 | |
t.Ps = append(t.Ps, v)
|
|
34 |
t.pointers = append(t.pointers, v)
|
|
35 |
t.enters++
|
|
36 |
if v {
|
|
37 |
t.count++
|
|
38 |
}
|
31 | 39 |
return nil
|
32 | 40 |
}
|
33 | 41 |
|
34 | 42 |
func (t *TestPointerWalker) PointerExit(v bool) error {
|
|
43 |
t.exits++
|
|
44 |
if t.pointers[len(t.pointers)-1] != v {
|
|
45 |
return fmt.Errorf("bad pointer exit '%t' at exit %d", v, t.exits)
|
|
46 |
}
|
|
47 |
t.pointers = t.pointers[:len(t.pointers)-1]
|
35 | 48 |
return nil
|
36 | 49 |
}
|
37 | 50 |
|
|
64 | 77 |
|
65 | 78 |
type TestMapWalker struct {
|
66 | 79 |
MapVal reflect.Value
|
67 | |
Keys []string
|
68 | |
Values []string
|
|
80 |
Keys map[string]bool
|
|
81 |
Values map[string]bool
|
69 | 82 |
}
|
70 | 83 |
|
71 | 84 |
func (t *TestMapWalker) Map(m reflect.Value) error {
|
|
75 | 88 |
|
76 | 89 |
func (t *TestMapWalker) MapElem(m, k, v reflect.Value) error {
|
77 | 90 |
if t.Keys == nil {
|
78 | |
t.Keys = make([]string, 0, 1)
|
79 | |
t.Values = make([]string, 0, 1)
|
80 | |
}
|
81 | |
|
82 | |
t.Keys = append(t.Keys, k.Interface().(string))
|
83 | |
t.Values = append(t.Values, v.Interface().(string))
|
|
91 |
t.Keys = make(map[string]bool)
|
|
92 |
t.Values = make(map[string]bool)
|
|
93 |
}
|
|
94 |
|
|
95 |
t.Keys[k.Interface().(string)] = true
|
|
96 |
t.Values[v.Interface().(string)] = true
|
84 | 97 |
return nil
|
85 | 98 |
}
|
86 | 99 |
|
|
188 | 201 |
}
|
189 | 202 |
if data.Bar[0].([]string)[0] != "bar" {
|
190 | 203 |
t.Fatalf("bad: %#v", data.Bar)
|
|
204 |
}
|
|
205 |
}
|
|
206 |
|
|
207 |
func TestWalk_Basic_ReplaceInterface(t *testing.T) {
|
|
208 |
w := new(TestPrimitiveReplaceWalker)
|
|
209 |
|
|
210 |
type S struct {
|
|
211 |
Foo []interface{}
|
|
212 |
}
|
|
213 |
|
|
214 |
data := &S{
|
|
215 |
Foo: []interface{}{"foo"},
|
|
216 |
}
|
|
217 |
|
|
218 |
err := Walk(data, w)
|
|
219 |
if err != nil {
|
|
220 |
t.Fatalf("err: %s", err)
|
191 | 221 |
}
|
192 | 222 |
}
|
193 | 223 |
|
|
293 | 323 |
t.Fatalf("Bad: %#v", w.MapVal.Interface())
|
294 | 324 |
}
|
295 | 325 |
|
296 | |
expectedK := []string{"foo", "bar"}
|
|
326 |
expectedK := map[string]bool{"foo": true, "bar": true}
|
297 | 327 |
if !reflect.DeepEqual(w.Keys, expectedK) {
|
298 | 328 |
t.Fatalf("Bad keys: %#v", w.Keys)
|
299 | 329 |
}
|
300 | 330 |
|
301 | |
expectedV := []string{"foov", "barv"}
|
|
331 |
expectedV := map[string]bool{"foov": true, "barv": true}
|
302 | 332 |
if !reflect.DeepEqual(w.Values, expectedV) {
|
303 | 333 |
t.Fatalf("Bad values: %#v", w.Values)
|
304 | 334 |
}
|
|
309 | 339 |
|
310 | 340 |
type S struct {
|
311 | 341 |
Foo string
|
312 | |
}
|
313 | |
|
314 | |
data := &S{
|
315 | |
Foo: "foo",
|
316 | |
}
|
317 | |
|
318 | |
err := Walk(data, w)
|
319 | |
if err != nil {
|
320 | |
t.Fatalf("err: %s", err)
|
321 | |
}
|
322 | |
|
323 | |
expected := []bool{true, false}
|
324 | |
if !reflect.DeepEqual(w.Ps, expected) {
|
325 | |
t.Fatalf("bad: %#v", w.Ps)
|
|
342 |
Bar *string
|
|
343 |
Baz **string
|
|
344 |
}
|
|
345 |
|
|
346 |
s := ""
|
|
347 |
sp := &s
|
|
348 |
|
|
349 |
data := &S{
|
|
350 |
Baz: &sp,
|
|
351 |
}
|
|
352 |
|
|
353 |
err := Walk(data, w)
|
|
354 |
if err != nil {
|
|
355 |
t.Fatalf("err: %s", err)
|
|
356 |
}
|
|
357 |
|
|
358 |
if w.enters != 5 {
|
|
359 |
t.Fatal("expected 4 values, saw", w.enters)
|
|
360 |
}
|
|
361 |
|
|
362 |
if w.count != 4 {
|
|
363 |
t.Fatal("exptec 3 pointers, saw", w.count)
|
|
364 |
}
|
|
365 |
|
|
366 |
if w.exits != w.enters {
|
|
367 |
t.Fatalf("number of enters (%d) and exits (%d) don't match", w.enters, w.exits)
|
|
368 |
}
|
|
369 |
}
|
|
370 |
|
|
371 |
func TestWalk_PointerPointer(t *testing.T) {
|
|
372 |
w := new(TestPointerWalker)
|
|
373 |
|
|
374 |
s := ""
|
|
375 |
sp := &s
|
|
376 |
pp := &sp
|
|
377 |
|
|
378 |
err := Walk(pp, w)
|
|
379 |
if err != nil {
|
|
380 |
t.Fatalf("err: %s", err)
|
|
381 |
}
|
|
382 |
|
|
383 |
if w.enters != 2 {
|
|
384 |
t.Fatal("expected 2 values, saw", w.enters)
|
|
385 |
}
|
|
386 |
|
|
387 |
if w.count != 2 {
|
|
388 |
t.Fatal("expected 2 pointers, saw", w.count)
|
|
389 |
}
|
|
390 |
|
|
391 |
if w.exits != w.enters {
|
|
392 |
t.Fatalf("number of enters (%d) and exits (%d) don't match", w.enters, w.exits)
|
326 | 393 |
}
|
327 | 394 |
}
|
328 | 395 |
|
|
351 | 418 |
}
|
352 | 419 |
}
|
353 | 420 |
|
|
421 |
func TestWalk_SliceWithPtr(t *testing.T) {
|
|
422 |
w := new(TestSliceWalker)
|
|
423 |
|
|
424 |
// This is key, the panic only happened when the slice field was
|
|
425 |
// an interface!
|
|
426 |
type I interface{}
|
|
427 |
|
|
428 |
type S struct {
|
|
429 |
Foo []I
|
|
430 |
}
|
|
431 |
|
|
432 |
type Empty struct{}
|
|
433 |
|
|
434 |
data := &S{
|
|
435 |
Foo: []I{&Empty{}},
|
|
436 |
}
|
|
437 |
|
|
438 |
err := Walk(data, w)
|
|
439 |
if err != nil {
|
|
440 |
t.Fatalf("err: %s", err)
|
|
441 |
}
|
|
442 |
|
|
443 |
if !reflect.DeepEqual(w.SliceVal.Interface(), data.Foo) {
|
|
444 |
t.Fatalf("bad: %#v", w.SliceVal.Interface())
|
|
445 |
}
|
|
446 |
|
|
447 |
if w.Count != 1 {
|
|
448 |
t.Fatalf("Bad count: %d", w.Count)
|
|
449 |
}
|
|
450 |
}
|
|
451 |
|
|
452 |
type testErr struct{}
|
|
453 |
|
|
454 |
func (t *testErr) Error() string {
|
|
455 |
return "test error"
|
|
456 |
}
|
|
457 |
|
354 | 458 |
func TestWalk_Struct(t *testing.T) {
|
355 | 459 |
w := new(TestStructWalker)
|
356 | 460 |
|
|
461 |
// This makes sure we can also walk over pointer-to-pointers, and the ever
|
|
462 |
// so rare pointer-to-interface
|
|
463 |
type S struct {
|
|
464 |
Foo string
|
|
465 |
Bar *string
|
|
466 |
Baz **string
|
|
467 |
Err *error
|
|
468 |
}
|
|
469 |
|
|
470 |
bar := "ptr"
|
|
471 |
baz := &bar
|
|
472 |
e := error(&testErr{})
|
|
473 |
|
|
474 |
data := &S{
|
|
475 |
Foo: "foo",
|
|
476 |
Bar: &bar,
|
|
477 |
Baz: &baz,
|
|
478 |
Err: &e,
|
|
479 |
}
|
|
480 |
|
|
481 |
err := Walk(data, w)
|
|
482 |
if err != nil {
|
|
483 |
t.Fatalf("err: %s", err)
|
|
484 |
}
|
|
485 |
|
|
486 |
expected := []string{"Foo", "Bar", "Baz", "Err"}
|
|
487 |
if !reflect.DeepEqual(w.Fields, expected) {
|
|
488 |
t.Fatalf("bad: %#v", w.Fields)
|
|
489 |
}
|
|
490 |
}
|
|
491 |
|
|
492 |
// Very similar to above test but used to fail for #2, copied here for
|
|
493 |
// regression testing
|
|
494 |
func TestWalk_StructWithPtr(t *testing.T) {
|
|
495 |
w := new(TestStructWalker)
|
|
496 |
|
357 | 497 |
type S struct {
|
358 | 498 |
Foo string
|
359 | 499 |
Bar string
|
|
500 |
Baz *int
|
360 | 501 |
}
|
361 | 502 |
|
362 | 503 |
data := &S{
|
|
369 | 510 |
t.Fatalf("err: %s", err)
|
370 | 511 |
}
|
371 | 512 |
|
372 | |
expected := []string{"Foo", "Bar"}
|
|
513 |
expected := []string{"Foo", "Bar", "Baz"}
|
373 | 514 |
if !reflect.DeepEqual(w.Fields, expected) {
|
374 | 515 |
t.Fatalf("bad: %#v", w.Fields)
|
375 | 516 |
}
|
376 | 517 |
}
|
|
518 |
|
|
519 |
type TestInterfaceMapWalker struct {
|
|
520 |
MapVal reflect.Value
|
|
521 |
Keys map[string]bool
|
|
522 |
Values map[interface{}]bool
|
|
523 |
}
|
|
524 |
|
|
525 |
func (t *TestInterfaceMapWalker) Map(m reflect.Value) error {
|
|
526 |
t.MapVal = m
|
|
527 |
return nil
|
|
528 |
}
|
|
529 |
|
|
530 |
func (t *TestInterfaceMapWalker) MapElem(m, k, v reflect.Value) error {
|
|
531 |
if t.Keys == nil {
|
|
532 |
t.Keys = make(map[string]bool)
|
|
533 |
t.Values = make(map[interface{}]bool)
|
|
534 |
}
|
|
535 |
|
|
536 |
t.Keys[k.Interface().(string)] = true
|
|
537 |
t.Values[v.Interface()] = true
|
|
538 |
return nil
|
|
539 |
}
|
|
540 |
|
|
541 |
func TestWalk_MapWithPointers(t *testing.T) {
|
|
542 |
w := new(TestInterfaceMapWalker)
|
|
543 |
|
|
544 |
type S struct {
|
|
545 |
Foo map[string]interface{}
|
|
546 |
}
|
|
547 |
|
|
548 |
a := "a"
|
|
549 |
b := "b"
|
|
550 |
|
|
551 |
data := &S{
|
|
552 |
Foo: map[string]interface{}{
|
|
553 |
"foo": &a,
|
|
554 |
"bar": &b,
|
|
555 |
"baz": 11,
|
|
556 |
"zab": (*int)(nil),
|
|
557 |
},
|
|
558 |
}
|
|
559 |
|
|
560 |
err := Walk(data, w)
|
|
561 |
if err != nil {
|
|
562 |
t.Fatalf("err: %s", err)
|
|
563 |
}
|
|
564 |
|
|
565 |
if !reflect.DeepEqual(w.MapVal.Interface(), data.Foo) {
|
|
566 |
t.Fatalf("Bad: %#v", w.MapVal.Interface())
|
|
567 |
}
|
|
568 |
|
|
569 |
expectedK := map[string]bool{"foo": true, "bar": true, "baz": true, "zab": true}
|
|
570 |
if !reflect.DeepEqual(w.Keys, expectedK) {
|
|
571 |
t.Fatalf("Bad keys: %#v", w.Keys)
|
|
572 |
}
|
|
573 |
|
|
574 |
expectedV := map[interface{}]bool{&a: true, &b: true, 11: true, (*int)(nil): true}
|
|
575 |
if !reflect.DeepEqual(w.Values, expectedV) {
|
|
576 |
t.Fatalf("Bad values: %#v", w.Values)
|
|
577 |
}
|
|
578 |
}
|
|
579 |
|
|
580 |
type TestStructWalker_fieldSkip struct {
|
|
581 |
Skip bool
|
|
582 |
Fields int
|
|
583 |
}
|
|
584 |
|
|
585 |
func (t *TestStructWalker_fieldSkip) Enter(l Location) error {
|
|
586 |
if l == StructField {
|
|
587 |
t.Fields++
|
|
588 |
}
|
|
589 |
|
|
590 |
return nil
|
|
591 |
}
|
|
592 |
|
|
593 |
func (t *TestStructWalker_fieldSkip) Exit(Location) error {
|
|
594 |
return nil
|
|
595 |
}
|
|
596 |
|
|
597 |
func (t *TestStructWalker_fieldSkip) Struct(v reflect.Value) error {
|
|
598 |
return nil
|
|
599 |
}
|
|
600 |
|
|
601 |
func (t *TestStructWalker_fieldSkip) StructField(sf reflect.StructField, v reflect.Value) error {
|
|
602 |
if t.Skip && sf.Name[0] == '_' {
|
|
603 |
return SkipEntry
|
|
604 |
}
|
|
605 |
|
|
606 |
return nil
|
|
607 |
}
|
|
608 |
|
|
609 |
func TestWalk_StructWithSkipEntry(t *testing.T) {
|
|
610 |
data := &struct {
|
|
611 |
Foo, _Bar int
|
|
612 |
}{
|
|
613 |
Foo: 1,
|
|
614 |
_Bar: 2,
|
|
615 |
}
|
|
616 |
|
|
617 |
{
|
|
618 |
var s TestStructWalker_fieldSkip
|
|
619 |
if err := Walk(data, &s); err != nil {
|
|
620 |
t.Fatalf("err: %s", err)
|
|
621 |
}
|
|
622 |
|
|
623 |
if s.Fields != 2 {
|
|
624 |
t.Fatalf("bad: %d", s.Fields)
|
|
625 |
}
|
|
626 |
}
|
|
627 |
|
|
628 |
{
|
|
629 |
var s TestStructWalker_fieldSkip
|
|
630 |
s.Skip = true
|
|
631 |
if err := Walk(data, &s); err != nil {
|
|
632 |
t.Fatalf("err: %s", err)
|
|
633 |
}
|
|
634 |
|
|
635 |
if s.Fields != 1 {
|
|
636 |
t.Fatalf("bad: %d", s.Fields)
|
|
637 |
}
|
|
638 |
}
|
|
639 |
}
|