| | |
| | |
| | |
| |
|
| | |
| |
|
| | package json_test |
| |
|
| | import ( |
| | "errors" |
| | "path" |
| | "reflect" |
| | "strings" |
| | "testing" |
| | "time" |
| |
|
| | jsonv1 "encoding/json" |
| | "encoding/json/jsontext" |
| | jsonv2 "encoding/json/v2" |
| | ) |
| |
|
| | |
| | |
| | |
| |
|
| | var jsonPackages = []struct { |
| | Version string |
| | Marshal func(any) ([]byte, error) |
| | Unmarshal func([]byte, any) error |
| | }{ |
| | {"v1", jsonv1.Marshal, jsonv1.Unmarshal}, |
| | {"v2", |
| | func(in any) ([]byte, error) { return jsonv2.Marshal(in) }, |
| | func(in []byte, out any) error { return jsonv2.Unmarshal(in, out) }}, |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestCaseSensitivity(t *testing.T) { |
| | type Fields struct { |
| | FieldA bool |
| | FieldB bool `json:"fooBar"` |
| | FieldC bool `json:"fizzBuzz,case:ignore"` |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | |
| | |
| | type goName = string |
| | type jsonName = string |
| | onlyV1 := json.Version == "v1" |
| | onlyV2 := json.Version == "v2" |
| | allMatches := map[goName]map[jsonName]bool{ |
| | "FieldA": { |
| | "FieldA": true, |
| | "fielda": onlyV1, |
| | "fieldA": onlyV1, |
| | "FIELDA": onlyV1, |
| | "FieldB": false, |
| | "FieldC": false, |
| | }, |
| | "FieldB": { |
| | "fooBar": true, |
| | "FooBar": onlyV1, |
| | "foobar": onlyV1, |
| | "FOOBAR": onlyV1, |
| | "fizzBuzz": false, |
| | "FieldA": false, |
| | "FieldB": false, |
| | "FieldC": false, |
| | }, |
| | "FieldC": { |
| | "fizzBuzz": true, |
| | "fizzbuzz": true, |
| | "FIZZBUZZ": true, |
| | "fizz_buzz": onlyV2, |
| | "fizz-buzz": onlyV2, |
| | "fooBar": false, |
| | "FieldA": false, |
| | "FieldC": false, |
| | "FieldB": false, |
| | }, |
| | } |
| |
|
| | for goFieldName, matches := range allMatches { |
| | for jsonMemberName, wantMatch := range matches { |
| | in := `{"` + jsonMemberName + `":true}` |
| | var s Fields |
| | if err := json.Unmarshal([]byte(in), &s); err != nil { |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| | gotMatch := reflect.ValueOf(s).FieldByName(goFieldName).Bool() |
| | if gotMatch != wantMatch { |
| | t.Fatalf("%T.%s = %v, want %v", s, goFieldName, gotMatch, wantMatch) |
| | } |
| | } |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestOmitEmptyOption(t *testing.T) { |
| | type Struct struct { |
| | Foo string `json:",omitempty"` |
| | Bar []int `json:",omitempty"` |
| | Baz *Struct `json:",omitempty"` |
| | } |
| | type Types struct { |
| | Bool bool `json:",omitempty"` |
| | StringA string `json:",omitempty"` |
| | StringB string `json:",omitempty"` |
| | BytesA []byte `json:",omitempty"` |
| | BytesB []byte `json:",omitempty"` |
| | BytesC []byte `json:",omitempty"` |
| | Int int `json:",omitempty"` |
| | MapA map[string]string `json:",omitempty"` |
| | MapB map[string]string `json:",omitempty"` |
| | MapC map[string]string `json:",omitempty"` |
| | StructA Struct `json:",omitempty"` |
| | StructB Struct `json:",omitempty"` |
| | StructC Struct `json:",omitempty"` |
| | SliceA []string `json:",omitempty"` |
| | SliceB []string `json:",omitempty"` |
| | SliceC []string `json:",omitempty"` |
| | Array [1]string `json:",omitempty"` |
| | PointerA *string `json:",omitempty"` |
| | PointerB *string `json:",omitempty"` |
| | PointerC *string `json:",omitempty"` |
| | InterfaceA any `json:",omitempty"` |
| | InterfaceB any `json:",omitempty"` |
| | InterfaceC any `json:",omitempty"` |
| | InterfaceD any `json:",omitempty"` |
| | } |
| |
|
| | something := "something" |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | in := Types{ |
| | Bool: false, |
| | StringA: "", |
| | StringB: something, |
| | BytesA: nil, |
| | BytesB: []byte{}, |
| | BytesC: []byte(something), |
| | Int: 0, |
| | MapA: nil, |
| | MapB: map[string]string{}, |
| | MapC: map[string]string{something: something}, |
| | StructA: Struct{}, |
| | StructB: Struct{Bar: []int{}, Baz: new(Struct)}, |
| | StructC: Struct{Foo: something}, |
| | SliceA: nil, |
| | SliceB: []string{}, |
| | SliceC: []string{something}, |
| | Array: [1]string{something}, |
| | PointerA: nil, |
| | PointerB: new(string), |
| | PointerC: &something, |
| | InterfaceA: nil, |
| | InterfaceB: (*string)(nil), |
| | InterfaceC: new(string), |
| | InterfaceD: &something, |
| | } |
| | b, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | var out map[string]any |
| | if err := json.Unmarshal(b, &out); err != nil { |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| |
|
| | onlyV1 := json.Version == "v1" |
| | onlyV2 := json.Version == "v2" |
| | wantPresent := map[string]bool{ |
| | "Bool": onlyV2, |
| | "StringA": false, |
| | "StringB": true, |
| | "BytesA": false, |
| | "BytesB": false, |
| | "BytesC": true, |
| | "Int": onlyV2, |
| | "MapA": false, |
| | "MapB": false, |
| | "MapC": true, |
| | "StructA": onlyV1, |
| | "StructB": onlyV1, |
| | "StructC": true, |
| | "SliceA": false, |
| | "SliceB": false, |
| | "SliceC": true, |
| | "Array": true, |
| | "PointerA": false, |
| | "PointerB": onlyV1, |
| | "PointerC": true, |
| | "InterfaceA": false, |
| | "InterfaceB": onlyV1, |
| | "InterfaceC": onlyV1, |
| | "InterfaceD": true, |
| | } |
| | for field, want := range wantPresent { |
| | _, got := out[field] |
| | if got != want { |
| | t.Fatalf("%T.%s = %v, want %v", in, field, got, want) |
| | } |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | func addr[T any](v T) *T { |
| | return &v |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestStringOption(t *testing.T) { |
| | type Types struct { |
| | String string `json:",string"` |
| | Bool bool `json:",string"` |
| | Int int `json:",string"` |
| | Float float64 `json:",string"` |
| | Map map[string]int `json:",string"` |
| | Struct struct{ Field int } `json:",string"` |
| | Slice []int `json:",string"` |
| | Array [1]int `json:",string"` |
| | PointerA *int `json:",string"` |
| | PointerB *int `json:",string"` |
| | PointerC **int `json:",string"` |
| | InterfaceA any `json:",string"` |
| | InterfaceB any `json:",string"` |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | in := Types{ |
| | String: "string", |
| | Bool: true, |
| | Int: 1, |
| | Float: 1, |
| | Map: map[string]int{"Name": 1}, |
| | Struct: struct{ Field int }{1}, |
| | Slice: []int{1}, |
| | Array: [1]int{1}, |
| | PointerA: nil, |
| | PointerB: addr(1), |
| | PointerC: addr(addr(1)), |
| | InterfaceA: nil, |
| | InterfaceB: 1, |
| | } |
| | quote := func(s string) string { |
| | b, _ := jsontext.AppendQuote(nil, s) |
| | return string(b) |
| | } |
| | quoteOnlyV1 := func(s string) string { |
| | if json.Version == "v1" { |
| | s = quote(s) |
| | } |
| | return s |
| | } |
| | quoteOnlyV2 := func(s string) string { |
| | if json.Version == "v2" { |
| | s = quote(s) |
| | } |
| | return s |
| | } |
| | want := strings.Join([]string{ |
| | `{`, |
| | `"String":` + quoteOnlyV1(`"string"`) + `,`, |
| | `"Bool":` + quoteOnlyV1("true") + `,`, |
| | `"Int":` + quote("1") + `,`, |
| | `"Float":` + quote("1") + `,`, |
| | `"Map":{"Name":` + quoteOnlyV2("1") + `},`, |
| | `"Struct":{"Field":` + quoteOnlyV2("1") + `},`, |
| | `"Slice":[` + quoteOnlyV2("1") + `],`, |
| | `"Array":[` + quoteOnlyV2("1") + `],`, |
| | `"PointerA":null,`, |
| | `"PointerB":` + quote("1") + `,`, |
| | `"PointerC":` + quoteOnlyV2("1") + `,`, |
| | `"InterfaceA":null,`, |
| | `"InterfaceB":` + quoteOnlyV2("1") + ``, |
| | `}`}, "") |
| | got, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Fatalf("json.Marshal = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal/Null", json.Version), func(t *testing.T) { |
| | var got Types |
| | err := json.Unmarshal([]byte(`{ |
| | "Bool": "null", |
| | "Int": "null", |
| | "PointerA": "null" |
| | }`), &got) |
| | switch { |
| | case !reflect.DeepEqual(got, Types{}): |
| | t.Fatalf("json.Unmarshal = %v, want %v", got, Types{}) |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| |
|
| | t.Run(path.Join("Unmarshal/Bool", json.Version), func(t *testing.T) { |
| | var got Types |
| | want := map[string]Types{ |
| | "v1": {Bool: true}, |
| | "v2": {Bool: false}, |
| | }[json.Version] |
| | err := json.Unmarshal([]byte(`{"Bool": "true"}`), &got) |
| | switch { |
| | case !reflect.DeepEqual(got, want): |
| | t.Fatalf("json.Unmarshal = %v, want %v", got, want) |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| |
|
| | t.Run(path.Join("Unmarshal/Shallow", json.Version), func(t *testing.T) { |
| | var got Types |
| | want := Types{Int: 1, PointerB: addr(1)} |
| | err := json.Unmarshal([]byte(`{ |
| | "Int": "1", |
| | "PointerB": "1" |
| | }`), &got) |
| | switch { |
| | case !reflect.DeepEqual(got, want): |
| | t.Fatalf("json.Unmarshal = %v, want %v", got, want) |
| | case err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| | }) |
| |
|
| | t.Run(path.Join("Unmarshal/Deep", json.Version), func(t *testing.T) { |
| | var got Types |
| | want := map[string]Types{ |
| | "v1": { |
| | Map: map[string]int{"Name": 0}, |
| | Slice: []int{0}, |
| | PointerC: addr(addr(0)), |
| | }, |
| | "v2": { |
| | Map: map[string]int{"Name": 1}, |
| | Struct: struct{ Field int }{1}, |
| | Slice: []int{1}, |
| | Array: [1]int{1}, |
| | PointerC: addr(addr(1)), |
| | }, |
| | }[json.Version] |
| | err := json.Unmarshal([]byte(`{ |
| | "Map": {"Name":"1"}, |
| | "Struct": {"Field":"1"}, |
| | "Slice": ["1"], |
| | "Array": ["1"], |
| | "PointerC": "1" |
| | }`), &got) |
| | switch { |
| | case !reflect.DeepEqual(got, want): |
| | t.Fatalf("json.Unmarshal =\n%v, want\n%v", got, want) |
| | case json.Version == "v1" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | case json.Version == "v2" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestNilSlicesAndMaps(t *testing.T) { |
| | type Composites struct { |
| | B []byte |
| | S []string |
| | M map[string]string |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | in := []Composites{ |
| | {B: []byte(nil), S: []string(nil), M: map[string]string(nil)}, |
| | {B: []byte{}, S: []string{}, M: map[string]string{}}, |
| | } |
| | want := map[string]string{ |
| | "v1": `[{"B":null,"S":null,"M":null},{"B":"","S":[],"M":{}}]`, |
| | "v2": `[{"B":"","S":[],"M":{}},{"B":"","S":[],"M":{}}]`, |
| | }[json.Version] |
| | got, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Fatalf("json.Marshal = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestArrays(t *testing.T) { |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal/TooFew", json.Version), func(t *testing.T) { |
| | var got [2]int |
| | err := json.Unmarshal([]byte(`[1]`), &got) |
| | switch { |
| | case got != [2]int{1, 0}: |
| | t.Fatalf(`json.Unmarshal = %v, want [1 0]`, got) |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal/TooMany", json.Version), func(t *testing.T) { |
| | var got [2]int |
| | err := json.Unmarshal([]byte(`[1,2,3]`), &got) |
| | switch { |
| | case got != [2]int{1, 2}: |
| | t.Fatalf(`json.Unmarshal = %v, want [1 2]`, got) |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestByteArrays(t *testing.T) { |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | in := [4]byte{1, 2, 3, 4} |
| | got, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | want := map[string]string{ |
| | "v1": `[1,2,3,4]`, |
| | "v2": `"AQIDBA=="`, |
| | }[json.Version] |
| | if string(got) != want { |
| | t.Fatalf("json.Marshal = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | in := map[string]string{ |
| | "v1": `[1,2,3,4]`, |
| | "v2": `"AQIDBA=="`, |
| | }[json.Version] |
| | var got [4]byte |
| | err := json.Unmarshal([]byte(in), &got) |
| | switch { |
| | case err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case got != [4]byte{1, 2, 3, 4}: |
| | t.Fatalf("json.Unmarshal = %v, want [1 2 3 4]", got) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | type CallCheck string |
| |
|
| | |
| | func (*CallCheck) MarshalJSON() ([]byte, error) { |
| | return []byte(`"CALLED"`), nil |
| | } |
| |
|
| | |
| | func (v *CallCheck) UnmarshalJSON([]byte) error { |
| | *v = `CALLED` |
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestPointerReceiver(t *testing.T) { |
| | type Values struct { |
| | S []CallCheck |
| | A [1]CallCheck |
| | M map[string]CallCheck |
| | V CallCheck |
| | I any |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | var cc CallCheck |
| | in := Values{ |
| | S: []CallCheck{cc}, |
| | A: [1]CallCheck{cc}, |
| | M: map[string]CallCheck{"": cc}, |
| | V: cc, |
| | I: cc, |
| | } |
| | want := map[string]string{ |
| | "v1": `{"S":["CALLED"],"A":[""],"M":{"":""},"V":"","I":""}`, |
| | "v2": `{"S":["CALLED"],"A":["CALLED"],"M":{"":"CALLED"},"V":"CALLED","I":"CALLED"}`, |
| | }[json.Version] |
| | got, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Fatalf("json.Marshal = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | in := `{"S":[""],"A":[""],"M":{"":""},"V":"","I":""}` |
| | called := CallCheck("CALLED") |
| | want := map[string]Values{ |
| | "v1": { |
| | S: []CallCheck{called}, |
| | A: [1]CallCheck{called}, |
| | M: map[string]CallCheck{"": called}, |
| | V: called, |
| | I: "", |
| | }, |
| | "v2": { |
| | S: []CallCheck{called}, |
| | A: [1]CallCheck{called}, |
| | M: map[string]CallCheck{"": called}, |
| | V: called, |
| | I: called, |
| | }, |
| | }[json.Version] |
| | got := Values{ |
| | A: [1]CallCheck{CallCheck("")}, |
| | S: []CallCheck{CallCheck("")}, |
| | M: map[string]CallCheck{"": CallCheck("")}, |
| | V: CallCheck(""), |
| | I: CallCheck(""), |
| | } |
| | if err := json.Unmarshal([]byte(in), &got); err != nil { |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| | if !reflect.DeepEqual(got, want) { |
| | t.Fatalf("json.Unmarshal = %v, want %v", got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestMapDeterminism(t *testing.T) { |
| | const iterations = 10 |
| | in := map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | outs := make(map[string]bool) |
| | for range iterations { |
| | b, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | outs[string(b)] = true |
| | } |
| | switch { |
| | case json.Version == "v1" && len(outs) != 1: |
| | t.Fatalf("json.Marshal encoded to %d unique forms, expected 1", len(outs)) |
| | case json.Version == "v2" && len(outs) == 1: |
| | t.Logf("json.Marshal encoded to 1 unique form by chance; are you feeling lucky?") |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestEscapeHTML(t *testing.T) { |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | const in = `<script> console.log("Hello, world!"); </script>` |
| | got, err := json.Marshal(in) |
| | if err != nil { |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | want := map[string]string{ |
| | "v1": `"\u003cscript\u003e console.log(\"Hello, world!\"); \u003c/script\u003e"`, |
| | "v2": `"<script> console.log(\"Hello, world!\"); </script>"`, |
| | }[json.Version] |
| | if string(got) != want { |
| | t.Fatalf("json.Marshal = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestInvalidUTF8(t *testing.T) { |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | got, err := json.Marshal("\xff") |
| | switch { |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Marshal error: %v", err) |
| | case json.Version == "v1" && string(got) != "\"\ufffd\"": |
| | t.Fatalf(`json.Marshal = %s, want %q`, got, "\ufffd") |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Marshal error is nil, want non-nil") |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | const in = "\"\xff\"" |
| | var got string |
| | err := json.Unmarshal([]byte(in), &got) |
| | switch { |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v1" && got != "\ufffd": |
| | t.Fatalf(`json.Unmarshal = %q, want "\ufffd"`, got) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestDuplicateNames(t *testing.T) { |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | const in = `{"Name":1,"Name":2}` |
| | var got struct{ Name int } |
| | err := json.Unmarshal([]byte(in), &got) |
| | switch { |
| | case json.Version == "v1" && err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case json.Version == "v1" && got != struct{ Name int }{2}: |
| | t.Fatalf(`json.Unmarshal = %v, want {2}`, got) |
| | case json.Version == "v2" && err == nil: |
| | t.Fatal("json.Unmarshal error is nil, want non-nil") |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestMergeNull(t *testing.T) { |
| | type Types struct { |
| | Bool bool |
| | String string |
| | Bytes []byte |
| | Int int |
| | Map map[string]string |
| | Struct struct{ Field string } |
| | Slice []string |
| | Array [1]string |
| | Pointer *string |
| | Interface any |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | |
| | in := Types{ |
| | Bool: true, |
| | String: "old", |
| | Bytes: []byte("old"), |
| | Int: 1234, |
| | Map: map[string]string{"old": "old"}, |
| | Struct: struct{ Field string }{"old"}, |
| | Slice: []string{"old"}, |
| | Array: [1]string{"old"}, |
| | Pointer: new(string), |
| | Interface: "old", |
| | } |
| |
|
| | |
| | if err := json.Unmarshal([]byte(`{ |
| | "Bool": null, |
| | "String": null, |
| | "Bytes": null, |
| | "Int": null, |
| | "Map": null, |
| | "Struct": null, |
| | "Slice": null, |
| | "Array": null, |
| | "Pointer": null, |
| | "Interface": null |
| | }`), &in); err != nil { |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| |
|
| | want := map[string]Types{ |
| | "v1": { |
| | Bool: true, |
| | String: "old", |
| | Int: 1234, |
| | Struct: struct{ Field string }{"old"}, |
| | Array: [1]string{"old"}, |
| | }, |
| | "v2": {}, |
| | }[json.Version] |
| | if !reflect.DeepEqual(in, want) { |
| | t.Fatalf("json.Unmarshal = %+v, want %+v", in, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestMergeComposite(t *testing.T) { |
| | type Tuple struct{ Old, New bool } |
| | type Composites struct { |
| | Slice []Tuple |
| | Array [1]Tuple |
| | Map map[string]Tuple |
| | MapPointer map[string]*Tuple |
| | Struct struct{ Tuple Tuple } |
| | StructPointer *struct{ Tuple Tuple } |
| | Interface any |
| | InterfacePointer any |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | |
| | in := Composites{ |
| | Slice: []Tuple{{Old: true}, {Old: true}}[:1], |
| | Array: [1]Tuple{{Old: true}}, |
| | Map: map[string]Tuple{"Tuple": {Old: true}}, |
| | MapPointer: map[string]*Tuple{"Tuple": {Old: true}}, |
| | Struct: struct{ Tuple Tuple }{Tuple{Old: true}}, |
| | StructPointer: &struct{ Tuple Tuple }{Tuple{Old: true}}, |
| | Interface: Tuple{Old: true}, |
| | InterfacePointer: &Tuple{Old: true}, |
| | } |
| |
|
| | |
| | if err := json.Unmarshal([]byte(`{ |
| | "Slice": [{"New":true}, {"New":true}], |
| | "Array": [{"New":true}], |
| | "Map": {"Tuple": {"New":true}}, |
| | "MapPointer": {"Tuple": {"New":true}}, |
| | "Struct": {"Tuple": {"New":true}}, |
| | "StructPointer": {"Tuple": {"New":true}}, |
| | "Interface": {"New":true}, |
| | "InterfacePointer": {"New":true} |
| | }`), &in); err != nil { |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| |
|
| | merged := Tuple{Old: true, New: true} |
| | replaced := Tuple{Old: false, New: true} |
| | want := map[string]Composites{ |
| | "v1": { |
| | Slice: []Tuple{merged, merged}, |
| | Array: [1]Tuple{merged}, |
| | Map: map[string]Tuple{"Tuple": replaced}, |
| | MapPointer: map[string]*Tuple{"Tuple": &replaced}, |
| | Struct: struct{ Tuple Tuple }{merged}, |
| | StructPointer: &struct{ Tuple Tuple }{merged}, |
| | Interface: map[string]any{"New": true}, |
| | InterfacePointer: &merged, |
| | }, |
| | "v2": { |
| | Slice: []Tuple{replaced, replaced}, |
| | Array: [1]Tuple{replaced}, |
| | Map: map[string]Tuple{"Tuple": merged}, |
| | MapPointer: map[string]*Tuple{"Tuple": &merged}, |
| | Struct: struct{ Tuple Tuple }{merged}, |
| | StructPointer: &struct{ Tuple Tuple }{merged}, |
| | Interface: merged, |
| | InterfacePointer: &merged, |
| | }, |
| | }[json.Version] |
| | if !reflect.DeepEqual(in, want) { |
| | t.Fatalf("json.Unmarshal = %+v, want %+v", in, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestTimeDurations(t *testing.T) { |
| | t.SkipNow() |
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Marshal", json.Version), func(t *testing.T) { |
| | got, err := json.Marshal(time.Minute) |
| | switch { |
| | case err != nil: |
| | t.Fatalf("json.Marshal error: %v", err) |
| | case json.Version == "v1" && string(got) != "60000000000": |
| | t.Fatalf("json.Marshal = %s, want 60000000000", got) |
| | case json.Version == "v2" && string(got) != `"1m0s"`: |
| | t.Fatalf(`json.Marshal = %s, want "1m0s"`, got) |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) { |
| | in := map[string]string{ |
| | "v1": "60000000000", |
| | "v2": `"1m0s"`, |
| | }[json.Version] |
| | var got time.Duration |
| | err := json.Unmarshal([]byte(in), &got) |
| | switch { |
| | case err != nil: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | case got != time.Minute: |
| | t.Fatalf("json.Unmarshal = %v, want 1m0s", got) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func TestEmptyStructs(t *testing.T) { |
| | never := func(string) bool { return false } |
| | onlyV2 := func(v string) bool { return v == "v2" } |
| | values := []struct { |
| | in any |
| | wantError func(string) bool |
| | }{ |
| | |
| | {in: addr(struct{}{}), wantError: never}, |
| | |
| | |
| | |
| | {in: errors.New("error"), wantError: onlyV2}, |
| | |
| | {in: addr(struct{ Exported, unexported int }{}), wantError: never}, |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run("Marshal", func(t *testing.T) { |
| | for _, value := range values { |
| | wantError := value.wantError(json.Version) |
| | _, err := json.Marshal(value.in) |
| | switch { |
| | case (err == nil) && wantError: |
| | t.Fatalf("json.Marshal error is nil, want non-nil") |
| | case (err != nil) && !wantError: |
| | t.Fatalf("json.Marshal error: %v", err) |
| | } |
| | } |
| | }) |
| | } |
| |
|
| | for _, json := range jsonPackages { |
| | t.Run("Unmarshal", func(t *testing.T) { |
| | for _, value := range values { |
| | wantError := value.wantError(json.Version) |
| | out := reflect.New(reflect.TypeOf(value.in).Elem()).Interface() |
| | err := json.Unmarshal([]byte("{}"), out) |
| | switch { |
| | case (err == nil) && wantError: |
| | t.Fatalf("json.Unmarshal error is nil, want non-nil") |
| | case (err != nil) && !wantError: |
| | t.Fatalf("json.Unmarshal error: %v", err) |
| | } |
| | } |
| | }) |
| | } |
| | } |
| |
|