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  }