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