github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/reflectkit/reflect_accessor_test.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package reflectkit
     7  
     8  import (
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/gojuno/minimock/v3"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/insolar/vanilla/reflectkit/mocks"
    17  )
    18  
    19  func TestMakeAddressable(t *testing.T) {
    20  	i := 10
    21  	require.Equal(t, i, MakeAddressable(reflect.ValueOf(i)).Interface())
    22  	require.Equal(t, &i, MakeAddressable(reflect.ValueOf(&i)).Interface())
    23  }
    24  
    25  func newInt(i int) *int {
    26  	return &i
    27  }
    28  
    29  func newEmptyInt() *int {
    30  	return nil
    31  }
    32  
    33  type testStruct struct {
    34  	I *int
    35  }
    36  
    37  // FieldValueGetter(index int, fd reflect.StructField, useAddr bool, baseOffset uintptr) FieldGetterFunc
    38  func TestFieldValueGetter(t *testing.T) {
    39  	for _, tc := range []struct {
    40  		name       string
    41  		index      int
    42  		fd         reflect.StructField
    43  		useAddr    bool
    44  		baseOffset uintptr
    45  		input      reflect.Value
    46  		res        interface{}
    47  	}{
    48  		{
    49  			name:  "plain",
    50  			input: reflect.ValueOf(testStruct{}),
    51  			res:   newEmptyInt(),
    52  		},
    53  		{
    54  			name: "pointer",
    55  			fd: reflect.StructField{
    56  				Name:   "I",
    57  				Type:   reflect.TypeOf(new(int)),
    58  				Offset: 0,
    59  				Index:  []int{},
    60  			},
    61  			input: reflect.ValueOf(testStruct{
    62  				I: newInt(2),
    63  			}),
    64  			useAddr: true,
    65  			res:     newInt(2),
    66  		},
    67  	} {
    68  		t.Run(tc.name, func(t *testing.T) {
    69  			fn := FieldValueGetter(tc.index, tc.fd, tc.useAddr, tc.baseOffset)
    70  			res := fn(MakeAddressable(tc.input))
    71  			if tc.res == nil || (reflect.ValueOf(tc.res).Kind() == reflect.Ptr && reflect.ValueOf(tc.res).IsNil()) {
    72  				require.Nil(t, res.Interface())
    73  				return
    74  			} else {
    75  				require.NotNil(t, res.Interface())
    76  			}
    77  
    78  			require.Equal(t, *tc.res.(*int), *res.Interface().(*int))
    79  		})
    80  	}
    81  }
    82  
    83  func TestValueToReceiverFactory(t *testing.T) {
    84  	t.Run("simple types", func(t *testing.T) {
    85  		type TestStruct struct {
    86  			B bool
    87  			b bool
    88  			I int64
    89  			i int64
    90  			U uint8
    91  			u uint8
    92  			F float64
    93  			f float32
    94  			C complex64
    95  			c complex128
    96  			S string
    97  			s string
    98  		}
    99  
   100  		input := TestStruct{
   101  			B: true,
   102  			b: false,
   103  			I: 10,
   104  			i: -1,
   105  			U: 0,
   106  			u: 10,
   107  			F: 0.1,
   108  			f: -0.1,
   109  			C: complex(0.1, 1),
   110  			c: complex(-0.1, 0),
   111  			S: "test",
   112  			s: "",
   113  		}
   114  		st := reflect.TypeOf(input)
   115  		for i := 0; i < st.NumField(); i++ {
   116  			field := st.Field(i)
   117  			t.Run(field.Type.String(), func(t *testing.T) {
   118  				val := ValueToReceiverFactory(field.Type.Kind(), nil)
   119  				t.Run("zero", func(t *testing.T) {
   120  					mc := minimock.NewController(t)
   121  					mockReceiver := mocks.NewTypedReceiverMock(mc).ReceiveZeroMock.Set(func(k1 reflect.Kind) {
   122  						assert.Equal(t, field.Type.Kind(), k1)
   123  					})
   124  					withZeroUnexported, withZero := val(true, field.Type, true)
   125  					withZero(reflect.Zero(field.Type), mockReceiver)
   126  					assert.False(t, withZeroUnexported)
   127  					mc.Finish()
   128  				})
   129  
   130  				t.Run("nonzero", func(t *testing.T) {
   131  					mc := minimock.NewController(t)
   132  					nonZeroValFn := FieldValueGetter(i, field, true, 0)
   133  					value := nonZeroValFn(MakeAddressable(reflect.ValueOf(input)))
   134  					mockReceiver := newMockByKind(mc, field.Type, value.Interface())
   135  					nonZeroUnexported, nonZero := val(true, field.Type, false)
   136  					nonZero(value, mockReceiver)
   137  					assert.False(t, nonZeroUnexported)
   138  					mc.Finish()
   139  				})
   140  			})
   141  		}
   142  
   143  	})
   144  	t.Run("nillable types fully defined at compile time", func(t *testing.T) {
   145  		type myFunc func()
   146  		type TestStruct struct {
   147  			f myFunc
   148  			F myFunc
   149  			s []byte
   150  			S []byte
   151  		}
   152  
   153  		input := TestStruct{
   154  			f: myFunc(func() {}),
   155  			F: myFunc(func() {}),
   156  			s: []byte{},
   157  			S: []byte{},
   158  		}
   159  		st := reflect.TypeOf(input)
   160  		for i := 0; i < st.NumField(); i++ {
   161  			field := st.Field(i)
   162  			exported := reflect.ValueOf(input).Field(i).CanInterface()
   163  			t.Run("with custom", func(t *testing.T) {
   164  				t.Run("zero", func(t *testing.T) {
   165  					mc := minimock.NewController(t)
   166  					custom := newCustomByKind()
   167  					val := ValueToReceiverFactory(field.Type.Kind(), custom)
   168  					mockReceiver := mocks.NewTypedReceiverMock(mc).ReceiveNilMock.Set(func(k1 reflect.Kind) {
   169  						assert.Equal(t, field.Type.Kind(), k1)
   170  					})
   171  					withZeroUnexported, withZero := val(!exported, field.Type, true)
   172  					withZero(reflect.Zero(field.Type), mockReceiver)
   173  					assert.Equal(t, !exported, withZeroUnexported)
   174  					mc.Finish()
   175  				})
   176  				t.Run("nonzero", func(t *testing.T) {
   177  					mc := minimock.NewController(t)
   178  					nonZeroValFn := FieldValueGetter(i, field, true, 0)
   179  					value := nonZeroValFn(MakeAddressable(reflect.ValueOf(input)))
   180  
   181  					custom := newCustomByKind()
   182  					val := ValueToReceiverFactory(field.Type.Kind(), custom)
   183  
   184  					mockReceiver := mocks.NewTypedReceiverMock(mc)
   185  					if !value.IsValid() || IsZero(value) {
   186  						mockReceiver.ReceiveZeroMock.Set(func(tKind reflect.Kind) {
   187  							assert.Equal(t, field.Type, tKind, "expected %s, got %s", field.Type.String(), tKind.String())
   188  						})
   189  					} else {
   190  						mockReceiver.ReceiveElseMock.Set(func(tKind reflect.Kind, v interface{}, isZero bool) {
   191  						})
   192  					}
   193  					nonZeroUnexported, nonZero := val(!exported, field.Type, true)
   194  					nonZero(value, mockReceiver)
   195  					assert.Equal(t, !exported, nonZeroUnexported)
   196  					mc.Finish()
   197  				})
   198  			})
   199  			t.Run("without custom", func(t *testing.T) {
   200  				t.Run("zero", func(t *testing.T) {
   201  					mc := minimock.NewController(t)
   202  					mockReceiver := mocks.NewTypedReceiverMock(mc).ReceiveNilMock.Set(func(k1 reflect.Kind) {
   203  						assert.Equal(t, field.Type.Kind(), k1)
   204  					})
   205  					val := ValueToReceiverFactory(field.Type.Kind(), nil)
   206  					withZeroUnexported, withZero := val(!exported, field.Type, true)
   207  					withZero(reflect.Zero(field.Type), mockReceiver)
   208  					assert.Equal(t, !exported, withZeroUnexported)
   209  					mc.Finish()
   210  				})
   211  				t.Run("nonzero", func(t *testing.T) {
   212  					val := ValueToReceiverFactory(field.Type.Kind(), nil)
   213  					mc := minimock.NewController(t)
   214  					nonZeroValFn := FieldValueGetter(i, field, true, 0)
   215  					value := nonZeroValFn(MakeAddressable(reflect.ValueOf(input)))
   216  					mockReceiver := newMockByKind(mc, field.Type, value.Interface())
   217  					withoutZeroUnexported, nonZero := val(!exported, field.Type, false)
   218  					nonZero(value, mockReceiver)
   219  					assert.Equal(t, !exported, withoutZeroUnexported)
   220  					mc.Finish()
   221  				})
   222  			})
   223  		}
   224  	})
   225  	t.Run("interface", func(t *testing.T) {
   226  		type myFunc func()
   227  		type TestStruct struct {
   228  			f interface{}
   229  			F interface{}
   230  		}
   231  
   232  		input := TestStruct{
   233  			f: myFunc(func() {}),
   234  			F: myFunc(func() {}),
   235  		}
   236  		st := reflect.TypeOf(input)
   237  		for i := 0; i < st.NumField(); i++ {
   238  			field := st.Field(i)
   239  			exported := reflect.ValueOf(input).Field(i).CanInterface()
   240  			t.Run("with custom", func(t *testing.T) {
   241  				t.Run("zero", func(t *testing.T) {
   242  					mc := minimock.NewController(t)
   243  					custom := newCustomByKind()
   244  					val := ValueToReceiverFactory(field.Type.Kind(), custom)
   245  					mockReceiver := mocks.NewTypedReceiverMock(mc).ReceiveNilMock.Set(func(k1 reflect.Kind) {
   246  						assert.Equal(t, reflect.Invalid, k1)
   247  					})
   248  					withZeroUnexported, withZero := val(!exported, field.Type, true)
   249  					withZero(reflect.Zero(field.Type), mockReceiver)
   250  					assert.Equal(t, !exported, withZeroUnexported)
   251  					mc.Finish()
   252  				})
   253  				t.Run("nonzero", func(t *testing.T) {
   254  					mc := minimock.NewController(t)
   255  					nonZeroValFn := FieldValueGetter(i, field, true, 0)
   256  					value := nonZeroValFn(MakeAddressable(reflect.ValueOf(input)))
   257  
   258  					custom := newCustomByKind()
   259  					val := ValueToReceiverFactory(field.Type.Kind(), custom)
   260  
   261  					mockReceiver := mocks.NewTypedReceiverMock(mc)
   262  					if !value.IsValid() || IsZero(value) {
   263  						mockReceiver.ReceiveZeroMock.Set(func(tKind reflect.Kind) {
   264  							assert.Equal(t, field.Type, tKind, "expected %s, got %s", field.Type.String(), tKind.String())
   265  						})
   266  					} else {
   267  						mockReceiver.ReceiveElseMock.Set(func(tKind reflect.Kind, v interface{}, isZero bool) {
   268  						})
   269  					}
   270  					nonZeroUnexported, nonZero := val(!exported, field.Type, true)
   271  					nonZero(value, mockReceiver)
   272  					assert.Equal(t, !exported, nonZeroUnexported)
   273  					mc.Finish()
   274  				})
   275  			})
   276  			t.Run("without custom", func(t *testing.T) {
   277  				t.Run("zero", func(t *testing.T) {
   278  					mc := minimock.NewController(t)
   279  					mockReceiver := mocks.NewTypedReceiverMock(mc).ReceiveNilMock.Set(func(k1 reflect.Kind) {
   280  						assert.Equal(t, field.Type.Kind(), k1)
   281  					})
   282  					val := ValueToReceiverFactory(field.Type.Kind(), nil)
   283  					withZeroUnexported, withZero := val(!exported, field.Type, true)
   284  					withZero(reflect.Zero(field.Type), mockReceiver)
   285  					assert.Equal(t, !exported, withZeroUnexported)
   286  					mc.Finish()
   287  				})
   288  				t.Run("nonzero", func(t *testing.T) {
   289  					val := ValueToReceiverFactory(field.Type.Kind(), nil)
   290  					mc := minimock.NewController(t)
   291  					nonZeroValFn := FieldValueGetter(i, field, true, 0)
   292  					value := nonZeroValFn(MakeAddressable(reflect.ValueOf(input)))
   293  					mockReceiver := newMockByKind(mc, field.Type, value.Interface())
   294  					withoutZeroUnexported, nonZero := val(!exported, field.Type, false)
   295  					nonZero(value, mockReceiver)
   296  					assert.Equal(t, !exported, withoutZeroUnexported)
   297  					mc.Finish()
   298  				})
   299  			})
   300  		}
   301  	})
   302  
   303  	t.Run("Excluded", func(t *testing.T) {
   304  		require.Nil(t, ValueToReceiverFactory(reflect.UnsafePointer, nil))
   305  	})
   306  }
   307  
   308  func newMockByKind(mc *minimock.Controller, t reflect.Type, val interface{}) *mocks.TypedReceiverMock {
   309  	res := mocks.NewTypedReceiverMock(mc)
   310  	switch t.Kind() {
   311  	case reflect.Bool:
   312  		res.ReceiveBoolMock.Set(func(k1 reflect.Kind, b1 bool) {
   313  			assert.Equal(mc, t.Kind(), k1)
   314  			assert.Equal(mc, val.(bool), b1)
   315  		})
   316  	case reflect.Int64, reflect.Int32:
   317  		res.ReceiveIntMock.Set(func(k1 reflect.Kind, i1 int64) {
   318  			assert.Equal(mc, t.Kind(), k1)
   319  		})
   320  	case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
   321  		res.ReceiveUintMock.Set(func(k1 reflect.Kind, i1 uint64) {
   322  			assert.Equal(mc, t.Kind(), k1)
   323  		})
   324  	case reflect.Float64, reflect.Float32:
   325  		res.ReceiveFloatMock.Set(func(k1 reflect.Kind, i1 float64) {
   326  			assert.Equal(mc, t.Kind(), k1)
   327  		})
   328  	case reflect.String:
   329  		res.ReceiveStringMock.Set(func(k1 reflect.Kind, s1 string) {
   330  			assert.Equal(mc, t.Kind(), k1)
   331  			assert.Equal(mc, val.(string), s1)
   332  		})
   333  	case reflect.Complex128, reflect.Complex64:
   334  		res.ReceiveComplexMock.Set(func(k1 reflect.Kind, c1 complex128) {
   335  			assert.Equal(mc, t.Kind(), k1)
   336  		})
   337  	case reflect.Func, reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
   338  		res.ReceiveElseMock.Set(func(t reflect.Kind, v interface{}, isZero bool) {
   339  		})
   340  	case reflect.Interface:
   341  		res.ReceiveElseMock.Set(func(t reflect.Kind, v interface{}, isZero bool) {
   342  		})
   343  	default:
   344  		assert.Fail(mc, "unexpected type")
   345  	}
   346  	return res
   347  }
   348  
   349  func newCustomByKind() IfaceToReceiverFactoryFunc {
   350  	return func(t reflect.Type, checkZero bool) IfaceToReceiverFunc {
   351  		return func(value interface{}, kind reflect.Kind, out TypedReceiver) {
   352  			val := reflect.ValueOf(value)
   353  			if checkZero {
   354  				switch {
   355  				case !val.IsValid() || val.IsNil():
   356  					out.ReceiveNil(kind)
   357  					return
   358  				case IsZero(val):
   359  					out.ReceiveZero(kind)
   360  					return
   361  				}
   362  			}
   363  
   364  			switch val.Kind() {
   365  			case reflect.Func, reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
   366  				out.ReceiveElse(val.Kind(), value, false)
   367  			default:
   368  				panic("unexpected input")
   369  			}
   370  
   371  		}
   372  	}
   373  }
   374  
   375  // IsZero could be removed after go 1.13, in 1.13 it is in reflect package
   376  func IsZero(v reflect.Value) bool {
   377  	switch v.Kind() {
   378  	case reflect.Array, reflect.String:
   379  		return v.Len() == 0
   380  	case reflect.Bool:
   381  		return !v.Bool()
   382  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   383  		return v.Int() == 0
   384  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   385  		return v.Uint() == 0
   386  	case reflect.Float32, reflect.Float64:
   387  		return v.Float() == 0
   388  	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   389  		return v.IsNil()
   390  	}
   391  	return false
   392  }