| | |
| | |
| | |
| |
|
| | |
| |
|
| | package json |
| |
|
| | import ( |
| | "bytes" |
| | "encoding" |
| | "fmt" |
| | "log" |
| | "math" |
| | "reflect" |
| | "regexp" |
| | "runtime/debug" |
| | "strconv" |
| | "sync" |
| | "testing" |
| | "testing/synctest" |
| | "time" |
| | ) |
| |
|
| | type OptionalsEmpty struct { |
| | Sr string `json:"sr"` |
| | So string `json:"so,omitempty"` |
| | Sw string `json:"-"` |
| |
|
| | Ir int `json:"omitempty"` |
| | Io int `json:"io,omitempty"` |
| |
|
| | Slr []string `json:"slr,random"` |
| | Slo []string `json:"slo,omitempty"` |
| |
|
| | Mr map[string]any `json:"mr"` |
| | Mo map[string]any `json:",omitempty"` |
| |
|
| | Fr float64 `json:"fr"` |
| | Fo float64 `json:"fo,omitempty"` |
| |
|
| | Br bool `json:"br"` |
| | Bo bool `json:"bo,omitempty"` |
| |
|
| | Ur uint `json:"ur"` |
| | Uo uint `json:"uo,omitempty"` |
| |
|
| | Str struct{} `json:"str"` |
| | Sto struct{} `json:"sto,omitempty"` |
| | } |
| |
|
| | func TestOmitEmpty(t *testing.T) { |
| | const want = `{ |
| | "sr": "", |
| | "omitempty": 0, |
| | "slr": null, |
| | "mr": {}, |
| | "fr": 0, |
| | "br": false, |
| | "ur": 0, |
| | "str": {}, |
| | "sto": {} |
| | }` |
| | var o OptionalsEmpty |
| | o.Sw = "something" |
| | o.Mr = map[string]any{} |
| | o.Mo = map[string]any{} |
| |
|
| | got, err := MarshalIndent(&o, "", " ") |
| | if err != nil { |
| | t.Fatalf("MarshalIndent error: %v", err) |
| | } |
| | if got := string(got); got != want { |
| | t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) |
| | } |
| | } |
| |
|
| | type NonZeroStruct struct{} |
| |
|
| | func (nzs NonZeroStruct) IsZero() bool { |
| | return false |
| | } |
| |
|
| | type NoPanicStruct struct { |
| | Int int `json:"int,omitzero"` |
| | } |
| |
|
| | func (nps *NoPanicStruct) IsZero() bool { |
| | return nps.Int != 0 |
| | } |
| |
|
| | type isZeroer interface { |
| | IsZero() bool |
| | } |
| |
|
| | type OptionalsZero struct { |
| | Sr string `json:"sr"` |
| | So string `json:"so,omitzero"` |
| | Sw string `json:"-"` |
| |
|
| | Ir int `json:"omitzero"` |
| | Io int `json:"io,omitzero"` |
| |
|
| | Slr []string `json:"slr,random"` |
| | Slo []string `json:"slo,omitzero"` |
| | SloNonNil []string `json:"slononnil,omitzero"` |
| |
|
| | Mr map[string]any `json:"mr"` |
| | Mo map[string]any `json:",omitzero"` |
| | Moo map[string]any `json:"moo,omitzero"` |
| |
|
| | Fr float64 `json:"fr"` |
| | Fo float64 `json:"fo,omitzero"` |
| | Foo float64 `json:"foo,omitzero"` |
| | Foo2 [2]float64 `json:"foo2,omitzero"` |
| |
|
| | Br bool `json:"br"` |
| | Bo bool `json:"bo,omitzero"` |
| |
|
| | Ur uint `json:"ur"` |
| | Uo uint `json:"uo,omitzero"` |
| |
|
| | Str struct{} `json:"str"` |
| | Sto struct{} `json:"sto,omitzero"` |
| |
|
| | Time time.Time `json:"time,omitzero"` |
| | TimeLocal time.Time `json:"timelocal,omitzero"` |
| | Nzs NonZeroStruct `json:"nzs,omitzero"` |
| |
|
| | NilIsZeroer isZeroer `json:"niliszeroer,omitzero"` |
| | NonNilIsZeroer isZeroer `json:"nonniliszeroer,omitzero"` |
| | NoPanicStruct0 isZeroer `json:"nps0,omitzero"` |
| | NoPanicStruct1 isZeroer `json:"nps1,omitzero"` |
| | NoPanicStruct2 *NoPanicStruct `json:"nps2,omitzero"` |
| | NoPanicStruct3 *NoPanicStruct `json:"nps3,omitzero"` |
| | NoPanicStruct4 NoPanicStruct `json:"nps4,omitzero"` |
| | } |
| |
|
| | func TestOmitZero(t *testing.T) { |
| | const want = `{ |
| | "sr": "", |
| | "omitzero": 0, |
| | "slr": null, |
| | "slononnil": [], |
| | "mr": {}, |
| | "Mo": {}, |
| | "fr": 0, |
| | "br": false, |
| | "ur": 0, |
| | "str": {}, |
| | "nzs": {}, |
| | "nps1": {}, |
| | "nps3": {}, |
| | "nps4": {} |
| | }` |
| | var o OptionalsZero |
| | o.Sw = "something" |
| | o.SloNonNil = make([]string, 0) |
| | o.Mr = map[string]any{} |
| | o.Mo = map[string]any{} |
| |
|
| | o.Foo = -0 |
| | o.Foo2 = [2]float64{+0, -0} |
| |
|
| | o.TimeLocal = time.Time{}.Local() |
| |
|
| | o.NonNilIsZeroer = time.Time{} |
| | o.NoPanicStruct0 = (*NoPanicStruct)(nil) |
| | o.NoPanicStruct1 = &NoPanicStruct{} |
| | o.NoPanicStruct3 = &NoPanicStruct{} |
| |
|
| | got, err := MarshalIndent(&o, "", " ") |
| | if err != nil { |
| | t.Fatalf("MarshalIndent error: %v", err) |
| | } |
| | if got := string(got); got != want { |
| | t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) |
| | } |
| | } |
| |
|
| | func TestOmitZeroMap(t *testing.T) { |
| | const want = `{ |
| | "foo": { |
| | "sr": "", |
| | "omitzero": 0, |
| | "slr": null, |
| | "mr": null, |
| | "fr": 0, |
| | "br": false, |
| | "ur": 0, |
| | "str": {}, |
| | "nzs": {}, |
| | "nps4": {} |
| | } |
| | }` |
| | m := map[string]OptionalsZero{"foo": {}} |
| | got, err := MarshalIndent(m, "", " ") |
| | if err != nil { |
| | t.Fatalf("MarshalIndent error: %v", err) |
| | } |
| | if got := string(got); got != want { |
| | fmt.Println(got) |
| | t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) |
| | } |
| | } |
| |
|
| | type OptionalsEmptyZero struct { |
| | Sr string `json:"sr"` |
| | So string `json:"so,omitempty,omitzero"` |
| | Sw string `json:"-"` |
| |
|
| | Io int `json:"io,omitempty,omitzero"` |
| |
|
| | Slr []string `json:"slr,random"` |
| | Slo []string `json:"slo,omitempty,omitzero"` |
| | SloNonNil []string `json:"slononnil,omitempty,omitzero"` |
| |
|
| | Mr map[string]any `json:"mr"` |
| | Mo map[string]any `json:",omitempty,omitzero"` |
| |
|
| | Fr float64 `json:"fr"` |
| | Fo float64 `json:"fo,omitempty,omitzero"` |
| |
|
| | Br bool `json:"br"` |
| | Bo bool `json:"bo,omitempty,omitzero"` |
| |
|
| | Ur uint `json:"ur"` |
| | Uo uint `json:"uo,omitempty,omitzero"` |
| |
|
| | Str struct{} `json:"str"` |
| | Sto struct{} `json:"sto,omitempty,omitzero"` |
| |
|
| | Time time.Time `json:"time,omitempty,omitzero"` |
| | Nzs NonZeroStruct `json:"nzs,omitempty,omitzero"` |
| | } |
| |
|
| | func TestOmitEmptyZero(t *testing.T) { |
| | const want = `{ |
| | "sr": "", |
| | "slr": null, |
| | "mr": {}, |
| | "fr": 0, |
| | "br": false, |
| | "ur": 0, |
| | "str": {}, |
| | "nzs": {} |
| | }` |
| | var o OptionalsEmptyZero |
| | o.Sw = "something" |
| | o.SloNonNil = make([]string, 0) |
| | o.Mr = map[string]any{} |
| | o.Mo = map[string]any{} |
| |
|
| | got, err := MarshalIndent(&o, "", " ") |
| | if err != nil { |
| | t.Fatalf("MarshalIndent error: %v", err) |
| | } |
| | if got := string(got); got != want { |
| | t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) |
| | } |
| | } |
| |
|
| | type StringTag struct { |
| | BoolStr bool `json:",string"` |
| | IntStr int64 `json:",string"` |
| | UintptrStr uintptr `json:",string"` |
| | StrStr string `json:",string"` |
| | NumberStr Number `json:",string"` |
| | } |
| |
|
| | func TestRoundtripStringTag(t *testing.T) { |
| | tests := []struct { |
| | CaseName |
| | in StringTag |
| | want string |
| | }{{ |
| | CaseName: Name("AllTypes"), |
| | in: StringTag{ |
| | BoolStr: true, |
| | IntStr: 42, |
| | UintptrStr: 44, |
| | StrStr: "xzbit", |
| | NumberStr: "46", |
| | }, |
| | want: `{ |
| | "BoolStr": "true", |
| | "IntStr": "42", |
| | "UintptrStr": "44", |
| | "StrStr": "\"xzbit\"", |
| | "NumberStr": "46" |
| | }`, |
| | }, { |
| | |
| | CaseName: Name("StringDoubleEscapes"), |
| | in: StringTag{ |
| | StrStr: "\b\f\n\r\t\"\\", |
| | NumberStr: "0", |
| | }, |
| | want: `{ |
| | "BoolStr": "false", |
| | "IntStr": "0", |
| | "UintptrStr": "0", |
| | "StrStr": "\"\\b\\f\\n\\r\\t\\\"\\\\\"", |
| | "NumberStr": "0" |
| | }`, |
| | }} |
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | got, err := MarshalIndent(&tt.in, "", "\t") |
| | if err != nil { |
| | t.Fatalf("%s: MarshalIndent error: %v", tt.Where, err) |
| | } |
| | if got := string(got); got != tt.want { |
| | t.Fatalf("%s: MarshalIndent:\n\tgot: %s\n\twant: %s", tt.Where, stripWhitespace(got), stripWhitespace(tt.want)) |
| | } |
| |
|
| | |
| | var s2 StringTag |
| | if err := Unmarshal(got, &s2); err != nil { |
| | t.Fatalf("%s: Decode error: %v", tt.Where, err) |
| | } |
| | if !reflect.DeepEqual(s2, tt.in) { |
| | t.Fatalf("%s: Decode:\n\tinput: %s\n\tgot: %#v\n\twant: %#v", tt.Where, indentNewlines(string(got)), s2, tt.in) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | type renamedByte byte |
| | type renamedByteSlice []byte |
| | type renamedRenamedByteSlice []renamedByte |
| |
|
| | func TestEncodeRenamedByteSlice(t *testing.T) { |
| | s := renamedByteSlice("abc") |
| | got, err := Marshal(s) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | want := `"YWJj"` |
| | if string(got) != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | r := renamedRenamedByteSlice("abc") |
| | got, err = Marshal(r) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | type SamePointerNoCycle struct { |
| | Ptr1, Ptr2 *SamePointerNoCycle |
| | } |
| |
|
| | var samePointerNoCycle = &SamePointerNoCycle{} |
| |
|
| | type PointerCycle struct { |
| | Ptr *PointerCycle |
| | } |
| |
|
| | var pointerCycle = &PointerCycle{} |
| |
|
| | type PointerCycleIndirect struct { |
| | Ptrs []any |
| | } |
| |
|
| | type RecursiveSlice []RecursiveSlice |
| |
|
| | var ( |
| | pointerCycleIndirect = &PointerCycleIndirect{} |
| | mapCycle = make(map[string]any) |
| | sliceCycle = []any{nil} |
| | sliceNoCycle = []any{nil, nil} |
| | recursiveSliceCycle = []RecursiveSlice{nil} |
| | ) |
| |
|
| | func init() { |
| | ptr := &SamePointerNoCycle{} |
| | samePointerNoCycle.Ptr1 = ptr |
| | samePointerNoCycle.Ptr2 = ptr |
| |
|
| | pointerCycle.Ptr = pointerCycle |
| | pointerCycleIndirect.Ptrs = []any{pointerCycleIndirect} |
| |
|
| | mapCycle["x"] = mapCycle |
| | sliceCycle[0] = sliceCycle |
| | sliceNoCycle[1] = sliceNoCycle[:1] |
| | const startDetectingCyclesAfter = 1e3 |
| | for i := startDetectingCyclesAfter; i > 0; i-- { |
| | sliceNoCycle = []any{sliceNoCycle} |
| | } |
| | recursiveSliceCycle[0] = recursiveSliceCycle |
| | } |
| |
|
| | func TestSamePointerNoCycle(t *testing.T) { |
| | if _, err := Marshal(samePointerNoCycle); err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | } |
| |
|
| | func TestSliceNoCycle(t *testing.T) { |
| | if _, err := Marshal(sliceNoCycle); err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | } |
| |
|
| | func TestUnsupportedValues(t *testing.T) { |
| | tests := []struct { |
| | CaseName |
| | in any |
| | }{ |
| | {Name(""), math.NaN()}, |
| | {Name(""), math.Inf(-1)}, |
| | {Name(""), math.Inf(1)}, |
| | {Name(""), pointerCycle}, |
| | {Name(""), pointerCycleIndirect}, |
| | {Name(""), mapCycle}, |
| | {Name(""), sliceCycle}, |
| | {Name(""), recursiveSliceCycle}, |
| | } |
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | if _, err := Marshal(tt.in); err != nil { |
| | if _, ok := err.(*UnsupportedValueError); !ok { |
| | t.Errorf("%s: Marshal error:\n\tgot: %T\n\twant: %T", tt.Where, err, new(UnsupportedValueError)) |
| | } |
| | } else { |
| | t.Errorf("%s: Marshal error: got nil, want non-nil", tt.Where) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | func TestMarshalTextFloatMap(t *testing.T) { |
| | m := map[textfloat]string{ |
| | textfloat(math.NaN()): "1", |
| | textfloat(math.NaN()): "1", |
| | } |
| | got, err := Marshal(m) |
| | if err != nil { |
| | t.Errorf("Marshal error: %v", err) |
| | } |
| | want := `{"TF:NaN":"1","TF:NaN":"1"}` |
| | if string(got) != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | |
| | type Ref int |
| |
|
| | func (*Ref) MarshalJSON() ([]byte, error) { |
| | return []byte(`"ref"`), nil |
| | } |
| |
|
| | func (r *Ref) UnmarshalJSON([]byte) error { |
| | *r = 12 |
| | return nil |
| | } |
| |
|
| | |
| | type Val int |
| |
|
| | func (Val) MarshalJSON() ([]byte, error) { |
| | return []byte(`"val"`), nil |
| | } |
| |
|
| | |
| | type RefText int |
| |
|
| | func (*RefText) MarshalText() ([]byte, error) { |
| | return []byte(`"ref"`), nil |
| | } |
| |
|
| | func (r *RefText) UnmarshalText([]byte) error { |
| | *r = 13 |
| | return nil |
| | } |
| |
|
| | |
| | type ValText int |
| |
|
| | func (ValText) MarshalText() ([]byte, error) { |
| | return []byte(`"val"`), nil |
| | } |
| |
|
| | func TestRefValMarshal(t *testing.T) { |
| | var s = struct { |
| | R0 Ref |
| | R1 *Ref |
| | R2 RefText |
| | R3 *RefText |
| | V0 Val |
| | V1 *Val |
| | V2 ValText |
| | V3 *ValText |
| | }{ |
| | R0: 12, |
| | R1: new(Ref), |
| | R2: 14, |
| | R3: new(RefText), |
| | V0: 13, |
| | V1: new(Val), |
| | V2: 15, |
| | V3: new(ValText), |
| | } |
| | const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` |
| | b, err := Marshal(&s) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | if got := string(b); got != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | |
| | type C int |
| |
|
| | func (C) MarshalJSON() ([]byte, error) { |
| | return []byte(`"<&>"`), nil |
| | } |
| |
|
| | |
| | type CText int |
| |
|
| | func (CText) MarshalText() ([]byte, error) { |
| | return []byte(`"<&>"`), nil |
| | } |
| |
|
| | func TestMarshalerEscaping(t *testing.T) { |
| | var c C |
| | want := `"\u003c\u0026\u003e"` |
| | b, err := Marshal(c) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | if got := string(b); got != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| |
|
| | var ct CText |
| | want = `"\"\u003c\u0026\u003e\""` |
| | b, err = Marshal(ct) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | if got := string(b); got != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | func TestAnonymousFields(t *testing.T) { |
| | tests := []struct { |
| | CaseName |
| | makeInput func() any |
| | want string |
| | }{{ |
| | |
| | |
| | |
| | CaseName: Name("AmbiguousField"), |
| | makeInput: func() any { |
| | type ( |
| | S1 struct{ x, X int } |
| | S2 struct{ x, X int } |
| | S struct { |
| | S1 |
| | S2 |
| | } |
| | ) |
| | return S{S1{1, 2}, S2{3, 4}} |
| | }, |
| | want: `{}`, |
| | }, { |
| | CaseName: Name("DominantField"), |
| | |
| | |
| | makeInput: func() any { |
| | type ( |
| | S1 struct{ x, X int } |
| | S2 struct{ x, X int } |
| | S struct { |
| | S1 |
| | S2 |
| | x, X int |
| | } |
| | ) |
| | return S{S1{1, 2}, S2{3, 4}, 5, 6} |
| | }, |
| | want: `{"X":6}`, |
| | }, { |
| | |
| | CaseName: Name("UnexportedEmbeddedInt"), |
| | makeInput: func() any { |
| | type ( |
| | myInt int |
| | S struct{ myInt } |
| | ) |
| | return S{5} |
| | }, |
| | want: `{}`, |
| | }, { |
| | |
| | CaseName: Name("ExportedEmbeddedInt"), |
| | makeInput: func() any { |
| | type ( |
| | MyInt int |
| | S struct{ MyInt } |
| | ) |
| | return S{5} |
| | }, |
| | want: `{"MyInt":5}`, |
| | }, { |
| | |
| | |
| | CaseName: Name("UnexportedEmbeddedIntPointer"), |
| | makeInput: func() any { |
| | type ( |
| | myInt int |
| | S struct{ *myInt } |
| | ) |
| | s := S{new(myInt)} |
| | *s.myInt = 5 |
| | return s |
| | }, |
| | want: `{}`, |
| | }, { |
| | |
| | |
| | CaseName: Name("ExportedEmbeddedIntPointer"), |
| | makeInput: func() any { |
| | type ( |
| | MyInt int |
| | S struct{ *MyInt } |
| | ) |
| | s := S{new(MyInt)} |
| | *s.MyInt = 5 |
| | return s |
| | }, |
| | want: `{"MyInt":5}`, |
| | }, { |
| | |
| | |
| | |
| | CaseName: Name("EmbeddedStruct"), |
| | makeInput: func() any { |
| | type ( |
| | s1 struct{ x, X int } |
| | S2 struct{ y, Y int } |
| | S struct { |
| | s1 |
| | S2 |
| | } |
| | ) |
| | return S{s1{1, 2}, S2{3, 4}} |
| | }, |
| | want: `{"X":2,"Y":4}`, |
| | }, { |
| | |
| | |
| | |
| | CaseName: Name("EmbeddedStructPointer"), |
| | makeInput: func() any { |
| | type ( |
| | s1 struct{ x, X int } |
| | S2 struct{ y, Y int } |
| | S struct { |
| | *s1 |
| | *S2 |
| | } |
| | ) |
| | return S{&s1{1, 2}, &S2{3, 4}} |
| | }, |
| | want: `{"X":2,"Y":4}`, |
| | }, { |
| | |
| | |
| | CaseName: Name("NestedStructAndInts"), |
| | makeInput: func() any { |
| | type ( |
| | MyInt1 int |
| | MyInt2 int |
| | myInt int |
| | s2 struct { |
| | MyInt2 |
| | myInt |
| | } |
| | s1 struct { |
| | MyInt1 |
| | myInt |
| | s2 |
| | } |
| | S struct { |
| | s1 |
| | myInt |
| | } |
| | ) |
| | return S{s1{1, 2, s2{3, 4}}, 6} |
| | }, |
| | want: `{"MyInt1":1,"MyInt2":3}`, |
| | }, { |
| | |
| | |
| | |
| | CaseName: Name("EmbeddedFieldBehindNilPointer"), |
| | makeInput: func() any { |
| | type ( |
| | S2 struct{ Field string } |
| | S struct{ *S2 } |
| | ) |
| | return S{} |
| | }, |
| | want: `{}`, |
| | }} |
| |
|
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | b, err := Marshal(tt.makeInput()) |
| | if err != nil { |
| | t.Fatalf("%s: Marshal error: %v", tt.Where, err) |
| | } |
| | if string(b) != tt.want { |
| | t.Fatalf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, b, tt.want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | type BugA struct { |
| | S string |
| | } |
| |
|
| | type BugB struct { |
| | BugA |
| | S string |
| | } |
| |
|
| | type BugC struct { |
| | S string |
| | } |
| |
|
| | |
| | type BugX struct { |
| | A int |
| | BugA |
| | BugB |
| | } |
| |
|
| | |
| | |
| | |
| | type nilJSONMarshaler string |
| |
|
| | func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { |
| | if nm == nil { |
| | return Marshal("0zenil0") |
| | } |
| | return Marshal("zenil:" + string(*nm)) |
| | } |
| |
|
| | |
| | |
| | |
| | type nilTextMarshaler string |
| |
|
| | func (nm *nilTextMarshaler) MarshalText() ([]byte, error) { |
| | if nm == nil { |
| | return []byte("0zenil0"), nil |
| | } |
| | return []byte("zenil:" + string(*nm)), nil |
| | } |
| |
|
| | |
| | func TestNilMarshal(t *testing.T) { |
| | tests := []struct { |
| | CaseName |
| | in any |
| | want string |
| | }{ |
| | {Name(""), nil, `null`}, |
| | {Name(""), new(float64), `0`}, |
| | {Name(""), []any(nil), `null`}, |
| | {Name(""), []string(nil), `null`}, |
| | {Name(""), map[string]string(nil), `null`}, |
| | {Name(""), []byte(nil), `null`}, |
| | {Name(""), struct{ M string }{"gopher"}, `{"M":"gopher"}`}, |
| | {Name(""), struct{ M Marshaler }{}, `{"M":null}`}, |
| | {Name(""), struct{ M Marshaler }{(*nilJSONMarshaler)(nil)}, `{"M":"0zenil0"}`}, |
| | {Name(""), struct{ M any }{(*nilJSONMarshaler)(nil)}, `{"M":null}`}, |
| | {Name(""), struct{ M encoding.TextMarshaler }{}, `{"M":null}`}, |
| | {Name(""), struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, `{"M":"0zenil0"}`}, |
| | {Name(""), struct{ M any }{(*nilTextMarshaler)(nil)}, `{"M":null}`}, |
| | } |
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | switch got, err := Marshal(tt.in); { |
| | case err != nil: |
| | t.Fatalf("%s: Marshal error: %v", tt.Where, err) |
| | case string(got) != tt.want: |
| | t.Fatalf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | func TestEmbeddedBug(t *testing.T) { |
| | v := BugB{ |
| | BugA{"A"}, |
| | "B", |
| | } |
| | b, err := Marshal(v) |
| | if err != nil { |
| | t.Fatal("Marshal error:", err) |
| | } |
| | want := `{"S":"B"}` |
| | got := string(b) |
| | if got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | |
| | x := BugX{ |
| | A: 23, |
| | } |
| | b, err = Marshal(x) |
| | if err != nil { |
| | t.Fatal("Marshal error:", err) |
| | } |
| | want = `{"A":23}` |
| | got = string(b) |
| | if got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | type BugD struct { |
| | XXX string `json:"S"` |
| | } |
| |
|
| | |
| | type BugY struct { |
| | BugA |
| | BugD |
| | } |
| |
|
| | |
| | func TestTaggedFieldDominates(t *testing.T) { |
| | v := BugY{ |
| | BugA{"BugA"}, |
| | BugD{"BugD"}, |
| | } |
| | b, err := Marshal(v) |
| | if err != nil { |
| | t.Fatal("Marshal error:", err) |
| | } |
| | want := `{"S":"BugD"}` |
| | got := string(b) |
| | if got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | |
| | type BugZ struct { |
| | BugA |
| | BugC |
| | BugY |
| | } |
| |
|
| | func TestDuplicatedFieldDisappears(t *testing.T) { |
| | v := BugZ{ |
| | BugA{"BugA"}, |
| | BugC{"BugC"}, |
| | BugY{ |
| | BugA{"nested BugA"}, |
| | BugD{"nested BugD"}, |
| | }, |
| | } |
| | b, err := Marshal(v) |
| | if err != nil { |
| | t.Fatal("Marshal error:", err) |
| | } |
| | want := `{}` |
| | got := string(b) |
| | if got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | func TestIssue10281(t *testing.T) { |
| | type Foo struct { |
| | N Number |
| | } |
| | x := Foo{Number(`invalid`)} |
| |
|
| | if _, err := Marshal(&x); err == nil { |
| | t.Fatalf("Marshal error: got nil, want non-nil") |
| | } |
| | } |
| |
|
| | func TestMarshalErrorAndReuseEncodeState(t *testing.T) { |
| | |
| | percent := debug.SetGCPercent(-1) |
| | defer debug.SetGCPercent(percent) |
| |
|
| | |
| | type Dummy struct { |
| | Name string |
| | Next *Dummy |
| | } |
| | dummy := Dummy{Name: "Dummy"} |
| | dummy.Next = &dummy |
| | if _, err := Marshal(dummy); err == nil { |
| | t.Errorf("Marshal error: got nil, want non-nil") |
| | } |
| |
|
| | type Data struct { |
| | A string |
| | I int |
| | } |
| | want := Data{A: "a", I: 1} |
| | b, err := Marshal(want) |
| | if err != nil { |
| | t.Errorf("Marshal error: %v", err) |
| | } |
| |
|
| | var got Data |
| | if err := Unmarshal(b, &got); err != nil { |
| | t.Errorf("Unmarshal error: %v", err) |
| | } |
| | if got != want { |
| | t.Errorf("Unmarshal:\n\tgot: %v\n\twant: %v", got, want) |
| | } |
| | } |
| |
|
| | func TestHTMLEscape(t *testing.T) { |
| | var b, want bytes.Buffer |
| | m := `{"M":"<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>"}` |
| | want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`)) |
| | HTMLEscape(&b, []byte(m)) |
| | if !bytes.Equal(b.Bytes(), want.Bytes()) { |
| | t.Errorf("HTMLEscape:\n\tgot: %s\n\twant: %s", b.Bytes(), want.Bytes()) |
| | } |
| | } |
| |
|
| | |
| | func TestEncodePointerString(t *testing.T) { |
| | type stringPointer struct { |
| | N *int64 `json:"n,string"` |
| | } |
| | var n int64 = 42 |
| | b, err := Marshal(stringPointer{N: &n}) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | if got, want := string(b), `{"n":"42"}`; got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | var back stringPointer |
| | switch err = Unmarshal(b, &back); { |
| | case err != nil: |
| | t.Fatalf("Unmarshal error: %v", err) |
| | case back.N == nil: |
| | t.Fatalf("Unmarshal: back.N = nil, want non-nil") |
| | case *back.N != 42: |
| | t.Fatalf("Unmarshal: *back.N = %d, want 42", *back.N) |
| | } |
| | } |
| |
|
| | var encodeStringTests = []struct { |
| | in string |
| | out string |
| | }{ |
| | {"\x00", `"\u0000"`}, |
| | {"\x01", `"\u0001"`}, |
| | {"\x02", `"\u0002"`}, |
| | {"\x03", `"\u0003"`}, |
| | {"\x04", `"\u0004"`}, |
| | {"\x05", `"\u0005"`}, |
| | {"\x06", `"\u0006"`}, |
| | {"\x07", `"\u0007"`}, |
| | {"\x08", `"\b"`}, |
| | {"\x09", `"\t"`}, |
| | {"\x0a", `"\n"`}, |
| | {"\x0b", `"\u000b"`}, |
| | {"\x0c", `"\f"`}, |
| | {"\x0d", `"\r"`}, |
| | {"\x0e", `"\u000e"`}, |
| | {"\x0f", `"\u000f"`}, |
| | {"\x10", `"\u0010"`}, |
| | {"\x11", `"\u0011"`}, |
| | {"\x12", `"\u0012"`}, |
| | {"\x13", `"\u0013"`}, |
| | {"\x14", `"\u0014"`}, |
| | {"\x15", `"\u0015"`}, |
| | {"\x16", `"\u0016"`}, |
| | {"\x17", `"\u0017"`}, |
| | {"\x18", `"\u0018"`}, |
| | {"\x19", `"\u0019"`}, |
| | {"\x1a", `"\u001a"`}, |
| | {"\x1b", `"\u001b"`}, |
| | {"\x1c", `"\u001c"`}, |
| | {"\x1d", `"\u001d"`}, |
| | {"\x1e", `"\u001e"`}, |
| | {"\x1f", `"\u001f"`}, |
| | } |
| |
|
| | func TestEncodeString(t *testing.T) { |
| | for _, tt := range encodeStringTests { |
| | b, err := Marshal(tt.in) |
| | if err != nil { |
| | t.Errorf("Marshal(%q) error: %v", tt.in, err) |
| | continue |
| | } |
| | out := string(b) |
| | if out != tt.out { |
| | t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) |
| | } |
| | } |
| | } |
| |
|
| | type jsonbyte byte |
| |
|
| | func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) } |
| |
|
| | type textbyte byte |
| |
|
| | func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) } |
| |
|
| | type jsonint int |
| |
|
| | func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) } |
| |
|
| | type textint int |
| |
|
| | func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } |
| |
|
| | func tenc(format string, a ...any) ([]byte, error) { |
| | var buf bytes.Buffer |
| | fmt.Fprintf(&buf, format, a...) |
| | return buf.Bytes(), nil |
| | } |
| |
|
| | type textfloat float64 |
| |
|
| | func (f textfloat) MarshalText() ([]byte, error) { return tenc(`TF:%0.2f`, f) } |
| |
|
| | |
| | func TestEncodeBytekind(t *testing.T) { |
| | tests := []struct { |
| | CaseName |
| | in any |
| | want string |
| | }{ |
| | {Name(""), byte(7), "7"}, |
| | {Name(""), jsonbyte(7), `{"JB":7}`}, |
| | {Name(""), textbyte(4), `"TB:4"`}, |
| | {Name(""), jsonint(5), `{"JI":5}`}, |
| | {Name(""), textint(1), `"TI:1"`}, |
| | {Name(""), []byte{0, 1}, `"AAE="`}, |
| | {Name(""), []jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, |
| | {Name(""), [][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, |
| | {Name(""), []textbyte{2, 3}, `["TB:2","TB:3"]`}, |
| | {Name(""), []jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, |
| | {Name(""), []textint{9, 3}, `["TI:9","TI:3"]`}, |
| | {Name(""), []int{9, 3}, `[9,3]`}, |
| | {Name(""), []textfloat{12, 3}, `["TF:12.00","TF:3.00"]`}, |
| | } |
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | b, err := Marshal(tt.in) |
| | if err != nil { |
| | t.Errorf("%s: Marshal error: %v", tt.Where, err) |
| | } |
| | got, want := string(b), tt.want |
| | if got != want { |
| | t.Errorf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | func TestTextMarshalerMapKeysAreSorted(t *testing.T) { |
| | got, err := Marshal(map[unmarshalerText]int{ |
| | {"x", "y"}: 1, |
| | {"y", "x"}: 2, |
| | {"a", "z"}: 3, |
| | {"z", "a"}: 4, |
| | }) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}` |
| | if string(got) != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | |
| | func TestNilMarshalerTextMapKey(t *testing.T) { |
| | got, err := Marshal(map[*unmarshalerText]int{ |
| | (*unmarshalerText)(nil): 1, |
| | {"A", "B"}: 2, |
| | }) |
| | if err != nil { |
| | t.Fatalf("Marshal error: %v", err) |
| | } |
| | const want = `{"":1,"A:B":2}` |
| | if string(got) != want { |
| | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | var re = regexp.MustCompile |
| |
|
| | |
| | var badFloatREs = []*regexp.Regexp{ |
| | re(`p`), |
| | re(`^\+`), |
| | re(`^-?0[^.]`), |
| | re(`^-?\.`), |
| | re(`\.(e|$)`), |
| | re(`\.[0-9]+0(e|$)`), |
| | re(`^-?(0|[0-9]{2,})\..*e`), |
| | re(`e[0-9]`), |
| | re(`e[+-]0`), |
| | re(`e-[1-6]$`), |
| | re(`e+(.|1.|20)$`), |
| | re(`^-?0\.0000000`), |
| | re(`^-?[0-9]{22}`), |
| | re(`[1-9][0-9]{16}[1-9]`), |
| | re(`[1-9][0-9.]{17}[1-9]`), |
| | |
| | re(`[1-9][0-9]{8}[1-9]`), |
| | re(`[1-9][0-9.]{9}[1-9]`), |
| | } |
| |
|
| | func TestMarshalFloat(t *testing.T) { |
| | t.Parallel() |
| | nfail := 0 |
| | test := func(f float64, bits int) { |
| | vf := any(f) |
| | if bits == 32 { |
| | f = float64(float32(f)) |
| | vf = float32(f) |
| | } |
| | bout, err := Marshal(vf) |
| | if err != nil { |
| | t.Errorf("Marshal(%T(%g)) error: %v", vf, vf, err) |
| | nfail++ |
| | return |
| | } |
| | out := string(bout) |
| |
|
| | |
| | g, err := strconv.ParseFloat(out, bits) |
| | if err != nil { |
| | t.Errorf("ParseFloat(%q) error: %v", out, err) |
| | nfail++ |
| | return |
| | } |
| | if f != g || fmt.Sprint(f) != fmt.Sprint(g) { |
| | t.Errorf("ParseFloat(%q):\n\tgot: %g\n\twant: %g", out, float32(g), vf) |
| | nfail++ |
| | return |
| | } |
| |
|
| | bad := badFloatREs |
| | if bits == 64 { |
| | bad = bad[:len(bad)-2] |
| | } |
| | for _, re := range bad { |
| | if re.MatchString(out) { |
| | t.Errorf("Marshal(%T(%g)) = %q; must not match /%s/", vf, vf, out, re) |
| | nfail++ |
| | return |
| | } |
| | } |
| | } |
| |
|
| | var ( |
| | bigger = math.Inf(+1) |
| | smaller = math.Inf(-1) |
| | ) |
| |
|
| | var digits = "1.2345678901234567890123" |
| | for i := len(digits); i >= 2; i-- { |
| | if testing.Short() && i < len(digits)-4 { |
| | break |
| | } |
| | for exp := -30; exp <= 30; exp++ { |
| | for _, sign := range "+-" { |
| | for bits := 32; bits <= 64; bits += 32 { |
| | s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) |
| | f, err := strconv.ParseFloat(s, bits) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | next := math.Nextafter |
| | if bits == 32 { |
| | next = func(g, h float64) float64 { |
| | return float64(math.Nextafter32(float32(g), float32(h))) |
| | } |
| | } |
| | test(f, bits) |
| | test(next(f, bigger), bits) |
| | test(next(f, smaller), bits) |
| | if nfail > 50 { |
| | t.Fatalf("stopping test early") |
| | } |
| | } |
| | } |
| | } |
| | } |
| | test(0, 64) |
| | test(math.Copysign(0, -1), 64) |
| | test(0, 32) |
| | test(math.Copysign(0, -1), 32) |
| | } |
| |
|
| | func TestMarshalRawMessageValue(t *testing.T) { |
| | type ( |
| | T1 struct { |
| | M RawMessage `json:",omitempty"` |
| | } |
| | T2 struct { |
| | M *RawMessage `json:",omitempty"` |
| | } |
| | ) |
| |
|
| | var ( |
| | rawNil = RawMessage(nil) |
| | rawEmpty = RawMessage([]byte{}) |
| | rawText = RawMessage([]byte(`"foo"`)) |
| | ) |
| |
|
| | tests := []struct { |
| | CaseName |
| | in any |
| | want string |
| | ok bool |
| | }{ |
| | |
| | {Name(""), rawNil, "null", true}, |
| | {Name(""), &rawNil, "null", true}, |
| | {Name(""), []any{rawNil}, "[null]", true}, |
| | {Name(""), &[]any{rawNil}, "[null]", true}, |
| | {Name(""), []any{&rawNil}, "[null]", true}, |
| | {Name(""), &[]any{&rawNil}, "[null]", true}, |
| | {Name(""), struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, |
| | {Name(""), &struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, |
| | {Name(""), struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, |
| | {Name(""), &struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, |
| | {Name(""), map[string]any{"M": rawNil}, `{"M":null}`, true}, |
| | {Name(""), &map[string]any{"M": rawNil}, `{"M":null}`, true}, |
| | {Name(""), map[string]any{"M": &rawNil}, `{"M":null}`, true}, |
| | {Name(""), &map[string]any{"M": &rawNil}, `{"M":null}`, true}, |
| | {Name(""), T1{rawNil}, "{}", true}, |
| | {Name(""), T2{&rawNil}, `{"M":null}`, true}, |
| | {Name(""), &T1{rawNil}, "{}", true}, |
| | {Name(""), &T2{&rawNil}, `{"M":null}`, true}, |
| |
|
| | |
| | {Name(""), rawEmpty, "", false}, |
| | {Name(""), &rawEmpty, "", false}, |
| | {Name(""), []any{rawEmpty}, "", false}, |
| | {Name(""), &[]any{rawEmpty}, "", false}, |
| | {Name(""), []any{&rawEmpty}, "", false}, |
| | {Name(""), &[]any{&rawEmpty}, "", false}, |
| | {Name(""), struct{ X RawMessage }{rawEmpty}, "", false}, |
| | {Name(""), &struct{ X RawMessage }{rawEmpty}, "", false}, |
| | {Name(""), struct{ X *RawMessage }{&rawEmpty}, "", false}, |
| | {Name(""), &struct{ X *RawMessage }{&rawEmpty}, "", false}, |
| | {Name(""), map[string]any{"nil": rawEmpty}, "", false}, |
| | {Name(""), &map[string]any{"nil": rawEmpty}, "", false}, |
| | {Name(""), map[string]any{"nil": &rawEmpty}, "", false}, |
| | {Name(""), &map[string]any{"nil": &rawEmpty}, "", false}, |
| | {Name(""), T1{rawEmpty}, "{}", true}, |
| | {Name(""), T2{&rawEmpty}, "", false}, |
| | {Name(""), &T1{rawEmpty}, "{}", true}, |
| | {Name(""), &T2{&rawEmpty}, "", false}, |
| |
|
| | |
| | |
| | |
| | |
| | |
| | {Name(""), rawText, `"foo"`, true}, |
| | {Name(""), &rawText, `"foo"`, true}, |
| | {Name(""), []any{rawText}, `["foo"]`, true}, |
| | {Name(""), &[]any{rawText}, `["foo"]`, true}, |
| | {Name(""), []any{&rawText}, `["foo"]`, true}, |
| | {Name(""), &[]any{&rawText}, `["foo"]`, true}, |
| | {Name(""), struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), map[string]any{"M": rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &map[string]any{"M": rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), T1{rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), T2{&rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &T1{rawText}, `{"M":"foo"}`, true}, |
| | {Name(""), &T2{&rawText}, `{"M":"foo"}`, true}, |
| | } |
| |
|
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | b, err := Marshal(tt.in) |
| | if ok := (err == nil); ok != tt.ok { |
| | if err != nil { |
| | t.Errorf("%s: Marshal error: %v", tt.Where, err) |
| | } else { |
| | t.Errorf("%s: Marshal error: got nil, want non-nil", tt.Where) |
| | } |
| | } |
| | if got := string(b); got != tt.want { |
| | t.Errorf("%s: Marshal:\n\tinput: %#v\n\tgot: %s\n\twant: %s", tt.Where, tt.in, got, tt.want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | type marshalPanic struct{} |
| |
|
| | func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) } |
| |
|
| | func TestMarshalPanic(t *testing.T) { |
| | defer func() { |
| | if got := recover(); !reflect.DeepEqual(got, 0xdead) { |
| | t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) |
| | } |
| | }() |
| | Marshal(&marshalPanic{}) |
| | t.Error("Marshal should have panicked") |
| | } |
| |
|
| | func TestMarshalUncommonFieldNames(t *testing.T) { |
| | v := struct { |
| | A0, À, Aβ int |
| | }{} |
| | b, err := Marshal(v) |
| | if err != nil { |
| | t.Fatal("Marshal error:", err) |
| | } |
| | want := `{"A0":0,"À":0,"Aβ":0}` |
| | got := string(b) |
| | if got != want { |
| | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) |
| | } |
| | } |
| |
|
| | func TestMarshalerError(t *testing.T) { |
| | s := "test variable" |
| | st := reflect.TypeOf(s) |
| | const errText = "json: test error" |
| |
|
| | tests := []struct { |
| | CaseName |
| | err *MarshalerError |
| | want string |
| | }{{ |
| | Name(""), |
| | &MarshalerError{st, fmt.Errorf(errText), ""}, |
| | "json: error calling MarshalJSON for type " + st.String() + ": " + errText, |
| | }, { |
| | Name(""), |
| | &MarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}, |
| | "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, |
| | }} |
| |
|
| | for _, tt := range tests { |
| | t.Run(tt.Name, func(t *testing.T) { |
| | got := tt.err.Error() |
| | if got != tt.want { |
| | t.Errorf("%s: Error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | type marshaledValue string |
| |
|
| | func (v marshaledValue) MarshalJSON() ([]byte, error) { |
| | return []byte(v), nil |
| | } |
| |
|
| | func TestIssue63379(t *testing.T) { |
| | for _, v := range []string{ |
| | "[]<", |
| | "[]>", |
| | "[]&", |
| | "[]\u2028", |
| | "[]\u2029", |
| | "{}<", |
| | "{}>", |
| | "{}&", |
| | "{}\u2028", |
| | "{}\u2029", |
| | } { |
| | _, err := Marshal(marshaledValue(v)) |
| | if err == nil { |
| | t.Errorf("expected error for %q", v) |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func TestSynctestMarshal(t *testing.T) { |
| | var wg sync.WaitGroup |
| | for range 5 { |
| | wg.Go(func() { |
| | synctest.Test(t, func(t *testing.T) { |
| | _, err := Marshal([]string{}) |
| | if err != nil { |
| | t.Errorf("Marshal: %v", err) |
| | } |
| | }) |
| | }) |
| | } |
| | wg.Wait() |
| | } |
| |
|