github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/reflect/visiblefields_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package reflect_test 6 7 import ( 8 . "reflect" 9 "strings" 10 "testing" 11 ) 12 13 type structField struct { 14 name string 15 index []int 16 } 17 18 var fieldsTests = []struct { 19 testName string 20 val any 21 expect []structField 22 }{{ 23 testName: "SimpleStruct", 24 val: struct { 25 A int 26 B string 27 C bool 28 }{}, 29 expect: []structField{{ 30 name: "A", 31 index: []int{0}, 32 }, { 33 name: "B", 34 index: []int{1}, 35 }, { 36 name: "C", 37 index: []int{2}, 38 }}, 39 }, { 40 testName: "NonEmbeddedStructMember", 41 val: struct { 42 A struct { 43 X int 44 } 45 }{}, 46 expect: []structField{{ 47 name: "A", 48 index: []int{0}, 49 }}, 50 }, { 51 testName: "EmbeddedExportedStruct", 52 val: struct { 53 SFG 54 }{}, 55 expect: []structField{{ 56 name: "SFG", 57 index: []int{0}, 58 }, { 59 name: "F", 60 index: []int{0, 0}, 61 }, { 62 name: "G", 63 index: []int{0, 1}, 64 }}, 65 }, { 66 testName: "EmbeddedUnexportedStruct", 67 val: struct { 68 sFG 69 }{}, 70 expect: []structField{{ 71 name: "sFG", 72 index: []int{0}, 73 }, { 74 name: "F", 75 index: []int{0, 0}, 76 }, { 77 name: "G", 78 index: []int{0, 1}, 79 }}, 80 }, { 81 testName: "TwoEmbeddedStructsWithCancelingMembers", 82 val: struct { 83 SFG 84 SF 85 }{}, 86 expect: []structField{{ 87 name: "SFG", 88 index: []int{0}, 89 }, { 90 name: "G", 91 index: []int{0, 1}, 92 }, { 93 name: "SF", 94 index: []int{1}, 95 }}, 96 }, { 97 testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths", 98 val: struct { 99 SFGH3 100 SG1 101 SFG2 102 SF2 103 L int 104 }{}, 105 expect: []structField{{ 106 name: "SFGH3", 107 index: []int{0}, 108 }, { 109 name: "SFGH2", 110 index: []int{0, 0}, 111 }, { 112 name: "SFGH1", 113 index: []int{0, 0, 0}, 114 }, { 115 name: "SFGH", 116 index: []int{0, 0, 0, 0}, 117 }, { 118 name: "H", 119 index: []int{0, 0, 0, 0, 2}, 120 }, { 121 name: "SG1", 122 index: []int{1}, 123 }, { 124 name: "SG", 125 index: []int{1, 0}, 126 }, { 127 name: "G", 128 index: []int{1, 0, 0}, 129 }, { 130 name: "SFG2", 131 index: []int{2}, 132 }, { 133 name: "SFG1", 134 index: []int{2, 0}, 135 }, { 136 name: "SFG", 137 index: []int{2, 0, 0}, 138 }, { 139 name: "SF2", 140 index: []int{3}, 141 }, { 142 name: "SF1", 143 index: []int{3, 0}, 144 }, { 145 name: "SF", 146 index: []int{3, 0, 0}, 147 }, { 148 name: "L", 149 index: []int{4}, 150 }}, 151 }, { 152 testName: "EmbeddedPointerStruct", 153 val: struct { 154 *SF 155 }{}, 156 expect: []structField{{ 157 name: "SF", 158 index: []int{0}, 159 }, { 160 name: "F", 161 index: []int{0, 0}, 162 }}, 163 }, { 164 testName: "EmbeddedNotAPointer", 165 val: struct { 166 M 167 }{}, 168 expect: []structField{{ 169 name: "M", 170 index: []int{0}, 171 }}, 172 }, { 173 testName: "RecursiveEmbedding", 174 val: Rec1{}, 175 expect: []structField{{ 176 name: "Rec2", 177 index: []int{0}, 178 }, { 179 name: "F", 180 index: []int{0, 0}, 181 }, { 182 name: "Rec1", 183 index: []int{0, 1}, 184 }}, 185 }, { 186 testName: "RecursiveEmbedding2", 187 val: Rec2{}, 188 expect: []structField{{ 189 name: "F", 190 index: []int{0}, 191 }, { 192 name: "Rec1", 193 index: []int{1}, 194 }, { 195 name: "Rec2", 196 index: []int{1, 0}, 197 }}, 198 }, { 199 testName: "RecursiveEmbedding3", 200 val: RS3{}, 201 expect: []structField{{ 202 name: "RS2", 203 index: []int{0}, 204 }, { 205 name: "RS1", 206 index: []int{1}, 207 }, { 208 name: "i", 209 index: []int{1, 0}, 210 }}, 211 }} 212 213 type SFG struct { 214 F int 215 G int 216 } 217 218 type SFG1 struct { 219 SFG 220 } 221 222 type SFG2 struct { 223 SFG1 224 } 225 226 type SFGH struct { 227 F int 228 G int 229 H int 230 } 231 232 type SFGH1 struct { 233 SFGH 234 } 235 236 type SFGH2 struct { 237 SFGH1 238 } 239 240 type SFGH3 struct { 241 SFGH2 242 } 243 244 type SF struct { 245 F int 246 } 247 248 type SF1 struct { 249 SF 250 } 251 252 type SF2 struct { 253 SF1 254 } 255 256 type SG struct { 257 G int 258 } 259 260 type SG1 struct { 261 SG 262 } 263 264 type sFG struct { 265 F int 266 G int 267 } 268 269 type RS1 struct { 270 i int 271 } 272 273 type RS2 struct { 274 RS1 275 } 276 277 type RS3 struct { 278 RS2 279 RS1 280 } 281 282 type M map[string]any 283 284 type Rec1 struct { 285 *Rec2 286 } 287 288 type Rec2 struct { 289 F string 290 *Rec1 291 } 292 293 func TestFields(t *testing.T) { 294 for _, test := range fieldsTests { 295 test := test 296 t.Run(test.testName, func(t *testing.T) { 297 typ := TypeOf(test.val) 298 fields := VisibleFields(typ) 299 if got, want := len(fields), len(test.expect); got != want { 300 t.Fatalf("unexpected field count; got %d want %d", got, want) 301 } 302 303 for j, field := range fields { 304 expect := test.expect[j] 305 t.Logf("field %d: %s", j, expect.name) 306 gotField := typ.FieldByIndex(field.Index) 307 // Unfortunately, FieldByIndex does not return 308 // a field with the same index that we passed in, 309 // so we set it to the expected value so that 310 // it can be compared later with the result of FieldByName. 311 gotField.Index = field.Index 312 expectField := typ.FieldByIndex(expect.index) 313 // ditto. 314 expectField.Index = expect.index 315 if !DeepEqual(gotField, expectField) { 316 t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField) 317 } 318 319 // Sanity check that we can actually access the field by the 320 // expected name. 321 gotField1, ok := typ.FieldByName(expect.name) 322 if !ok { 323 t.Fatalf("field %q not accessible by name", expect.name) 324 } 325 if !DeepEqual(gotField1, expectField) { 326 t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField) 327 } 328 } 329 }) 330 } 331 } 332 333 // Must not panic with nil embedded pointer. 334 func TestFieldByIndexErr(t *testing.T) { 335 // TODO(dgryski): FieldByIndexErr not implemented yet -- skip 336 return 337 338 type A struct { 339 S string 340 } 341 type B struct { 342 *A 343 } 344 v := ValueOf(B{}) 345 _, err := v.FieldByIndexErr([]int{0, 0}) 346 if err == nil { 347 t.Fatal("expected error") 348 } 349 if !strings.Contains(err.Error(), "embedded struct field A") { 350 t.Fatal(err) 351 } 352 }