1// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package proptools
16
17import (
18	"reflect"
19	"testing"
20)
21
22type testType struct {
23	NoTag       string
24	EmptyTag    string ``
25	OtherTag    string `foo:"bar"`
26	MatchingTag string `name:"value"`
27	ExtraValues string `name:"foo,value,bar"`
28	ExtraTags   string `foo:"bar" name:"value"`
29}
30
31func TestHasTag(t *testing.T) {
32	tests := []struct {
33		field string
34		want  bool
35	}{
36		{
37			field: "NoTag",
38			want:  false,
39		},
40		{
41			field: "EmptyTag",
42			want:  false,
43		},
44		{
45			field: "OtherTag",
46			want:  false,
47		},
48		{
49			field: "MatchingTag",
50			want:  true,
51		},
52		{
53			field: "ExtraValues",
54			want:  true,
55		},
56		{
57			field: "ExtraTags",
58			want:  true,
59		},
60	}
61	for _, test := range tests {
62		t.Run(test.field, func(t *testing.T) {
63			field, _ := reflect.TypeOf(testType{}).FieldByName(test.field)
64			if got := HasTag(field, "name", "value"); got != test.want {
65				t.Errorf(`HasTag(%q, "name", "value") = %v, want %v`, field.Tag, got, test.want)
66			}
67		})
68	}
69}
70
71func BenchmarkHasTag(b *testing.B) {
72	tests := []struct {
73		field string
74	}{
75		{
76			field: "NoTag",
77		},
78		{
79			field: "EmptyTag",
80		},
81		{
82			field: "OtherTag",
83		},
84		{
85			field: "MatchingTag",
86		},
87		{
88			field: "ExtraValues",
89		},
90		{
91			field: "ExtraTags",
92		},
93	}
94	for _, test := range tests {
95		b.Run(test.field, func(b *testing.B) {
96			field, _ := reflect.TypeOf(testType{}).FieldByName(test.field)
97			for i := 0; i < b.N; i++ {
98				HasTag(field, "name", "value")
99			}
100		})
101	}
102}
103
104func TestPropertyIndexesWithTag(t *testing.T) {
105	tests := []struct {
106		name string
107		ps   interface{}
108		want [][]int
109	}{
110		{
111			name: "none",
112			ps: &struct {
113				Foo string
114			}{},
115			want: nil,
116		},
117		{
118			name: "one",
119			ps: &struct {
120				Foo string `name:"value"`
121			}{},
122			want: [][]int{{0}},
123		},
124		{
125			name: "two",
126			ps: &struct {
127				Foo string `name:"value"`
128				Bar string `name:"value"`
129			}{},
130			want: [][]int{{0}, {1}},
131		},
132		{
133			name: "some",
134			ps: &struct {
135				Foo string `name:"other"`
136				Bar string `name:"value"`
137			}{},
138			want: [][]int{{1}},
139		},
140		{
141			name: "embedded",
142			ps: &struct {
143				Foo struct {
144					Bar string `name:"value"`
145				}
146			}{},
147			want: [][]int{{0, 0}},
148		},
149		{
150			name: "embedded ptr",
151			ps: &struct {
152				Foo *struct {
153					Bar string `name:"value"`
154				}
155			}{},
156			want: [][]int{{0, 0}},
157		},
158		{
159			name: "slice of struct",
160			ps: &struct {
161				Other int
162				Foo   []struct {
163					Other int
164					Bar   string `name:"value"`
165				}
166			}{},
167			want: [][]int{{1, 1}},
168		},
169		{
170			name: "slice^2 of struct",
171			ps: &struct {
172				Other int
173				Foo   []struct {
174					Other int
175					Bar   []struct {
176						Other int
177						Baz   string `name:"value"`
178					}
179				}
180			}{},
181			want: [][]int{{1, 1, 1}},
182		},
183		{
184			name: "nil",
185			ps: (*struct {
186				Foo string `name:"value"`
187			})(nil),
188			want: [][]int{{0}},
189		},
190	}
191	for _, test := range tests {
192		t.Run(test.name, func(t *testing.T) {
193			if got := PropertyIndexesWithTag(test.ps, "name", "value"); !reflect.DeepEqual(got, test.want) {
194				t.Errorf("PropertyIndexesWithTag() = %v, want %v", got, test.want)
195			}
196		})
197	}
198}
199