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