istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/pkg/util/reflect_test.go (about) 1 // Copyright Istio Authors 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 15 package util 16 17 import ( 18 "reflect" 19 "testing" 20 ) 21 22 // TODO: add missing unit tests (istio/istio#17246). 23 24 // errToString returns the string representation of err and the empty string if 25 // err is nil. 26 func errToString(err error) string { 27 if err == nil { 28 return "" 29 } 30 return err.Error() 31 } 32 33 // to ptr conversion utility functions 34 func toInt8Ptr(i int8) *int8 { return &i } 35 36 func TestIsValueNil(t *testing.T) { 37 if !IsValueNil(nil) { 38 t.Error("got IsValueNil(nil) false, want true") 39 } 40 if !IsValueNil((*int)(nil)) { 41 t.Error("got IsValueNil(ptr) false, want true") 42 } 43 if !IsValueNil(map[int]int(nil)) { 44 t.Error("got IsValueNil(map) false, want true") 45 } 46 if !IsValueNil([]int(nil)) { 47 t.Error("got IsValueNil(slice) false, want true") 48 } 49 if !IsValueNil(any(nil)) { 50 t.Error("got IsValueNil(interface) false, want true") 51 } 52 53 if IsValueNil(toInt8Ptr(42)) { 54 t.Error("got IsValueNil(ptr) true, want false") 55 } 56 if IsValueNil(map[int]int{42: 42}) { 57 t.Error("got IsValueNil(map) true, want false") 58 } 59 if IsValueNil([]int{1, 2, 3}) { 60 t.Error("got IsValueNil(slice) true, want false") 61 } 62 if IsValueNil(any(42)) { 63 t.Error("got IsValueNil(interface) true, want false") 64 } 65 } 66 67 func TestIsValueNilOrDefault(t *testing.T) { 68 if !IsValueNilOrDefault(nil) { 69 t.Error("got IsValueNilOrDefault(nil) false, want true") 70 } 71 if !IsValueNilOrDefault((*int)(nil)) { 72 t.Error("got IsValueNilOrDefault(ptr) false, want true") 73 } 74 if !IsValueNilOrDefault(map[int]int(nil)) { 75 t.Error("got IsValueNilOrDefault(map) false, want true") 76 } 77 if !IsValueNilOrDefault([]int(nil)) { 78 t.Error("got IsValueNilOrDefault(slice) false, want true") 79 } 80 if !IsValueNilOrDefault(any(nil)) { 81 t.Error("got IsValueNilOrDefault(interface) false, want true") 82 } 83 if !IsValueNilOrDefault(int(0)) { 84 t.Error("got IsValueNilOrDefault(int(0)) false, want true") 85 } 86 if !IsValueNilOrDefault("") { 87 t.Error("got IsValueNilOrDefault(\"\") false, want true") 88 } 89 if !IsValueNilOrDefault(false) { 90 t.Error("got IsValueNilOrDefault(false) false, want true") 91 } 92 i := 32 93 ip := &i 94 if IsValueNilOrDefault(&ip) { 95 t.Error("got IsValueNilOrDefault(ptr to ptr) false, want true") 96 } 97 } 98 99 func TestIsValueFuncs(t *testing.T) { 100 testInt := int(42) 101 testStruct := struct{}{} 102 testSlice := []bool{} 103 testMap := map[bool]bool{} 104 var testNilSlice []bool 105 var testNilMap map[bool]bool 106 107 allValues := []any{nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, testSlice, &testSlice, testNilMap, testMap, &testMap} 108 109 tests := []struct { 110 desc string 111 function func(v reflect.Value) bool 112 okValues []any 113 }{ 114 { 115 desc: "IsValuePtr", 116 function: IsValuePtr, 117 okValues: []any{&testInt, &testStruct, &testSlice, &testMap}, 118 }, 119 { 120 desc: "IsValueStruct", 121 function: IsValueStruct, 122 okValues: []any{testStruct}, 123 }, 124 { 125 desc: "IsValueInterface", 126 function: IsValueInterface, 127 okValues: []any{}, 128 }, 129 { 130 desc: "IsValueStructPtr", 131 function: IsValueStructPtr, 132 okValues: []any{&testStruct}, 133 }, 134 { 135 desc: "IsValueMap", 136 function: IsValueMap, 137 okValues: []any{testNilMap, testMap}, 138 }, 139 { 140 desc: "IsValueSlice", 141 function: IsValueSlice, 142 okValues: []any{testNilSlice, testSlice}, 143 }, 144 { 145 desc: "IsValueScalar", 146 function: IsValueScalar, 147 okValues: []any{testInt, &testInt}, 148 }, 149 } 150 151 for _, tt := range tests { 152 for vidx, v := range allValues { 153 if got, want := tt.function(reflect.ValueOf(v)), isInListOfInterface(tt.okValues, v); got != want { 154 t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) 155 } 156 } 157 } 158 } 159 160 func TestValuesAreSameType(t *testing.T) { 161 type EnumType int64 162 163 tests := []struct { 164 inDesc string 165 inV1 any 166 inV2 any 167 want bool 168 }{ 169 { 170 inDesc: "success both are int32 types", 171 inV1: int32(42), 172 inV2: int32(43), 173 want: true, 174 }, 175 { 176 inDesc: "fail unmatching int types", 177 inV1: int16(42), 178 inV2: int32(43), 179 want: false, 180 }, 181 { 182 inDesc: "fail unmatching int and string type", 183 inV1: int32(42), 184 inV2: "42", 185 want: false, 186 }, 187 { 188 inDesc: "fail EnumType and int64 types", 189 inV1: EnumType(42), 190 inV2: int64(43), 191 want: false, 192 }, 193 } 194 195 for _, tt := range tests { 196 t.Run(tt.inDesc, func(t *testing.T) { 197 got := ValuesAreSameType(reflect.ValueOf(tt.inV1), reflect.ValueOf(tt.inV2)) 198 if got != tt.want { 199 t.Errorf("got %v, want %v for comparing %T against %T", got, tt.want, tt.inV1, tt.inV2) 200 } 201 }) 202 } 203 } 204 205 func TestIsTypeFuncs(t *testing.T) { 206 testInt := int(42) 207 testStruct := struct{}{} 208 testSlice := []bool{} 209 testSliceOfInterface := []any{} 210 testMap := map[bool]bool{} 211 var testNilSlice []bool 212 var testNilMap map[bool]bool 213 214 allTypes := []any{ 215 nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, 216 testSlice, &testSlice, testSliceOfInterface, testNilMap, testMap, &testMap, 217 } 218 219 tests := []struct { 220 desc string 221 function func(v reflect.Type) bool 222 okTypes []any 223 }{ 224 { 225 desc: "IsTypeStructPtr", 226 function: IsTypeStructPtr, 227 okTypes: []any{&testStruct}, 228 }, 229 { 230 desc: "IsTypeSlicePtr", 231 function: IsTypeSlicePtr, 232 okTypes: []any{&testSlice}, 233 }, 234 { 235 desc: "IsTypeMap", 236 function: IsTypeMap, 237 okTypes: []any{testNilMap, testMap}, 238 }, 239 { 240 desc: "IsTypeInterface", 241 function: IsTypeInterface, 242 okTypes: []any{}, 243 }, 244 { 245 desc: "IsTypeSliceOfInterface", 246 function: IsTypeSliceOfInterface, 247 okTypes: []any{testSliceOfInterface}, 248 }, 249 } 250 251 for _, tt := range tests { 252 for vidx, v := range allTypes { 253 if got, want := tt.function(reflect.TypeOf(v)), isInListOfInterface(tt.okTypes, v); got != want { 254 t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) 255 } 256 } 257 } 258 } 259 260 type interfaceContainer struct { 261 I anInterface 262 } 263 264 type anInterface interface { 265 IsU() 266 } 267 268 type implementsInterface struct { 269 A string 270 } 271 272 func (*implementsInterface) IsU() {} 273 274 func TestIsValueInterface(t *testing.T) { 275 intf := &interfaceContainer{ 276 I: &implementsInterface{ 277 A: "a", 278 }, 279 } 280 iField := reflect.ValueOf(intf).Elem().FieldByName("I") 281 if !IsValueInterface(iField) { 282 t.Errorf("IsValueInterface(): got false, want true") 283 } 284 } 285 286 func TestIsTypeInterface(t *testing.T) { 287 intf := &interfaceContainer{ 288 I: &implementsInterface{ 289 A: "a", 290 }, 291 } 292 testIfField := reflect.ValueOf(intf).Elem().Field(0) 293 294 if !IsTypeInterface(testIfField.Type()) { 295 t.Errorf("IsTypeInterface(): got false, want true") 296 } 297 } 298 299 func isInListOfInterface(lv []any, v any) bool { 300 for _, vv := range lv { 301 if reflect.DeepEqual(vv, v) { 302 return true 303 } 304 } 305 return false 306 } 307 308 func TestDeleteFromSlicePtr(t *testing.T) { 309 parentSlice := []int{42, 43, 44, 45} 310 var parentSliceI any = parentSlice 311 if err := DeleteFromSlicePtr(&parentSliceI, 1); err != nil { 312 t.Fatalf("got error: %s, want error: nil", err) 313 } 314 wantSlice := []int{42, 44, 45} 315 if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { 316 t.Errorf("got:\n%v\nwant:\n%v\n", got, want) 317 } 318 319 badParent := struct{}{} 320 wantErr := `deleteFromSlicePtr parent type is *struct {}, must be *[]interface{}` 321 if got, want := errToString(DeleteFromSlicePtr(&badParent, 1)), wantErr; got != want { 322 t.Fatalf("got error: %s, want error: %s", got, want) 323 } 324 } 325 326 func TestUpdateSlicePtr(t *testing.T) { 327 parentSlice := []int{42, 43, 44, 45} 328 var parentSliceI any = parentSlice 329 if err := UpdateSlicePtr(&parentSliceI, 1, 42); err != nil { 330 t.Fatalf("got error: %s, want error: nil", err) 331 } 332 wantSlice := []int{42, 42, 44, 45} 333 if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { 334 t.Errorf("got:\n%v\nwant:\n%v\n", got, want) 335 } 336 337 badParent := struct{}{} 338 wantErr := `updateSlicePtr parent type is *struct {}, must be *[]interface{}` 339 if got, want := errToString(UpdateSlicePtr(&badParent, 1, 42)), wantErr; got != want { 340 t.Fatalf("got error: %s, want error: %s", got, want) 341 } 342 } 343 344 func TestInsertIntoMap(t *testing.T) { 345 parentMap := map[int]string{42: "forty two", 43: "forty three"} 346 key := 44 347 value := "forty four" 348 if err := InsertIntoMap(parentMap, key, value); err != nil { 349 t.Fatalf("got error: %s, want error: nil", err) 350 } 351 wantMap := map[int]string{42: "forty two", 43: "forty three", 44: "forty four"} 352 if got, want := parentMap, wantMap; !reflect.DeepEqual(got, want) { 353 t.Errorf("got:\n%v\nwant:\n%v\n", got, want) 354 } 355 356 badParent := struct{}{} 357 wantErr := `insertIntoMap parent type is *struct {}, must be map` 358 if got, want := errToString(InsertIntoMap(&badParent, key, value)), wantErr; got != want { 359 t.Fatalf("got error: %s, want error: %s", got, want) 360 } 361 } 362 363 var ( 364 allIntTypes = []any{int(-42), int8(-43), int16(-44), int32(-45), int64(-46)} 365 allUintTypes = []any{uint(42), uint8(43), uint16(44), uint32(45), uint64(46)} 366 allIntegerTypes = append(allIntTypes, allUintTypes...) 367 nonIntTypes = []any{nil, "", []int{}, map[string]bool{}} 368 allTypes = append(allIntegerTypes, nonIntTypes...) 369 ) 370 371 func TestIsInteger(t *testing.T) { 372 tests := []struct { 373 desc string 374 function func(v reflect.Kind) bool 375 want []any 376 }{ 377 { 378 desc: "ints", 379 function: IsIntKind, 380 want: allIntTypes, 381 }, 382 { 383 desc: "uints", 384 function: IsUintKind, 385 want: allUintTypes, 386 }, 387 } 388 389 for _, tt := range tests { 390 var got []any 391 for _, v := range allTypes { 392 if tt.function(reflect.ValueOf(v).Kind()) { 393 got = append(got, v) 394 } 395 } 396 if !reflect.DeepEqual(got, tt.want) { 397 t.Errorf("%s: got %v, want %v", tt.desc, got, tt.want) 398 } 399 } 400 } 401 402 func TestToIntValue(t *testing.T) { 403 var got []int 404 for _, v := range allTypes { 405 if i, ok := ToIntValue(v); ok { 406 got = append(got, i) 407 } 408 } 409 want := []int{-42, -43, -44, -45, -46, 42, 43, 44, 45, 46} 410 if !reflect.DeepEqual(got, want) { 411 t.Errorf("got %v, want %v", got, want) 412 } 413 }