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 }