...

Source file src/reflect/visiblefields_test.go

Documentation: reflect

     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  	type A struct {
   336  		S string
   337  	}
   338  	type B struct {
   339  		*A
   340  	}
   341  	v := ValueOf(B{})
   342  	_, err := v.FieldByIndexErr([]int{0, 0})
   343  	if err == nil {
   344  		t.Fatal("expected error")
   345  	}
   346  	if !strings.Contains(err.Error(), "embedded struct field A") {
   347  		t.Fatal(err)
   348  	}
   349  }
   350  

View as plain text