From 25d0b8365020ef4051dffd4f7704328402914613 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 15 Aug 2022 00:22:04 -0600 Subject: mkdir -p compat/json && mv borrowed_*_test.go compat/json/ # [ci-skip] --- borrowed_decode_test.go | 2590 ----------------------------------------------- 1 file changed, 2590 deletions(-) delete mode 100644 borrowed_decode_test.go (limited to 'borrowed_decode_test.go') diff --git a/borrowed_decode_test.go b/borrowed_decode_test.go deleted file mode 100644 index ac8594b..0000000 --- a/borrowed_decode_test.go +++ /dev/null @@ -1,2590 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package lowmemjson // MODIFIED - -import ( - "bytes" - "encoding" - "errors" - "fmt" - "image" - "io" // MODIFIED - "math" - "math/big" - "net" - "reflect" - "strconv" - "strings" - "testing" - "time" -) - -type T struct { - X string - Y int - Z int `json:"-"` -} - -type U struct { - Alphabet string `json:"alpha"` -} - -type V struct { - F1 any - F2 int32 - F3 Number - F4 *VOuter -} - -type VOuter struct { - V V -} - -type W struct { - S SS -} - -type P struct { - PP PP -} - -type PP struct { - T T - Ts []T -} - -type SS string - -func (*SS) UnmarshalJSON(data []byte) error { - return &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(SS(""))} -} - -// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and -// without UseNumber -var ifaceNumAsFloat64 = map[string]any{ - "k1": float64(1), - "k2": "s", - "k3": []any{float64(1), float64(2.0), float64(3e-3)}, - "k4": map[string]any{"kk1": "s", "kk2": float64(2)}, -} - -var ifaceNumAsNumber = map[string]any{ - "k1": Number("1"), - "k2": "s", - "k3": []any{Number("1"), Number("2.0"), Number("3e-3")}, - "k4": map[string]any{"kk1": "s", "kk2": Number("2")}, -} - -type tx struct { - x int -} - -type u8 uint8 - -// A type that can unmarshal itself. - -type unmarshaler struct { - T bool -} - -func (u *unmarshaler) UnmarshalJSON(b []byte) error { - *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called. - return nil -} - -type ustruct struct { - M unmarshaler -} - -type unmarshalerText struct { - A, B string -} - -// needed for re-marshaling tests -func (u unmarshalerText) MarshalText() ([]byte, error) { - return []byte(u.A + ":" + u.B), nil -} - -func (u *unmarshalerText) UnmarshalText(b []byte) error { - pos := bytes.IndexByte(b, ':') - if pos == -1 { - return errors.New("missing separator") - } - u.A, u.B = string(b[:pos]), string(b[pos+1:]) - return nil -} - -var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil) - -type ustructText struct { - M unmarshalerText -} - -// u8marshal is an integer type that can marshal/unmarshal itself. -type u8marshal uint8 - -func (u8 u8marshal) MarshalText() ([]byte, error) { - return []byte(fmt.Sprintf("u%d", u8)), nil -} - -var errMissingU8Prefix = errors.New("missing 'u' prefix") - -func (u8 *u8marshal) UnmarshalText(b []byte) error { - if !bytes.HasPrefix(b, []byte{'u'}) { - return errMissingU8Prefix - } - n, err := strconv.Atoi(string(b[1:])) - if err != nil { - return err - } - *u8 = u8marshal(n) - return nil -} - -var _ encoding.TextUnmarshaler = (*u8marshal)(nil) - -var ( - umtrue = unmarshaler{true} - umslice = []unmarshaler{{true}} - umstruct = ustruct{unmarshaler{true}} - - umtrueXY = unmarshalerText{"x", "y"} - umsliceXY = []unmarshalerText{{"x", "y"}} - umstructXY = ustructText{unmarshalerText{"x", "y"}} - - ummapXY = map[unmarshalerText]bool{{"x", "y"}: true} -) - -// Test data structures for anonymous fields. - -type Point struct { - Z int -} - -type Top struct { - Level0 int - Embed0 - *Embed0a - *Embed0b `json:"e,omitempty"` // treated as named - Embed0c `json:"-"` // ignored - Loop - Embed0p // has Point with X, Y, used - Embed0q // has Point with Z, used - embed // contains exported field -} - -type Embed0 struct { - Level1a int // overridden by Embed0a's Level1a with json tag - Level1b int // used because Embed0a's Level1b is renamed - Level1c int // used because Embed0a's Level1c is ignored - Level1d int // annihilated by Embed0a's Level1d - Level1e int `json:"x"` // annihilated by Embed0a.Level1e -} - -type Embed0a struct { - Level1a int `json:"Level1a,omitempty"` - Level1b int `json:"LEVEL1B,omitempty"` - Level1c int `json:"-"` - Level1d int // annihilated by Embed0's Level1d - Level1f int `json:"x"` // annihilated by Embed0's Level1e -} - -type Embed0b Embed0 - -type Embed0c Embed0 - -type Embed0p struct { - image.Point -} - -type Embed0q struct { - Point -} - -type embed struct { - Q int -} - -type Loop struct { - Loop1 int `json:",omitempty"` - Loop2 int `json:",omitempty"` - *Loop -} - -// From reflect test: -// The X in S6 and S7 annihilate, but they also block the X in S8.S9. -type S5 struct { - S6 - S7 - S8 -} - -type S6 struct { - X int -} - -type S7 S6 - -type S8 struct { - S9 -} - -type S9 struct { - X int - Y int -} - -// From reflect test: -// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. -type S10 struct { - S11 - S12 - S13 -} - -type S11 struct { - S6 -} - -type S12 struct { - S6 -} - -type S13 struct { - S8 -} - -type Ambig struct { - // Given "hello", the first match should win. - First int `json:"HELLO"` - Second int `json:"Hello"` -} - -type XYZ struct { - X any - Y any - Z any -} - -type unexportedWithMethods struct{} - -func (unexportedWithMethods) F() {} - -type byteWithMarshalJSON byte - -func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil -} - -func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error { - if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' { - return fmt.Errorf("bad quoted string") - } - i, err := strconv.ParseInt(string(data[2:4]), 16, 8) - if err != nil { - return fmt.Errorf("bad hex") - } - *b = byteWithMarshalJSON(i) - return nil -} - -type byteWithPtrMarshalJSON byte - -func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) { - return byteWithMarshalJSON(*b).MarshalJSON() -} - -func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error { - return (*byteWithMarshalJSON)(b).UnmarshalJSON(data) -} - -type byteWithMarshalText byte - -func (b byteWithMarshalText) MarshalText() ([]byte, error) { - return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil -} - -func (b *byteWithMarshalText) UnmarshalText(data []byte) error { - if len(data) != 3 || data[0] != 'Z' { - return fmt.Errorf("bad quoted string") - } - i, err := strconv.ParseInt(string(data[1:3]), 16, 8) - if err != nil { - return fmt.Errorf("bad hex") - } - *b = byteWithMarshalText(i) - return nil -} - -type byteWithPtrMarshalText byte - -func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) { - return byteWithMarshalText(*b).MarshalText() -} - -func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error { - return (*byteWithMarshalText)(b).UnmarshalText(data) -} - -type intWithMarshalJSON int - -func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil -} - -func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error { - if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' { - return fmt.Errorf("bad quoted string") - } - i, err := strconv.ParseInt(string(data[2:4]), 16, 8) - if err != nil { - return fmt.Errorf("bad hex") - } - *b = intWithMarshalJSON(i) - return nil -} - -type intWithPtrMarshalJSON int - -func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) { - return intWithMarshalJSON(*b).MarshalJSON() -} - -func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error { - return (*intWithMarshalJSON)(b).UnmarshalJSON(data) -} - -type intWithMarshalText int - -func (b intWithMarshalText) MarshalText() ([]byte, error) { - return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil -} - -func (b *intWithMarshalText) UnmarshalText(data []byte) error { - if len(data) != 3 || data[0] != 'Z' { - return fmt.Errorf("bad quoted string") - } - i, err := strconv.ParseInt(string(data[1:3]), 16, 8) - if err != nil { - return fmt.Errorf("bad hex") - } - *b = intWithMarshalText(i) - return nil -} - -type intWithPtrMarshalText int - -func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) { - return intWithMarshalText(*b).MarshalText() -} - -func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error { - return (*intWithMarshalText)(b).UnmarshalText(data) -} - -type mapStringToStringData struct { - 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"` -} - -type DoublePtr struct { - I **int - J **int -} - -var unmarshalTests = []unmarshalTest{ - // 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.TypeOf(""), 7, "T", "X"}}, - {in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(""), 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.TypeOf(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}, - - // 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"}, - - // 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}, - - {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}, - - // 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{Err: "unexpected end of JSON input", Offset: 5}}, // MODIFIED - {in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{Err: "invalid character '}' in numeric literal", Offset: 9}}, // MODIFIED - - // 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}}, - - // 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")}, - - // 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)}}, - - // 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}, - - // 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}, - - // 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}, - - // 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"}, - }, - { - 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"}, - }, - { - 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}, - }, - - // 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}, - }, - { - 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.TypeOf(0), Offset: 2}, - }, - { - in: `{"256":"abc"}`, - ptr: new(map[uint8]string), - err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2}, - }, - { - in: `{"128":"abc"}`, - ptr: new(map[int8]string), - err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2}, - }, - { - in: `{"-1":"abc"}`, - ptr: new(map[uint8]string), - err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2}, - }, - { - in: `{"F":{"a":2,"3":4}}`, - ptr: new(map[string]map[int]int), - err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(int(0)), Offset: 7}, - }, - { - in: `{"F":{"a":2,"3":4}}`, - ptr: new(map[string]map[uint]int), - err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(uint(0)), Offset: 7}, - }, - - // Map keys can be encoding.TextUnmarshalers. - {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}, - - { - in: `{ - "Level0": 1, - "Level1b": 2, - "Level1c": 3, - "x": 4, - "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 - }`, - ptr: new(Top), - out: Top{ - Level0: 1, - Embed0: Embed0{ - Level1b: 2, - Level1c: 3, - }, - Embed0a: &Embed0a{ - Level1a: 5, - Level1b: 6, - }, - Embed0b: &Embed0b{ - Level1a: 8, - Level1b: 9, - Level1c: 10, - Level1d: 11, - Level1e: 12, - }, - Loop: Loop{ - Loop1: 13, - Loop2: 14, - }, - Embed0p: Embed0p{ - Point: image.Point{X: 15, Y: 16}, - }, - Embed0q: Embed0q{ - Point: Point{Z: 17}, - }, - embed: embed{ - Q: 18, - }, - }, - }, - { - 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}}}, - }, - { - 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}}}}, - }, - { - 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}, - }, - - // invalid UTF-8 is coerced to valid UTF-8. - { - in: "\"hello\xffworld\"", - ptr: new(string), - out: "hello\ufffdworld", - }, - { - in: "\"hello\xc2\xc2world\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\xc2\xffworld\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\\ud800world\"", - ptr: new(string), - out: "hello\ufffdworld", - }, - { - in: "\"hello\\ud800\\ud800world\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - 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", - }, - - // 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"}, - }, - - // issue 8305 - { - in: `{"2009-11-10T23:00:00Z": "hello world"}`, - ptr: new(map[Point]string), - err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1}, - }, - { - in: `{"asdf": "hello world"}`, - ptr: new(map[unmarshaler]string), - err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1}, - }, - - // related to issue 13783. - // Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type, - // similar to marshaling a slice of typed int. - // These tests check that, assuming the byte type also has valid decoding methods, - // either the old base64 string encoding or the new per-element encoding can be - // 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}, - }, - { - in: `["Z01","Z02","Z03"]`, - ptr: new([]byteWithMarshalJSON), - out: []byteWithMarshalJSON{1, 2, 3}, - golden: true, - }, - { - in: `"AQID"`, - ptr: new([]byteWithMarshalText), - out: []byteWithMarshalText{1, 2, 3}, - }, - { - in: `["Z01","Z02","Z03"]`, - ptr: new([]byteWithMarshalText), - out: []byteWithMarshalText{1, 2, 3}, - golden: true, - }, - { - in: `"AQID"`, - ptr: new([]byteWithPtrMarshalJSON), - out: []byteWithPtrMarshalJSON{1, 2, 3}, - }, - { - in: `["Z01","Z02","Z03"]`, - ptr: new([]byteWithPtrMarshalJSON), - out: []byteWithPtrMarshalJSON{1, 2, 3}, - golden: true, - }, - { - in: `"AQID"`, - ptr: new([]byteWithPtrMarshalText), - out: []byteWithPtrMarshalText{1, 2, 3}, - }, - { - 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, - }, - { - 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, - }, - { - 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}, - - { - in: `{"V": {"F2": "hello"}}`, - ptr: new(VOuter), - err: &UnmarshalTypeError{ - Value: "string", - Struct: "V", - Field: "V.F2", - Type: reflect.TypeOf(int32(0)), - Offset: 20, - }, - }, - { - in: `{"V": {"F4": {}, "F2": "hello"}}`, - ptr: new(VOuter), - err: &UnmarshalTypeError{ - Value: "string", - Struct: "V", - Field: "V.F2", - Type: reflect.TypeOf(int32(0)), - Offset: 30, - }, - }, - - // 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`)}, - - // additional tests for disallowUnknownFields - { - in: `{ - "Level0": 1, - "Level1b": 2, - "Level1c": 3, - "x": 4, - "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, - "extra": true - }`, - ptr: new(Top), - err: fmt.Errorf("json: unknown field \"extra\""), - disallowUnknownFields: true, - }, - { - in: `{ - "Level0": 1, - "Level1b": 2, - "Level1c": 3, - "x": 4, - "Level1a": 5, - "LEVEL1B": 6, - "e": { - "Level1a": 8, - "Level1b": 9, - "Level1c": 10, - "Level1d": 11, - "x": 12, - "extra": null - }, - "Loop1": 13, - "Loop2": 14, - "X": 15, - "Y": 16, - "Z": 17, - "Q": 18 - }`, - ptr: new(Top), - err: fmt.Errorf("json: unknown field \"extra\""), - disallowUnknownFields: true, - }, - // issue 26444 - // UnmarshalTypeError without field & struct values - { - in: `{"data":{"test1": "bob", "test2": 123}}`, - ptr: new(mapStringToStringData), - err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 37, Struct: "mapStringToStringData", Field: "data"}, - }, - { - in: `{"data":{"test1": 123, "test2": "bob"}}`, - ptr: new(mapStringToStringData), - err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), 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.TypeOf(&MustNotUnmarshalText{}), Offset: 1}, - }, - { - in: `{"foo": "bar"}`, - ptr: new(MustNotUnmarshalText), - err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(&MustNotUnmarshalText{}), Offset: 1}, - }, - // #22369 - { - in: `{"PP": {"T": {"Y": "bad-type"}}}`, - ptr: new(P), - err: &UnmarshalTypeError{ - Value: "string", - Struct: "T", - Field: "PP.T.Y", - Type: reflect.TypeOf(int(0)), - Offset: 29, - }, - }, - { - in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`, - ptr: new(PP), - err: &UnmarshalTypeError{ - Value: "string", - Struct: "T", - Field: "Ts.Y", - Type: reflect.TypeOf(int(0)), - Offset: 29, - }, - }, - // #14702 - { - in: `invalid`, - ptr: new(Number), - err: &SyntaxError{ - Err: "invalid character 'i' looking for beginning of value", // MODIFIED - Offset: 1, - }, - }, - { - 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"`), - }, - { - 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"`), - }, -} - -func TestMarshal(t *testing.T) { - b, err := Marshal(allValue) - if err != nil { - t.Fatalf("Marshal allValue: %v", err) - } - if string(b) != allValueCompact { - t.Errorf("Marshal allValueCompact") - diff(t, b, []byte(allValueCompact)) - return - } - - b, err = Marshal(pallValue) - if err != nil { - t.Fatalf("Marshal pallValue: %v", err) - } - if string(b) != pallValueCompact { - t.Errorf("Marshal pallValueCompact") - 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 TestMarshalNumberZeroVal(t *testing.T) { - var n Number - out, err := Marshal(n) - if err != nil { - t.Fatal(err) - } - outStr := string(out) - if outStr != "0" { - t.Fatalf("Invalid zero val for Number: %q", outStr) - } -} - -func TestMarshalEmbeds(t *testing.T) { - t.Skip() // TODO - top := &Top{ - Level0: 1, - Embed0: Embed0{ - Level1b: 2, - Level1c: 3, - }, - Embed0a: &Embed0a{ - Level1a: 5, - Level1b: 6, - }, - Embed0b: &Embed0b{ - Level1a: 8, - Level1b: 9, - Level1c: 10, - Level1d: 11, - Level1e: 12, - }, - Loop: Loop{ - Loop1: 13, - Loop2: 14, - }, - Embed0p: Embed0p{ - Point: image.Point{X: 15, Y: 16}, - }, - Embed0q: Embed0q{ - Point: Point{Z: 17}, - }, - embed: embed{ - Q: 18, - }, - } - b, err := Marshal(top) - if err != nil { - t.Fatal(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) - } -} - -func equalError(a, b error) bool { - if a == nil { - return b == nil - } - if b == nil { - return a == nil - } - return true // a.Error() == b.Error() // MODIFIED -} - -func TestUnmarshal(t *testing.T) { - t.Skip() // TODO - for i, tt := range unmarshalTests { - scan := &ReEncoder{ - Out: io.Discard, - } - in := []byte(tt.in) - if _, err := scan.Write(in); err != nil { - if !equalError(err, tt.err) { - t.Errorf("#%d: checkValid: %#v\n\n%s", i, err, tt.in) - continue - } - } - 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\n\n%s", i, v.Elem().Interface(), tt.out, tt.in) - 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 - } - if tt.golden && !bytes.Equal(enc, in) { - t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, 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.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err) - continue - } - 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 - } - } - } -} - -func TestUnmarshalMarshal(t *testing.T) { - initBig() - var v any - if err := Unmarshal(jsonBig, &v); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - b, err := Marshal(v) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if !bytes.Equal(jsonBig, b) { - t.Errorf("Marshal jsonBig") - 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) - } - } -} - -func TestLargeByteSlice(t *testing.T) { - s0 := make([]byte, 2000) - for i := range s0 { - s0[i] = byte(i) - } - b, err := Marshal(s0) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - var s1 []byte - if err := Unmarshal(b, &s1); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !bytes.Equal(s0, s1) { - t.Errorf("Marshal large byte slice") - diff(t, s0, s1) - } -} - -type Xint struct { - X int -} - -func TestUnmarshalInterface(t *testing.T) { - var xint Xint - var i any = &xint - if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if xint.X != 1 { - t.Fatalf("Did not write to xint") - } -} - -func TestUnmarshalPtrPtr(t *testing.T) { - var xint Xint - pxint := &xint - if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if xint.X != 1 { - t.Fatalf("Did not write to xint") - } -} - -func TestEscape(t *testing.T) { - const input = `"foobar"` + " [\u2028 \u2029]" - const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"` - b, 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) - } -} - -// 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 err == nil { // if got != tt.err { // MODIFIED - t.Errorf("%d. got err = %q, want %q", n, got, tt.err) - } - } -} - -func noSpace(c rune) rune { - if isSpace(byte(c)) { //only used for ascii - return -1 - } - return c -} - -type All struct { - Bool bool - Int int - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Uint uint - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Uintptr uintptr - Float32 float32 - Float64 float64 - - Foo string `json:"bar"` - Foo2 string `json:"bar2,dummyopt"` - - IntStr int64 `json:",string"` - UintptrStr uintptr `json:",string"` - - PBool *bool - PInt *int - PInt8 *int8 - PInt16 *int16 - PInt32 *int32 - PInt64 *int64 - PUint *uint - PUint8 *uint8 - PUint16 *uint16 - PUint32 *uint32 - PUint64 *uint64 - PUintptr *uintptr - PFloat32 *float32 - PFloat64 *float64 - - String string - PString *string - - Map map[string]Small - MapP map[string]*Small - PMap *map[string]Small - PMapP *map[string]*Small - - EmptyMap map[string]Small - NilMap map[string]Small - - Slice []Small - SliceP []*Small - PSlice *[]Small - PSliceP *[]*Small - - EmptySlice []Small - NilSlice []Small - - StringSlice []string - ByteSlice []byte - - Small Small - PSmall *Small - PPSmall **Small - - Interface any - PInterface *any - - unexported int -} - -type Small struct { - Tag string -} - -var allValue = All{ - Bool: true, - Int: 2, - Int8: 3, - Int16: 4, - Int32: 5, - Int64: 6, - Uint: 7, - Uint8: 8, - Uint16: 9, - Uint32: 10, - Uint64: 11, - Uintptr: 12, - Float32: 14.1, - Float64: 15.1, - Foo: "foo", - Foo2: "foo2", - IntStr: 42, - UintptrStr: 44, - String: "16", - Map: map[string]Small{ - "17": {Tag: "tag17"}, - "18": {Tag: "tag18"}, - }, - MapP: map[string]*Small{ - "19": {Tag: "tag19"}, - "20": nil, - }, - EmptyMap: map[string]Small{}, - Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, - SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, - EmptySlice: []Small{}, - StringSlice: []string{"str24", "str25", "str26"}, - ByteSlice: []byte{27, 28, 29}, - Small: Small{Tag: "tag30"}, - PSmall: &Small{Tag: "tag31"}, - Interface: 5.2, -} - -var pallValue = All{ - PBool: &allValue.Bool, - PInt: &allValue.Int, - PInt8: &allValue.Int8, - PInt16: &allValue.Int16, - PInt32: &allValue.Int32, - PInt64: &allValue.Int64, - PUint: &allValue.Uint, - PUint8: &allValue.Uint8, - PUint16: &allValue.Uint16, - PUint32: &allValue.Uint32, - PUint64: &allValue.Uint64, - PUintptr: &allValue.Uintptr, - PFloat32: &allValue.Float32, - PFloat64: &allValue.Float64, - PString: &allValue.String, - PMap: &allValue.Map, - PMapP: &allValue.MapP, - PSlice: &allValue.Slice, - PSliceP: &allValue.SliceP, - PPSmall: &allValue.PSmall, - PInterface: &allValue.Interface, -} - -var allValueIndent = `{ - "Bool": true, - "Int": 2, - "Int8": 3, - "Int16": 4, - "Int32": 5, - "Int64": 6, - "Uint": 7, - "Uint8": 8, - "Uint16": 9, - "Uint32": 10, - "Uint64": 11, - "Uintptr": 12, - "Float32": 14.1, - "Float64": 15.1, - "bar": "foo", - "bar2": "foo2", - "IntStr": "42", - "UintptrStr": "44", - "PBool": null, - "PInt": null, - "PInt8": null, - "PInt16": null, - "PInt32": null, - "PInt64": null, - "PUint": null, - "PUint8": null, - "PUint16": null, - "PUint32": null, - "PUint64": null, - "PUintptr": null, - "PFloat32": null, - "PFloat64": null, - "String": "16", - "PString": null, - "Map": { - "17": { - "Tag": "tag17" - }, - "18": { - "Tag": "tag18" - } - }, - "MapP": { - "19": { - "Tag": "tag19" - }, - "20": null - }, - "PMap": null, - "PMapP": null, - "EmptyMap": {}, - "NilMap": null, - "Slice": [ - { - "Tag": "tag20" - }, - { - "Tag": "tag21" - } - ], - "SliceP": [ - { - "Tag": "tag22" - }, - null, - { - "Tag": "tag23" - } - ], - "PSlice": null, - "PSliceP": null, - "EmptySlice": [], - "NilSlice": null, - "StringSlice": [ - "str24", - "str25", - "str26" - ], - "ByteSlice": "Gxwd", - "Small": { - "Tag": "tag30" - }, - "PSmall": { - "Tag": "tag31" - }, - "PPSmall": null, - "Interface": 5.2, - "PInterface": null -}` - -var allValueCompact = strings.Map(noSpace, allValueIndent) - -var pallValueIndent = `{ - "Bool": false, - "Int": 0, - "Int8": 0, - "Int16": 0, - "Int32": 0, - "Int64": 0, - "Uint": 0, - "Uint8": 0, - "Uint16": 0, - "Uint32": 0, - "Uint64": 0, - "Uintptr": 0, - "Float32": 0, - "Float64": 0, - "bar": "", - "bar2": "", - "IntStr": "0", - "UintptrStr": "0", - "PBool": true, - "PInt": 2, - "PInt8": 3, - "PInt16": 4, - "PInt32": 5, - "PInt64": 6, - "PUint": 7, - "PUint8": 8, - "PUint16": 9, - "PUint32": 10, - "PUint64": 11, - "PUintptr": 12, - "PFloat32": 14.1, - "PFloat64": 15.1, - "String": "", - "PString": "16", - "Map": null, - "MapP": null, - "PMap": { - "17": { - "Tag": "tag17" - }, - "18": { - "Tag": "tag18" - } - }, - "PMapP": { - "19": { - "Tag": "tag19" - }, - "20": null - }, - "EmptyMap": null, - "NilMap": null, - "Slice": null, - "SliceP": null, - "PSlice": [ - { - "Tag": "tag20" - }, - { - "Tag": "tag21" - } - ], - "PSliceP": [ - { - "Tag": "tag22" - }, - null, - { - "Tag": "tag23" - } - ], - "EmptySlice": null, - "NilSlice": null, - "StringSlice": null, - "ByteSlice": null, - "Small": { - "Tag": "" - }, - "PSmall": null, - "PPSmall": { - "Tag": "tag31" - }, - "Interface": null, - "PInterface": 5.2 -}` - -var pallValueCompact = strings.Map(noSpace, pallValueIndent) - -func TestRefUnmarshal(t *testing.T) { - type S struct { - // Ref is defined in encode_test.go. - R0 Ref - R1 *Ref - R2 RefText - R3 *RefText - } - want := S{ - R0: 12, - R1: new(Ref), - R2: 13, - R3: new(RefText), - } - *want.R1 = 12 - *want.R3 = 13 - - var got S - if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got %+v, want %+v", got, want) - } -} - -// Test that the empty string doesn't panic decoding when ,string is specified -// Issue 3450 -func TestEmptyString(t *testing.T) { - type T2 struct { - Number1 int `json:",string"` - Number2 int `json:",string"` - } - 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") - } -} - -// Test that a null for ,string is not replaced with the previous quoted string (issue 7046). -// It should also not be an error (issue 2540, issue 8587). -func TestNullString(t *testing.T) { - type T struct { - A int `json:",string"` - B int `json:",string"` - C *int `json:",string"` - } - data := []byte(`{"A": "1", "B": null, "C": null}`) - var s T - 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) - } -} - -func intp(x int) *int { - p := new(int) - *p = x - return p -} - -func intpp(x *int) **int { - pp := new(*int) - *pp = x - 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) { - t.Skip() // TODO - 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) - } - } -} - -type NullTest struct { - Bool bool - Int int - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Uint uint - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Float32 float32 - Float64 float64 - String string - PBool *bool - Map map[string]string - Slice []string - Interface any - - PRaw *RawMessage - PTime *time.Time - PBigInt *big.Int - PText *MustNotUnmarshalText - PBuffer *bytes.Buffer // has methods, just not relevant ones - PStruct *struct{} - - Raw RawMessage - Time time.Time - BigInt big.Int - Text MustNotUnmarshalText - Buffer bytes.Buffer - Struct struct{} -} - -// JSON null values should be ignored for primitives and string values instead of resulting in an error. -// Issue 2540 -func TestUnmarshalNulls(t *testing.T) { - // Unmarshal docs: - // The JSON null value unmarshals into an interface, map, pointer, or slice - // by setting that Go value to nil. Because null is often used in JSON to mean - // ``not present,'' unmarshaling a JSON null into any other Go type has no effect - // on the value and produces no error. - - jsonData := []byte(`{ - "Bool" : null, - "Int" : null, - "Int8" : null, - "Int16" : null, - "Int32" : null, - "Int64" : null, - "Uint" : null, - "Uint8" : null, - "Uint16" : null, - "Uint32" : null, - "Uint64" : null, - "Float32" : null, - "Float64" : null, - "String" : null, - "PBool": null, - "Map": null, - "Slice": null, - "Interface": null, - "PRaw": null, - "PTime": null, - "PBigInt": null, - "PText": null, - "PBuffer": null, - "PStruct": null, - "Raw": null, - "Time": null, - "BigInt": null, - "Text": null, - "Buffer": null, - "Struct": null - }`) - nulls := NullTest{ - Bool: true, - Int: 2, - Int8: 3, - Int16: 4, - Int32: 5, - Int64: 6, - Uint: 7, - Uint8: 8, - Uint16: 9, - Uint32: 10, - Uint64: 11, - Float32: 12.1, - Float64: 13.1, - String: "14", - PBool: new(bool), - Map: map[string]string{}, - Slice: []string{}, - Interface: new(MustNotUnmarshalJSON), - PRaw: new(RawMessage), - PTime: new(time.Time), - PBigInt: new(big.Int), - PText: new(MustNotUnmarshalText), - PStruct: new(struct{}), - PBuffer: new(bytes.Buffer), - Raw: RawMessage("123"), - Time: time.Unix(123456789, 0), - BigInt: *big.NewInt(123), - } - - before := nulls.Time.String() - - err := Unmarshal(jsonData, &nulls) - if err != nil { - t.Errorf("Unmarshal of null values failed: %v", err) - } - if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 || - nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 || - nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" { - t.Errorf("Unmarshal of null values affected primitives") - } - - if nulls.PBool != nil { - t.Errorf("Unmarshal of null did not clear nulls.PBool") - } - if nulls.Map != nil { - t.Errorf("Unmarshal of null did not clear nulls.Map") - } - if nulls.Slice != nil { - t.Errorf("Unmarshal of null did not clear nulls.Slice") - } - if nulls.Interface != nil { - t.Errorf("Unmarshal of null did not clear nulls.Interface") - } - if nulls.PRaw != nil { - t.Errorf("Unmarshal of null did not clear nulls.PRaw") - } - if nulls.PTime != nil { - t.Errorf("Unmarshal of null did not clear nulls.PTime") - } - if nulls.PBigInt != nil { - t.Errorf("Unmarshal of null did not clear nulls.PBigInt") - } - if nulls.PText != nil { - t.Errorf("Unmarshal of null did not clear nulls.PText") - } - if nulls.PBuffer != nil { - t.Errorf("Unmarshal of null did not clear nulls.PBuffer") - } - if nulls.PStruct != nil { - t.Errorf("Unmarshal of null did not clear nulls.PStruct") - } - - if string(nulls.Raw) != "null" { - t.Errorf("Unmarshal of RawMessage null did not record null: %v", string(nulls.Raw)) - } - if nulls.Time.String() != before { - t.Errorf("Unmarshal of time.Time null set time to %v", nulls.Time.String()) - } - if nulls.BigInt.String() != "123" { - t.Errorf("Unmarshal of big.Int null set int to %v", nulls.BigInt.String()) - } -} - -type MustNotUnmarshalJSON struct{} - -func (x MustNotUnmarshalJSON) UnmarshalJSON(data []byte) error { - return errors.New("MustNotUnmarshalJSON was used") -} - -type MustNotUnmarshalText struct{} - -func (x MustNotUnmarshalText) UnmarshalText(text []byte) error { - return errors.New("MustNotUnmarshalText was used") -} - -func TestStringKind(t *testing.T) { - type stringKind string - - var m1, m2 map[stringKind]int - m1 = map[stringKind]int{ - "foo": 42, - } - - data, err := Marshal(m1) - if err != nil { - t.Errorf("Unexpected error marshaling: %v", err) - } - - err = Unmarshal(data, &m2) - if err != nil { - t.Errorf("Unexpected error unmarshaling: %v", err) - } - - if !reflect.DeepEqual(m1, m2) { - t.Error("Items should be equal after encoding and then decoding") - } -} - -// Custom types with []byte as underlying type could not be marshaled -// and then unmarshaled. -// Issue 8962. -func TestByteKind(t *testing.T) { - type byteKind []byte - - a := byteKind("hello") - - data, err := Marshal(a) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(data, []byte(`"aGVsbG8="`)) { // MODIFIED - t.Errorf("expected %q == %q", data, `"aGVsbG8="`) // MODIFIED - } // MODIFIED - var b byteKind - err = Unmarshal(data, &b) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(a, b) { - t.Errorf("expected %v == %v", a, b) - } -} - -// The fix for issue 8962 introduced a regression. -// Issue 12921. -func TestSliceOfCustomByte(t *testing.T) { - type Uint8 uint8 - - a := []Uint8("hello") - - data, err := Marshal(a) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(data, []byte(`"aGVsbG8="`)) { // MODIFIED - t.Errorf("expected %q == %q", data, `"aGVsbG8="`) // MODIFIED - } // MODIFIED - var b []Uint8 - err = Unmarshal(data, &b) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(a, b) { - t.Fatalf("expected %v == %v", a, b) - } -} - -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 err == nil { // if _, ok := err.(*UnmarshalTypeError); !ok { // MODIFIED - t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", - item.src, item.dest, err) - } - } -} - -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 err == nil { // _, ok := err.(*SyntaxError); !ok { // MODIFIED - t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err) - } - } -} - -// Test handling of unexported fields that should be ignored. -// Issue 4660 -type unexportedFields struct { - Name string - m map[string]any `json:"-"` - m2 map[string]any `json:"abcd"` - - s []int `json:"-"` -} - -func TestUnmarshalUnexported(t *testing.T) { - input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}, "s": [2, 3]}` - want := &unexportedFields{Name: "Bob"} - - out := &unexportedFields{} - err := Unmarshal([]byte(input), out) - if err != nil { - t.Errorf("got error %v, expected nil", err) - } - if !reflect.DeepEqual(out, want) { - t.Errorf("got %q, want %q", out, want) - } -} - -// Time3339 is a time.Time which encodes to and from JSON -// as an RFC 3339 time in UTC. -type Time3339 time.Time - -func (t *Time3339) UnmarshalJSON(b []byte) error { - if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) - } - tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) - if err != nil { - return err - } - *t = Time3339(tm) - return nil -} - -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) - } -} - -// Test that extra object elements in an array do not result in a -// "data changing underfoot" error. -// Issue 3717 -func TestSkipArrayObjects(t *testing.T) { - json := `[{}]` - var dest [0]any - - err := Unmarshal([]byte(json), &dest) - if err != nil { - t.Errorf("got error %q, want nil", err) - } -} - -// Test semantics of pre-filled data, such as struct fields, map elements, -// slices, and arrays. -// Issues 4900 and 8837, among others. -func TestPrefilled(t *testing.T) { - // Values here change, cannot reuse table across runs. - var prefillTests = []struct { - 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) - } - } -} - -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) - } - } -} - -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 { // MODIFIED - // t.Errorf("Unmarshal = %q; want %q", got, tt.want) // MODIFIED - // } // MODIFIED - } -} - -// Test that string option is ignored for invalid types. -// Issue 9812. -func TestInvalidStringOption(t *testing.T) { - num := 0 - item := struct { - T time.Time `json:",string"` - M map[string]string `json:",string"` - S []string `json:",string"` - A [1]string `json:",string"` - I any `json:",string"` - P *int `json:",string"` - }{M: make(map[string]string), S: make([]string, 0), I: num, P: &num} - - data, err := Marshal(item) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - - err = Unmarshal(data, &item) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } -} - -// Test unmarshal behavior with regards to embedded unexported structs. -// -// (Issue 21357) If the embedded struct is a pointer and is unallocated, -// this returns an error because unmarshal cannot set the field. -// -// (Issue 24152) If the embedded struct is given an explicit name, -// ensure that the normal unmarshal logic does not panic in reflect. -// -// (Issue 28145) If the embedded struct is given an explicit name and has -// exported methods, don't cause a panic trying to get its value. -func TestUnmarshalEmbeddedUnexported(t *testing.T) { - t.Skip() // TODO - type ( - embed1 struct{ Q int } - embed2 struct{ Q int } - embed3 struct { - Q int64 `json:",string"` - } - S1 struct { - *embed1 - R int - } - S2 struct { - *embed1 - Q int - } - S3 struct { - embed1 - R int - } - S4 struct { - *embed1 - embed2 - } - S5 struct { - *embed3 - R int - } - S6 struct { - embed1 `json:"embed1"` - } - S7 struct { - embed1 `json:"embed1"` - embed2 - } - S8 struct { - embed1 `json:"embed1"` - embed2 `json:"embed2"` - Q int - } - S9 struct { - unexportedWithMethods `json:"embed"` - } - ) - - tests := []struct { - 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"), - }, { - // The top level Q field takes precedence. - 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}, - }, { - // 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), - }, { - // 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"), - }, { - // Issue 24152, ensure decodeState.indirect does not panic. - 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. - // - // This relies on obscure behavior of reflect where it is possible - // to set a forwarded exported field on an unexported embedded struct - // even though there is a name conflict, even when it would have been - // impossible to do so according to Go visibility rules. - // Go forbids this because it is ambiguous whether S7.Q refers to - // S7.embed1.Q or S7.embed2.Q. Since embed1 and embed2 are unexported, - // 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}}, - }, { - // 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}, - }, { - // Issue 228145, similar to the cases above. - 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) - } - } -} - -func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) { - t.Skip() // TODO - tests := []struct { - in string - err error - }{{ - 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}, - }, { - in: `1 [] [true:]`, - err: &SyntaxError{"invalid character ':' after array element", 11}, - }, { - in: `1 {} {"x"=}`, - err: &SyntaxError{"invalid character '=' after object key", 14}, - }, { - 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 - } - } - if !reflect.DeepEqual(err, tt.err) { - t.Errorf("#%d: got %#v, want %#v", i, err, tt.err) - } - } -} - -type unmarshalPanic struct{} - -func (unmarshalPanic) UnmarshalJSON([]byte) error { panic(0xdead) } - -func TestUnmarshalPanic(t *testing.T) { - defer func() { - if got := recover(); !reflect.DeepEqual(got, 0xdead) { - t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) - } - }() - Unmarshal([]byte("{}"), &unmarshalPanic{}) - t.Fatalf("Unmarshal should have panicked") -} - -// The decoder used to hang if decoding into an interface pointing to its own address. -// See golang.org/issues/31740. -func TestUnmarshalRecursivePointer(t *testing.T) { - t.Skip() // TODO - var v any - v = &v - data := []byte(`{"a": "b"}`) - - if err := Unmarshal(data, v); err != nil { - t.Fatal(err) - } -} - -type textUnmarshalerString string - -func (m *textUnmarshalerString) UnmarshalText(text []byte) error { - *m = textUnmarshalerString(strings.ToLower(string(text))) - return nil -} - -// Test unmarshal to a map, where the map key is a user defined type. -// See golang.org/issues/34437. -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) - } - - if _, ok := p["foo"]; !ok { - t.Errorf(`Key "foo" does not exist in map: %v`, p) - } -} - -func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) { - // 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) - } - if _, ok := p["开源"]; !ok { - t.Errorf(`Key "开源" does not exist in map: %v`, p) - } - - // See golang.org/issues/38126. - type T struct { - F1 string `json:"F1,string"` - } - t1 := T{"aaa\tbbb"} - - b, err := Marshal(t1) - if err != nil { - t.Fatalf("Marshal unexpected error: %v", err) - } - var t2 T - if err := Unmarshal(b, &t2); err != nil { - t.Fatalf("Unmarshal unexpected error: %v", err) - } - if t1 != t2 { - t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2) - } - - // See golang.org/issues/39555. - input := map[textUnmarshalerString]string{"FOO": "", `"`: ""} - - encoded, err := Marshal(input) - if err != nil { - t.Fatalf("Marshal unexpected error: %v", err) - } - var got map[textUnmarshalerString]string - if err := Unmarshal(encoded, &got); err != nil { - t.Fatalf("Unmarshal unexpected error: %v", err) - } - want := map[textUnmarshalerString]string{"foo": "", `"`: ""} - if !reflect.DeepEqual(want, got) { - t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got) - } -} - -func TestUnmarshalMaxDepth(t *testing.T) { - t.Skip() // TODO - testcases := []struct { - name string - 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, - }, - } - - targets := []struct { - name string - newValue func() any - }{ - { - 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 - }, - }, - { - 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 - }, - }, - } - - for _, tc := range testcases { - for _, target := range targets { - t.Run(target.name+"-"+tc.name, func(t *testing.T) { - err := Unmarshal([]byte(tc.data), target.newValue()) - if !tc.errMaxDepth { - if err != nil { - t.Errorf("unexpected error: %v", 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) - } - } - }) - } - } -} -- cgit v1.2.3-2-g168b