1
16
17 package v1
18
19 import (
20 enjson "encoding/json"
21 "fmt"
22 "math"
23 "reflect"
24 "testing"
25 "time"
26
27 "github.com/stretchr/testify/assert"
28 "sigs.k8s.io/json"
29
30 "k8s.io/apimachinery/pkg/api/resource"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 )
33
34 func TestVModule(t *testing.T) {
35 testcases := []struct {
36 arg string
37 expectError string
38 expectValue VModuleConfiguration
39 expectParam string
40 }{
41 {
42 arg: "gopher*=1",
43 expectValue: VModuleConfiguration{
44 {
45 FilePattern: "gopher*",
46 Verbosity: 1,
47 },
48 },
49 },
50 {
51 arg: "foo=1,bar=2",
52 expectValue: VModuleConfiguration{
53 {
54 FilePattern: "foo",
55 Verbosity: 1,
56 },
57 {
58 FilePattern: "bar",
59 Verbosity: 2,
60 },
61 },
62 },
63 {
64 arg: "foo=1,bar=2,",
65 expectValue: VModuleConfiguration{
66 {
67 FilePattern: "foo",
68 Verbosity: 1,
69 },
70 {
71 FilePattern: "bar",
72 Verbosity: 2,
73 },
74 },
75 expectParam: "foo=1,bar=2",
76 },
77 {
78 arg: "gopher*",
79 expectError: `"gopher*" does not have the pattern=N format`,
80 },
81 {
82 arg: "=1",
83 expectError: `"=1" does not have the pattern=N format`,
84 },
85 {
86 arg: "foo=-1",
87 expectError: `parsing verbosity in "foo=-1": strconv.ParseUint: parsing "-1": invalid syntax`,
88 },
89 {
90 arg: fmt.Sprintf("validint32=%d", math.MaxInt32),
91 expectValue: VModuleConfiguration{
92 {
93 FilePattern: "validint32",
94 Verbosity: math.MaxInt32,
95 },
96 },
97 },
98 {
99 arg: fmt.Sprintf("invalidint32=%d", math.MaxInt32+1),
100 expectError: `parsing verbosity in "invalidint32=2147483648": strconv.ParseUint: parsing "2147483648": value out of range`,
101 },
102 }
103
104 for _, test := range testcases {
105 t.Run(test.arg, func(t *testing.T) {
106 var actual VModuleConfiguration
107 value := VModuleConfigurationPflag(&actual)
108 err := value.Set(test.arg)
109 if test.expectError != "" {
110 if err == nil {
111 t.Fatal("parsing should have failed")
112 }
113 assert.Equal(t, test.expectError, err.Error(), "parse error")
114 } else {
115 if err != nil {
116 t.Fatalf("unexpected error: %v", err)
117 }
118 param := value.String()
119 expectParam := test.expectParam
120 if expectParam == "" {
121 expectParam = test.arg
122 }
123 assert.Equal(t, expectParam, param, "encoded parameter value not identical")
124 }
125 })
126 }
127 }
128
129
130
131 func TestCompatibility(t *testing.T) {
132 testcases := map[string]struct {
133
134 fixture string
135
136 baseConfig LoggingConfiguration
137
138
139 expectAllFields bool
140
141 expectConfig LoggingConfiguration
142 }{
143 "defaults": {
144
145 fixture: "{}",
146 baseConfig: *NewLoggingConfiguration(),
147 expectConfig: *NewLoggingConfiguration(),
148 },
149 "all-fields": {
150
151
152
153
154 fixture: `{
155 "format": "json",
156 "flushFrequency": 1,
157 "verbosity": 5,
158 "vmodule": [
159 {"filePattern": "someFile", "verbosity": 10},
160 {"filePattern": "anotherFile", "verbosity": 1}
161 ],
162 "options": {
163 "text": {
164 "splitStream": true,
165 "infoBufferSize": "2048"
166 },
167 "json": {
168 "splitStream": true,
169 "infoBufferSize": "1024"
170 }
171 }
172 }
173 `,
174 baseConfig: LoggingConfiguration{},
175 expectAllFields: true,
176 expectConfig: LoggingConfiguration{
177 Format: JSONLogFormat,
178 FlushFrequency: TimeOrMetaDuration{Duration: metav1.Duration{Duration: time.Nanosecond}},
179 Verbosity: VerbosityLevel(5),
180 VModule: VModuleConfiguration{
181 {
182 FilePattern: "someFile",
183 Verbosity: VerbosityLevel(10),
184 },
185 {
186 FilePattern: "anotherFile",
187 Verbosity: VerbosityLevel(1),
188 },
189 },
190 Options: FormatOptions{
191 Text: TextOptions{
192 OutputRoutingOptions: OutputRoutingOptions{
193 SplitStream: true,
194 InfoBufferSize: resource.QuantityValue{
195 Quantity: *resource.NewQuantity(2048, resource.DecimalSI),
196 },
197 },
198 },
199 JSON: JSONOptions{
200 OutputRoutingOptions: OutputRoutingOptions{
201 SplitStream: true,
202 InfoBufferSize: resource.QuantityValue{
203 Quantity: *resource.NewQuantity(1024, resource.DecimalSI),
204 },
205 },
206 },
207 },
208 },
209 },
210 }
211
212 for name, tc := range testcases {
213 t.Run(name, func(t *testing.T) {
214
215
216 config := tc.baseConfig
217 if strictErr, err := json.UnmarshalStrict([]byte(tc.fixture), &config); err != nil {
218 t.Fatalf("unexpected unmarshal error: %v", err)
219 } else if strictErr != nil {
220 t.Fatalf("unexpected strict unmarshal error: %v", strictErr)
221 }
222
223
224 _ = tc.expectConfig.Options.Text.InfoBufferSize.String()
225 _ = tc.expectConfig.Options.JSON.InfoBufferSize.String()
226 assert.Equal(t, tc.expectConfig, config)
227 if tc.expectAllFields {
228 notZeroRecursive(t, config, "LoggingConfiguration")
229 }
230 })
231 }
232 }
233
234
235
236
237 func notZeroRecursive(t *testing.T, i interface{}, path string) bool {
238 typeOfI := reflect.TypeOf(i)
239
240 if i == nil || reflect.DeepEqual(i, reflect.Zero(typeOfI).Interface()) {
241 t.Errorf("%s: should not have been zero, but was %v", path, i)
242 return false
243 }
244
245 valid := true
246 kind := typeOfI.Kind()
247 value := reflect.ValueOf(i)
248 switch kind {
249 case reflect.Pointer:
250 if !notZeroRecursive(t, value.Elem().Interface(), path) {
251 valid = false
252 }
253 case reflect.Struct:
254 for i := 0; i < typeOfI.NumField(); i++ {
255 if !typeOfI.Field(i).IsExported() {
256
257 continue
258 }
259 if typeOfI.Field(i).Tag.Get("json") == "-" {
260
261 continue
262 }
263 if !notZeroRecursive(t, value.Field(i).Interface(), path+"."+typeOfI.Field(i).Name) {
264 valid = false
265 }
266 }
267 case reflect.Map:
268 iter := value.MapRange()
269 for iter.Next() {
270 k := iter.Key()
271 v := iter.Value()
272 if !notZeroRecursive(t, k.Interface(), path+"."+"<key>") {
273 valid = false
274 }
275 if !notZeroRecursive(t, v.Interface(), path+"["+fmt.Sprintf("%v", k.Interface())+"]") {
276 valid = false
277 }
278 }
279 case reflect.Slice, reflect.Array:
280 for i := 0; i < value.Len(); i++ {
281 if !notZeroRecursive(t, value.Index(i).Interface(), path+"["+fmt.Sprintf("%d", i)+"]") {
282 valid = false
283 }
284 }
285 }
286
287 return valid
288 }
289
290 func TestTimeOrMetaDuration_UnmarshalJSON(t *testing.T) {
291 tests := []struct {
292 name string
293 tomd *TimeOrMetaDuration
294 arg any
295 wanted string
296 }{
297 {
298 name: "string values unmarshal as metav1.Duration",
299 tomd: &TimeOrMetaDuration{},
300 arg: "1s",
301 wanted: `"1s"`,
302 }, {
303 name: "int values unmarshal as metav1.Duration",
304 tomd: &TimeOrMetaDuration{},
305 arg: 1000000000,
306 wanted: `1000000000`,
307 }, {
308 name: "invalid value return error",
309 tomd: &TimeOrMetaDuration{},
310 arg: "invalid",
311 wanted: "time: invalid duration \"invalid\"",
312 },
313 }
314 for _, tt := range tests {
315 t.Run(tt.name, func(t *testing.T) {
316 b, err := enjson.Marshal(tt.arg)
317 if err != nil {
318 t.Errorf("unexpect error: %v", err)
319 }
320
321 if err := tt.tomd.UnmarshalJSON(b); err == nil {
322 data, err := tt.tomd.MarshalJSON()
323 if err != nil {
324 t.Fatal(err)
325 }
326 if tt.wanted != string(data) {
327 t.Errorf("unexpected wanted for %s, wanted: %v, got: %v", tt.name, tt.wanted, string(data))
328 }
329 } else {
330 if err.Error() != tt.wanted {
331 t.Errorf("UnmarshalJSON() error = %v", err)
332 }
333 }
334 })
335 }
336
337 }
338
View as plain text