1
2
3
4
5
6
7 package json_test
8
9 import (
10 "errors"
11 "path"
12 "reflect"
13 "strings"
14 "testing"
15 "time"
16
17 jsonv1 "encoding/json"
18 "encoding/json/jsontext"
19 jsonv2 "encoding/json/v2"
20 )
21
22
23
24
25
26 var jsonPackages = []struct {
27 Version string
28 Marshal func(any) ([]byte, error)
29 Unmarshal func([]byte, any) error
30 }{
31 {"v1", jsonv1.Marshal, jsonv1.Unmarshal},
32 {"v2",
33 func(in any) ([]byte, error) { return jsonv2.Marshal(in) },
34 func(in []byte, out any) error { return jsonv2.Unmarshal(in, out) }},
35 }
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 func TestCaseSensitivity(t *testing.T) {
52 type Fields struct {
53 FieldA bool
54 FieldB bool `json:"fooBar"`
55 FieldC bool `json:"fizzBuzz,case:ignore"`
56 }
57
58 for _, json := range jsonPackages {
59 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
60
61
62 type goName = string
63 type jsonName = string
64 onlyV1 := json.Version == "v1"
65 onlyV2 := json.Version == "v2"
66 allMatches := map[goName]map[jsonName]bool{
67 "FieldA": {
68 "FieldA": true,
69 "fielda": onlyV1,
70 "fieldA": onlyV1,
71 "FIELDA": onlyV1,
72 "FieldB": false,
73 "FieldC": false,
74 },
75 "FieldB": {
76 "fooBar": true,
77 "FooBar": onlyV1,
78 "foobar": onlyV1,
79 "FOOBAR": onlyV1,
80 "fizzBuzz": false,
81 "FieldA": false,
82 "FieldB": false,
83 "FieldC": false,
84 },
85 "FieldC": {
86 "fizzBuzz": true,
87 "fizzbuzz": true,
88 "FIZZBUZZ": true,
89 "fizz_buzz": onlyV2,
90 "fizz-buzz": onlyV2,
91 "fooBar": false,
92 "FieldA": false,
93 "FieldC": false,
94 "FieldB": false,
95 },
96 }
97
98 for goFieldName, matches := range allMatches {
99 for jsonMemberName, wantMatch := range matches {
100 in := `{"` + jsonMemberName + `":true}`
101 var s Fields
102 if err := json.Unmarshal([]byte(in), &s); err != nil {
103 t.Fatalf("json.Unmarshal error: %v", err)
104 }
105 gotMatch := reflect.ValueOf(s).FieldByName(goFieldName).Bool()
106 if gotMatch != wantMatch {
107 t.Fatalf("%T.%s = %v, want %v", s, goFieldName, gotMatch, wantMatch)
108 }
109 }
110 }
111 })
112 }
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 func TestOmitEmptyOption(t *testing.T) {
142 type Struct struct {
143 Foo string `json:",omitempty"`
144 Bar []int `json:",omitempty"`
145 Baz *Struct `json:",omitempty"`
146 }
147 type Types struct {
148 Bool bool `json:",omitempty"`
149 StringA string `json:",omitempty"`
150 StringB string `json:",omitempty"`
151 BytesA []byte `json:",omitempty"`
152 BytesB []byte `json:",omitempty"`
153 BytesC []byte `json:",omitempty"`
154 Int int `json:",omitempty"`
155 MapA map[string]string `json:",omitempty"`
156 MapB map[string]string `json:",omitempty"`
157 MapC map[string]string `json:",omitempty"`
158 StructA Struct `json:",omitempty"`
159 StructB Struct `json:",omitempty"`
160 StructC Struct `json:",omitempty"`
161 SliceA []string `json:",omitempty"`
162 SliceB []string `json:",omitempty"`
163 SliceC []string `json:",omitempty"`
164 Array [1]string `json:",omitempty"`
165 PointerA *string `json:",omitempty"`
166 PointerB *string `json:",omitempty"`
167 PointerC *string `json:",omitempty"`
168 InterfaceA any `json:",omitempty"`
169 InterfaceB any `json:",omitempty"`
170 InterfaceC any `json:",omitempty"`
171 InterfaceD any `json:",omitempty"`
172 }
173
174 something := "something"
175 for _, json := range jsonPackages {
176 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
177 in := Types{
178 Bool: false,
179 StringA: "",
180 StringB: something,
181 BytesA: nil,
182 BytesB: []byte{},
183 BytesC: []byte(something),
184 Int: 0,
185 MapA: nil,
186 MapB: map[string]string{},
187 MapC: map[string]string{something: something},
188 StructA: Struct{},
189 StructB: Struct{Bar: []int{}, Baz: new(Struct)},
190 StructC: Struct{Foo: something},
191 SliceA: nil,
192 SliceB: []string{},
193 SliceC: []string{something},
194 Array: [1]string{something},
195 PointerA: nil,
196 PointerB: new(string),
197 PointerC: &something,
198 InterfaceA: nil,
199 InterfaceB: (*string)(nil),
200 InterfaceC: new(string),
201 InterfaceD: &something,
202 }
203 b, err := json.Marshal(in)
204 if err != nil {
205 t.Fatalf("json.Marshal error: %v", err)
206 }
207 var out map[string]any
208 if err := json.Unmarshal(b, &out); err != nil {
209 t.Fatalf("json.Unmarshal error: %v", err)
210 }
211
212 onlyV1 := json.Version == "v1"
213 onlyV2 := json.Version == "v2"
214 wantPresent := map[string]bool{
215 "Bool": onlyV2,
216 "StringA": false,
217 "StringB": true,
218 "BytesA": false,
219 "BytesB": false,
220 "BytesC": true,
221 "Int": onlyV2,
222 "MapA": false,
223 "MapB": false,
224 "MapC": true,
225 "StructA": onlyV1,
226 "StructB": onlyV1,
227 "StructC": true,
228 "SliceA": false,
229 "SliceB": false,
230 "SliceC": true,
231 "Array": true,
232 "PointerA": false,
233 "PointerB": onlyV1,
234 "PointerC": true,
235 "InterfaceA": false,
236 "InterfaceB": onlyV1,
237 "InterfaceC": onlyV1,
238 "InterfaceD": true,
239 }
240 for field, want := range wantPresent {
241 _, got := out[field]
242 if got != want {
243 t.Fatalf("%T.%s = %v, want %v", in, field, got, want)
244 }
245 }
246 })
247 }
248 }
249
250 func addr[T any](v T) *T {
251 return &v
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 func TestStringOption(t *testing.T) {
298 type Types struct {
299 String string `json:",string"`
300 Bool bool `json:",string"`
301 Int int `json:",string"`
302 Float float64 `json:",string"`
303 Map map[string]int `json:",string"`
304 Struct struct{ Field int } `json:",string"`
305 Slice []int `json:",string"`
306 Array [1]int `json:",string"`
307 PointerA *int `json:",string"`
308 PointerB *int `json:",string"`
309 PointerC **int `json:",string"`
310 InterfaceA any `json:",string"`
311 InterfaceB any `json:",string"`
312 }
313
314 for _, json := range jsonPackages {
315 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
316 in := Types{
317 String: "string",
318 Bool: true,
319 Int: 1,
320 Float: 1,
321 Map: map[string]int{"Name": 1},
322 Struct: struct{ Field int }{1},
323 Slice: []int{1},
324 Array: [1]int{1},
325 PointerA: nil,
326 PointerB: addr(1),
327 PointerC: addr(addr(1)),
328 InterfaceA: nil,
329 InterfaceB: 1,
330 }
331 quote := func(s string) string {
332 b, _ := jsontext.AppendQuote(nil, s)
333 return string(b)
334 }
335 quoteOnlyV1 := func(s string) string {
336 if json.Version == "v1" {
337 s = quote(s)
338 }
339 return s
340 }
341 quoteOnlyV2 := func(s string) string {
342 if json.Version == "v2" {
343 s = quote(s)
344 }
345 return s
346 }
347 want := strings.Join([]string{
348 `{`,
349 `"String":` + quoteOnlyV1(`"string"`) + `,`,
350 `"Bool":` + quoteOnlyV1("true") + `,`,
351 `"Int":` + quote("1") + `,`,
352 `"Float":` + quote("1") + `,`,
353 `"Map":{"Name":` + quoteOnlyV2("1") + `},`,
354 `"Struct":{"Field":` + quoteOnlyV2("1") + `},`,
355 `"Slice":[` + quoteOnlyV2("1") + `],`,
356 `"Array":[` + quoteOnlyV2("1") + `],`,
357 `"PointerA":null,`,
358 `"PointerB":` + quote("1") + `,`,
359 `"PointerC":` + quoteOnlyV2("1") + `,`,
360 `"InterfaceA":null,`,
361 `"InterfaceB":` + quoteOnlyV2("1") + ``,
362 `}`}, "")
363 got, err := json.Marshal(in)
364 if err != nil {
365 t.Fatalf("json.Marshal error: %v", err)
366 }
367 if string(got) != want {
368 t.Fatalf("json.Marshal = %s, want %s", got, want)
369 }
370 })
371 }
372
373 for _, json := range jsonPackages {
374 t.Run(path.Join("Unmarshal/Null", json.Version), func(t *testing.T) {
375 var got Types
376 err := json.Unmarshal([]byte(`{
377 "Bool": "null",
378 "Int": "null",
379 "PointerA": "null"
380 }`), &got)
381 switch {
382 case !reflect.DeepEqual(got, Types{}):
383 t.Fatalf("json.Unmarshal = %v, want %v", got, Types{})
384 case json.Version == "v1" && err != nil:
385 t.Fatalf("json.Unmarshal error: %v", err)
386 case json.Version == "v2" && err == nil:
387 t.Fatal("json.Unmarshal error is nil, want non-nil")
388 }
389 })
390
391 t.Run(path.Join("Unmarshal/Bool", json.Version), func(t *testing.T) {
392 var got Types
393 want := map[string]Types{
394 "v1": {Bool: true},
395 "v2": {Bool: false},
396 }[json.Version]
397 err := json.Unmarshal([]byte(`{"Bool": "true"}`), &got)
398 switch {
399 case !reflect.DeepEqual(got, want):
400 t.Fatalf("json.Unmarshal = %v, want %v", got, want)
401 case json.Version == "v1" && err != nil:
402 t.Fatalf("json.Unmarshal error: %v", err)
403 case json.Version == "v2" && err == nil:
404 t.Fatal("json.Unmarshal error is nil, want non-nil")
405 }
406 })
407
408 t.Run(path.Join("Unmarshal/Shallow", json.Version), func(t *testing.T) {
409 var got Types
410 want := Types{Int: 1, PointerB: addr(1)}
411 err := json.Unmarshal([]byte(`{
412 "Int": "1",
413 "PointerB": "1"
414 }`), &got)
415 switch {
416 case !reflect.DeepEqual(got, want):
417 t.Fatalf("json.Unmarshal = %v, want %v", got, want)
418 case err != nil:
419 t.Fatalf("json.Unmarshal error: %v", err)
420 }
421 })
422
423 t.Run(path.Join("Unmarshal/Deep", json.Version), func(t *testing.T) {
424 var got Types
425 want := map[string]Types{
426 "v1": {
427 Map: map[string]int{"Name": 0},
428 Slice: []int{0},
429 PointerC: addr(addr(0)),
430 },
431 "v2": {
432 Map: map[string]int{"Name": 1},
433 Struct: struct{ Field int }{1},
434 Slice: []int{1},
435 Array: [1]int{1},
436 PointerC: addr(addr(1)),
437 },
438 }[json.Version]
439 err := json.Unmarshal([]byte(`{
440 "Map": {"Name":"1"},
441 "Struct": {"Field":"1"},
442 "Slice": ["1"],
443 "Array": ["1"],
444 "PointerC": "1"
445 }`), &got)
446 switch {
447 case !reflect.DeepEqual(got, want):
448 t.Fatalf("json.Unmarshal =\n%v, want\n%v", got, want)
449 case json.Version == "v1" && err == nil:
450 t.Fatal("json.Unmarshal error is nil, want non-nil")
451 case json.Version == "v2" && err != nil:
452 t.Fatalf("json.Unmarshal error: %v", err)
453 }
454 })
455 }
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484 func TestNilSlicesAndMaps(t *testing.T) {
485 type Composites struct {
486 B []byte
487 S []string
488 M map[string]string
489 }
490
491 for _, json := range jsonPackages {
492 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
493 in := []Composites{
494 {B: []byte(nil), S: []string(nil), M: map[string]string(nil)},
495 {B: []byte{}, S: []string{}, M: map[string]string{}},
496 }
497 want := map[string]string{
498 "v1": `[{"B":null,"S":null,"M":null},{"B":"","S":[],"M":{}}]`,
499 "v2": `[{"B":"","S":[],"M":{}},{"B":"","S":[],"M":{}}]`,
500 }[json.Version]
501 got, err := json.Marshal(in)
502 if err != nil {
503 t.Fatalf("json.Marshal error: %v", err)
504 }
505 if string(got) != want {
506 t.Fatalf("json.Marshal = %s, want %s", got, want)
507 }
508 })
509 }
510 }
511
512
513
514
515
516
517
518
519 func TestArrays(t *testing.T) {
520 for _, json := range jsonPackages {
521 t.Run(path.Join("Unmarshal/TooFew", json.Version), func(t *testing.T) {
522 var got [2]int
523 err := json.Unmarshal([]byte(`[1]`), &got)
524 switch {
525 case got != [2]int{1, 0}:
526 t.Fatalf(`json.Unmarshal = %v, want [1 0]`, got)
527 case json.Version == "v1" && err != nil:
528 t.Fatalf("json.Unmarshal error: %v", err)
529 case json.Version == "v2" && err == nil:
530 t.Fatal("json.Unmarshal error is nil, want non-nil")
531 }
532 })
533 }
534
535 for _, json := range jsonPackages {
536 t.Run(path.Join("Unmarshal/TooMany", json.Version), func(t *testing.T) {
537 var got [2]int
538 err := json.Unmarshal([]byte(`[1,2,3]`), &got)
539 switch {
540 case got != [2]int{1, 2}:
541 t.Fatalf(`json.Unmarshal = %v, want [1 2]`, got)
542 case json.Version == "v1" && err != nil:
543 t.Fatalf("json.Unmarshal error: %v", err)
544 case json.Version == "v2" && err == nil:
545 t.Fatal("json.Unmarshal error is nil, want non-nil")
546 }
547 })
548 }
549 }
550
551
552
553
554
555
556
557
558
559
560
561 func TestByteArrays(t *testing.T) {
562 for _, json := range jsonPackages {
563 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
564 in := [4]byte{1, 2, 3, 4}
565 got, err := json.Marshal(in)
566 if err != nil {
567 t.Fatalf("json.Marshal error: %v", err)
568 }
569 want := map[string]string{
570 "v1": `[1,2,3,4]`,
571 "v2": `"AQIDBA=="`,
572 }[json.Version]
573 if string(got) != want {
574 t.Fatalf("json.Marshal = %s, want %s", got, want)
575 }
576 })
577 }
578
579 for _, json := range jsonPackages {
580 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
581 in := map[string]string{
582 "v1": `[1,2,3,4]`,
583 "v2": `"AQIDBA=="`,
584 }[json.Version]
585 var got [4]byte
586 err := json.Unmarshal([]byte(in), &got)
587 switch {
588 case err != nil:
589 t.Fatalf("json.Unmarshal error: %v", err)
590 case got != [4]byte{1, 2, 3, 4}:
591 t.Fatalf("json.Unmarshal = %v, want [1 2 3 4]", got)
592 }
593 })
594 }
595 }
596
597
598 type CallCheck string
599
600
601 func (*CallCheck) MarshalJSON() ([]byte, error) {
602 return []byte(`"CALLED"`), nil
603 }
604
605
606 func (v *CallCheck) UnmarshalJSON([]byte) error {
607 *v = `CALLED`
608 return nil
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628 func TestPointerReceiver(t *testing.T) {
629 type Values struct {
630 S []CallCheck
631 A [1]CallCheck
632 M map[string]CallCheck
633 V CallCheck
634 I any
635 }
636
637 for _, json := range jsonPackages {
638 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
639 var cc CallCheck
640 in := Values{
641 S: []CallCheck{cc},
642 A: [1]CallCheck{cc},
643 M: map[string]CallCheck{"": cc},
644 V: cc,
645 I: cc,
646 }
647 want := map[string]string{
648 "v1": `{"S":["CALLED"],"A":[""],"M":{"":""},"V":"","I":""}`,
649 "v2": `{"S":["CALLED"],"A":["CALLED"],"M":{"":"CALLED"},"V":"CALLED","I":"CALLED"}`,
650 }[json.Version]
651 got, err := json.Marshal(in)
652 if err != nil {
653 t.Fatalf("json.Marshal error: %v", err)
654 }
655 if string(got) != want {
656 t.Fatalf("json.Marshal = %s, want %s", got, want)
657 }
658 })
659 }
660
661 for _, json := range jsonPackages {
662 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
663 in := `{"S":[""],"A":[""],"M":{"":""},"V":"","I":""}`
664 called := CallCheck("CALLED")
665 want := map[string]Values{
666 "v1": {
667 S: []CallCheck{called},
668 A: [1]CallCheck{called},
669 M: map[string]CallCheck{"": called},
670 V: called,
671 I: "",
672 },
673 "v2": {
674 S: []CallCheck{called},
675 A: [1]CallCheck{called},
676 M: map[string]CallCheck{"": called},
677 V: called,
678 I: called,
679 },
680 }[json.Version]
681 got := Values{
682 A: [1]CallCheck{CallCheck("")},
683 S: []CallCheck{CallCheck("")},
684 M: map[string]CallCheck{"": CallCheck("")},
685 V: CallCheck(""),
686 I: CallCheck(""),
687 }
688 if err := json.Unmarshal([]byte(in), &got); err != nil {
689 t.Fatalf("json.Unmarshal error: %v", err)
690 }
691 if !reflect.DeepEqual(got, want) {
692 t.Fatalf("json.Unmarshal = %v, want %v", got, want)
693 }
694 })
695 }
696 }
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712 func TestMapDeterminism(t *testing.T) {
713 const iterations = 10
714 in := map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
715
716 for _, json := range jsonPackages {
717 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
718 outs := make(map[string]bool)
719 for range iterations {
720 b, err := json.Marshal(in)
721 if err != nil {
722 t.Fatalf("json.Marshal error: %v", err)
723 }
724 outs[string(b)] = true
725 }
726 switch {
727 case json.Version == "v1" && len(outs) != 1:
728 t.Fatalf("json.Marshal encoded to %d unique forms, expected 1", len(outs))
729 case json.Version == "v2" && len(outs) == 1:
730 t.Logf("json.Marshal encoded to 1 unique form by chance; are you feeling lucky?")
731 }
732 })
733 }
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747 func TestEscapeHTML(t *testing.T) {
748 for _, json := range jsonPackages {
749 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
750 const in = `<script> console.log("Hello, world!"); </script>`
751 got, err := json.Marshal(in)
752 if err != nil {
753 t.Fatalf("json.Marshal error: %v", err)
754 }
755 want := map[string]string{
756 "v1": `"\u003cscript\u003e console.log(\"Hello, world!\"); \u003c/script\u003e"`,
757 "v2": `"<script> console.log(\"Hello, world!\"); </script>"`,
758 }[json.Version]
759 if string(got) != want {
760 t.Fatalf("json.Marshal = %s, want %s", got, want)
761 }
762 })
763 }
764 }
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782 func TestInvalidUTF8(t *testing.T) {
783 for _, json := range jsonPackages {
784 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
785 got, err := json.Marshal("\xff")
786 switch {
787 case json.Version == "v1" && err != nil:
788 t.Fatalf("json.Marshal error: %v", err)
789 case json.Version == "v1" && string(got) != `"\ufffd"`:
790 t.Fatalf(`json.Marshal = %s, want "\ufffd"`, got)
791 case json.Version == "v2" && err == nil:
792 t.Fatal("json.Marshal error is nil, want non-nil")
793 }
794 })
795 }
796
797 for _, json := range jsonPackages {
798 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
799 const in = "\"\xff\""
800 var got string
801 err := json.Unmarshal([]byte(in), &got)
802 switch {
803 case json.Version == "v1" && err != nil:
804 t.Fatalf("json.Unmarshal error: %v", err)
805 case json.Version == "v1" && got != "\ufffd":
806 t.Fatalf(`json.Unmarshal = %q, want "\ufffd"`, got)
807 case json.Version == "v2" && err == nil:
808 t.Fatal("json.Unmarshal error is nil, want non-nil")
809 }
810 })
811 }
812 }
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842 func TestDuplicateNames(t *testing.T) {
843 for _, json := range jsonPackages {
844 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
845 const in = `{"Name":1,"Name":2}`
846 var got struct{ Name int }
847 err := json.Unmarshal([]byte(in), &got)
848 switch {
849 case json.Version == "v1" && err != nil:
850 t.Fatalf("json.Unmarshal error: %v", err)
851 case json.Version == "v1" && got != struct{ Name int }{2}:
852 t.Fatalf(`json.Unmarshal = %v, want {2}`, got)
853 case json.Version == "v2" && err == nil:
854 t.Fatal("json.Unmarshal error is nil, want non-nil")
855 }
856 })
857 }
858 }
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873 func TestMergeNull(t *testing.T) {
874 type Types struct {
875 Bool bool
876 String string
877 Bytes []byte
878 Int int
879 Map map[string]string
880 Struct struct{ Field string }
881 Slice []string
882 Array [1]string
883 Pointer *string
884 Interface any
885 }
886
887 for _, json := range jsonPackages {
888 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
889
890 in := Types{
891 Bool: true,
892 String: "old",
893 Bytes: []byte("old"),
894 Int: 1234,
895 Map: map[string]string{"old": "old"},
896 Struct: struct{ Field string }{"old"},
897 Slice: []string{"old"},
898 Array: [1]string{"old"},
899 Pointer: new(string),
900 Interface: "old",
901 }
902
903
904 if err := json.Unmarshal([]byte(`{
905 "Bool": null,
906 "String": null,
907 "Bytes": null,
908 "Int": null,
909 "Map": null,
910 "Struct": null,
911 "Slice": null,
912 "Array": null,
913 "Pointer": null,
914 "Interface": null
915 }`), &in); err != nil {
916 t.Fatalf("json.Unmarshal error: %v", err)
917 }
918
919 want := map[string]Types{
920 "v1": {
921 Bool: true,
922 String: "old",
923 Int: 1234,
924 Struct: struct{ Field string }{"old"},
925 Array: [1]string{"old"},
926 },
927 "v2": {},
928 }[json.Version]
929 if !reflect.DeepEqual(in, want) {
930 t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
931 }
932 })
933 }
934 }
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953 func TestMergeComposite(t *testing.T) {
954 type Tuple struct{ Old, New bool }
955 type Composites struct {
956 Slice []Tuple
957 Array [1]Tuple
958 Map map[string]Tuple
959 MapPointer map[string]*Tuple
960 Struct struct{ Tuple Tuple }
961 StructPointer *struct{ Tuple Tuple }
962 Interface any
963 InterfacePointer any
964 }
965
966 for _, json := range jsonPackages {
967 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
968
969 in := Composites{
970 Slice: []Tuple{{Old: true}, {Old: true}}[:1],
971 Array: [1]Tuple{{Old: true}},
972 Map: map[string]Tuple{"Tuple": {Old: true}},
973 MapPointer: map[string]*Tuple{"Tuple": {Old: true}},
974 Struct: struct{ Tuple Tuple }{Tuple{Old: true}},
975 StructPointer: &struct{ Tuple Tuple }{Tuple{Old: true}},
976 Interface: Tuple{Old: true},
977 InterfacePointer: &Tuple{Old: true},
978 }
979
980
981 if err := json.Unmarshal([]byte(`{
982 "Slice": [{"New":true}, {"New":true}],
983 "Array": [{"New":true}],
984 "Map": {"Tuple": {"New":true}},
985 "MapPointer": {"Tuple": {"New":true}},
986 "Struct": {"Tuple": {"New":true}},
987 "StructPointer": {"Tuple": {"New":true}},
988 "Interface": {"New":true},
989 "InterfacePointer": {"New":true}
990 }`), &in); err != nil {
991 t.Fatalf("json.Unmarshal error: %v", err)
992 }
993
994 merged := Tuple{Old: true, New: true}
995 replaced := Tuple{Old: false, New: true}
996 want := map[string]Composites{
997 "v1": {
998 Slice: []Tuple{merged, merged},
999 Array: [1]Tuple{merged},
1000 Map: map[string]Tuple{"Tuple": replaced},
1001 MapPointer: map[string]*Tuple{"Tuple": &replaced},
1002 Struct: struct{ Tuple Tuple }{merged},
1003 StructPointer: &struct{ Tuple Tuple }{merged},
1004 Interface: map[string]any{"New": true},
1005 InterfacePointer: &merged,
1006 },
1007 "v2": {
1008 Slice: []Tuple{replaced, replaced},
1009 Array: [1]Tuple{replaced},
1010 Map: map[string]Tuple{"Tuple": merged},
1011 MapPointer: map[string]*Tuple{"Tuple": &merged},
1012 Struct: struct{ Tuple Tuple }{merged},
1013 StructPointer: &struct{ Tuple Tuple }{merged},
1014 Interface: merged,
1015 InterfacePointer: &merged,
1016 },
1017 }[json.Version]
1018 if !reflect.DeepEqual(in, want) {
1019 t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
1020 }
1021 })
1022 }
1023 }
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 func TestTimeDurations(t *testing.T) {
1041 for _, json := range jsonPackages {
1042 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
1043 got, err := json.Marshal(time.Minute)
1044 switch {
1045 case err != nil:
1046 t.Fatalf("json.Marshal error: %v", err)
1047 case json.Version == "v1" && string(got) != "60000000000":
1048 t.Fatalf("json.Marshal = %s, want 60000000000", got)
1049 case json.Version == "v2" && string(got) != `"1m0s"`:
1050 t.Fatalf(`json.Marshal = %s, want "1m0s"`, got)
1051 }
1052 })
1053 }
1054
1055 for _, json := range jsonPackages {
1056 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
1057 in := map[string]string{
1058 "v1": "60000000000",
1059 "v2": `"1m0s"`,
1060 }[json.Version]
1061 var got time.Duration
1062 err := json.Unmarshal([]byte(in), &got)
1063 switch {
1064 case err != nil:
1065 t.Fatalf("json.Unmarshal error: %v", err)
1066 case got != time.Minute:
1067 t.Fatalf("json.Unmarshal = %v, want 1m0s", got)
1068 }
1069 })
1070 }
1071 }
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082 func TestEmptyStructs(t *testing.T) {
1083 never := func(string) bool { return false }
1084 onlyV2 := func(v string) bool { return v == "v2" }
1085 values := []struct {
1086 in any
1087 wantError func(string) bool
1088 }{
1089
1090 {in: addr(struct{}{}), wantError: never},
1091
1092
1093
1094 {in: errors.New("error"), wantError: onlyV2},
1095
1096 {in: addr(struct{ Exported, unexported int }{}), wantError: never},
1097 }
1098
1099 for _, json := range jsonPackages {
1100 t.Run("Marshal", func(t *testing.T) {
1101 for _, value := range values {
1102 wantError := value.wantError(json.Version)
1103 _, err := json.Marshal(value.in)
1104 switch {
1105 case (err == nil) && wantError:
1106 t.Fatalf("json.Marshal error is nil, want non-nil")
1107 case (err != nil) && !wantError:
1108 t.Fatalf("json.Marshal error: %v", err)
1109 }
1110 }
1111 })
1112 }
1113
1114 for _, json := range jsonPackages {
1115 t.Run("Unmarshal", func(t *testing.T) {
1116 for _, value := range values {
1117 wantError := value.wantError(json.Version)
1118 out := reflect.New(reflect.TypeOf(value.in).Elem()).Interface()
1119 err := json.Unmarshal([]byte("{}"), out)
1120 switch {
1121 case (err == nil) && wantError:
1122 t.Fatalf("json.Unmarshal error is nil, want non-nil")
1123 case (err != nil) && !wantError:
1124 t.Fatalf("json.Unmarshal error: %v", err)
1125 }
1126 }
1127 })
1128 }
1129 }
1130
View as plain text