Data map[string]string `json:"data"`
}
-type unmarshalTest struct {
- in string
- ptr any // new(type)
- out any
- err error
- useNumber bool
- golden bool
- disallowUnknownFields bool
-}
-
type B struct {
B bool `json:",string"`
}
J **int
}
-var unmarshalTests = []unmarshalTest{
+var unmarshalTests = []struct {
+ CaseName
+ in string
+ ptr any // new(type)
+ out any
+ err error
+ useNumber bool
+ golden bool
+ disallowUnknownFields bool
+}{
// basic types
- {in: `true`, ptr: new(bool), out: true},
- {in: `1`, ptr: new(int), out: 1},
- {in: `1.2`, ptr: new(float64), out: 1.2},
- {in: `-5`, ptr: new(int16), out: int16(-5)},
- {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
- {in: `2`, ptr: new(Number), out: Number("2")},
- {in: `2`, ptr: new(any), out: float64(2.0)},
- {in: `2`, ptr: new(any), out: Number("2"), useNumber: true},
- {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
- {in: `"http:\/\/"`, ptr: new(string), out: "http://"},
- {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
- {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
- {in: "null", ptr: new(any), out: nil},
- {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), 7, "T", "X"}},
- {in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "T", "X"}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
- {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
- {in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
- {in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S"}},
- {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
- {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
- {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
- {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
+ {CaseName: Name(""), in: `true`, ptr: new(bool), out: true},
+ {CaseName: Name(""), in: `1`, ptr: new(int), out: 1},
+ {CaseName: Name(""), in: `1.2`, ptr: new(float64), out: 1.2},
+ {CaseName: Name(""), in: `-5`, ptr: new(int16), out: int16(-5)},
+ {CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
+ {CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2")},
+ {CaseName: Name(""), in: `2`, ptr: new(any), out: float64(2.0)},
+ {CaseName: Name(""), in: `2`, ptr: new(any), out: Number("2"), useNumber: true},
+ {CaseName: Name(""), in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
+ {CaseName: Name(""), in: `"http:\/\/"`, ptr: new(string), out: "http://"},
+ {CaseName: Name(""), in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
+ {CaseName: Name(""), in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
+ {CaseName: Name(""), in: "null", ptr: new(any), out: nil},
+ {CaseName: Name(""), in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), 7, "T", "X"}},
+ {CaseName: Name(""), in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "T", "X"}},
+ {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
+ {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
+ {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
+ {CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S"}},
+ {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
+ {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
+ {CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
+ {CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
// raw values with whitespace
- {in: "\n true ", ptr: new(bool), out: true},
- {in: "\t 1 ", ptr: new(int), out: 1},
- {in: "\r 1.2 ", ptr: new(float64), out: 1.2},
- {in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
- {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
+ {CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},
+ {CaseName: Name(""), in: "\t 1 ", ptr: new(int), out: 1},
+ {CaseName: Name(""), in: "\r 1.2 ", ptr: new(float64), out: 1.2},
+ {CaseName: Name(""), in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
+ {CaseName: Name(""), in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
// Z has a "-" tag.
- {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
- {in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},
+ {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
+ {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},
- {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
- {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
- {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
- {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
- {in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
+ {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
+ {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
+ {CaseName: Name(""), in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
+ {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
+ {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
// syntax errors
- {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
- {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
- {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
- {in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}},
- {in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}},
+ {CaseName: Name(""), in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
+ {CaseName: Name(""), in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
+ {CaseName: Name(""), in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
+ {CaseName: Name(""), in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}},
+ {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}},
// raw value errors
- {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
- {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
- {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
- {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
+ {CaseName: Name(""), in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {CaseName: Name(""), in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
+ {CaseName: Name(""), in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {CaseName: Name(""), in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
+ {CaseName: Name(""), in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {CaseName: Name(""), in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
+ {CaseName: Name(""), in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {CaseName: Name(""), in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
// array tests
- {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
- {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
- {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
- {in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},
+ {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
+ {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
+ {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
+ {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},
// empty array to interface test
- {in: `[]`, ptr: new([]any), out: []any{}},
- {in: `null`, ptr: new([]any), out: []any(nil)},
- {in: `{"T":[]}`, ptr: new(map[string]any), out: map[string]any{"T": []any{}}},
- {in: `{"T":null}`, ptr: new(map[string]any), out: map[string]any{"T": any(nil)}},
+ {CaseName: Name(""), in: `[]`, ptr: new([]any), out: []any{}},
+ {CaseName: Name(""), in: `null`, ptr: new([]any), out: []any(nil)},
+ {CaseName: Name(""), in: `{"T":[]}`, ptr: new(map[string]any), out: map[string]any{"T": []any{}}},
+ {CaseName: Name(""), in: `{"T":null}`, ptr: new(map[string]any), out: map[string]any{"T": any(nil)}},
// composite tests
- {in: allValueIndent, ptr: new(All), out: allValue},
- {in: allValueCompact, ptr: new(All), out: allValue},
- {in: allValueIndent, ptr: new(*All), out: &allValue},
- {in: allValueCompact, ptr: new(*All), out: &allValue},
- {in: pallValueIndent, ptr: new(All), out: pallValue},
- {in: pallValueCompact, ptr: new(All), out: pallValue},
- {in: pallValueIndent, ptr: new(*All), out: &pallValue},
- {in: pallValueCompact, ptr: new(*All), out: &pallValue},
+ {CaseName: Name(""), in: allValueIndent, ptr: new(All), out: allValue},
+ {CaseName: Name(""), in: allValueCompact, ptr: new(All), out: allValue},
+ {CaseName: Name(""), in: allValueIndent, ptr: new(*All), out: &allValue},
+ {CaseName: Name(""), in: allValueCompact, ptr: new(*All), out: &allValue},
+ {CaseName: Name(""), in: pallValueIndent, ptr: new(All), out: pallValue},
+ {CaseName: Name(""), in: pallValueCompact, ptr: new(All), out: pallValue},
+ {CaseName: Name(""), in: pallValueIndent, ptr: new(*All), out: &pallValue},
+ {CaseName: Name(""), in: pallValueCompact, ptr: new(*All), out: &pallValue},
// unmarshal interface test
- {in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
- {in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},
- {in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},
- {in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},
- {in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},
+ {CaseName: Name(""), in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
+ {CaseName: Name(""), in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},
+ {CaseName: Name(""), in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},
+ {CaseName: Name(""), in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},
+ {CaseName: Name(""), in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},
// UnmarshalText interface test
- {in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},
- {in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},
- {in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},
- {in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},
- {in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},
+ {CaseName: Name(""), in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},
+ {CaseName: Name(""), in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},
+ {CaseName: Name(""), in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},
+ {CaseName: Name(""), in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},
+ {CaseName: Name(""), in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},
// integer-keyed map test
{
- in: `{"-1":"a","0":"b","1":"c"}`,
- ptr: new(map[int]string),
- out: map[int]string{-1: "a", 0: "b", 1: "c"},
+ CaseName: Name(""),
+ in: `{"-1":"a","0":"b","1":"c"}`,
+ ptr: new(map[int]string),
+ out: map[int]string{-1: "a", 0: "b", 1: "c"},
},
{
- in: `{"0":"a","10":"c","9":"b"}`,
- ptr: new(map[u8]string),
- out: map[u8]string{0: "a", 9: "b", 10: "c"},
+ CaseName: Name(""),
+ in: `{"0":"a","10":"c","9":"b"}`,
+ ptr: new(map[u8]string),
+ out: map[u8]string{0: "a", 9: "b", 10: "c"},
},
{
- in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
- ptr: new(map[int64]string),
- out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
+ CaseName: Name(""),
+ in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
+ ptr: new(map[int64]string),
+ out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
},
{
- in: `{"18446744073709551615":"max"}`,
- ptr: new(map[uint64]string),
- out: map[uint64]string{math.MaxUint64: "max"},
+ CaseName: Name(""),
+ in: `{"18446744073709551615":"max"}`,
+ ptr: new(map[uint64]string),
+ out: map[uint64]string{math.MaxUint64: "max"},
},
{
- in: `{"0":false,"10":true}`,
- ptr: new(map[uintptr]bool),
- out: map[uintptr]bool{0: false, 10: true},
+ CaseName: Name(""),
+ in: `{"0":false,"10":true}`,
+ ptr: new(map[uintptr]bool),
+ out: map[uintptr]bool{0: false, 10: true},
},
// Check that MarshalText and UnmarshalText take precedence
// over default integer handling in map keys.
{
- in: `{"u2":4}`,
- ptr: new(map[u8marshal]int),
- out: map[u8marshal]int{2: 4},
+ CaseName: Name(""),
+ in: `{"u2":4}`,
+ ptr: new(map[u8marshal]int),
+ out: map[u8marshal]int{2: 4},
},
{
- in: `{"2":4}`,
- ptr: new(map[u8marshal]int),
- err: errMissingU8Prefix,
+ CaseName: Name(""),
+ in: `{"2":4}`,
+ ptr: new(map[u8marshal]int),
+ err: errMissingU8Prefix,
},
// integer-keyed map errors
{
- in: `{"abc":"abc"}`,
- ptr: new(map[int]string),
- err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Offset: 2},
+ CaseName: Name(""),
+ in: `{"abc":"abc"}`,
+ ptr: new(map[int]string),
+ err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Offset: 2},
},
{
- in: `{"256":"abc"}`,
- ptr: new(map[uint8]string),
- err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Offset: 2},
+ CaseName: Name(""),
+ in: `{"256":"abc"}`,
+ ptr: new(map[uint8]string),
+ err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Offset: 2},
},
{
- in: `{"128":"abc"}`,
- ptr: new(map[int8]string),
- err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Offset: 2},
+ CaseName: Name(""),
+ in: `{"128":"abc"}`,
+ ptr: new(map[int8]string),
+ err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Offset: 2},
},
{
- in: `{"-1":"abc"}`,
- ptr: new(map[uint8]string),
- err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Offset: 2},
+ CaseName: Name(""),
+ in: `{"-1":"abc"}`,
+ ptr: new(map[uint8]string),
+ err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Offset: 2},
},
{
- in: `{"F":{"a":2,"3":4}}`,
- ptr: new(map[string]map[int]int),
- err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Offset: 7},
+ CaseName: Name(""),
+ in: `{"F":{"a":2,"3":4}}`,
+ ptr: new(map[string]map[int]int),
+ err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Offset: 7},
},
{
- in: `{"F":{"a":2,"3":4}}`,
- ptr: new(map[string]map[uint]int),
- err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Offset: 7},
+ CaseName: Name(""),
+ in: `{"F":{"a":2,"3":4}}`,
+ ptr: new(map[string]map[uint]int),
+ err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Offset: 7},
},
// Map keys can be encoding.TextUnmarshalers.
- {in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
+ {CaseName: Name(""), in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
// If multiple values for the same key exists, only the most recent value is used.
- {in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
+ {CaseName: Name(""), in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
{
+ CaseName: Name(""),
in: `{
"Level0": 1,
"Level1b": 2,
},
},
{
- in: `{"hello": 1}`,
- ptr: new(Ambig),
- out: Ambig{First: 1},
+ CaseName: Name(""),
+ in: `{"hello": 1}`,
+ ptr: new(Ambig),
+ out: Ambig{First: 1},
},
{
- in: `{"X": 1,"Y":2}`,
- ptr: new(S5),
- out: S5{S8: S8{S9: S9{Y: 2}}},
+ CaseName: Name(""),
+ in: `{"X": 1,"Y":2}`,
+ ptr: new(S5),
+ out: S5{S8: S8{S9: S9{Y: 2}}},
},
{
+ CaseName: Name(""),
in: `{"X": 1,"Y":2}`,
ptr: new(S5),
err: fmt.Errorf("json: unknown field \"X\""),
disallowUnknownFields: true,
},
{
- in: `{"X": 1,"Y":2}`,
- ptr: new(S10),
- out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
+ CaseName: Name(""),
+ in: `{"X": 1,"Y":2}`,
+ ptr: new(S10),
+ out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
},
{
+ CaseName: Name(""),
in: `{"X": 1,"Y":2}`,
ptr: new(S10),
err: fmt.Errorf("json: unknown field \"X\""),
disallowUnknownFields: true,
},
{
- in: `{"I": 0, "I": null, "J": null}`,
- ptr: new(DoublePtr),
- out: DoublePtr{I: nil, J: nil},
+ CaseName: Name(""),
+ in: `{"I": 0, "I": null, "J": null}`,
+ ptr: new(DoublePtr),
+ out: DoublePtr{I: nil, J: nil},
},
// invalid UTF-8 is coerced to valid UTF-8.
{
- in: "\"hello\xffworld\"",
- ptr: new(string),
- out: "hello\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\xffworld\"",
+ ptr: new(string),
+ out: "hello\ufffdworld",
},
{
- in: "\"hello\xc2\xc2world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\xc2\xc2world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
},
{
- in: "\"hello\xc2\xffworld\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\xc2\xffworld\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
},
{
- in: "\"hello\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffdworld",
},
{
- in: "\"hello\\ud800\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\\ud800\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
},
{
- in: "\"hello\\ud800\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\\ud800\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
},
{
- in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
+ CaseName: Name(""),
+ in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
},
// Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.
{
- in: `{"2009-11-10T23:00:00Z": "hello world"}`,
- ptr: new(map[time.Time]string),
- out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
+ CaseName: Name(""),
+ in: `{"2009-11-10T23:00:00Z": "hello world"}`,
+ ptr: new(map[time.Time]string),
+ out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
},
// issue 8305
{
- in: `{"2009-11-10T23:00:00Z": "hello world"}`,
- ptr: new(map[Point]string),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[Point]string](), Offset: 1},
+ CaseName: Name(""),
+ in: `{"2009-11-10T23:00:00Z": "hello world"}`,
+ ptr: new(map[Point]string),
+ err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[Point]string](), Offset: 1},
},
{
- in: `{"asdf": "hello world"}`,
- ptr: new(map[unmarshaler]string),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[unmarshaler]string](), Offset: 1},
+ CaseName: Name(""),
+ in: `{"asdf": "hello world"}`,
+ ptr: new(map[unmarshaler]string),
+ err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[unmarshaler]string](), Offset: 1},
},
// related to issue 13783.
// successfully unmarshaled. The custom unmarshalers were accessible in earlier
// versions of Go, even though the custom marshaler was not.
{
- in: `"AQID"`,
- ptr: new([]byteWithMarshalJSON),
- out: []byteWithMarshalJSON{1, 2, 3},
+ CaseName: Name(""),
+ in: `"AQID"`,
+ ptr: new([]byteWithMarshalJSON),
+ out: []byteWithMarshalJSON{1, 2, 3},
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithMarshalJSON),
- out: []byteWithMarshalJSON{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithMarshalJSON),
+ out: []byteWithMarshalJSON{1, 2, 3},
+ golden: true,
},
{
- in: `"AQID"`,
- ptr: new([]byteWithMarshalText),
- out: []byteWithMarshalText{1, 2, 3},
+ CaseName: Name(""),
+ in: `"AQID"`,
+ ptr: new([]byteWithMarshalText),
+ out: []byteWithMarshalText{1, 2, 3},
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithMarshalText),
- out: []byteWithMarshalText{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithMarshalText),
+ out: []byteWithMarshalText{1, 2, 3},
+ golden: true,
},
{
- in: `"AQID"`,
- ptr: new([]byteWithPtrMarshalJSON),
- out: []byteWithPtrMarshalJSON{1, 2, 3},
+ CaseName: Name(""),
+ in: `"AQID"`,
+ ptr: new([]byteWithPtrMarshalJSON),
+ out: []byteWithPtrMarshalJSON{1, 2, 3},
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithPtrMarshalJSON),
- out: []byteWithPtrMarshalJSON{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithPtrMarshalJSON),
+ out: []byteWithPtrMarshalJSON{1, 2, 3},
+ golden: true,
},
{
- in: `"AQID"`,
- ptr: new([]byteWithPtrMarshalText),
- out: []byteWithPtrMarshalText{1, 2, 3},
+ CaseName: Name(""),
+ in: `"AQID"`,
+ ptr: new([]byteWithPtrMarshalText),
+ out: []byteWithPtrMarshalText{1, 2, 3},
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithPtrMarshalText),
- out: []byteWithPtrMarshalText{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithPtrMarshalText),
+ out: []byteWithPtrMarshalText{1, 2, 3},
+ golden: true,
},
// ints work with the marshaler but not the base64 []byte case
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithMarshalJSON),
- out: []intWithMarshalJSON{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithMarshalJSON),
+ out: []intWithMarshalJSON{1, 2, 3},
+ golden: true,
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithMarshalText),
- out: []intWithMarshalText{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithMarshalText),
+ out: []intWithMarshalText{1, 2, 3},
+ golden: true,
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithPtrMarshalJSON),
- out: []intWithPtrMarshalJSON{1, 2, 3},
- golden: true,
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithPtrMarshalJSON),
+ out: []intWithPtrMarshalJSON{1, 2, 3},
+ golden: true,
},
{
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithPtrMarshalText),
- out: []intWithPtrMarshalText{1, 2, 3},
- golden: true,
- },
-
- {in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
- {in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
- {in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
- {in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
- {in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
- {in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
- {in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
- {in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
- {in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
- {in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
- {in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
+ CaseName: Name(""),
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithPtrMarshalText),
+ out: []intWithPtrMarshalText{1, 2, 3},
+ golden: true,
+ },
+
+ {CaseName: Name(""), in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
+ {CaseName: Name(""), in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
+ {CaseName: Name(""), in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
+ {CaseName: Name(""), in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
+ {CaseName: Name(""), in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
+ {CaseName: Name(""), in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
+ {CaseName: Name(""), in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
+ {CaseName: Name(""), in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
+ {CaseName: Name(""), in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
+ {CaseName: Name(""), in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
+ {CaseName: Name(""), in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
{
- in: `{"V": {"F2": "hello"}}`,
- ptr: new(VOuter),
+ CaseName: Name(""),
+ in: `{"V": {"F2": "hello"}}`,
+ ptr: new(VOuter),
err: &UnmarshalTypeError{
Value: "string",
Struct: "V",
},
},
{
- in: `{"V": {"F4": {}, "F2": "hello"}}`,
- ptr: new(VOuter),
+ CaseName: Name(""),
+ in: `{"V": {"F4": {}, "F2": "hello"}}`,
+ ptr: new(VOuter),
err: &UnmarshalTypeError{
Value: "string",
Struct: "V",
// issue 15146.
// invalid inputs in wrongStringTests below.
- {in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
- {in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
- {in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
- {in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
- {in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
- {in: `{"B": "null"}`, ptr: new(B), out: B{false}},
- {in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
- {in: `{"B": [2, 3]}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal unquoted value into bool`)},
+ {CaseName: Name(""), in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
+ {CaseName: Name(""), in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
+ {CaseName: Name(""), in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
+ {CaseName: Name(""), in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
+ {CaseName: Name(""), in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
+ {CaseName: Name(""), in: `{"B": "null"}`, ptr: new(B), out: B{false}},
+ {CaseName: Name(""), in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
+ {CaseName: Name(""), in: `{"B": [2, 3]}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal unquoted value into bool`)},
// additional tests for disallowUnknownFields
{
+ CaseName: Name(""),
in: `{
"Level0": 1,
"Level1b": 2,
disallowUnknownFields: true,
},
{
+ CaseName: Name(""),
in: `{
"Level0": 1,
"Level1b": 2,
// issue 26444
// UnmarshalTypeError without field & struct values
{
- in: `{"data":{"test1": "bob", "test2": 123}}`,
- ptr: new(mapStringToStringData),
- err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 37, Struct: "mapStringToStringData", Field: "data"},
+ CaseName: Name(""),
+ in: `{"data":{"test1": "bob", "test2": 123}}`,
+ ptr: new(mapStringToStringData),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 37, Struct: "mapStringToStringData", Field: "data"},
},
{
- in: `{"data":{"test1": 123, "test2": "bob"}}`,
- ptr: new(mapStringToStringData),
- err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 21, Struct: "mapStringToStringData", Field: "data"},
+ CaseName: Name(""),
+ in: `{"data":{"test1": 123, "test2": "bob"}}`,
+ ptr: new(mapStringToStringData),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 21, Struct: "mapStringToStringData", Field: "data"},
},
// trying to decode JSON arrays or objects via TextUnmarshaler
{
- in: `[1, 2, 3]`,
- ptr: new(MustNotUnmarshalText),
- err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
+ CaseName: Name(""),
+ in: `[1, 2, 3]`,
+ ptr: new(MustNotUnmarshalText),
+ err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
},
{
- in: `{"foo": "bar"}`,
- ptr: new(MustNotUnmarshalText),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
+ CaseName: Name(""),
+ in: `{"foo": "bar"}`,
+ ptr: new(MustNotUnmarshalText),
+ err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
},
// #22369
{
- in: `{"PP": {"T": {"Y": "bad-type"}}}`,
- ptr: new(P),
+ CaseName: Name(""),
+ in: `{"PP": {"T": {"Y": "bad-type"}}}`,
+ ptr: new(P),
err: &UnmarshalTypeError{
Value: "string",
Struct: "T",
},
},
{
- in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`,
- ptr: new(PP),
+ CaseName: Name(""),
+ in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`,
+ ptr: new(PP),
err: &UnmarshalTypeError{
Value: "string",
Struct: "T",
},
// #14702
{
- in: `invalid`,
- ptr: new(Number),
+ CaseName: Name(""),
+ in: `invalid`,
+ ptr: new(Number),
err: &SyntaxError{
msg: "invalid character 'i' looking for beginning of value",
Offset: 1,
},
},
{
- in: `"invalid"`,
- ptr: new(Number),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ CaseName: Name(""),
+ in: `"invalid"`,
+ ptr: new(Number),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
},
{
- in: `{"A":"invalid"}`,
- ptr: new(struct{ A Number }),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ CaseName: Name(""),
+ in: `{"A":"invalid"}`,
+ ptr: new(struct{ A Number }),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
},
{
- in: `{"A":"invalid"}`,
+ CaseName: Name(""),
+ in: `{"A":"invalid"}`,
ptr: new(struct {
A Number `json:",string"`
}),
err: fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into json.Number", `invalid`),
},
{
- in: `{"A":"invalid"}`,
- ptr: new(map[string]Number),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ CaseName: Name(""),
+ in: `{"A":"invalid"}`,
+ ptr: new(map[string]Number),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
},
}
func TestMarshal(t *testing.T) {
b, err := Marshal(allValue)
if err != nil {
- t.Fatalf("Marshal allValue: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if string(b) != allValueCompact {
- t.Errorf("Marshal allValueCompact")
+ t.Errorf("Marshal:")
diff(t, b, []byte(allValueCompact))
return
}
b, err = Marshal(pallValue)
if err != nil {
- t.Fatalf("Marshal pallValue: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if string(b) != pallValueCompact {
- t.Errorf("Marshal pallValueCompact")
+ t.Errorf("Marshal:")
diff(t, b, []byte(pallValueCompact))
return
}
}
-var badUTF8 = []struct {
- in, out string
-}{
- {"hello\xffworld", `"hello\ufffdworld"`},
- {"", `""`},
- {"\xff", `"\ufffd"`},
- {"\xff\xff", `"\ufffd\ufffd"`},
- {"a\xffb", `"a\ufffdb"`},
- {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
-}
-
-func TestMarshalBadUTF8(t *testing.T) {
- for _, tt := range badUTF8 {
- b, err := Marshal(tt.in)
- if string(b) != tt.out || err != nil {
- t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
- }
+func TestMarshalInvalidUTF8(t *testing.T) {
+ tests := []struct {
+ CaseName
+ in string
+ want string
+ }{
+ {Name(""), "hello\xffworld", `"hello\ufffdworld"`},
+ {Name(""), "", `""`},
+ {Name(""), "\xff", `"\ufffd"`},
+ {Name(""), "\xff\xff", `"\ufffd\ufffd"`},
+ {Name(""), "a\xffb", `"a\ufffdb"`},
+ {Name(""), "\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ got, err := Marshal(tt.in)
+ if string(got) != tt.want || err != nil {
+ t.Errorf("%s: Marshal(%q):\n\tgot: (%q, %v)\n\twant: (%q, nil)", tt.Where, tt.in, got, err, tt.want)
+ }
+ })
}
}
var n Number
out, err := Marshal(n)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Marshal error: %v", err)
}
- outStr := string(out)
- if outStr != "0" {
- t.Fatalf("Invalid zero val for Number: %q", outStr)
+ got := string(out)
+ if got != "0" {
+ t.Fatalf("Marshal: got %s, want 0", got)
}
}
Q: 18,
},
}
- b, err := Marshal(top)
+ got, err := Marshal(top)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Marshal error: %v", err)
}
want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}"
- if string(b) != want {
- t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
+ if string(got) != want {
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
func equalError(a, b error) bool {
- if a == nil {
- return b == nil
- }
- if b == nil {
- return a == nil
+ if a == nil || b == nil {
+ return a == nil && b == nil
}
return a.Error() == b.Error()
}
func TestUnmarshal(t *testing.T) {
- for i, tt := range unmarshalTests {
- var scan scanner
- in := []byte(tt.in)
- if err := checkValid(in, &scan); err != nil {
- if !equalError(err, tt.err) {
- t.Errorf("#%d: checkValid: %#v", i, err)
- continue
+ for _, tt := range unmarshalTests {
+ t.Run(tt.Name, func(t *testing.T) {
+ in := []byte(tt.in)
+ var scan scanner
+ if err := checkValid(in, &scan); err != nil {
+ if !equalError(err, tt.err) {
+ t.Fatalf("%s: checkValid error: %#v", tt.Where, err)
+ }
+ }
+ if tt.ptr == nil {
+ return
}
- }
- if tt.ptr == nil {
- continue
- }
-
- typ := reflect.TypeOf(tt.ptr)
- if typ.Kind() != reflect.Pointer {
- t.Errorf("#%d: unmarshalTest.ptr %T is not a pointer type", i, tt.ptr)
- continue
- }
- typ = typ.Elem()
-
- // v = new(right-type)
- v := reflect.New(typ)
-
- if !reflect.DeepEqual(tt.ptr, v.Interface()) {
- // There's no reason for ptr to point to non-zero data,
- // as we decode into new(right-type), so the data is
- // discarded.
- // This can easily mean tests that silently don't test
- // what they should. To test decoding into existing
- // data, see TestPrefilled.
- t.Errorf("#%d: unmarshalTest.ptr %#v is not a pointer to a zero value", i, tt.ptr)
- continue
- }
-
- dec := NewDecoder(bytes.NewReader(in))
- if tt.useNumber {
- dec.UseNumber()
- }
- if tt.disallowUnknownFields {
- dec.DisallowUnknownFields()
- }
- if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
- t.Errorf("#%d: %v, want %v", i, err, tt.err)
- continue
- } else if err != nil {
- continue
- }
- if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
- t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
- data, _ := Marshal(v.Elem().Interface())
- println(string(data))
- data, _ = Marshal(tt.out)
- println(string(data))
- continue
- }
- // Check round trip also decodes correctly.
- if tt.err == nil {
- enc, err := Marshal(v.Interface())
- if err != nil {
- t.Errorf("#%d: error re-marshaling: %v", i, err)
- continue
+ typ := reflect.TypeOf(tt.ptr)
+ if typ.Kind() != reflect.Pointer {
+ t.Fatalf("%s: unmarshalTest.ptr %T is not a pointer type", tt.Where, tt.ptr)
}
- if tt.golden && !bytes.Equal(enc, in) {
- t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
+ typ = typ.Elem()
+
+ // v = new(right-type)
+ v := reflect.New(typ)
+
+ if !reflect.DeepEqual(tt.ptr, v.Interface()) {
+ // There's no reason for ptr to point to non-zero data,
+ // as we decode into new(right-type), so the data is
+ // discarded.
+ // This can easily mean tests that silently don't test
+ // what they should. To test decoding into existing
+ // data, see TestPrefilled.
+ t.Fatalf("%s: unmarshalTest.ptr %#v is not a pointer to a zero value", tt.Where, tt.ptr)
}
- vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec = NewDecoder(bytes.NewReader(enc))
+
+ dec := NewDecoder(bytes.NewReader(in))
if tt.useNumber {
dec.UseNumber()
}
- if err := dec.Decode(vv.Interface()); err != nil {
- t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
- continue
+ if tt.disallowUnknownFields {
+ dec.DisallowUnknownFields()
}
- if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
- t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
- t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
- t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
- continue
+ if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
+ t.Fatalf("%s: Decode error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
+ } else if err != nil {
+ return
}
- }
+ if got := v.Elem().Interface(); !reflect.DeepEqual(got, tt.out) {
+ gotJSON, _ := Marshal(got)
+ wantJSON, _ := Marshal(tt.out)
+ t.Fatalf("%s: Decode:\n\tgot: %#+v\n\twant: %#+v\n\n\tgotJSON: %s\n\twantJSON: %s", tt.Where, got, tt.out, gotJSON, wantJSON)
+ }
+
+ // Check round trip also decodes correctly.
+ if tt.err == nil {
+ enc, err := Marshal(v.Interface())
+ if err != nil {
+ t.Fatalf("%s: Marshal error after roundtrip: %v", tt.Where, err)
+ }
+ if tt.golden && !bytes.Equal(enc, in) {
+ t.Errorf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, enc, in)
+ }
+ vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
+ dec = NewDecoder(bytes.NewReader(enc))
+ if tt.useNumber {
+ dec.UseNumber()
+ }
+ if err := dec.Decode(vv.Interface()); err != nil {
+ t.Fatalf("%s: Decode(%#q) error after roundtrip: %v", tt.Where, enc, err)
+ }
+ if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
+ t.Fatalf("%s: Decode:\n\tgot: %#+v\n\twant: %#+v\n\n\tgotJSON: %s\n\twantJSON: %s",
+ tt.Where, v.Elem().Interface(), vv.Elem().Interface(),
+ stripWhitespace(string(enc)), stripWhitespace(string(in)))
+ }
+ }
+ })
}
}
initBig()
var v any
if err := Unmarshal(jsonBig, &v); err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
b, err := Marshal(v)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if !bytes.Equal(jsonBig, b) {
- t.Errorf("Marshal jsonBig")
+ t.Errorf("Marshal:")
diff(t, b, jsonBig)
return
}
}
-var numberTests = []struct {
- in string
- i int64
- intErr string
- f float64
- floatErr string
-}{
- {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
- {in: "-12", i: -12, f: -12.0},
- {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
-}
-
// Independent of Decode, basic coverage of the accessors in Number
func TestNumberAccessors(t *testing.T) {
- for _, tt := range numberTests {
- n := Number(tt.in)
- if s := n.String(); s != tt.in {
- t.Errorf("Number(%q).String() is %q", tt.in, s)
- }
- if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
- t.Errorf("Number(%q).Int64() is %d", tt.in, i)
- } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
- t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err)
- }
- if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
- t.Errorf("Number(%q).Float64() is %g", tt.in, f)
- } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {
- t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err)
- }
+ tests := []struct {
+ CaseName
+ in string
+ i int64
+ intErr string
+ f float64
+ floatErr string
+ }{
+ {CaseName: Name(""), in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
+ {CaseName: Name(""), in: "-12", i: -12, f: -12.0},
+ {CaseName: Name(""), in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ n := Number(tt.in)
+ if got := n.String(); got != tt.in {
+ t.Errorf("%s: Number(%q).String() = %s, want %s", tt.Where, tt.in, got, tt.in)
+ }
+ if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
+ t.Errorf("%s: Number(%q).Int64() = %d, want %d", tt.Where, tt.in, i, tt.i)
+ } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
+ t.Errorf("%s: Number(%q).Int64() error:\n\tgot: %v\n\twant: %v", tt.Where, tt.in, err, tt.intErr)
+ }
+ if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
+ t.Errorf("%s: Number(%q).Float64() = %g, want %g", tt.Where, tt.in, f, tt.f)
+ } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {
+ t.Errorf("%s: Number(%q).Float64() error:\n\tgot %v\n\twant: %v", tt.Where, tt.in, err, tt.floatErr)
+ }
+ })
}
}
}
b, err := Marshal(s0)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
var s1 []byte
if err := Unmarshal(b, &s1); err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if !bytes.Equal(s0, s1) {
- t.Errorf("Marshal large byte slice")
+ t.Errorf("Marshal:")
diff(t, s0, s1)
}
}
var xint Xint
var i any = &xint
if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if xint.X != 1 {
- t.Fatalf("Did not write to xint")
+ t.Fatalf("xint.X = %d, want 1", xint.X)
}
}
t.Fatalf("Unmarshal: %v", err)
}
if xint.X != 1 {
- t.Fatalf("Did not write to xint")
+ t.Fatalf("xint.X = %d, want 1", xint.X)
}
}
func TestEscape(t *testing.T) {
const input = `"foobar"<html>` + " [\u2028 \u2029]"
- const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
- b, err := Marshal(input)
+ const want = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
+ got, err := Marshal(input)
if err != nil {
t.Fatalf("Marshal error: %v", err)
}
- if s := string(b); s != expected {
- t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
+ if string(got) != want {
+ t.Errorf("Marshal(%#q):\n\tgot: %s\n\twant: %s", input, got, want)
}
}
-// WrongString is a struct that's misusing the ,string modifier.
-type WrongString struct {
- Message string `json:"result,string"`
-}
-
-type wrongStringTest struct {
- in, err string
-}
-
-var wrongStringTests = []wrongStringTest{
- {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
- {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
- {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
- {`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`},
- {`{"result":"\""}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"" into string`},
- {`{"result":"\"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"foo" into string`},
-}
-
// If people misuse the ,string modifier, the error message should be
// helpful, telling the user that they're doing it wrong.
func TestErrorMessageFromMisusedString(t *testing.T) {
- for n, tt := range wrongStringTests {
- r := strings.NewReader(tt.in)
- var s WrongString
- err := NewDecoder(r).Decode(&s)
- got := fmt.Sprintf("%v", err)
- if got != tt.err {
- t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
- }
+ // WrongString is a struct that's misusing the ,string modifier.
+ type WrongString struct {
+ Message string `json:"result,string"`
}
-}
-
-func noSpace(c rune) rune {
- if isSpace(byte(c)) { //only used for ascii
- return -1
+ tests := []struct {
+ CaseName
+ in, err string
+ }{
+ {Name(""), `{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
+ {Name(""), `{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
+ {Name(""), `{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
+ {Name(""), `{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`},
+ {Name(""), `{"result":"\""}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"" into string`},
+ {Name(""), `{"result":"\"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"foo" into string`},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ r := strings.NewReader(tt.in)
+ var s WrongString
+ err := NewDecoder(r).Decode(&s)
+ got := fmt.Sprintf("%v", err)
+ if got != tt.err {
+ t.Errorf("%s: Decode error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.err)
+ }
+ })
}
- return c
}
type All struct {
"PInterface": null
}`
-var allValueCompact = strings.Map(noSpace, allValueIndent)
+var allValueCompact = stripWhitespace(allValueIndent)
var pallValueIndent = `{
"Bool": false,
"PInterface": 5.2
}`
-var pallValueCompact = strings.Map(noSpace, pallValueIndent)
+var pallValueCompact = stripWhitespace(pallValueIndent)
func TestRefUnmarshal(t *testing.T) {
type S struct {
var got S
if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if !reflect.DeepEqual(got, want) {
- t.Errorf("got %+v, want %+v", got, want)
+ t.Errorf("Unmarsha:\n\tgot: %+v\n\twant: %+v", got, want)
}
}
}
data := `{"Number1":"1", "Number2":""}`
dec := NewDecoder(strings.NewReader(data))
- var t2 T2
- err := dec.Decode(&t2)
- if err == nil {
- t.Fatal("Decode: did not return error")
- }
- if t2.Number1 != 1 {
- t.Fatal("Decode: did not set Number1")
+ var got T2
+ switch err := dec.Decode(&got); {
+ case err == nil:
+ t.Fatalf("Decode error: got nil, want non-nil")
+ case got.Number1 != 1:
+ t.Fatalf("Decode: got.Number1 = %d, want 1", got.Number1)
}
}
s.B = 1
s.C = new(int)
*s.C = 2
- err := Unmarshal(data, &s)
- if err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if s.B != 1 || s.C != nil {
- t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C)
+ switch err := Unmarshal(data, &s); {
+ case err != nil:
+ t.Fatalf("Unmarshal error: %v", err)
+ case s.B != 1:
+ t.Fatalf("Unmarshal: s.B = %d, want 1", s.B)
+ case s.C != nil:
+ t.Fatalf("Unmarshal: s.C = %d, want non-nil", s.C)
}
}
return pp
}
-var interfaceSetTests = []struct {
- pre any
- json string
- post any
-}{
- {"foo", `"bar"`, "bar"},
- {"foo", `2`, 2.0},
- {"foo", `true`, true},
- {"foo", `null`, nil},
-
- {nil, `null`, nil},
- {new(int), `null`, nil},
- {(*int)(nil), `null`, nil},
- {new(*int), `null`, new(*int)},
- {(**int)(nil), `null`, nil},
- {intp(1), `null`, nil},
- {intpp(nil), `null`, intpp(nil)},
- {intpp(intp(1)), `null`, intpp(nil)},
-}
-
func TestInterfaceSet(t *testing.T) {
- for _, tt := range interfaceSetTests {
- b := struct{ X any }{tt.pre}
- blob := `{"X":` + tt.json + `}`
- if err := Unmarshal([]byte(blob), &b); err != nil {
- t.Errorf("Unmarshal %#q: %v", blob, err)
- continue
- }
- if !reflect.DeepEqual(b.X, tt.post) {
- t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
- }
+ tests := []struct {
+ CaseName
+ pre any
+ json string
+ post any
+ }{
+ {Name(""), "foo", `"bar"`, "bar"},
+ {Name(""), "foo", `2`, 2.0},
+ {Name(""), "foo", `true`, true},
+ {Name(""), "foo", `null`, nil},
+
+ {Name(""), nil, `null`, nil},
+ {Name(""), new(int), `null`, nil},
+ {Name(""), (*int)(nil), `null`, nil},
+ {Name(""), new(*int), `null`, new(*int)},
+ {Name(""), (**int)(nil), `null`, nil},
+ {Name(""), intp(1), `null`, nil},
+ {Name(""), intpp(nil), `null`, intpp(nil)},
+ {Name(""), intpp(intp(1)), `null`, intpp(nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ b := struct{ X any }{tt.pre}
+ blob := `{"X":` + tt.json + `}`
+ if err := Unmarshal([]byte(blob), &b); err != nil {
+ t.Fatalf("%s: Unmarshal(%#q) error: %v", tt.Where, blob, err)
+ }
+ if !reflect.DeepEqual(b.X, tt.post) {
+ t.Errorf("%s: Unmarshal(%#q):\n\tpre.X: %#v\n\tgot.X: %#v\n\twant.X: %#v", tt.Where, blob, tt.pre, b.X, tt.post)
+ }
+ })
}
}
func TestStringKind(t *testing.T) {
type stringKind string
-
- var m1, m2 map[stringKind]int
- m1 = map[stringKind]int{
- "foo": 42,
- }
-
- data, err := Marshal(m1)
+ want := map[stringKind]int{"foo": 42}
+ data, err := Marshal(want)
if err != nil {
- t.Errorf("Unexpected error marshaling: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
-
- err = Unmarshal(data, &m2)
+ var got map[stringKind]int
+ err = Unmarshal(data, &got)
if err != nil {
- t.Errorf("Unexpected error unmarshaling: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
-
- if !reflect.DeepEqual(m1, m2) {
- t.Error("Items should be equal after encoding and then decoding")
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("Marshal/Unmarshal mismatch:\n\tgot: %v\n\twant: %v", got, want)
}
}
// Issue 8962.
func TestByteKind(t *testing.T) {
type byteKind []byte
-
- a := byteKind("hello")
-
- data, err := Marshal(a)
+ want := byteKind("hello")
+ data, err := Marshal(want)
if err != nil {
- t.Error(err)
+ t.Fatalf("Marshal error: %v", err)
}
- var b byteKind
- err = Unmarshal(data, &b)
+ var got byteKind
+ err = Unmarshal(data, &got)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Unmarshal error: %v", err)
}
- if !reflect.DeepEqual(a, b) {
- t.Errorf("expected %v == %v", a, b)
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("Marshal/Unmarshal mismatch:\n\tgot: %v\n\twant: %v", got, want)
}
}
// Issue 12921.
func TestSliceOfCustomByte(t *testing.T) {
type Uint8 uint8
-
- a := []Uint8("hello")
-
- data, err := Marshal(a)
+ want := []Uint8("hello")
+ data, err := Marshal(want)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Marshal error: %v", err)
}
- var b []Uint8
- err = Unmarshal(data, &b)
+ var got []Uint8
+ err = Unmarshal(data, &got)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Unmarshal error: %v", err)
}
- if !reflect.DeepEqual(a, b) {
- t.Fatalf("expected %v == %v", a, b)
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("Marshal/Unmarshal mismatch:\n\tgot: %v\n\twant: %v", got, want)
}
}
-var decodeTypeErrorTests = []struct {
- dest any
- src string
-}{
- {new(string), `{"user": "name"}`}, // issue 4628.
- {new(error), `{}`}, // issue 4222
- {new(error), `[]`},
- {new(error), `""`},
- {new(error), `123`},
- {new(error), `true`},
-}
-
func TestUnmarshalTypeError(t *testing.T) {
- for _, item := range decodeTypeErrorTests {
- err := Unmarshal([]byte(item.src), item.dest)
- if _, ok := err.(*UnmarshalTypeError); !ok {
- t.Errorf("expected type error for Unmarshal(%q, type %T): got %T",
- item.src, item.dest, err)
- }
+ tests := []struct {
+ CaseName
+ dest any
+ in string
+ }{
+ {Name(""), new(string), `{"user": "name"}`}, // issue 4628.
+ {Name(""), new(error), `{}`}, // issue 4222
+ {Name(""), new(error), `[]`},
+ {Name(""), new(error), `""`},
+ {Name(""), new(error), `123`},
+ {Name(""), new(error), `true`},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ err := Unmarshal([]byte(tt.in), tt.dest)
+ if _, ok := err.(*UnmarshalTypeError); !ok {
+ t.Errorf("%s: Unmarshal(%#q, %T):\n\tgot: %T\n\twant: %T",
+ tt.Where, tt.in, tt.dest, err, new(UnmarshalTypeError))
+ }
+ })
}
}
-var unmarshalSyntaxTests = []string{
- "tru",
- "fals",
- "nul",
- "123e",
- `"hello`,
- `[1,2,3`,
- `{"key":1`,
- `{"key":1,`,
-}
-
func TestUnmarshalSyntax(t *testing.T) {
var x any
- for _, src := range unmarshalSyntaxTests {
- err := Unmarshal([]byte(src), &x)
- if _, ok := err.(*SyntaxError); !ok {
- t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err)
- }
+ tests := []struct {
+ CaseName
+ in string
+ }{
+ {Name(""), "tru"},
+ {Name(""), "fals"},
+ {Name(""), "nul"},
+ {Name(""), "123e"},
+ {Name(""), `"hello`},
+ {Name(""), `[1,2,3`},
+ {Name(""), `{"key":1`},
+ {Name(""), `{"key":1,`},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ err := Unmarshal([]byte(tt.in), &x)
+ if _, ok := err.(*SyntaxError); !ok {
+ t.Errorf("%s: Unmarshal(%#q, any):\n\tgot: %T\n\twant: %T",
+ tt.Where, tt.in, err, new(SyntaxError))
+ }
+ })
}
}
out := &unexportedFields{}
err := Unmarshal([]byte(input), out)
if err != nil {
- t.Errorf("got error %v, expected nil", err)
+ t.Errorf("Unmarshal error: %v", err)
}
if !reflect.DeepEqual(out, want) {
- t.Errorf("got %q, want %q", out, want)
+ t.Errorf("Unmarshal:\n\tgot: %+v\n\twant: %+v", out, want)
}
}
func TestUnmarshalJSONLiteralError(t *testing.T) {
var t3 Time3339
- err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3)
- if err == nil {
- t.Fatalf("expected error; got time %v", time.Time(t3))
- }
- if !strings.Contains(err.Error(), "range") {
- t.Errorf("got err = %v; want out of range error", err)
+ switch err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3); {
+ case err == nil:
+ t.Fatalf("Unmarshal error: got nil, want non-nil")
+ case !strings.Contains(err.Error(), "range"):
+ t.Errorf("Unmarshal error:\n\tgot: %v\n\twant: out of range", err)
}
}
err := Unmarshal([]byte(json), &dest)
if err != nil {
- t.Errorf("got error %q, want nil", err)
+ t.Errorf("Unmarshal error: %v", err)
}
}
// Issues 4900 and 8837, among others.
func TestPrefilled(t *testing.T) {
// Values here change, cannot reuse table across runs.
- var prefillTests = []struct {
+ tests := []struct {
+ CaseName
in string
ptr any
out any
- }{
- {
- in: `{"X": 1, "Y": 2}`,
- ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
- out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
- },
- {
- in: `{"X": 1, "Y": 2}`,
- ptr: &map[string]any{"X": float32(3), "Y": int16(4), "Z": 1.5},
- out: &map[string]any{"X": float64(1), "Y": float64(2), "Z": 1.5},
- },
- {
- in: `[2]`,
- ptr: &[]int{1},
- out: &[]int{2},
- },
- {
- in: `[2, 3]`,
- ptr: &[]int{1},
- out: &[]int{2, 3},
- },
- {
- in: `[2, 3]`,
- ptr: &[...]int{1},
- out: &[...]int{2},
- },
- {
- in: `[3]`,
- ptr: &[...]int{1, 2},
- out: &[...]int{3, 0},
- },
- }
-
- for _, tt := range prefillTests {
- ptrstr := fmt.Sprintf("%v", tt.ptr)
- err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
- if !reflect.DeepEqual(tt.ptr, tt.out) {
- t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
- }
+ }{{
+ CaseName: Name(""),
+ in: `{"X": 1, "Y": 2}`,
+ ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
+ out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
+ }, {
+ CaseName: Name(""),
+ in: `{"X": 1, "Y": 2}`,
+ ptr: &map[string]any{"X": float32(3), "Y": int16(4), "Z": 1.5},
+ out: &map[string]any{"X": float64(1), "Y": float64(2), "Z": 1.5},
+ }, {
+ CaseName: Name(""),
+ in: `[2]`,
+ ptr: &[]int{1},
+ out: &[]int{2},
+ }, {
+ CaseName: Name(""),
+ in: `[2, 3]`,
+ ptr: &[]int{1},
+ out: &[]int{2, 3},
+ }, {
+ CaseName: Name(""),
+ in: `[2, 3]`,
+ ptr: &[...]int{1},
+ out: &[...]int{2},
+ }, {
+ CaseName: Name(""),
+ in: `[3]`,
+ ptr: &[...]int{1, 2},
+ out: &[...]int{3, 0},
+ }}
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ ptrstr := fmt.Sprintf("%v", tt.ptr)
+ err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
+ if err != nil {
+ t.Errorf("%s: Unmarshal error: %v", tt.Where, err)
+ }
+ if !reflect.DeepEqual(tt.ptr, tt.out) {
+ t.Errorf("%s: Unmarshal(%#q, %T):\n\tgot: %v\n\twant: %v", tt.Where, tt.in, ptrstr, tt.ptr, tt.out)
+ }
+ })
}
}
-var invalidUnmarshalTests = []struct {
- v any
- want string
-}{
- {nil, "json: Unmarshal(nil)"},
- {struct{}{}, "json: Unmarshal(non-pointer struct {})"},
- {(*int)(nil), "json: Unmarshal(nil *int)"},
-}
-
func TestInvalidUnmarshal(t *testing.T) {
buf := []byte(`{"a":"1"}`)
- for _, tt := range invalidUnmarshalTests {
- err := Unmarshal(buf, tt.v)
- if err == nil {
- t.Errorf("Unmarshal expecting error, got nil")
- continue
- }
- if got := err.Error(); got != tt.want {
- t.Errorf("Unmarshal = %q; want %q", got, tt.want)
- }
+ tests := []struct {
+ CaseName
+ v any
+ want string
+ }{
+ {Name(""), nil, "json: Unmarshal(nil)"},
+ {Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
+ {Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ err := Unmarshal(buf, tt.v)
+ if err == nil {
+ t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
+ }
+ if got := err.Error(); got != tt.want {
+ t.Errorf("%s: Unmarshal error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want)
+ }
+ })
}
}
-var invalidUnmarshalTextTests = []struct {
- v any
- want string
-}{
- {nil, "json: Unmarshal(nil)"},
- {struct{}{}, "json: Unmarshal(non-pointer struct {})"},
- {(*int)(nil), "json: Unmarshal(nil *int)"},
- {new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
-}
-
func TestInvalidUnmarshalText(t *testing.T) {
buf := []byte(`123`)
- for _, tt := range invalidUnmarshalTextTests {
- err := Unmarshal(buf, tt.v)
- if err == nil {
- t.Errorf("Unmarshal expecting error, got nil")
- continue
- }
- if got := err.Error(); got != tt.want {
- t.Errorf("Unmarshal = %q; want %q", got, tt.want)
- }
+ tests := []struct {
+ CaseName
+ v any
+ want string
+ }{
+ {Name(""), nil, "json: Unmarshal(nil)"},
+ {Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
+ {Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
+ {Name(""), new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ err := Unmarshal(buf, tt.v)
+ if err == nil {
+ t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
+ }
+ if got := err.Error(); got != tt.want {
+ t.Errorf("%s: Unmarshal error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want)
+ }
+ })
}
}
data, err := Marshal(item)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
err = Unmarshal(data, &item)
if err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
}
)
tests := []struct {
+ CaseName
in string
ptr any
out any
err error
}{{
// Error since we cannot set S1.embed1, but still able to set S1.R.
- in: `{"R":2,"Q":1}`,
- ptr: new(S1),
- out: &S1{R: 2},
- err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"),
+ CaseName: Name(""),
+ in: `{"R":2,"Q":1}`,
+ ptr: new(S1),
+ out: &S1{R: 2},
+ err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"),
}, {
// The top level Q field takes precedence.
- in: `{"Q":1}`,
- ptr: new(S2),
- out: &S2{Q: 1},
+ CaseName: Name(""),
+ in: `{"Q":1}`,
+ ptr: new(S2),
+ out: &S2{Q: 1},
}, {
// No issue with non-pointer variant.
- in: `{"R":2,"Q":1}`,
- ptr: new(S3),
- out: &S3{embed1: embed1{Q: 1}, R: 2},
+ CaseName: Name(""),
+ in: `{"R":2,"Q":1}`,
+ ptr: new(S3),
+ out: &S3{embed1: embed1{Q: 1}, R: 2},
}, {
// No error since both embedded structs have field R, which annihilate each other.
// Thus, no attempt is made at setting S4.embed1.
- in: `{"R":2}`,
- ptr: new(S4),
- out: new(S4),
+ CaseName: Name(""),
+ in: `{"R":2}`,
+ ptr: new(S4),
+ out: new(S4),
}, {
// Error since we cannot set S5.embed1, but still able to set S5.R.
- in: `{"R":2,"Q":1}`,
- ptr: new(S5),
- out: &S5{R: 2},
- err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"),
+ CaseName: Name(""),
+ in: `{"R":2,"Q":1}`,
+ ptr: new(S5),
+ out: &S5{R: 2},
+ err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"),
}, {
// Issue 24152, ensure decodeState.indirect does not panic.
- in: `{"embed1": {"Q": 1}}`,
- ptr: new(S6),
- out: &S6{embed1{1}},
+ CaseName: Name(""),
+ in: `{"embed1": {"Q": 1}}`,
+ ptr: new(S6),
+ out: &S6{embed1{1}},
}, {
// Issue 24153, check that we can still set forwarded fields even in
// the presence of a name conflict.
// it should be impossible for an external package to set either Q.
//
// It is probably okay for a future reflect change to break this.
- in: `{"embed1": {"Q": 1}, "Q": 2}`,
- ptr: new(S7),
- out: &S7{embed1{1}, embed2{2}},
+ CaseName: Name(""),
+ in: `{"embed1": {"Q": 1}, "Q": 2}`,
+ ptr: new(S7),
+ out: &S7{embed1{1}, embed2{2}},
}, {
// Issue 24153, similar to the S7 case.
- in: `{"embed1": {"Q": 1}, "embed2": {"Q": 2}, "Q": 3}`,
- ptr: new(S8),
- out: &S8{embed1{1}, embed2{2}, 3},
+ CaseName: Name(""),
+ in: `{"embed1": {"Q": 1}, "embed2": {"Q": 2}, "Q": 3}`,
+ ptr: new(S8),
+ out: &S8{embed1{1}, embed2{2}, 3},
}, {
// Issue 228145, similar to the cases above.
- in: `{"embed": {}}`,
- ptr: new(S9),
- out: &S9{},
+ CaseName: Name(""),
+ in: `{"embed": {}}`,
+ ptr: new(S9),
+ out: &S9{},
}}
-
- for i, tt := range tests {
- err := Unmarshal([]byte(tt.in), tt.ptr)
- if !equalError(err, tt.err) {
- t.Errorf("#%d: %v, want %v", i, err, tt.err)
- }
- if !reflect.DeepEqual(tt.ptr, tt.out) {
- t.Errorf("#%d: mismatch\ngot: %#+v\nwant: %#+v", i, tt.ptr, tt.out)
- }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ err := Unmarshal([]byte(tt.in), tt.ptr)
+ if !equalError(err, tt.err) {
+ t.Errorf("%s: Unmarshal error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
+ }
+ if !reflect.DeepEqual(tt.ptr, tt.out) {
+ t.Errorf("%s: Unmarshal:\n\tgot: %#+v\n\twant: %#+v", tt.Where, tt.ptr, tt.out)
+ }
+ })
}
}
func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {
tests := []struct {
+ CaseName
in string
err error
}{{
- in: `1 false null :`,
- err: &SyntaxError{"invalid character ':' looking for beginning of value", 14},
+ CaseName: Name(""),
+ in: `1 false null :`,
+ err: &SyntaxError{"invalid character ':' looking for beginning of value", 14},
}, {
- in: `1 [] [,]`,
- err: &SyntaxError{"invalid character ',' looking for beginning of value", 7},
+ CaseName: Name(""),
+ in: `1 [] [,]`,
+ err: &SyntaxError{"invalid character ',' looking for beginning of value", 7},
}, {
- in: `1 [] [true:]`,
- err: &SyntaxError{"invalid character ':' after array element", 11},
+ CaseName: Name(""),
+ in: `1 [] [true:]`,
+ err: &SyntaxError{"invalid character ':' after array element", 11},
}, {
- in: `1 {} {"x"=}`,
- err: &SyntaxError{"invalid character '=' after object key", 14},
+ CaseName: Name(""),
+ in: `1 {} {"x"=}`,
+ err: &SyntaxError{"invalid character '=' after object key", 14},
}, {
- in: `falsetruenul#`,
- err: &SyntaxError{"invalid character '#' in literal null (expecting 'l')", 13},
+ CaseName: Name(""),
+ in: `falsetruenul#`,
+ err: &SyntaxError{"invalid character '#' in literal null (expecting 'l')", 13},
}}
- for i, tt := range tests {
- dec := NewDecoder(strings.NewReader(tt.in))
- var err error
- for {
- var v any
- if err = dec.Decode(&v); err != nil {
- break
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ dec := NewDecoder(strings.NewReader(tt.in))
+ var err error
+ for err == nil {
+ var v any
+ err = dec.Decode(&v)
}
- }
- if !reflect.DeepEqual(err, tt.err) {
- t.Errorf("#%d: got %#v, want %#v", i, err, tt.err)
- }
+ if !reflect.DeepEqual(err, tt.err) {
+ t.Errorf("%s: Decode error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
+ }
+ })
}
}
data := []byte(`{"a": "b"}`)
if err := Unmarshal(data, v); err != nil {
- t.Fatal(err)
+ t.Fatalf("Unmarshal error: %v", err)
}
}
func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
var p map[textUnmarshalerString]string
if err := Unmarshal([]byte(`{"FOO": "1"}`), &p); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if _, ok := p["foo"]; !ok {
- t.Errorf(`Key "foo" does not exist in map: %v`, p)
+ t.Errorf(`key "foo" missing in map: %v`, p)
}
}
// See golang.org/issues/38105.
var p map[textUnmarshalerString]string
if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if _, ok := p["开源"]; !ok {
- t.Errorf(`Key "开源" does not exist in map: %v`, p)
+ t.Errorf(`key "开源" missing in map: %v`, p)
}
// See golang.org/issues/38126.
type T struct {
F1 string `json:"F1,string"`
}
- t1 := T{"aaa\tbbb"}
+ wantT := T{"aaa\tbbb"}
- b, err := Marshal(t1)
+ b, err := Marshal(wantT)
if err != nil {
- t.Fatalf("Marshal unexpected error: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
- var t2 T
- if err := Unmarshal(b, &t2); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
+ var gotT T
+ if err := Unmarshal(b, &gotT); err != nil {
+ t.Fatalf("Unmarshal error: %v", err)
}
- if t1 != t2 {
- t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
+ if gotT != wantT {
+ t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %q\n\twant: %q", gotT, wantT)
}
// See golang.org/issues/39555.
encoded, err := Marshal(input)
if err != nil {
- t.Fatalf("Marshal unexpected error: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
var got map[textUnmarshalerString]string
if err := Unmarshal(encoded, &got); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
- if !reflect.DeepEqual(want, got) {
- t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got)
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %q\n\twant: %q", gotT, wantT)
}
}
func TestUnmarshalMaxDepth(t *testing.T) {
- testcases := []struct {
- name string
+ tests := []struct {
+ CaseName
data string
errMaxDepth bool
- }{
- {
- name: "ArrayUnderMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
- errMaxDepth: false,
- },
- {
- name: "ArrayOverMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ArrayOverStackDepth",
- data: `{"a":` + strings.Repeat(`[`, 3000000) + strings.Repeat(`]`, 3000000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ObjectUnderMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
- errMaxDepth: false,
- },
- {
- name: "ObjectOverMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ObjectOverStackDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 3000000) + `0` + strings.Repeat(`}`, 3000000) + `}`,
- errMaxDepth: true,
- },
- }
+ }{{
+ CaseName: Name("ArrayUnderMaxNestingDepth"),
+ data: `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
+ errMaxDepth: false,
+ }, {
+ CaseName: Name("ArrayOverMaxNestingDepth"),
+ data: `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
+ errMaxDepth: true,
+ }, {
+ CaseName: Name("ArrayOverStackDepth"),
+ data: `{"a":` + strings.Repeat(`[`, 3000000) + strings.Repeat(`]`, 3000000) + `}`,
+ errMaxDepth: true,
+ }, {
+ CaseName: Name("ObjectUnderMaxNestingDepth"),
+ data: `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
+ errMaxDepth: false,
+ }, {
+ CaseName: Name("ObjectOverMaxNestingDepth"),
+ data: `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
+ errMaxDepth: true,
+ }, {
+ CaseName: Name("ObjectOverStackDepth"),
+ data: `{"a":` + strings.Repeat(`{"a":`, 3000000) + `0` + strings.Repeat(`}`, 3000000) + `}`,
+ errMaxDepth: true,
+ }}
targets := []struct {
- name string
+ CaseName
newValue func() any
- }{
- {
- name: "unstructured",
- newValue: func() any {
- var v any
- return &v
- },
+ }{{
+ CaseName: Name("unstructured"),
+ newValue: func() any {
+ var v any
+ return &v
},
- {
- name: "typed named field",
- newValue: func() any {
- v := struct {
- A any `json:"a"`
- }{}
- return &v
- },
+ }, {
+ CaseName: Name("typed named field"),
+ newValue: func() any {
+ v := struct {
+ A any `json:"a"`
+ }{}
+ return &v
},
- {
- name: "typed missing field",
- newValue: func() any {
- v := struct {
- B any `json:"b"`
- }{}
- return &v
- },
+ }, {
+ CaseName: Name("typed missing field"),
+ newValue: func() any {
+ v := struct {
+ B any `json:"b"`
+ }{}
+ return &v
},
- {
- name: "custom unmarshaler",
- newValue: func() any {
- v := unmarshaler{}
- return &v
- },
+ }, {
+ CaseName: Name("custom unmarshaler"),
+ newValue: func() any {
+ v := unmarshaler{}
+ return &v
},
- }
+ }}
- for _, tc := range testcases {
+ for _, tt := range tests {
for _, target := range targets {
- t.Run(target.name+"-"+tc.name, func(t *testing.T) {
- err := Unmarshal([]byte(tc.data), target.newValue())
- if !tc.errMaxDepth {
+ t.Run(target.Name+"-"+tt.Name, func(t *testing.T) {
+ err := Unmarshal([]byte(tt.data), target.newValue())
+ if !tt.errMaxDepth {
if err != nil {
- t.Errorf("unexpected error: %v", err)
+ t.Errorf("%s: %s: Unmarshal error: %v", tt.Where, target.Where, err)
}
} else {
- if err == nil {
- t.Errorf("expected error containing 'exceeded max depth', got none")
- } else if !strings.Contains(err.Error(), "exceeded max depth") {
- t.Errorf("expected error containing 'exceeded max depth', got: %v", err)
+ if err == nil || !strings.Contains(err.Error(), "exceeded max depth") {
+ t.Errorf("%s: %s: Unmarshal error:\n\tgot: %v\n\twant: exceeded max depth", tt.Where, target.Where, err)
}
}
})
Sto struct{} `json:"sto,omitempty"`
}
-var optionalsExpected = `{
+func TestOmitEmpty(t *testing.T) {
+ var want = `{
"sr": "",
"omitempty": 0,
"slr": null,
"str": {},
"sto": {}
}`
-
-func TestOmitEmpty(t *testing.T) {
var o Optionals
o.Sw = "something"
o.Mr = map[string]any{}
got, err := MarshalIndent(&o, "", " ")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("MarshalIndent error: %v", err)
}
- if got := string(got); got != optionalsExpected {
- t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
+ if got := string(got); got != want {
+ t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want))
}
}
func TestRoundtripStringTag(t *testing.T) {
tests := []struct {
- name string
+ CaseName
in StringTag
want string // empty to just test that we roundtrip
- }{
- {
- 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("AllTypes"),
+ in: StringTag{
+ BoolStr: true,
+ IntStr: 42,
+ UintptrStr: 44,
+ StrStr: "xzbit",
+ NumberStr: "46",
},
- {
- // See golang.org/issues/38173.
- name: "StringDoubleEscapes",
- in: StringTag{
- StrStr: "\b\f\n\r\t\"\\",
- NumberStr: "0", // just to satisfy the roundtrip
- },
- want: `{
- "BoolStr": "false",
- "IntStr": "0",
- "UintptrStr": "0",
- "StrStr": "\"\\b\\f\\n\\r\\t\\\"\\\\\"",
- "NumberStr": "0"
- }`,
+ want: `{
+ "BoolStr": "true",
+ "IntStr": "42",
+ "UintptrStr": "44",
+ "StrStr": "\"xzbit\"",
+ "NumberStr": "46"
+}`,
+ }, {
+ // See golang.org/issues/38173.
+ CaseName: Name("StringDoubleEscapes"),
+ in: StringTag{
+ StrStr: "\b\f\n\r\t\"\\",
+ NumberStr: "0", // just to satisfy the roundtrip
},
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- // Indent with a tab prefix to make the multi-line string
- // literals in the table nicer to read.
- got, err := MarshalIndent(&test.in, "\t\t\t", "\t")
+ 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.Fatal(err)
+ t.Fatalf("%s: MarshalIndent error: %v", tt.Where, err)
}
- if got := string(got); got != test.want {
- t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
+ if got := string(got); got != tt.want {
+ t.Fatalf("%s: MarshalIndent:\n\tgot: %s\n\twant: %s", tt.Where, stripWhitespace(got), stripWhitespace(tt.want))
}
// Verify that it round-trips.
var s2 StringTag
if err := Unmarshal(got, &s2); err != nil {
- t.Fatalf("Decode: %v", err)
+ t.Fatalf("%s: Decode error: %v", tt.Where, err)
}
- if !reflect.DeepEqual(test.in, s2) {
- t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
+ 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)
}
})
}
func TestEncodeRenamedByteSlice(t *testing.T) {
s := renamedByteSlice("abc")
- result, err := Marshal(s)
+ got, err := Marshal(s)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Marshal error: %v", err)
}
- expect := `"YWJj"`
- if string(result) != expect {
- t.Errorf(" got %s want %s", result, expect)
+ want := `"YWJj"`
+ if string(got) != want {
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
r := renamedRenamedByteSlice("abc")
- result, err = Marshal(r)
+ got, err = Marshal(r)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Marshal error: %v", err)
}
- if string(result) != expect {
- t.Errorf(" got %s want %s", result, expect)
+ if string(got) != want {
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
func TestSamePointerNoCycle(t *testing.T) {
if _, err := Marshal(samePointerNoCycle); err != nil {
- t.Fatalf("unexpected error: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
}
func TestSliceNoCycle(t *testing.T) {
if _, err := Marshal(sliceNoCycle); err != nil {
- t.Fatalf("unexpected error: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
}
-var unsupportedValues = []any{
- math.NaN(),
- math.Inf(-1),
- math.Inf(1),
- pointerCycle,
- pointerCycleIndirect,
- mapCycle,
- sliceCycle,
- recursiveSliceCycle,
-}
-
func TestUnsupportedValues(t *testing.T) {
- for _, v := range unsupportedValues {
- if _, err := Marshal(v); err != nil {
- if _, ok := err.(*UnsupportedValueError); !ok {
- t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
+ 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)
}
- } else {
- t.Errorf("for %v, expected error", v)
- }
+ })
}
}
}
got, err := Marshal(m)
if err != nil {
- t.Errorf("Marshal() error: %v", err)
+ t.Errorf("Marshal error: %v", err)
}
want := `{"TF:NaN":"1","TF:NaN":"1"}`
if string(got) != want {
- t.Errorf("Marshal() = %s, want %s", got, want)
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
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: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if got := string(b); got != want {
- t.Errorf("got %q, want %q", got, want)
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
want := `"\u003c\u0026\u003e"`
b, err := Marshal(c)
if err != nil {
- t.Fatalf("Marshal(c): %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if got := string(b); got != want {
- t.Errorf("Marshal(c) = %#q, want %#q", 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(ct): %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if got := string(b); got != want {
- t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
func TestAnonymousFields(t *testing.T) {
tests := []struct {
- label string // Test name
+ CaseName
makeInput func() any // Function to create input value
want string // Expected JSON output
}{{
// Both S1 and S2 have a field named X. From the perspective of S,
// it is ambiguous which one X refers to.
// This should not serialize either field.
- label: "AmbiguousField",
+ CaseName: Name("AmbiguousField"),
makeInput: func() any {
type (
S1 struct{ x, X int }
},
want: `{}`,
}, {
- label: "DominantField",
+ CaseName: Name("DominantField"),
// Both S1 and S2 have a field named X, but since S has an X field as
// well, it takes precedence over S1.X and S2.X.
makeInput: func() any {
want: `{"X":6}`,
}, {
// Unexported embedded field of non-struct type should not be serialized.
- label: "UnexportedEmbeddedInt",
+ CaseName: Name("UnexportedEmbeddedInt"),
makeInput: func() any {
type (
myInt int
want: `{}`,
}, {
// Exported embedded field of non-struct type should be serialized.
- label: "ExportedEmbeddedInt",
+ CaseName: Name("ExportedEmbeddedInt"),
makeInput: func() any {
type (
MyInt int
}, {
// Unexported embedded field of pointer to non-struct type
// should not be serialized.
- label: "UnexportedEmbeddedIntPointer",
+ CaseName: Name("UnexportedEmbeddedIntPointer"),
makeInput: func() any {
type (
myInt int
}, {
// Exported embedded field of pointer to non-struct type
// should be serialized.
- label: "ExportedEmbeddedIntPointer",
+ CaseName: Name("ExportedEmbeddedIntPointer"),
makeInput: func() any {
type (
MyInt int
// Exported fields of embedded structs should have their
// exported fields be serialized regardless of whether the struct types
// themselves are exported.
- label: "EmbeddedStruct",
+ CaseName: Name("EmbeddedStruct"),
makeInput: func() any {
type (
s1 struct{ x, X int }
// Exported fields of pointers to embedded structs should have their
// exported fields be serialized regardless of whether the struct types
// themselves are exported.
- label: "EmbeddedStructPointer",
+ CaseName: Name("EmbeddedStructPointer"),
makeInput: func() any {
type (
s1 struct{ x, X int }
}, {
// Exported fields on embedded unexported structs at multiple levels
// of nesting should still be serialized.
- label: "NestedStructAndInts",
+ CaseName: Name("NestedStructAndInts"),
makeInput: func() any {
type (
MyInt1 int
// If an anonymous struct pointer field is nil, we should ignore
// the embedded fields behind it. Not properly doing so may
// result in the wrong output or reflect panics.
- label: "EmbeddedFieldBehindNilPointer",
+ CaseName: Name("EmbeddedFieldBehindNilPointer"),
makeInput: func() any {
type (
S2 struct{ Field string }
}}
for _, tt := range tests {
- t.Run(tt.label, func(t *testing.T) {
+ t.Run(tt.Name, func(t *testing.T) {
b, err := Marshal(tt.makeInput())
if err != nil {
- t.Fatalf("Marshal() = %v, want nil error", err)
+ t.Fatalf("%s: Marshal error: %v", tt.Where, err)
}
if string(b) != tt.want {
- t.Fatalf("Marshal() = %q, want %q", b, tt.want)
+ t.Fatalf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, b, tt.want)
}
})
}
// See golang.org/issue/16042 and golang.org/issue/34235.
func TestNilMarshal(t *testing.T) {
- testCases := []struct {
- v any
+ tests := []struct {
+ CaseName
+ in any
want string
}{
- {v: nil, want: `null`},
- {v: new(float64), want: `0`},
- {v: []any(nil), want: `null`},
- {v: []string(nil), want: `null`},
- {v: map[string]string(nil), want: `null`},
- {v: []byte(nil), want: `null`},
- {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`},
- {v: struct{ M Marshaler }{}, want: `{"M":null}`},
- {v: struct{ M Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
- {v: struct{ M any }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`},
- {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`},
- {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
- {v: struct{ M any }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`},
- }
-
- for _, tt := range testCases {
- out, err := Marshal(tt.v)
- if err != nil || string(out) != tt.want {
- t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want)
- continue
- }
+ {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)
+ }
+ })
}
}
}
b, err := Marshal(v)
if err != nil {
- t.Fatal("Marshal:", err)
+ t.Fatal("Marshal error:", err)
}
want := `{"S":"B"}`
got := string(b)
if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
// Now check that the duplicate field, S, does not appear.
x := BugX{
}
b, err = Marshal(x)
if err != nil {
- t.Fatal("Marshal:", err)
+ t.Fatal("Marshal error:", err)
}
want = `{"A":23}`
got = string(b)
if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
}
b, err := Marshal(v)
if err != nil {
- t.Fatal("Marshal:", err)
+ t.Fatal("Marshal error:", err)
}
want := `{"S":"BugD"}`
got := string(b)
if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
}
b, err := Marshal(v)
if err != nil {
- t.Fatal("Marshal:", err)
+ t.Fatal("Marshal error:", err)
}
want := `{}`
got := string(b)
if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
}
x := Foo{Number(`invalid`)}
- b, err := Marshal(&x)
- if err == nil {
- t.Errorf("Marshal(&x) = %#q; want error", b)
+ if _, err := Marshal(&x); err == nil {
+ t.Fatalf("Marshal error: got nil, want non-nil")
}
}
}
dummy := Dummy{Name: "Dummy"}
dummy.Next = &dummy
- if b, err := Marshal(dummy); err == nil {
- t.Errorf("Marshal(dummy) = %#q; want error", b)
+ if _, err := Marshal(dummy); err == nil {
+ t.Errorf("Marshal error: got nil, want non-nil")
}
type Data struct {
A string
I int
}
- data := Data{A: "a", I: 1}
- b, err := Marshal(data)
+ want := Data{A: "a", I: 1}
+ b, err := Marshal(want)
if err != nil {
- t.Errorf("Marshal(%v) = %v", data, err)
+ t.Errorf("Marshal error: %v", err)
}
- var data2 Data
- if err := Unmarshal(b, &data2); err != nil {
- t.Errorf("Unmarshal(%v) = %v", data2, err)
+ var got Data
+ if err := Unmarshal(b, &got); err != nil {
+ t.Errorf("Unmarshal error: %v", err)
}
- if data2 != data {
- t.Errorf("expect: %v, but get: %v", data, data2)
+ if got != want {
+ t.Errorf("Unmarshal:\n\tgot: %v\n\twant: %v", got, want)
}
}
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(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes())
+ t.Errorf("HTMLEscape:\n\tgot: %s\n\twant: %s", b.Bytes(), want.Bytes())
}
}
var n int64 = 42
b, err := Marshal(stringPointer{N: &n})
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
if got, want := string(b), `{"n":"42"}`; got != want {
- t.Errorf("Marshal = %s, want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
var back stringPointer
- err = Unmarshal(b, &back)
- if err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if back.N == nil {
- t.Fatalf("Unmarshaled nil N field")
- }
- if *back.N != 42 {
- t.Fatalf("*N = %d; want 42", *back.N)
+ 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)
}
}
for _, tt := range encodeStringTests {
b, err := Marshal(tt.in)
if err != nil {
- t.Errorf("Marshal(%q): %v", tt.in, err)
+ t.Errorf("Marshal(%q) error: %v", tt.in, err)
continue
}
out := string(b)
// Issue 13783
func TestEncodeBytekind(t *testing.T) {
- testdata := []struct {
- data any
+ tests := []struct {
+ CaseName
+ in any
want string
}{
- {byte(7), "7"},
- {jsonbyte(7), `{"JB":7}`},
- {textbyte(4), `"TB:4"`},
- {jsonint(5), `{"JI":5}`},
- {textint(1), `"TI:1"`},
- {[]byte{0, 1}, `"AAE="`},
- {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`},
- {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`},
- {[]textbyte{2, 3}, `["TB:2","TB:3"]`},
- {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
- {[]textint{9, 3}, `["TI:9","TI:3"]`},
- {[]int{9, 3}, `[9,3]`},
- {[]textfloat{12, 3}, `["TF:12.00","TF:3.00"]`},
- }
- for _, d := range testdata {
- js, err := Marshal(d.data)
- if err != nil {
- t.Error(err)
- continue
- }
- got, want := string(js), d.want
- if got != want {
- t.Errorf("got %s, want %s", got, want)
- }
+ {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) {
- b, err := Marshal(map[unmarshalerText]int{
+ got, err := Marshal(map[unmarshalerText]int{
{"x", "y"}: 1,
{"y", "x"}: 2,
{"a", "z"}: 3,
{"z", "a"}: 4,
})
if err != nil {
- t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}`
- if string(b) != want {
- t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
+ if string(got) != want {
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
// https://golang.org/issue/33675
func TestNilMarshalerTextMapKey(t *testing.T) {
- b, err := Marshal(map[*unmarshalerText]int{
+ got, err := Marshal(map[*unmarshalerText]int{
(*unmarshalerText)(nil): 1,
{"A", "B"}: 2,
})
if err != nil {
- t.Fatalf("Failed to Marshal *text.Marshaler: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
const want = `{"":1,"A:B":2}`
- if string(b) != want {
- t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want)
+ if string(got) != want {
+ t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
}
bout, err := Marshal(vf)
if err != nil {
- t.Errorf("Marshal(%T(%g)): %v", vf, vf, err)
+ t.Errorf("Marshal(%T(%g)) error: %v", vf, vf, err)
nfail++
return
}
// result must convert back to the same float
g, err := strconv.ParseFloat(out, bits)
if err != nil {
- t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err)
+ t.Errorf("ParseFloat(%q) error: %v", out, err)
nfail++
return
}
if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0
- t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf)
+ t.Errorf("ParseFloat(%q):\n\tgot: %g\n\twant: %g", out, float32(g), vf)
nfail++
return
}
}
for _, re := range bad {
if re.MatchString(out) {
- t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re)
+ t.Errorf("Marshal(%T(%g)) = %q; must not match /%s/", vf, vf, out, re)
nfail++
return
}
)
tests := []struct {
+ CaseName
in any
want string
ok bool
}{
// Test with nil RawMessage.
- {rawNil, "null", true},
- {&rawNil, "null", true},
- {[]any{rawNil}, "[null]", true},
- {&[]any{rawNil}, "[null]", true},
- {[]any{&rawNil}, "[null]", true},
- {&[]any{&rawNil}, "[null]", true},
- {struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
- {&struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
- {struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
- {&struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
- {map[string]any{"M": rawNil}, `{"M":null}`, true},
- {&map[string]any{"M": rawNil}, `{"M":null}`, true},
- {map[string]any{"M": &rawNil}, `{"M":null}`, true},
- {&map[string]any{"M": &rawNil}, `{"M":null}`, true},
- {T1{rawNil}, "{}", true},
- {T2{&rawNil}, `{"M":null}`, true},
- {&T1{rawNil}, "{}", true},
- {&T2{&rawNil}, `{"M":null}`, true},
+ {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},
// Test with empty, but non-nil, RawMessage.
- {rawEmpty, "", false},
- {&rawEmpty, "", false},
- {[]any{rawEmpty}, "", false},
- {&[]any{rawEmpty}, "", false},
- {[]any{&rawEmpty}, "", false},
- {&[]any{&rawEmpty}, "", false},
- {struct{ X RawMessage }{rawEmpty}, "", false},
- {&struct{ X RawMessage }{rawEmpty}, "", false},
- {struct{ X *RawMessage }{&rawEmpty}, "", false},
- {&struct{ X *RawMessage }{&rawEmpty}, "", false},
- {map[string]any{"nil": rawEmpty}, "", false},
- {&map[string]any{"nil": rawEmpty}, "", false},
- {map[string]any{"nil": &rawEmpty}, "", false},
- {&map[string]any{"nil": &rawEmpty}, "", false},
- {T1{rawEmpty}, "{}", true},
- {T2{&rawEmpty}, "", false},
- {&T1{rawEmpty}, "{}", true},
- {&T2{&rawEmpty}, "", false},
+ {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},
// Test with RawMessage with some text.
//
// The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo".
// This behavior was intentionally changed in Go 1.8.
// See https://golang.org/issues/14493#issuecomment-255857318
- {rawText, `"foo"`, true}, // Issue6458
- {&rawText, `"foo"`, true},
- {[]any{rawText}, `["foo"]`, true}, // Issue6458
- {&[]any{rawText}, `["foo"]`, true}, // Issue6458
- {[]any{&rawText}, `["foo"]`, true},
- {&[]any{&rawText}, `["foo"]`, true},
- {struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
- {&struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true},
- {struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
- {&struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
- {map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
- {&map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
- {map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
- {&map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
- {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
- {T2{&rawText}, `{"M":"foo"}`, true},
- {&T1{rawText}, `{"M":"foo"}`, true},
- {&T2{&rawText}, `{"M":"foo"}`, true},
- }
-
- for i, tt := range tests {
- b, err := Marshal(tt.in)
- if ok := (err == nil); ok != tt.ok {
- if err != nil {
- t.Errorf("test %d, unexpected failure: %v", i, err)
- } else {
- t.Errorf("test %d, unexpected success", i)
+ {Name(""), rawText, `"foo"`, true}, // Issue6458
+ {Name(""), &rawText, `"foo"`, true},
+ {Name(""), []any{rawText}, `["foo"]`, true}, // Issue6458
+ {Name(""), &[]any{rawText}, `["foo"]`, true}, // Issue6458
+ {Name(""), []any{&rawText}, `["foo"]`, true},
+ {Name(""), &[]any{&rawText}, `["foo"]`, true},
+ {Name(""), struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {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}, // Issue6458
+ {Name(""), &map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {Name(""), map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
+ {Name(""), &map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
+ {Name(""), T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {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("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want)
- }
+ 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)
+ }
+ })
}
}
}{}
b, err := Marshal(v)
if err != nil {
- t.Fatal("Marshal:", err)
+ t.Fatal("Marshal error:", err)
}
want := `{"A0":0,"À":0,"Aβ":0}`
got := string(b)
if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
errText := "json: test error"
tests := []struct {
+ CaseName
err *MarshalerError
want string
- }{
- {
- &MarshalerError{st, fmt.Errorf(errText), ""},
- "json: error calling MarshalJSON for type " + st.String() + ": " + errText,
- },
- {
- &MarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"},
- "json: error calling TestMarshalerError for type " + st.String() + ": " + errText,
- },
- }
+ }{{
+ 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 i, tt := range tests {
- got := tt.err.Error()
- if got != tt.want {
- t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want)
- }
+ 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)
+ }
+ })
}
}
import (
"bytes"
+ "fmt"
"io"
"log"
"net"
"net/http"
"net/http/httptest"
+ "path"
"reflect"
+ "runtime"
"runtime/debug"
"strings"
"testing"
)
+// TODO(https://go.dev/issue/52751): Replace with native testing support.
+
+// CaseName is a case name annotated with a file and line.
+type CaseName struct {
+ Name string
+ Where CasePos
+}
+
+// Name annotates a case name with the file and line of the caller.
+func Name(s string) (c CaseName) {
+ c.Name = s
+ runtime.Callers(2, c.Where.pc[:])
+ return c
+}
+
+// CasePos represents a file and line number.
+type CasePos struct{ pc [1]uintptr }
+
+func (pos CasePos) String() string {
+ frames := runtime.CallersFrames(pos.pc[:])
+ frame, _ := frames.Next()
+ return fmt.Sprintf("%s:%d", path.Base(frame.File), frame.Line)
+}
+
// Test values for the stream test.
// One of each JSON kind.
var streamTest = []any{
enc.SetIndent("", "")
for j, v := range streamTest[0:i] {
if err := enc.Encode(v); err != nil {
- t.Fatalf("encode #%d: %v", j, err)
+ t.Fatalf("#%d.%d Encode error: %v", i, j, err)
}
}
if have, want := buf.String(), nlines(streamEncoded, i); have != want {
- t.Errorf("encoding %d items: mismatch", i)
+ t.Errorf("encoding %d items: mismatch:", i)
diff(t, []byte(have), []byte(want))
break
}
var buf bytes.Buffer
enc := NewEncoder(&buf)
if err := enc.Encode(dummy); err == nil {
- t.Errorf("Encode(dummy) == nil; want error")
+ t.Errorf("Encode(dummy) error: got nil, want non-nil")
}
type Data struct {
A string
I int
}
- data := Data{A: "a", I: 1}
- if err := enc.Encode(data); err != nil {
- t.Errorf("Marshal(%v) = %v", data, err)
+ want := Data{A: "a", I: 1}
+ if err := enc.Encode(want); err != nil {
+ t.Errorf("Marshal error: %v", err)
}
- var data2 Data
- if err := Unmarshal(buf.Bytes(), &data2); err != nil {
- t.Errorf("Unmarshal(%v) = %v", data2, err)
+ var got Data
+ if err := Unmarshal(buf.Bytes(), &got); err != nil {
+ t.Errorf("Unmarshal error: %v", err)
}
- if data2 != data {
- t.Errorf("expect: %v, but get: %v", data, data2)
+ if got != want {
+ t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %v\n\twant: %v", got, want)
}
}
enc.Encode(v)
}
if have, want := buf.String(), streamEncodedIndent; have != want {
- t.Error("indented encoding mismatch")
+ t.Error("Encode mismatch:")
diff(t, []byte(have), []byte(want))
}
}
Bar string `json:"bar,string"`
}{`<html>foobar</html>`}
- for _, tt := range []struct {
- name string
+ tests := []struct {
+ CaseName
v any
wantEscape string
want string
}{
- {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`},
- {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
- {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
+ {Name("c"), c, `"\u003c\u0026\u003e"`, `"<&>"`},
+ {Name("ct"), ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
+ {Name(`"<&>"`), "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
{
- "tagStruct", tagStruct,
+ Name("tagStruct"), tagStruct,
`{"\u003c\u003e\u0026#! ":0,"Invalid":0}`,
`{"<>&#! ":0,"Invalid":0}`,
},
{
- `"<str>"`, marshalerStruct,
+ Name(`"<str>"`), marshalerStruct,
`{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
`{"NonPtr":"<str>","Ptr":"<str>"}`,
},
{
- "stringOption", stringOption,
+ Name("stringOption"), stringOption,
`{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
`{"bar":"\"<html>foobar</html>\""}`,
},
- } {
- var buf strings.Builder
- enc := NewEncoder(&buf)
- if err := enc.Encode(tt.v); err != nil {
- t.Errorf("Encode(%s): %s", tt.name, err)
- continue
- }
- if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
- t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
- }
- buf.Reset()
- enc.SetEscapeHTML(false)
- if err := enc.Encode(tt.v); err != nil {
- t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
- continue
- }
- if got := strings.TrimSpace(buf.String()); got != tt.want {
- t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
- tt.name, got, tt.want)
- }
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ var buf strings.Builder
+ enc := NewEncoder(&buf)
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("%s: Encode(%s) error: %s", tt.Where, tt.Name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
+ t.Errorf("%s: Encode(%s):\n\tgot: %s\n\twant: %s", tt.Where, tt.Name, got, tt.wantEscape)
+ }
+ buf.Reset()
+ enc.SetEscapeHTML(false)
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("%s: SetEscapeHTML(false) Encode(%s) error: %s", tt.Where, tt.Name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.want {
+ t.Errorf("%s: SetEscapeHTML(false) Encode(%s):\n\tgot: %s\n\twant: %s",
+ tt.Where, tt.Name, got, tt.want)
+ }
+ })
}
}
dec := NewDecoder(&buf)
for j := range out {
if err := dec.Decode(&out[j]); err != nil {
- t.Fatalf("decode #%d/%d: %v", j, i, err)
+ t.Fatalf("decode #%d/%d error: %v", j, i, err)
}
}
if !reflect.DeepEqual(out, streamTest[0:i]) {
- t.Errorf("decoding %d items: mismatch", i)
+ t.Errorf("decoding %d items: mismatch:", i)
for j := range out {
if !reflect.DeepEqual(out[j], streamTest[j]) {
- t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j])
+ t.Errorf("#%d:\n\tgot: %v\n\twant: %v", j, out[j], streamTest[j])
}
}
break
t.Fatal(err)
}
if m.Name != "Gopher" {
- t.Errorf("Name = %q; want Gopher", m.Name)
+ t.Errorf("Name = %s, want Gopher", m.Name)
}
rest, err := io.ReadAll(d.Buffered())
if err != nil {
t.Fatal(err)
}
- if g, w := string(rest), " extra "; g != w {
- t.Errorf("Remaining = %q; want %q", g, w)
+ if got, want := string(rest), " extra "; got != want {
+ t.Errorf("Remaining = %s, want %s", got, want)
}
}
Y float32
}
const raw = `["\u0056",null]`
- const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
- err := Unmarshal([]byte(msg), &data)
+ const want = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
+ err := Unmarshal([]byte(want), &data)
if err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if string([]byte(data.Id)) != raw {
- t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw)
+ t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", []byte(data.Id), raw)
}
- b, err := Marshal(&data)
+ got, err := Marshal(&data)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
- if string(b) != msg {
- t.Fatalf("Marshal: have %#q want %#q", b, msg)
+ if string(got) != want {
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
IdPtr *RawMessage
Y float32
}
- const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
- err := Unmarshal([]byte(msg), &data)
+ const want = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
+ err := Unmarshal([]byte(want), &data)
if err != nil {
- t.Fatalf("Unmarshal: %v", err)
+ t.Fatalf("Unmarshal error: %v", err)
}
if want, got := "null", string(data.Id); want != got {
- t.Fatalf("Raw mismatch: have %q, want %q", got, want)
+ t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", got, want)
}
if data.IdPtr != nil {
- t.Fatalf("Raw pointer mismatch: have non-nil, want nil")
+ t.Fatalf("pointer mismatch: got non-nil, want nil")
}
- b, err := Marshal(&data)
+ got, err := Marshal(&data)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal error: %v", err)
}
- if string(b) != msg {
- t.Fatalf("Marshal: have %#q want %#q", b, msg)
+ if string(got) != want {
+ t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
}
}
-var blockingTests = []string{
- `{"x": 1}`,
- `[1, 2, 3]`,
-}
-
func TestBlocking(t *testing.T) {
- for _, enc := range blockingTests {
- r, w := net.Pipe()
- go w.Write([]byte(enc))
- var val any
-
- // If Decode reads beyond what w.Write writes above,
- // it will block, and the test will deadlock.
- if err := NewDecoder(r).Decode(&val); err != nil {
- t.Errorf("decoding %s: %v", enc, err)
- }
- r.Close()
- w.Close()
+ tests := []struct {
+ CaseName
+ in string
+ }{
+ {Name(""), `{"x": 1}`},
+ {Name(""), `[1, 2, 3]`},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ r, w := net.Pipe()
+ go w.Write([]byte(tt.in))
+ var val any
+
+ // If Decode reads beyond what w.Write writes above,
+ // it will block, and the test will deadlock.
+ if err := NewDecoder(r).Decode(&val); err != nil {
+ t.Errorf("%s: NewDecoder(%s).Decode error: %v", tt.Where, tt.in, err)
+ }
+ r.Close()
+ w.Close()
+ })
}
}
-type tokenStreamCase struct {
- json string
- expTokens []any
-}
-
type decodeThis struct {
v any
}
-var tokenStreamCases = []tokenStreamCase{
- // streaming token cases
- {json: `10`, expTokens: []any{float64(10)}},
- {json: ` [10] `, expTokens: []any{
- Delim('['), float64(10), Delim(']')}},
- {json: ` [false,10,"b"] `, expTokens: []any{
- Delim('['), false, float64(10), "b", Delim(']')}},
- {json: `{ "a": 1 }`, expTokens: []any{
- Delim('{'), "a", float64(1), Delim('}')}},
- {json: `{"a": 1, "b":"3"}`, expTokens: []any{
- Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
- {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
- Delim('['),
- Delim('{'), "a", float64(1), Delim('}'),
- Delim('{'), "a", float64(2), Delim('}'),
- Delim(']')}},
- {json: `{"obj": {"a": 1}}`, expTokens: []any{
- Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
- Delim('}')}},
- {json: `{"obj": [{"a": 1}]}`, expTokens: []any{
- Delim('{'), "obj", Delim('['),
- Delim('{'), "a", float64(1), Delim('}'),
- Delim(']'), Delim('}')}},
-
- // streaming tokens with intermittent Decode()
- {json: `{ "a": 1 }`, expTokens: []any{
- Delim('{'), "a",
- decodeThis{float64(1)},
- Delim('}')}},
- {json: ` [ { "a" : 1 } ] `, expTokens: []any{
- Delim('['),
- decodeThis{map[string]any{"a": float64(1)}},
- Delim(']')}},
- {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
- Delim('['),
- decodeThis{map[string]any{"a": float64(1)}},
- decodeThis{map[string]any{"a": float64(2)}},
- Delim(']')}},
- {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{
- Delim('{'), "obj", Delim('['),
- decodeThis{map[string]any{"a": float64(1)}},
- Delim(']'), Delim('}')}},
-
- {json: `{"obj": {"a": 1}}`, expTokens: []any{
- Delim('{'), "obj",
- decodeThis{map[string]any{"a": float64(1)}},
- Delim('}')}},
- {json: `{"obj": [{"a": 1}]}`, expTokens: []any{
- Delim('{'), "obj",
- decodeThis{[]any{
- map[string]any{"a": float64(1)},
- }},
- Delim('}')}},
- {json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{
- Delim('['),
- decodeThis{map[string]any{"a": float64(1)}},
- decodeThis{&SyntaxError{"expected comma after array element", 11}},
- }},
- {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{
- Delim('{'), strings.Repeat("a", 513),
- decodeThis{&SyntaxError{"expected colon after object key", 518}},
- }},
- {json: `{ "\a" }`, expTokens: []any{
- Delim('{'),
- &SyntaxError{"invalid character 'a' in string escape code", 3},
- }},
- {json: ` \a`, expTokens: []any{
- &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1},
- }},
-}
-
func TestDecodeInStream(t *testing.T) {
- for ci, tcase := range tokenStreamCases {
-
- dec := NewDecoder(strings.NewReader(tcase.json))
- for i, etk := range tcase.expTokens {
-
- var tk any
- var err error
-
- if dt, ok := etk.(decodeThis); ok {
- etk = dt.v
- err = dec.Decode(&tk)
- } else {
- tk, err = dec.Token()
- }
- if experr, ok := etk.(error); ok {
- if err == nil || !reflect.DeepEqual(err, experr) {
- t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err)
+ tests := []struct {
+ CaseName
+ json string
+ expTokens []any
+ }{
+ // streaming token cases
+ {CaseName: Name(""), json: `10`, expTokens: []any{float64(10)}},
+ {CaseName: Name(""), json: ` [10] `, expTokens: []any{
+ Delim('['), float64(10), Delim(']')}},
+ {CaseName: Name(""), json: ` [false,10,"b"] `, expTokens: []any{
+ Delim('['), false, float64(10), "b", Delim(']')}},
+ {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
+ Delim('{'), "a", float64(1), Delim('}')}},
+ {CaseName: Name(""), json: `{"a": 1, "b":"3"}`, expTokens: []any{
+ Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
+ {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
+ Delim('['),
+ Delim('{'), "a", float64(1), Delim('}'),
+ Delim('{'), "a", float64(2), Delim('}'),
+ Delim(']')}},
+ {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
+ Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
+ Delim('}')}},
+ {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
+ Delim('{'), "obj", Delim('['),
+ Delim('{'), "a", float64(1), Delim('}'),
+ Delim(']'), Delim('}')}},
+
+ // streaming tokens with intermittent Decode()
+ {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
+ Delim('{'), "a",
+ decodeThis{float64(1)},
+ Delim('}')}},
+ {CaseName: Name(""), json: ` [ { "a" : 1 } ] `, expTokens: []any{
+ Delim('['),
+ decodeThis{map[string]any{"a": float64(1)}},
+ Delim(']')}},
+ {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
+ Delim('['),
+ decodeThis{map[string]any{"a": float64(1)}},
+ decodeThis{map[string]any{"a": float64(2)}},
+ Delim(']')}},
+ {CaseName: Name(""), json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{
+ Delim('{'), "obj", Delim('['),
+ decodeThis{map[string]any{"a": float64(1)}},
+ Delim(']'), Delim('}')}},
+
+ {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
+ Delim('{'), "obj",
+ decodeThis{map[string]any{"a": float64(1)}},
+ Delim('}')}},
+ {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
+ Delim('{'), "obj",
+ decodeThis{[]any{
+ map[string]any{"a": float64(1)},
+ }},
+ Delim('}')}},
+ {CaseName: Name(""), json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{
+ Delim('['),
+ decodeThis{map[string]any{"a": float64(1)}},
+ decodeThis{&SyntaxError{"expected comma after array element", 11}},
+ }},
+ {CaseName: Name(""), json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{
+ Delim('{'), strings.Repeat("a", 513),
+ decodeThis{&SyntaxError{"expected colon after object key", 518}},
+ }},
+ {CaseName: Name(""), json: `{ "\a" }`, expTokens: []any{
+ Delim('{'),
+ &SyntaxError{"invalid character 'a' in string escape code", 3},
+ }},
+ {CaseName: Name(""), json: ` \a`, expTokens: []any{
+ &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1},
+ }},
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ dec := NewDecoder(strings.NewReader(tt.json))
+ for i, want := range tt.expTokens {
+ var got any
+ var err error
+
+ if dt, ok := want.(decodeThis); ok {
+ want = dt.v
+ err = dec.Decode(&got)
+ } else {
+ got, err = dec.Token()
+ }
+ if errWant, ok := want.(error); ok {
+ if err == nil || !reflect.DeepEqual(err, errWant) {
+ t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: %v", tt.Where, tt.json, err, errWant)
+ }
+ break
+ } else if err != nil {
+ t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: nil", tt.Where, tt.json, err)
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("%s: token %d:\n\tinput: %s\n\tgot: %T(%v)\n\twant: %T(%v)", tt.Where, i, tt.json, got, got, want, want)
}
- break
- } else if err == io.EOF {
- t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json)
- break
- } else if err != nil {
- t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json)
- break
- }
- if !reflect.DeepEqual(tk, etk) {
- t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk)
- break
}
- }
+ })
}
}
defer ts.Close()
res, err := http.Get(ts.URL)
if err != nil {
- log.Fatalf("GET failed: %v", err)
+ log.Fatalf("http.Get error: %v", err)
}
defer res.Body.Close()
d := NewDecoder(res.Body)
err = d.Decode(&foo)
if err != nil {
- t.Fatalf("Decode: %v", err)
+ t.Fatalf("Decode error: %v", err)
}
if foo.Foo != "bar" {
- t.Errorf("decoded %q; want \"bar\"", foo.Foo)
+ t.Errorf(`Decode: got %q, want "bar"`, foo.Foo)
}
// make sure we get the EOF the second time
err = d.Decode(&foo)
if err != io.EOF {
- t.Errorf("err = %v; want io.EOF", err)
+ t.Errorf("Decode error:\n\tgot: %v\n\twant: io.EOF", err)
}
}