github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/reflect/value_test.go (about) 1 package reflect_test 2 3 import ( 4 "encoding/base64" 5 . "reflect" 6 "sort" 7 "strings" 8 "testing" 9 ) 10 11 func TestTinyIndirectPointers(t *testing.T) { 12 var m = map[string]int{} 13 m["x"] = 1 14 15 var a = &m 16 17 if ValueOf(a).Elem().Len() != 1 { 18 t.Errorf("bad map length via reflect") 19 } 20 21 var b struct { 22 Decoded *[3]byte 23 } 24 25 v1 := New(TypeOf(b.Decoded).Elem()) 26 27 var bb [3]byte 28 bb[0] = 0xaa 29 30 v1.Elem().Set(ValueOf(bb)) 31 32 if v1.Elem().Index(0).Uint() != 0xaa { 33 t.Errorf("bad indirect array index via reflect") 34 } 35 } 36 37 func TestTinyMap(t *testing.T) { 38 39 m := make(map[string]int) 40 41 mtyp := TypeOf(m) 42 43 if got, want := mtyp.Key().Kind().String(), "string"; got != want { 44 t.Errorf("m.Type().Key().String()=%q, want %q", got, want) 45 } 46 47 if got, want := mtyp.Elem().Kind().String(), "int"; got != want { 48 t.Errorf("m.Elem().String()=%q, want %q", got, want) 49 } 50 51 m["foo"] = 2 52 53 mref := ValueOf(m) 54 two := mref.MapIndex(ValueOf("foo")) 55 56 if got, want := two.Interface().(int), 2; got != want { 57 t.Errorf("MapIndex(`foo`)=%v, want %v", got, want) 58 } 59 60 m["bar"] = 3 61 m["baz"] = 4 62 m["qux"] = 5 63 64 it := mref.MapRange() 65 66 var gotKeys []string 67 for it.Next() { 68 k := it.Key() 69 v := it.Value() 70 71 kstr := k.Interface().(string) 72 vint := v.Interface().(int) 73 74 gotKeys = append(gotKeys, kstr) 75 76 if m[kstr] != vint { 77 t.Errorf("m[%v]=%v, want %v", kstr, vint, m[kstr]) 78 } 79 } 80 var wantKeys []string 81 for k := range m { 82 wantKeys = append(wantKeys, k) 83 } 84 sort.Strings(gotKeys) 85 sort.Strings(wantKeys) 86 87 if !equal(gotKeys, wantKeys) { 88 t.Errorf("MapRange return unexpected keys: got %v, want %v", gotKeys, wantKeys) 89 } 90 91 refMapKeys := mref.MapKeys() 92 gotKeys = gotKeys[:0] 93 for _, v := range refMapKeys { 94 gotKeys = append(gotKeys, v.Interface().(string)) 95 } 96 97 sort.Strings(gotKeys) 98 if !equal(gotKeys, wantKeys) { 99 t.Errorf("MapKeys return unexpected keys: got %v, want %v", gotKeys, wantKeys) 100 } 101 102 mref.SetMapIndex(ValueOf("bar"), Value{}) 103 if _, ok := m["bar"]; ok { 104 t.Errorf("SetMapIndex failed to delete `bar`") 105 } 106 107 mref.SetMapIndex(ValueOf("baz"), ValueOf(6)) 108 if got, want := m["baz"], 6; got != want { 109 t.Errorf("SetMapIndex(bar, 6) got %v, want %v", got, want) 110 } 111 112 m2ref := MakeMap(mref.Type()) 113 m2ref.SetMapIndex(ValueOf("foo"), ValueOf(2)) 114 115 m2 := m2ref.Interface().(map[string]int) 116 117 if m2["foo"] != 2 { 118 t.Errorf("MakeMap failed to create map") 119 } 120 121 type stringint struct { 122 s string 123 i int 124 } 125 126 simap := make(map[stringint]int) 127 128 refsimap := MakeMap(TypeOf(simap)) 129 130 refsimap.SetMapIndex(ValueOf(stringint{"hello", 4}), ValueOf(6)) 131 132 six := refsimap.MapIndex(ValueOf(stringint{"hello", 4})) 133 134 if six.Interface().(int) != 6 { 135 t.Errorf("m[hello, 4]=%v, want 6", six) 136 } 137 138 keys := refsimap.MapKeys() 139 if len(keys) != 1 { 140 t.Errorf("refsimap: MapKeys()=%v, want 1", len(keys)) 141 } 142 if keys[0].Type() != TypeOf(stringint{}) { 143 t.Errorf("keys[0] has wrong type: %v", keys[0].Type().String()) 144 } 145 146 sikey := keys[0].Interface().(stringint) 147 148 if sikey != (stringint{"hello", 4}) { 149 t.Errorf("sikey has unexpected value: %#v", sikey) 150 } 151 152 // make sure we can look up interface keys with reflection 153 type unmarshalerText struct { 154 A, B string 155 } 156 157 ut := make(map[unmarshalerText]bool) 158 159 refut := ValueOf(ut) 160 161 // put in a key with the compiler 162 ut[unmarshalerText{"x", "y"}] = true 163 164 // make sure we can get it out 165 v2 := refut.MapIndex(ValueOf(unmarshalerText{"x", "y"})) 166 if !v2.IsValid() || !v2.Bool() { 167 t.Errorf("Failed to look up map struct key with refection") 168 } 169 170 // put in a key with reflection 171 refut.SetMapIndex(ValueOf(unmarshalerText{"y", "z"}), ValueOf(true)) 172 173 // make sure we can get it out with the compiler 174 if !ut[unmarshalerText{"y", "z"}] { 175 t.Errorf("Failed to look up up reflect-set map key with compiler") 176 } 177 178 utKeys := refut.MapKeys() 179 180 // make sure keys extracted via reflection have the correct type 181 if _, ok := utKeys[0].Interface().(unmarshalerText); !ok { 182 t.Errorf("Map keys via MapKeys() have wrong type: %v", utKeys[0].Type().String()) 183 } 184 185 // and via iteration 186 187 utIter := refut.MapRange() 188 utIter.Next() 189 utIterKey := utIter.Key() 190 191 if _, ok := utIterKey.Interface().(unmarshalerText); !ok { 192 t.Errorf("Map keys via MapIter() have wrong type: %v", utIterKey.Type().String()) 193 } 194 195 { 196 m := map[any]any{1: 2} 197 rv := ValueOf(m) 198 iter := rv.MapRange() 199 200 iter.Next() 201 k := iter.Key() 202 if k.Kind().String() != "interface" { 203 t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String()) 204 } 205 206 keys := rv.MapKeys() 207 if k := keys[0]; k.Kind().String() != "interface" { 208 t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String()) 209 } 210 211 } 212 } 213 214 // For an interface type, it returns the number of exported and unexported methods. 215 216 type counter interface { 217 count() int 218 } 219 220 type count struct { 221 i int 222 } 223 224 func (c *count) count() int { 225 return c.i 226 } 227 228 func TestMapInterfaceKeys(t *testing.T) { 229 m := make(map[interface{}]int) 230 for i := 0; i < 20; i++ { 231 c := &count{i} 232 m[c] = i 233 } 234 235 for k, v := range m { 236 if got := m[k]; got != v { 237 t.Error("lookup failure got", got, "want", v) 238 } 239 } 240 241 refm := ValueOf(m) 242 243 keys := refm.MapKeys() 244 if len(keys) != 20 { 245 t.Error("failed to look up 20 keys") 246 } 247 for _, k := range keys { 248 c := k.Interface().(*count) 249 e := refm.MapIndex(k) 250 if e.Interface().(int) != c.i { 251 t.Error("reflect lookup failure:", c.i) 252 } 253 } 254 255 it := refm.MapRange() 256 var totalKeys int 257 for it.Next() { 258 totalKeys++ 259 k := it.Key() 260 v := it.Value().Interface().(int) 261 262 c := k.Interface().(*count) 263 if v != c.i || refm.MapIndex(k).Interface().(int) != c.i { 264 t.Error("reflect iter lookup failure:", c.i) 265 } 266 } 267 268 if totalKeys != 20 { 269 t.Errorf("failed to get 20 keys") 270 } 271 } 272 273 func TestMapInterfaceElem(t *testing.T) { 274 m := make(map[string]interface{}) 275 refm := ValueOf(m) 276 277 four := ValueOf(4) 278 hello := ValueOf("hello") 279 280 refm.SetMapIndex(hello, four) 281 282 if v := refm.MapIndex(hello).Interface().(int); v != 4 { 283 t.Errorf("failed to get value assigned to interface via MapIndex") 284 } 285 286 if v := m["hello"].(int); v != 4 { 287 t.Errorf("failed to get value assigned to interface via direct lookup") 288 } 289 } 290 291 func TestTinySlice(t *testing.T) { 292 s := []int{0, 10, 20} 293 refs := ValueOf(s) 294 295 for i := 3; i < 10; i++ { 296 refs = Append(refs, ValueOf(i*10)) 297 } 298 299 s = refs.Interface().([]int) 300 301 for i := 0; i < 10; i++ { 302 if s[i] != i*10 { 303 t.Errorf("s[%d]=%d, want %d", i, s[i], i*10) 304 } 305 } 306 307 s28 := s[2:8] 308 s28ref := refs.Slice(2, 8) 309 310 if len(s28) != s28ref.Len() || cap(s28) != s28ref.Cap() { 311 t.Errorf("Slice: len(s28)=%d s28ref.Len()=%d cap(s28)=%d s28ref.Cap()=%d\n", len(s28), s28ref.Len(), cap(s28), s28ref.Cap()) 312 } 313 314 for i, got := range s28 { 315 want := int(s28ref.Index(i).Int()) 316 if got != want { 317 t.Errorf("s28[%d]=%d, want %d", i, got, want) 318 } 319 } 320 321 s268 := s[2:6:8] 322 s268ref := refs.Slice3(2, 6, 8) 323 324 if len(s268) != s268ref.Len() || cap(s268) != s268ref.Cap() { 325 t.Errorf("Slice3: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s268), s268ref.Len(), cap(s268), s268ref.Cap()) 326 } 327 328 for i, got := range s268 { 329 want := int(s268ref.Index(i).Int()) 330 if got != want { 331 t.Errorf("s268[%d]=%d, want %d", i, got, want) 332 } 333 } 334 335 // should be equivalent to s28 now, except for the capacity which doesn't change 336 337 s268ref = ValueOf(&s268).Elem() 338 s268ref.SetLen(6) 339 if len(s28) != s268ref.Len() || cap(s268) != s268ref.Cap() { 340 t.Errorf("SetLen: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s28), s268ref.Len(), cap(s268), s268ref.Cap()) 341 } 342 343 for i, got := range s28 { 344 want := int(s268ref.Index(i).Int()) 345 if got != want { 346 t.Errorf("s28[%d]=%d, want %d", i, got, want) 347 } 348 } 349 350 refs = MakeSlice(TypeOf(s), 5, 10) 351 s = refs.Interface().([]int) 352 353 if len(s) != refs.Len() || cap(s) != refs.Cap() { 354 t.Errorf("len(s)=%v refs.Len()=%v cap(s)=%v refs.Cap()=%v", len(s), refs.Len(), cap(s), refs.Cap()) 355 } 356 } 357 358 func TestTinyBytes(t *testing.T) { 359 s := []byte("abcde") 360 refs := ValueOf(s) 361 362 s2 := refs.Bytes() 363 364 if !equal(s, s2) { 365 t.Errorf("Failed to get Bytes(): %v != %v", s, s2) 366 } 367 368 Copy(refs, ValueOf("12345")) 369 370 if string(s) != "12345" { 371 t.Errorf("Copy()=%q, want `12345`", string(s)) 372 } 373 374 // test small arrays that fit in a pointer 375 a := [3]byte{10, 20, 30} 376 v := ValueOf(&a) 377 vslice := v.Elem().Bytes() 378 if len(vslice) != 3 || cap(vslice) != 3 { 379 t.Errorf("len(vslice)=%v, cap(vslice)=%v", len(vslice), cap(vslice)) 380 } 381 382 for i, got := range vslice { 383 if want := (byte(i) + 1) * 10; got != want { 384 t.Errorf("vslice[%d]=%d, want %d", i, got, want) 385 } 386 } 387 } 388 389 func TestTinyNamedTypes(t *testing.T) { 390 type namedString string 391 392 named := namedString("foo") 393 if got, want := TypeOf(named).Name(), "namedString"; got != want { 394 t.Errorf("TypeOf.Name()=%v, want %v", got, want) 395 } 396 397 if got, want := TypeOf(named).String(), "reflect_test.namedString"; got != want { 398 t.Errorf("TypeOf.String()=%v, want %v", got, want) 399 } 400 401 errorType := TypeOf((*error)(nil)).Elem() 402 if s := errorType.String(); s != "error" { 403 t.Errorf("error type = %v, want error", s) 404 } 405 406 m := make(map[[4]uint16]string) 407 408 if got, want := TypeOf(m).String(), "map[[4]uint16]string"; got != want { 409 t.Errorf("Type.String()=%v, want %v", got, want) 410 } 411 412 s := struct { 413 a int8 414 b int8 415 c int8 416 d int8 417 e int8 418 f int32 419 }{} 420 421 if got, want := TypeOf(s).String(), "struct { a int8; b int8; c int8; d int8; e int8; f int32 }"; got != want { 422 t.Errorf("Type.String()=%v, want %v", got, want) 423 } 424 425 if got, want := ValueOf(m).String(), "<map[[4]uint16]string Value>"; got != want { 426 t.Errorf("Value.String()=%v, want %v", got, want) 427 } 428 429 if got, want := TypeOf(base64.Encoding{}).String(), "base64.Encoding"; got != want { 430 t.Errorf("Type.String(base64.Encoding{})=%v, want %v", got, want) 431 } 432 433 type Repository struct { 434 RoleName *string `json:"role_name,omitempty"` 435 } 436 437 var repo *Repository 438 v := ValueOf(&repo).Elem() 439 n := New(v.Type().Elem()) 440 v.Set(n) 441 } 442 443 func TestTinyStruct(t *testing.T) { 444 type barStruct struct { 445 QuxString string 446 BazInt int 447 } 448 449 type foobar struct { 450 Foo string `foo:"struct tag"` 451 Bar barStruct 452 } 453 454 var fb foobar 455 fb.Bar.QuxString = "qux" 456 457 reffb := TypeOf(fb) 458 459 q := reffb.FieldByIndex([]int{1, 0}) 460 if want := "QuxString"; q.Name != want { 461 t.Errorf("FieldByIndex=%v, want %v", q.Name, want) 462 } 463 464 var ok bool 465 q, ok = reffb.FieldByName("Foo") 466 if q.Name != "Foo" || !ok { 467 t.Errorf("FieldByName(Foo)=%v,%v, want Foo, true", q.Name, ok) 468 } 469 470 if got, want := q.Tag, `foo:"struct tag"`; string(got) != want { 471 t.Errorf("StrucTag for Foo=%v, want %v", got, want) 472 } 473 474 q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "bar" }) 475 if q.Name != "Bar" || !ok { 476 t.Errorf("FieldByNameFunc(bar)=%v,%v, want Bar, true", q.Name, ok) 477 } 478 479 q, ok = reffb.FieldByName("Snorble") 480 if q.Name != "" || ok { 481 t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false", q.Name, ok) 482 } 483 484 q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "snorble" }) 485 if q.Name != "" || ok { 486 t.Errorf("FieldByName(snorble)=%v,%v, want ``, false", q.Name, ok) 487 } 488 } 489 490 func TestTinyZero(t *testing.T) { 491 s := "hello, world" 492 var sptr *string = &s 493 v := ValueOf(&sptr).Elem() 494 v.Set(Zero(v.Type())) 495 496 sptr = v.Interface().(*string) 497 498 if sptr != nil { 499 t.Errorf("failed to set a nil string pointer") 500 } 501 502 sl := []int{1, 2, 3} 503 v = ValueOf(&sl).Elem() 504 v.Set(Zero(v.Type())) 505 sl = v.Interface().([]int) 506 507 if sl != nil { 508 t.Errorf("failed to set a nil slice") 509 } 510 } 511 512 func addrDecode(body interface{}) { 513 vbody := ValueOf(body) 514 ptr := vbody.Elem() 515 pptr := ptr.Addr() 516 addrSetInt(pptr.Interface()) 517 } 518 519 func addrSetInt(intf interface{}) { 520 ptr := intf.(*uint64) 521 *ptr = 112358 522 } 523 524 func TestTinyAddr(t *testing.T) { 525 var n uint64 526 addrDecode(&n) 527 if n != 112358 { 528 t.Errorf("Failed to set t=112358, got %v", n) 529 } 530 531 v := ValueOf(&n) 532 if got, want := v.Elem().Addr().CanAddr(), false; got != want { 533 t.Errorf("Elem.Addr.CanAddr=%v, want %v", got, want) 534 } 535 } 536 537 func TestTinyNilType(t *testing.T) { 538 var a any = nil 539 typ := TypeOf(a) 540 if typ != nil { 541 t.Errorf("Type of any{nil} is not nil") 542 } 543 } 544 545 func TestTinySetBytes(t *testing.T) { 546 var b []byte 547 refb := ValueOf(&b).Elem() 548 s := []byte("hello") 549 refb.SetBytes(s) 550 s[0] = 'b' 551 552 refbSlice := refb.Interface().([]byte) 553 554 if len(refbSlice) != len(s) || b[0] != s[0] || refbSlice[0] != s[0] { 555 t.Errorf("SetBytes(): reflection slice mismatch") 556 } 557 } 558 559 type methodStruct struct { 560 i int 561 } 562 563 func (m methodStruct) ValueMethod1() int { 564 return m.i 565 } 566 567 func (m methodStruct) valueMethod2() int { 568 return m.i 569 } 570 571 func (m *methodStruct) PointerMethod1() int { 572 return m.i 573 } 574 575 func (m *methodStruct) PointerMethod2() int { 576 return m.i 577 } 578 579 func (m *methodStruct) pointerMethod3() int { 580 return m.i 581 } 582 583 func TestTinyNumMethods(t *testing.T) { 584 refptrt := TypeOf(&methodStruct{}) 585 if got, want := refptrt.NumMethod(), 1+2; got != want { 586 t.Errorf("Pointer Methods=%v, want %v", got, want) 587 } 588 589 reft := refptrt.Elem() 590 if got, want := reft.NumMethod(), 1; got != want { 591 t.Errorf("Value Methods=%v, want %v", got, want) 592 } 593 } 594 595 func TestAssignableTo(t *testing.T) { 596 var a any 597 refa := ValueOf(&a).Elem() 598 refa.Set(ValueOf(4)) 599 if got, want := refa.Interface().(int), 4; got != want { 600 t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want) 601 } 602 } 603 604 func TestConvert(t *testing.T) { 605 v := ValueOf(int64(3)) 606 c := v.Convert(TypeOf(byte(0))) 607 608 if c.Type().Kind() != Uint8 || c.Uint() != 3 { 609 t.Errorf("Convert(uint64 -> byte failed: kind=%v, value=%d", c.Type().Kind().String(), c.Uint()) 610 } 611 612 v = ValueOf("hello") 613 c = v.Convert(TypeOf([]byte(""))) 614 615 if c.Type().Kind() != Slice || c.Type().Elem().Kind() != Uint8 && c.Len() != 5 && string(c.Bytes()) != "hello" { 616 t.Errorf("Convert(string -> []byte") 617 } 618 619 type namedString string 620 621 c = v.Convert(TypeOf(namedString(""))) 622 if c.Type().Kind() != String || c.Type().Name() != "namedString" { 623 t.Errorf("Convert(string -> namedString") 624 } 625 } 626 627 func TestIssue4040(t *testing.T) { 628 var value interface{} = uint16(0) 629 630 // get the pointer to the interface value 631 inPtr := ValueOf(&value) 632 633 // dereference to get the actual value (an interface) 634 inElem := inPtr.Elem() 635 636 // create a new value of the same concrete type 637 uint16Type := TypeOf(uint16(0)) 638 newUint16Value := New(uint16Type).Elem() 639 newUint16Value.Set(ValueOf(uint16(13))) 640 641 // set the new value to the interface 642 inElem.Set(newUint16Value) 643 644 if value.(uint16) != 13 { 645 t.Errorf("Failed to set interface value from uint16") 646 } 647 } 648 649 func equal[T comparable](a, b []T) bool { 650 if len(a) != len(b) { 651 return false 652 } 653 654 for i, aa := range a { 655 if b[i] != aa { 656 return false 657 } 658 } 659 return true 660 }