25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

554 satır
13 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package fields
  15. import (
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "reflect"
  20. "testing"
  21. "time"
  22. "cloud.google.com/go/internal/testutil"
  23. "github.com/google/go-cmp/cmp"
  24. )
  25. type embed1 struct {
  26. Em1 int
  27. Dup int // annihilates with embed2.Dup
  28. Shadow int
  29. embed3
  30. }
  31. type embed2 struct {
  32. Dup int
  33. embed3
  34. embed4
  35. }
  36. type embed3 struct {
  37. Em3 int // annihilated because embed3 is in both embed1 and embed2
  38. embed5
  39. }
  40. type embed4 struct {
  41. Em4 int
  42. Dup int // annihilation of Dup in embed1, embed2 hides this Dup
  43. *embed1 // ignored because it occurs at a higher level
  44. }
  45. type embed5 struct {
  46. x int
  47. }
  48. type Anonymous int
  49. type S1 struct {
  50. Exported int
  51. unexported int
  52. Shadow int // shadows S1.Shadow
  53. embed1
  54. *embed2
  55. Anonymous
  56. }
  57. type Time struct {
  58. time.Time
  59. }
  60. var intType = reflect.TypeOf(int(0))
  61. func field(name string, tval interface{}, index ...int) *Field {
  62. return &Field{
  63. Name: name,
  64. Type: reflect.TypeOf(tval),
  65. Index: index,
  66. ParsedTag: []string(nil),
  67. }
  68. }
  69. func tfield(name string, tval interface{}, index ...int) *Field {
  70. return &Field{
  71. Name: name,
  72. Type: reflect.TypeOf(tval),
  73. Index: index,
  74. NameFromTag: true,
  75. ParsedTag: []string(nil),
  76. }
  77. }
  78. func TestFieldsNoTags(t *testing.T) {
  79. c := NewCache(nil, nil, nil)
  80. got, err := c.Fields(reflect.TypeOf(S1{}))
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. want := []*Field{
  85. field("Exported", int(0), 0),
  86. field("Shadow", int(0), 2),
  87. field("Em1", int(0), 3, 0),
  88. field("Em4", int(0), 4, 2, 0),
  89. field("Anonymous", Anonymous(0), 5),
  90. }
  91. for _, f := range want {
  92. f.ParsedTag = nil
  93. }
  94. if msg, ok := compareFields(got, want); !ok {
  95. t.Error(msg)
  96. }
  97. }
  98. func TestAgainstJSONEncodingNoTags(t *testing.T) {
  99. // Demonstrates that this package produces the same set of fields as encoding/json.
  100. s1 := S1{
  101. Exported: 1,
  102. unexported: 2,
  103. Shadow: 3,
  104. embed1: embed1{
  105. Em1: 4,
  106. Dup: 5,
  107. Shadow: 6,
  108. embed3: embed3{
  109. Em3: 7,
  110. embed5: embed5{x: 8},
  111. },
  112. },
  113. embed2: &embed2{
  114. Dup: 9,
  115. embed3: embed3{
  116. Em3: 10,
  117. embed5: embed5{x: 11},
  118. },
  119. embed4: embed4{
  120. Em4: 12,
  121. Dup: 13,
  122. embed1: &embed1{Em1: 14},
  123. },
  124. },
  125. Anonymous: Anonymous(15),
  126. }
  127. var want S1
  128. want.embed2 = &embed2{} // need this because reflection won't create it
  129. jsonRoundTrip(t, s1, &want)
  130. var got S1
  131. got.embed2 = &embed2{}
  132. fields, err := NewCache(nil, nil, nil).Fields(reflect.TypeOf(got))
  133. if err != nil {
  134. t.Fatal(err)
  135. }
  136. setFields(fields, &got, s1)
  137. if !testutil.Equal(got, want,
  138. cmp.AllowUnexported(S1{}, embed1{}, embed2{}, embed3{}, embed4{}, embed5{})) {
  139. t.Errorf("got\n%+v\nwant\n%+v", got, want)
  140. }
  141. }
  142. // Tests use of LeafTypes parameter to NewCache
  143. func TestAgainstJSONEncodingEmbeddedTime(t *testing.T) {
  144. timeLeafFn := func(t reflect.Type) bool {
  145. return t == reflect.TypeOf(time.Time{})
  146. }
  147. // Demonstrates that this package can produce the same set of
  148. // fields as encoding/json for a struct with an embedded time.Time.
  149. now := time.Now().UTC()
  150. myt := Time{
  151. now,
  152. }
  153. var want Time
  154. jsonRoundTrip(t, myt, &want)
  155. var got Time
  156. fields, err := NewCache(nil, nil, timeLeafFn).Fields(reflect.TypeOf(got))
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. setFields(fields, &got, myt)
  161. if !testutil.Equal(got, want) {
  162. t.Errorf("got\n%+v\nwant\n%+v", got, want)
  163. }
  164. }
  165. type S2 struct {
  166. NoTag int
  167. XXX int `json:"tag"` // tag name takes precedence
  168. Anonymous `json:"anon"` // anonymous non-structs also get their name from the tag
  169. Embed `json:"em"` // embedded structs with tags become fields
  170. Tag int
  171. YYY int `json:"Tag"` // tag takes precedence over untagged field of the same name
  172. Empty int `json:""` // empty tag is noop
  173. tEmbed1
  174. tEmbed2
  175. }
  176. type Embed struct {
  177. Em int
  178. }
  179. type tEmbed1 struct {
  180. Dup int
  181. X int `json:"Dup2"`
  182. }
  183. type tEmbed2 struct {
  184. Y int `json:"Dup"` // takes precedence over tEmbed1.Dup because it is tagged
  185. Z int `json:"Dup2"` // same name as tEmbed1.X and both tagged, so ignored
  186. }
  187. func jsonTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
  188. return ParseStandardTag("json", t)
  189. }
  190. func validateFunc(t reflect.Type) (err error) {
  191. if t.Kind() != reflect.Struct {
  192. return errors.New("non-struct type used")
  193. }
  194. for i := 0; i < t.NumField(); i++ {
  195. if t.Field(i).Type.Kind() == reflect.Slice {
  196. return fmt.Errorf("slice field found at field %s on struct %s", t.Field(i).Name, t.Name())
  197. }
  198. }
  199. return nil
  200. }
  201. func TestFieldsWithTags(t *testing.T) {
  202. got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
  203. if err != nil {
  204. t.Fatal(err)
  205. }
  206. want := []*Field{
  207. field("NoTag", int(0), 0),
  208. tfield("tag", int(0), 1),
  209. tfield("anon", Anonymous(0), 2),
  210. tfield("em", Embed{}, 4),
  211. tfield("Tag", int(0), 6),
  212. field("Empty", int(0), 7),
  213. tfield("Dup", int(0), 8, 0),
  214. }
  215. if msg, ok := compareFields(got, want); !ok {
  216. t.Error(msg)
  217. }
  218. }
  219. func TestAgainstJSONEncodingWithTags(t *testing.T) {
  220. // Demonstrates that this package produces the same set of fields as encoding/json.
  221. s2 := S2{
  222. NoTag: 1,
  223. XXX: 2,
  224. Anonymous: 3,
  225. Embed: Embed{
  226. Em: 4,
  227. },
  228. tEmbed1: tEmbed1{
  229. Dup: 5,
  230. X: 6,
  231. },
  232. tEmbed2: tEmbed2{
  233. Y: 7,
  234. Z: 8,
  235. },
  236. }
  237. var want S2
  238. jsonRoundTrip(t, s2, &want)
  239. var got S2
  240. fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(got))
  241. if err != nil {
  242. t.Fatal(err)
  243. }
  244. setFields(fields, &got, s2)
  245. if !testutil.Equal(got, want, cmp.AllowUnexported(S2{})) {
  246. t.Errorf("got\n%+v\nwant\n%+v", got, want)
  247. }
  248. }
  249. func TestUnexportedAnonymousNonStruct(t *testing.T) {
  250. // An unexported anonymous non-struct field should not be recorded.
  251. // This is currently a bug in encoding/json.
  252. // https://github.com/golang/go/issues/18009
  253. type S struct{}
  254. got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
  255. if err != nil {
  256. t.Fatal(err)
  257. }
  258. if len(got) != 0 {
  259. t.Errorf("got %d fields, want 0", len(got))
  260. }
  261. }
  262. func TestUnexportedAnonymousStruct(t *testing.T) {
  263. // An unexported anonymous struct with a tag is ignored.
  264. // This is currently a bug in encoding/json.
  265. // https://github.com/golang/go/issues/18009
  266. type (
  267. s1 struct{ X int }
  268. S2 struct {
  269. s1 `json:"Y"`
  270. }
  271. )
  272. got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
  273. if err != nil {
  274. t.Fatal(err)
  275. }
  276. if len(got) != 0 {
  277. t.Errorf("got %d fields, want 0", len(got))
  278. }
  279. }
  280. func TestDominantField(t *testing.T) {
  281. // With fields sorted by index length and then by tag presence,
  282. // the dominant field is always the first. Make sure all error
  283. // cases are caught.
  284. for _, test := range []struct {
  285. fields []Field
  286. wantOK bool
  287. }{
  288. // A single field is OK.
  289. {[]Field{{Index: []int{0}}}, true},
  290. {[]Field{{Index: []int{0}, NameFromTag: true}}, true},
  291. // A single field at top level is OK.
  292. {[]Field{{Index: []int{0}}, {Index: []int{1, 0}}}, true},
  293. {[]Field{{Index: []int{0}}, {Index: []int{1, 0}, NameFromTag: true}}, true},
  294. {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1, 0}, NameFromTag: true}}, true},
  295. // A single tagged field is OK.
  296. {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}}}, true},
  297. // Two untagged fields at the same level is an error.
  298. {[]Field{{Index: []int{0}}, {Index: []int{1}}}, false},
  299. // Two tagged fields at the same level is an error.
  300. {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}, NameFromTag: true}}, false},
  301. } {
  302. _, gotOK := dominantField(test.fields)
  303. if gotOK != test.wantOK {
  304. t.Errorf("%v: got %t, want %t", test.fields, gotOK, test.wantOK)
  305. }
  306. }
  307. }
  308. func TestIgnore(t *testing.T) {
  309. type S struct {
  310. X int `json:"-"`
  311. }
  312. got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
  313. if err != nil {
  314. t.Fatal(err)
  315. }
  316. if len(got) != 0 {
  317. t.Errorf("got %d fields, want 0", len(got))
  318. }
  319. }
  320. func TestParsedTag(t *testing.T) {
  321. type S struct {
  322. X int `json:"name,omitempty"`
  323. }
  324. got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
  325. if err != nil {
  326. t.Fatal(err)
  327. }
  328. want := []*Field{
  329. {Name: "name", NameFromTag: true, Type: intType,
  330. Index: []int{0}, ParsedTag: []string{"omitempty"}},
  331. }
  332. if msg, ok := compareFields(got, want); !ok {
  333. t.Error(msg)
  334. }
  335. }
  336. func TestValidateFunc(t *testing.T) {
  337. type MyInvalidStruct struct {
  338. A string
  339. B []int
  340. }
  341. _, err := NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyInvalidStruct{}))
  342. if err == nil {
  343. t.Fatal("expected error, got nil")
  344. }
  345. type MyValidStruct struct {
  346. A string
  347. B int
  348. }
  349. _, err = NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyValidStruct{}))
  350. if err != nil {
  351. t.Fatalf("expected nil, got error: %s\n", err)
  352. }
  353. }
  354. func compareFields(got []Field, want []*Field) (msg string, ok bool) {
  355. if len(got) != len(want) {
  356. return fmt.Sprintf("got %d fields, want %d", len(got), len(want)), false
  357. }
  358. for i, g := range got {
  359. w := *want[i]
  360. if !fieldsEqual(&g, &w) {
  361. return fmt.Sprintf("got\n%+v\nwant\n%+v", g, w), false
  362. }
  363. }
  364. return "", true
  365. }
  366. // Need this because Field contains a function, which cannot be compared even
  367. // by testutil.Equal.
  368. func fieldsEqual(f1, f2 *Field) bool {
  369. if f1 == nil || f2 == nil {
  370. return f1 == f2
  371. }
  372. return f1.Name == f2.Name &&
  373. f1.NameFromTag == f2.NameFromTag &&
  374. f1.Type == f2.Type &&
  375. testutil.Equal(f1.ParsedTag, f2.ParsedTag)
  376. }
  377. // Set the fields of dst from those of src.
  378. // dst must be a pointer to a struct value.
  379. // src must be a struct value.
  380. func setFields(fields []Field, dst, src interface{}) {
  381. vsrc := reflect.ValueOf(src)
  382. vdst := reflect.ValueOf(dst).Elem()
  383. for _, f := range fields {
  384. fdst := vdst.FieldByIndex(f.Index)
  385. fsrc := vsrc.FieldByIndex(f.Index)
  386. fdst.Set(fsrc)
  387. }
  388. }
  389. func jsonRoundTrip(t *testing.T, in, out interface{}) {
  390. bytes, err := json.Marshal(in)
  391. if err != nil {
  392. t.Fatal(err)
  393. }
  394. if err := json.Unmarshal(bytes, out); err != nil {
  395. t.Fatal(err)
  396. }
  397. }
  398. type S3 struct {
  399. S4
  400. Abc int
  401. AbC int
  402. Tag int
  403. X int `json:"Tag"`
  404. unexported int
  405. }
  406. type S4 struct {
  407. ABc int
  408. Y int `json:"Abc"` // ignored because of top-level Abc
  409. }
  410. func TestMatchingField(t *testing.T) {
  411. fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
  412. if err != nil {
  413. t.Fatal(err)
  414. }
  415. for _, test := range []struct {
  416. name string
  417. want *Field
  418. }{
  419. // Exact match wins.
  420. {"Abc", field("Abc", int(0), 1)},
  421. {"AbC", field("AbC", int(0), 2)},
  422. {"ABc", field("ABc", int(0), 0, 0)},
  423. // If there are multiple matches but no exact match or tag,
  424. // the first field wins, lexicographically by index.
  425. // Here, "ABc" is at a deeper embedding level, but since S4 appears
  426. // first in S3, its index precedes the other fields of S3.
  427. {"abc", field("ABc", int(0), 0, 0)},
  428. // Tag name takes precedence over untagged field of the same name.
  429. {"Tag", tfield("Tag", int(0), 4)},
  430. // Unexported fields disappear.
  431. {"unexported", nil},
  432. // Untagged embedded structs disappear.
  433. {"S4", nil},
  434. } {
  435. if got := fields.Match(test.name); !fieldsEqual(got, test.want) {
  436. t.Errorf("match %q:\ngot %+v\nwant %+v", test.name, got, test.want)
  437. }
  438. }
  439. }
  440. func TestAgainstJSONMatchingField(t *testing.T) {
  441. s3 := S3{
  442. S4: S4{ABc: 1, Y: 2},
  443. Abc: 3,
  444. AbC: 4,
  445. Tag: 5,
  446. X: 6,
  447. unexported: 7,
  448. }
  449. var want S3
  450. jsonRoundTrip(t, s3, &want)
  451. v := reflect.ValueOf(want)
  452. fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
  453. if err != nil {
  454. t.Fatal(err)
  455. }
  456. for _, test := range []struct {
  457. name string
  458. got int
  459. }{
  460. {"Abc", 3},
  461. {"AbC", 4},
  462. {"ABc", 1},
  463. {"abc", 1},
  464. {"Tag", 6},
  465. } {
  466. f := fields.Match(test.name)
  467. if f == nil {
  468. t.Fatalf("%s: no match", test.name)
  469. }
  470. w := v.FieldByIndex(f.Index).Interface()
  471. if test.got != w {
  472. t.Errorf("%s: got %d, want %d", test.name, test.got, w)
  473. }
  474. }
  475. }
  476. func TestTagErrors(t *testing.T) {
  477. called := false
  478. c := NewCache(func(t reflect.StructTag) (string, bool, interface{}, error) {
  479. called = true
  480. s := t.Get("f")
  481. if s == "bad" {
  482. return "", false, nil, errors.New("error")
  483. }
  484. return s, true, nil, nil
  485. }, nil, nil)
  486. type T struct {
  487. X int `f:"ok"`
  488. Y int `f:"bad"`
  489. }
  490. _, err := c.Fields(reflect.TypeOf(T{}))
  491. if !called {
  492. t.Fatal("tag parser not called")
  493. }
  494. if err == nil {
  495. t.Error("want error, got nil")
  496. }
  497. // Second time, we should cache the error.
  498. called = false
  499. _, err = c.Fields(reflect.TypeOf(T{}))
  500. if called {
  501. t.Fatal("tag parser called on second time")
  502. }
  503. if err == nil {
  504. t.Error("want error, got nil")
  505. }
  506. }