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 func TestStringOption(t *testing.T) {
296 type AllTypes struct {
297 String string `json:",string"`
298 Bool bool `json:",string"`
299 Int int `json:",string"`
300 Float float64 `json:",string"`
301 Map map[string]int `json:",string"`
302 Struct struct{ Field int } `json:",string"`
303 Slice []int `json:",string"`
304 Array [1]int `json:",string"`
305 PointerA *int `json:",string"`
306 PointerB *int `json:",string"`
307 PointerC **int `json:",string"`
308 InterfaceA any `json:",string"`
309 InterfaceB any `json:",string"`
310 }
311
312 type V2Types struct {
313 Int int `json:",string"`
314 Float float64 `json:",string"`
315 PointerA *int `json:",string"`
316 }
317
318 for _, json := range jsonPackages {
319 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
320 in := AllTypes{
321 String: "string",
322 Bool: true,
323 Int: 1,
324 Float: 1,
325 Map: map[string]int{"Name": 1},
326 Struct: struct{ Field int }{1},
327 Slice: []int{1},
328 Array: [1]int{1},
329 PointerA: nil,
330 PointerB: addr(1),
331 PointerC: addr(addr(1)),
332 InterfaceA: nil,
333 InterfaceB: 1,
334 }
335 quote := func(s string) string {
336 b, _ := jsontext.AppendQuote(nil, s)
337 return string(b)
338 }
339 quoteOnlyV1 := func(s string) string {
340 if json.Version == "v1" {
341 s = quote(s)
342 }
343 return s
344 }
345 quoteOnlyV2 := func(s string) string {
346 if json.Version == "v2" {
347 s = quote(s)
348 }
349 return s
350 }
351 want := strings.Join([]string{
352 `{`,
353 `"String":` + quoteOnlyV1(`"string"`) + `,`,
354 `"Bool":` + quoteOnlyV1("true") + `,`,
355 `"Int":` + quote("1") + `,`,
356 `"Float":` + quote("1") + `,`,
357 `"Map":{"Name":1},`,
358 `"Struct":{"Field":1},`,
359 `"Slice":[1],`,
360 `"Array":[1],`,
361 `"PointerA":null,`,
362 `"PointerB":` + quote("1") + `,`,
363 `"PointerC":` + quoteOnlyV2("1") + `,`,
364 `"InterfaceA":null,`,
365 `"InterfaceB":` + quoteOnlyV2("1"),
366 `}`}, "")
367 var got []byte
368 var err error
369 if json.Version == "v2" {
370
371
372 got, err = jsonv2.Marshal(in, jsonv1.ReportErrorsWithLegacySemantics(true))
373 } else {
374 got, err = json.Marshal(in)
375 }
376 if err != nil {
377 t.Fatalf("json.Marshal error: %v", err)
378 }
379 if string(got) != want {
380 t.Fatalf("json.Marshal = %s, want %s", got, want)
381 }
382 })
383 }
384
385 for _, json := range jsonPackages {
386 t.Run(path.Join("Unmarshal/Null", json.Version), func(t *testing.T) {
387 var got AllTypes
388 err := json.Unmarshal([]byte(`{
389 "Bool": "null",
390 "Int": "null",
391 "PointerA": "null"
392 }`), &got)
393 switch {
394 case json.Version == "v1" && err != nil:
395 t.Fatalf("json.Unmarshal error: %v", err)
396 case json.Version == "v2" && err == nil:
397 t.Fatal("json.Unmarshal error is nil, want non-nil")
398 case !reflect.DeepEqual(got, AllTypes{}):
399 t.Fatalf("json.Unmarshal = %+v, want %+v", got, AllTypes{})
400 }
401 })
402
403 t.Run(path.Join("Unmarshal/Bool", json.Version), func(t *testing.T) {
404 var got AllTypes
405 want := map[string]AllTypes{
406 "v1": {Bool: true},
407 "v2": {Bool: false},
408 }[json.Version]
409 err := json.Unmarshal([]byte(`{"Bool": "true"}`), &got)
410 switch {
411 case json.Version == "v1" && err != nil:
412 t.Fatalf("json.Unmarshal error: %v", err)
413 case json.Version == "v2" && err == nil:
414 t.Fatal("json.Unmarshal error is nil, want non-nil")
415 case !reflect.DeepEqual(got, want):
416 t.Fatalf("json.Unmarshal = %v, want %v", got, want)
417 }
418 })
419
420 t.Run(path.Join("Unmarshal/Shallow", json.Version), func(t *testing.T) {
421 var got V2Types
422 want := V2Types{Int: 1, PointerA: addr(1)}
423 err := json.Unmarshal([]byte(`{
424 "Int": "1",
425 "PointerA": "1"
426 }`), &got)
427 switch {
428 case err != nil:
429 t.Fatalf("json.Unmarshal error: %v", err)
430 case !reflect.DeepEqual(got, want):
431 t.Fatalf("json.Unmarshal =\n%+v, want\n%+v", got, want)
432 }
433 })
434 }
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458 func TestNilSlicesAndMaps(t *testing.T) {
459 type Composites struct {
460 B []byte
461 S []string
462 M map[string]string
463 }
464
465 for _, json := range jsonPackages {
466 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
467 in := []Composites{
468 {B: []byte(nil), S: []string(nil), M: map[string]string(nil)},
469 {B: []byte{}, S: []string{}, M: map[string]string{}},
470 }
471 want := map[string]string{
472 "v1": `[{"B":null,"S":null,"M":null},{"B":"","S":[],"M":{}}]`,
473 "v2": `[{"B":"","S":[],"M":{}},{"B":"","S":[],"M":{}}]`,
474 }[json.Version]
475 got, err := json.Marshal(in)
476 if err != nil {
477 t.Fatalf("json.Marshal error: %v", err)
478 }
479 if string(got) != want {
480 t.Fatalf("json.Marshal = %s, want %s", got, want)
481 }
482 })
483 }
484 }
485
486
487
488
489
490
491
492
493 func TestArrays(t *testing.T) {
494 for _, json := range jsonPackages {
495 t.Run(path.Join("Unmarshal/TooFew", json.Version), func(t *testing.T) {
496 var got [2]int
497 err := json.Unmarshal([]byte(`[1]`), &got)
498 switch {
499 case got != [2]int{1, 0}:
500 t.Fatalf(`json.Unmarshal = %v, want [1 0]`, got)
501 case json.Version == "v1" && err != nil:
502 t.Fatalf("json.Unmarshal error: %v", err)
503 case json.Version == "v2" && err == nil:
504 t.Fatal("json.Unmarshal error is nil, want non-nil")
505 }
506 })
507 }
508
509 for _, json := range jsonPackages {
510 t.Run(path.Join("Unmarshal/TooMany", json.Version), func(t *testing.T) {
511 var got [2]int
512 err := json.Unmarshal([]byte(`[1,2,3]`), &got)
513 switch {
514 case got != [2]int{1, 2}:
515 t.Fatalf(`json.Unmarshal = %v, want [1 2]`, got)
516 case json.Version == "v1" && err != nil:
517 t.Fatalf("json.Unmarshal error: %v", err)
518 case json.Version == "v2" && err == nil:
519 t.Fatal("json.Unmarshal error is nil, want non-nil")
520 }
521 })
522 }
523 }
524
525
526
527
528
529
530
531 func TestByteArrays(t *testing.T) {
532 for _, json := range jsonPackages {
533 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
534 in := [4]byte{1, 2, 3, 4}
535 got, err := json.Marshal(in)
536 if err != nil {
537 t.Fatalf("json.Marshal error: %v", err)
538 }
539 want := map[string]string{
540 "v1": `[1,2,3,4]`,
541 "v2": `"AQIDBA=="`,
542 }[json.Version]
543 if string(got) != want {
544 t.Fatalf("json.Marshal = %s, want %s", got, want)
545 }
546 })
547 }
548
549 for _, json := range jsonPackages {
550 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
551 in := map[string]string{
552 "v1": `[1,2,3,4]`,
553 "v2": `"AQIDBA=="`,
554 }[json.Version]
555 var got [4]byte
556 err := json.Unmarshal([]byte(in), &got)
557 switch {
558 case err != nil:
559 t.Fatalf("json.Unmarshal error: %v", err)
560 case got != [4]byte{1, 2, 3, 4}:
561 t.Fatalf("json.Unmarshal = %v, want [1 2 3 4]", got)
562 }
563 })
564 }
565 }
566
567
568 type CallCheck string
569
570
571 func (*CallCheck) MarshalJSON() ([]byte, error) {
572 return []byte(`"CALLED"`), nil
573 }
574
575
576 func (v *CallCheck) UnmarshalJSON([]byte) error {
577 *v = `CALLED`
578 return nil
579 }
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598 func TestPointerReceiver(t *testing.T) {
599 type Values struct {
600 S []CallCheck
601 A [1]CallCheck
602 M map[string]CallCheck
603 V CallCheck
604 I any
605 }
606
607 for _, json := range jsonPackages {
608 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
609 var cc CallCheck
610 in := Values{
611 S: []CallCheck{cc},
612 A: [1]CallCheck{cc},
613 M: map[string]CallCheck{"": cc},
614 V: cc,
615 I: cc,
616 }
617 want := map[string]string{
618 "v1": `{"S":["CALLED"],"A":[""],"M":{"":""},"V":"","I":""}`,
619 "v2": `{"S":["CALLED"],"A":["CALLED"],"M":{"":"CALLED"},"V":"CALLED","I":"CALLED"}`,
620 }[json.Version]
621 got, err := json.Marshal(in)
622 if err != nil {
623 t.Fatalf("json.Marshal error: %v", err)
624 }
625 if string(got) != want {
626 t.Fatalf("json.Marshal = %s, want %s", got, want)
627 }
628 })
629 }
630
631 for _, json := range jsonPackages {
632 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
633 in := `{"S":[""],"A":[""],"M":{"":""},"V":"","I":""}`
634 called := CallCheck("CALLED")
635 want := map[string]Values{
636 "v1": {
637 S: []CallCheck{called},
638 A: [1]CallCheck{called},
639 M: map[string]CallCheck{"": called},
640 V: called,
641 I: "",
642 },
643 "v2": {
644 S: []CallCheck{called},
645 A: [1]CallCheck{called},
646 M: map[string]CallCheck{"": called},
647 V: called,
648 I: called,
649 },
650 }[json.Version]
651 got := Values{
652 A: [1]CallCheck{CallCheck("")},
653 S: []CallCheck{CallCheck("")},
654 M: map[string]CallCheck{"": CallCheck("")},
655 V: CallCheck(""),
656 I: CallCheck(""),
657 }
658 if err := json.Unmarshal([]byte(in), &got); err != nil {
659 t.Fatalf("json.Unmarshal error: %v", err)
660 }
661 if !reflect.DeepEqual(got, want) {
662 t.Fatalf("json.Unmarshal = %v, want %v", got, want)
663 }
664 })
665 }
666 }
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 func TestMapDeterminism(t *testing.T) {
683 const iterations = 10
684 in := map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
685
686 for _, json := range jsonPackages {
687 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
688 outs := make(map[string]bool)
689 for range iterations {
690 b, err := json.Marshal(in)
691 if err != nil {
692 t.Fatalf("json.Marshal error: %v", err)
693 }
694 outs[string(b)] = true
695 }
696 switch {
697 case json.Version == "v1" && len(outs) != 1:
698 t.Fatalf("json.Marshal encoded to %d unique forms, expected 1", len(outs))
699 case json.Version == "v2" && len(outs) == 1:
700 t.Logf("json.Marshal encoded to 1 unique form by chance; are you feeling lucky?")
701 }
702 })
703 }
704 }
705
706
707
708
709
710
711
712
713
714
715
716
717 func TestEscapeHTML(t *testing.T) {
718 for _, json := range jsonPackages {
719 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
720 const in = `<script> console.log("Hello, world!"); </script>`
721 got, err := json.Marshal(in)
722 if err != nil {
723 t.Fatalf("json.Marshal error: %v", err)
724 }
725 want := map[string]string{
726 "v1": `"\u003cscript\u003e console.log(\"Hello, world!\"); \u003c/script\u003e"`,
727 "v2": `"<script> console.log(\"Hello, world!\"); </script>"`,
728 }[json.Version]
729 if string(got) != want {
730 t.Fatalf("json.Marshal = %s, want %s", got, want)
731 }
732 })
733 }
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752 func TestInvalidUTF8(t *testing.T) {
753 for _, json := range jsonPackages {
754 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
755 got, err := json.Marshal("\xff")
756 switch {
757 case json.Version == "v1" && err != nil:
758 t.Fatalf("json.Marshal error: %v", err)
759 case json.Version == "v1" && string(got) != "\"\ufffd\"":
760 t.Fatalf(`json.Marshal = %s, want %q`, got, "\ufffd")
761 case json.Version == "v2" && err == nil:
762 t.Fatal("json.Marshal error is nil, want non-nil")
763 }
764 })
765 }
766
767 for _, json := range jsonPackages {
768 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
769 const in = "\"\xff\""
770 var got string
771 err := json.Unmarshal([]byte(in), &got)
772 switch {
773 case json.Version == "v1" && err != nil:
774 t.Fatalf("json.Unmarshal error: %v", err)
775 case json.Version == "v1" && got != "\ufffd":
776 t.Fatalf(`json.Unmarshal = %q, want "\ufffd"`, got)
777 case json.Version == "v2" && err == nil:
778 t.Fatal("json.Unmarshal error is nil, want non-nil")
779 }
780 })
781 }
782 }
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812 func TestDuplicateNames(t *testing.T) {
813 for _, json := range jsonPackages {
814 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
815 const in = `{"Name":1,"Name":2}`
816 var got struct{ Name int }
817 err := json.Unmarshal([]byte(in), &got)
818 switch {
819 case json.Version == "v1" && err != nil:
820 t.Fatalf("json.Unmarshal error: %v", err)
821 case json.Version == "v1" && got != struct{ Name int }{2}:
822 t.Fatalf(`json.Unmarshal = %v, want {2}`, got)
823 case json.Version == "v2" && err == nil:
824 t.Fatal("json.Unmarshal error is nil, want non-nil")
825 }
826 })
827 }
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843 func TestMergeNull(t *testing.T) {
844 type Types struct {
845 Bool bool
846 String string
847 Bytes []byte
848 Int int
849 Map map[string]string
850 Struct struct{ Field string }
851 Slice []string
852 Array [1]string
853 Pointer *string
854 Interface any
855 }
856
857 for _, json := range jsonPackages {
858 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
859
860 in := Types{
861 Bool: true,
862 String: "old",
863 Bytes: []byte("old"),
864 Int: 1234,
865 Map: map[string]string{"old": "old"},
866 Struct: struct{ Field string }{"old"},
867 Slice: []string{"old"},
868 Array: [1]string{"old"},
869 Pointer: new(string),
870 Interface: "old",
871 }
872
873
874 if err := json.Unmarshal([]byte(`{
875 "Bool": null,
876 "String": null,
877 "Bytes": null,
878 "Int": null,
879 "Map": null,
880 "Struct": null,
881 "Slice": null,
882 "Array": null,
883 "Pointer": null,
884 "Interface": null
885 }`), &in); err != nil {
886 t.Fatalf("json.Unmarshal error: %v", err)
887 }
888
889 want := map[string]Types{
890 "v1": {
891 Bool: true,
892 String: "old",
893 Int: 1234,
894 Struct: struct{ Field string }{"old"},
895 Array: [1]string{"old"},
896 },
897 "v2": {},
898 }[json.Version]
899 if !reflect.DeepEqual(in, want) {
900 t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
901 }
902 })
903 }
904 }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923 func TestMergeComposite(t *testing.T) {
924 type Tuple struct{ Old, New bool }
925 type Composites struct {
926 Slice []Tuple
927 Array [1]Tuple
928 Map map[string]Tuple
929 MapPointer map[string]*Tuple
930 Struct struct{ Tuple Tuple }
931 StructPointer *struct{ Tuple Tuple }
932 Interface any
933 InterfacePointer any
934 }
935
936 for _, json := range jsonPackages {
937 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
938
939 in := Composites{
940 Slice: []Tuple{{Old: true}, {Old: true}}[:1],
941 Array: [1]Tuple{{Old: true}},
942 Map: map[string]Tuple{"Tuple": {Old: true}},
943 MapPointer: map[string]*Tuple{"Tuple": {Old: true}},
944 Struct: struct{ Tuple Tuple }{Tuple{Old: true}},
945 StructPointer: &struct{ Tuple Tuple }{Tuple{Old: true}},
946 Interface: Tuple{Old: true},
947 InterfacePointer: &Tuple{Old: true},
948 }
949
950
951 if err := json.Unmarshal([]byte(`{
952 "Slice": [{"New":true}, {"New":true}],
953 "Array": [{"New":true}],
954 "Map": {"Tuple": {"New":true}},
955 "MapPointer": {"Tuple": {"New":true}},
956 "Struct": {"Tuple": {"New":true}},
957 "StructPointer": {"Tuple": {"New":true}},
958 "Interface": {"New":true},
959 "InterfacePointer": {"New":true}
960 }`), &in); err != nil {
961 t.Fatalf("json.Unmarshal error: %v", err)
962 }
963
964 merged := Tuple{Old: true, New: true}
965 replaced := Tuple{Old: false, New: true}
966 want := map[string]Composites{
967 "v1": {
968 Slice: []Tuple{merged, merged},
969 Array: [1]Tuple{merged},
970 Map: map[string]Tuple{"Tuple": replaced},
971 MapPointer: map[string]*Tuple{"Tuple": &replaced},
972 Struct: struct{ Tuple Tuple }{merged},
973 StructPointer: &struct{ Tuple Tuple }{merged},
974 Interface: map[string]any{"New": true},
975 InterfacePointer: &merged,
976 },
977 "v2": {
978 Slice: []Tuple{replaced, replaced},
979 Array: [1]Tuple{replaced},
980 Map: map[string]Tuple{"Tuple": merged},
981 MapPointer: map[string]*Tuple{"Tuple": &merged},
982 Struct: struct{ Tuple Tuple }{merged},
983 StructPointer: &struct{ Tuple Tuple }{merged},
984 Interface: merged,
985 InterfacePointer: &merged,
986 },
987 }[json.Version]
988 if !reflect.DeepEqual(in, want) {
989 t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
990 }
991 })
992 }
993 }
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006 func TestTimeDurations(t *testing.T) {
1007 t.SkipNow()
1008 for _, json := range jsonPackages {
1009 t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
1010 got, err := json.Marshal(time.Minute)
1011 switch {
1012 case err != nil:
1013 t.Fatalf("json.Marshal error: %v", err)
1014 case json.Version == "v1" && string(got) != "60000000000":
1015 t.Fatalf("json.Marshal = %s, want 60000000000", got)
1016 case json.Version == "v2" && string(got) != `"1m0s"`:
1017 t.Fatalf(`json.Marshal = %s, want "1m0s"`, got)
1018 }
1019 })
1020 }
1021
1022 for _, json := range jsonPackages {
1023 t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
1024 in := map[string]string{
1025 "v1": "60000000000",
1026 "v2": `"1m0s"`,
1027 }[json.Version]
1028 var got time.Duration
1029 err := json.Unmarshal([]byte(in), &got)
1030 switch {
1031 case err != nil:
1032 t.Fatalf("json.Unmarshal error: %v", err)
1033 case got != time.Minute:
1034 t.Fatalf("json.Unmarshal = %v, want 1m0s", got)
1035 }
1036 })
1037 }
1038 }
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049 func TestEmptyStructs(t *testing.T) {
1050 never := func(string) bool { return false }
1051 onlyV2 := func(v string) bool { return v == "v2" }
1052 values := []struct {
1053 in any
1054 wantError func(string) bool
1055 }{
1056
1057 {in: addr(struct{}{}), wantError: never},
1058
1059
1060
1061 {in: errors.New("error"), wantError: onlyV2},
1062
1063 {in: addr(struct{ Exported, unexported int }{}), wantError: never},
1064 }
1065
1066 for _, json := range jsonPackages {
1067 t.Run("Marshal", func(t *testing.T) {
1068 for _, value := range values {
1069 wantError := value.wantError(json.Version)
1070 _, err := json.Marshal(value.in)
1071 switch {
1072 case (err == nil) && wantError:
1073 t.Fatalf("json.Marshal error is nil, want non-nil")
1074 case (err != nil) && !wantError:
1075 t.Fatalf("json.Marshal error: %v", err)
1076 }
1077 }
1078 })
1079 }
1080
1081 for _, json := range jsonPackages {
1082 t.Run("Unmarshal", func(t *testing.T) {
1083 for _, value := range values {
1084 wantError := value.wantError(json.Version)
1085 out := reflect.New(reflect.TypeOf(value.in).Elem()).Interface()
1086 err := json.Unmarshal([]byte("{}"), out)
1087 switch {
1088 case (err == nil) && wantError:
1089 t.Fatalf("json.Unmarshal error is nil, want non-nil")
1090 case (err != nil) && !wantError:
1091 t.Fatalf("json.Unmarshal error: %v", err)
1092 }
1093 }
1094 })
1095 }
1096 }
1097
View as plain text