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