| | |
| | |
| | |
| |
|
| | |
| |
|
| | package jsontext |
| |
|
| | import ( |
| | "bytes" |
| | "errors" |
| | "fmt" |
| | "io" |
| | "net" |
| | "path" |
| | "reflect" |
| | "slices" |
| | "strings" |
| | "testing" |
| | "testing/iotest" |
| |
|
| | "encoding/json/internal/jsonflags" |
| | "encoding/json/internal/jsontest" |
| | "encoding/json/internal/jsonwire" |
| | ) |
| |
|
| | |
| | func equalTokens(xs, ys []Token) bool { |
| | if len(xs) != len(ys) { |
| | return false |
| | } |
| | for i := range xs { |
| | if !(reflect.DeepEqual(xs[i], ys[i]) || xs[i].String() == ys[i].String()) { |
| | return false |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | |
| | func TestDecoder(t *testing.T) { |
| | for _, td := range coderTestdata { |
| | for _, typeName := range []string{"Token", "Value", "TokenDelims"} { |
| | t.Run(path.Join(td.name.Name, typeName), func(t *testing.T) { |
| | testDecoder(t, td.name.Where, typeName, td) |
| | }) |
| | } |
| | } |
| | } |
| | func testDecoder(t *testing.T, where jsontest.CasePos, typeName string, td coderTestdataEntry) { |
| | dec := NewDecoder(bytes.NewBufferString(td.in)) |
| | switch typeName { |
| | case "Token": |
| | var tokens []Token |
| | var pointers []Pointer |
| | for { |
| | tok, err := dec.ReadToken() |
| | if err != nil { |
| | if err == io.EOF { |
| | break |
| | } |
| | t.Fatalf("%s: Decoder.ReadToken error: %v", where, err) |
| | } |
| | tokens = append(tokens, tok.Clone()) |
| | if td.pointers != nil { |
| | pointers = append(pointers, dec.StackPointer()) |
| | } |
| | } |
| | if !equalTokens(tokens, td.tokens) { |
| | t.Fatalf("%s: tokens mismatch:\ngot %v\nwant %v", where, tokens, td.tokens) |
| | } |
| | if !slices.Equal(pointers, td.pointers) { |
| | t.Fatalf("%s: pointers mismatch:\ngot %q\nwant %q", where, pointers, td.pointers) |
| | } |
| | case "Value": |
| | val, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("%s: Decoder.ReadValue error: %v", where, err) |
| | } |
| | got := string(val) |
| | want := strings.TrimSpace(td.in) |
| | if got != want { |
| | t.Fatalf("%s: Decoder.ReadValue = %s, want %s", where, got, want) |
| | } |
| | case "TokenDelims": |
| | |
| | var tokens []Token |
| | loop: |
| | for { |
| | switch dec.PeekKind() { |
| | case '{', '}', '[', ']': |
| | tok, err := dec.ReadToken() |
| | if err != nil { |
| | if err == io.EOF { |
| | break loop |
| | } |
| | t.Fatalf("%s: Decoder.ReadToken error: %v", where, err) |
| | } |
| | tokens = append(tokens, tok.Clone()) |
| | default: |
| | val, err := dec.ReadValue() |
| | if err != nil { |
| | if err == io.EOF { |
| | break loop |
| | } |
| | t.Fatalf("%s: Decoder.ReadValue error: %v", where, err) |
| | } |
| | tokens = append(tokens, rawToken(string(val))) |
| | } |
| | } |
| | if !equalTokens(tokens, td.tokens) { |
| | t.Fatalf("%s: tokens mismatch:\ngot %v\nwant %v", where, tokens, td.tokens) |
| | } |
| | } |
| | } |
| |
|
| | |
| | func TestFaultyDecoder(t *testing.T) { |
| | for _, td := range coderTestdata { |
| | for _, typeName := range []string{"Token", "Value"} { |
| | t.Run(path.Join(td.name.Name, typeName), func(t *testing.T) { |
| | testFaultyDecoder(t, td.name.Where, typeName, td) |
| | }) |
| | } |
| | } |
| | } |
| | func testFaultyDecoder(t *testing.T, where jsontest.CasePos, typeName string, td coderTestdataEntry) { |
| | b := &FaultyBuffer{ |
| | B: []byte(td.in), |
| | MaxBytes: 1, |
| | MayError: io.ErrNoProgress, |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | dec := NewDecoder(b) |
| | switch typeName { |
| | case "Token": |
| | var tokens []Token |
| | for { |
| | tok, err := dec.ReadToken() |
| | if err != nil { |
| | if err == io.EOF { |
| | break |
| | } |
| | if !errors.Is(err, io.ErrNoProgress) { |
| | t.Fatalf("%s: %d: Decoder.ReadToken error: %v", where, len(tokens), err) |
| | } |
| | continue |
| | } |
| | tokens = append(tokens, tok.Clone()) |
| | } |
| | if !equalTokens(tokens, td.tokens) { |
| | t.Fatalf("%s: tokens mismatch:\ngot %s\nwant %s", where, tokens, td.tokens) |
| | } |
| | case "Value": |
| | for { |
| | val, err := dec.ReadValue() |
| | if err != nil { |
| | if err == io.EOF { |
| | break |
| | } |
| | if !errors.Is(err, io.ErrNoProgress) { |
| | t.Fatalf("%s: Decoder.ReadValue error: %v", where, err) |
| | } |
| | continue |
| | } |
| | got := string(val) |
| | want := strings.TrimSpace(td.in) |
| | if got != want { |
| | t.Fatalf("%s: Decoder.ReadValue = %s, want %s", where, got, want) |
| | } |
| | } |
| | } |
| | } |
| |
|
| | type decoderMethodCall struct { |
| | wantKind Kind |
| | wantOut tokOrVal |
| | wantErr error |
| | wantPointer Pointer |
| | } |
| |
|
| | var decoderErrorTestdata = []struct { |
| | name jsontest.CaseName |
| | opts []Options |
| | in string |
| | calls []decoderMethodCall |
| | wantOffset int |
| | }{{ |
| | name: jsontest.Name("InvalidStart"), |
| | in: ` #`, |
| | calls: []decoderMethodCall{ |
| | {0, zeroToken, newInvalidCharacterError("#", "at start of value").withPos(" ", ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError("#", "at start of value").withPos(" ", ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("StreamN0"), |
| | in: ` `, |
| | calls: []decoderMethodCall{ |
| | {0, zeroToken, io.EOF, ""}, |
| | {0, zeroValue, io.EOF, ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("StreamN1"), |
| | in: ` null `, |
| | calls: []decoderMethodCall{ |
| | {'n', Null, nil, ""}, |
| | {0, zeroToken, io.EOF, ""}, |
| | {0, zeroValue, io.EOF, ""}, |
| | }, |
| | wantOffset: len(` null`), |
| | }, { |
| | name: jsontest.Name("StreamN2"), |
| | in: ` nullnull `, |
| | calls: []decoderMethodCall{ |
| | {'n', Null, nil, ""}, |
| | {'n', Null, nil, ""}, |
| | {0, zeroToken, io.EOF, ""}, |
| | {0, zeroValue, io.EOF, ""}, |
| | }, |
| | wantOffset: len(` nullnull`), |
| | }, { |
| | name: jsontest.Name("StreamN2/ExtraComma"), |
| | in: ` null , null `, |
| | calls: []decoderMethodCall{ |
| | {'n', Null, nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", `at start of value`).withPos(` null `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", `at start of value`).withPos(` null `, ""), ""}, |
| | }, |
| | wantOffset: len(` null`), |
| | }, { |
| | name: jsontest.Name("TruncatedNull"), |
| | in: `nul`, |
| | calls: []decoderMethodCall{ |
| | {'n', zeroToken, E(io.ErrUnexpectedEOF).withPos(`nul`, ""), ""}, |
| | {'n', zeroValue, E(io.ErrUnexpectedEOF).withPos(`nul`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("InvalidNull"), |
| | in: `nulL`, |
| | calls: []decoderMethodCall{ |
| | {'n', zeroToken, newInvalidCharacterError("L", `in literal null (expecting 'l')`).withPos(`nul`, ""), ""}, |
| | {'n', zeroValue, newInvalidCharacterError("L", `in literal null (expecting 'l')`).withPos(`nul`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("TruncatedFalse"), |
| | in: `fals`, |
| | calls: []decoderMethodCall{ |
| | {'f', zeroToken, E(io.ErrUnexpectedEOF).withPos(`fals`, ""), ""}, |
| | {'f', zeroValue, E(io.ErrUnexpectedEOF).withPos(`fals`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("InvalidFalse"), |
| | in: `falsE`, |
| | calls: []decoderMethodCall{ |
| | {'f', zeroToken, newInvalidCharacterError("E", `in literal false (expecting 'e')`).withPos(`fals`, ""), ""}, |
| | {'f', zeroValue, newInvalidCharacterError("E", `in literal false (expecting 'e')`).withPos(`fals`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("TruncatedTrue"), |
| | in: `tru`, |
| | calls: []decoderMethodCall{ |
| | {'t', zeroToken, E(io.ErrUnexpectedEOF).withPos(`tru`, ""), ""}, |
| | {'t', zeroValue, E(io.ErrUnexpectedEOF).withPos(`tru`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("InvalidTrue"), |
| | in: `truE`, |
| | calls: []decoderMethodCall{ |
| | {'t', zeroToken, newInvalidCharacterError("E", `in literal true (expecting 'e')`).withPos(`tru`, ""), ""}, |
| | {'t', zeroValue, newInvalidCharacterError("E", `in literal true (expecting 'e')`).withPos(`tru`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("TruncatedString"), |
| | in: `"start`, |
| | calls: []decoderMethodCall{ |
| | {'"', zeroToken, E(io.ErrUnexpectedEOF).withPos(`"start`, ""), ""}, |
| | {'"', zeroValue, E(io.ErrUnexpectedEOF).withPos(`"start`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("InvalidString"), |
| | in: `"ok` + "\x00", |
| | calls: []decoderMethodCall{ |
| | {'"', zeroToken, newInvalidCharacterError("\x00", `in string (expecting non-control character)`).withPos(`"ok`, ""), ""}, |
| | {'"', zeroValue, newInvalidCharacterError("\x00", `in string (expecting non-control character)`).withPos(`"ok`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("ValidString/AllowInvalidUTF8/Token"), |
| | opts: []Options{AllowInvalidUTF8(true)}, |
| | in: "\"living\xde\xad\xbe\xef\"", |
| | calls: []decoderMethodCall{ |
| | {'"', rawToken("\"living\xde\xad\xbe\xef\""), nil, ""}, |
| | }, |
| | wantOffset: len("\"living\xde\xad\xbe\xef\""), |
| | }, { |
| | name: jsontest.Name("ValidString/AllowInvalidUTF8/Value"), |
| | opts: []Options{AllowInvalidUTF8(true)}, |
| | in: "\"living\xde\xad\xbe\xef\"", |
| | calls: []decoderMethodCall{ |
| | {'"', Value("\"living\xde\xad\xbe\xef\""), nil, ""}, |
| | }, |
| | wantOffset: len("\"living\xde\xad\xbe\xef\""), |
| | }, { |
| | name: jsontest.Name("InvalidString/RejectInvalidUTF8"), |
| | opts: []Options{AllowInvalidUTF8(false)}, |
| | in: "\"living\xde\xad\xbe\xef\"", |
| | calls: []decoderMethodCall{ |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos("\"living\xde\xad", ""), ""}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos("\"living\xde\xad", ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("TruncatedNumber"), |
| | in: `0.`, |
| | calls: []decoderMethodCall{ |
| | {'0', zeroToken, E(io.ErrUnexpectedEOF), ""}, |
| | {'0', zeroValue, E(io.ErrUnexpectedEOF), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("InvalidNumber"), |
| | in: `0.e`, |
| | calls: []decoderMethodCall{ |
| | {'0', zeroToken, newInvalidCharacterError("e", "in number (expecting digit)").withPos(`0.`, ""), ""}, |
| | {'0', zeroValue, newInvalidCharacterError("e", "in number (expecting digit)").withPos(`0.`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("TruncatedObject/AfterStart"), |
| | in: `{`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos("{", ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos("{", ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos("{", ""), ""}, |
| | }, |
| | wantOffset: len(`{`), |
| | }, { |
| | name: jsontest.Name("TruncatedObject/AfterName"), |
| | in: `{"0"`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0"`, "/0"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"0"`, "/0"), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0"`, "/0"), ""}, |
| | }, |
| | wantOffset: len(`{"0"`), |
| | }, { |
| | name: jsontest.Name("TruncatedObject/AfterColon"), |
| | in: `{"0":`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":`, "/0"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"0":`, "/0"), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":`, "/0"), ""}, |
| | }, |
| | wantOffset: len(`{"0"`), |
| | }, { |
| | name: jsontest.Name("TruncatedObject/AfterValue"), |
| | in: `{"0":0`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":0`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"0":0`, ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":0`, ""), ""}, |
| | }, |
| | wantOffset: len(`{"0":0`), |
| | }, { |
| | name: jsontest.Name("TruncatedObject/AfterComma"), |
| | in: `{"0":0,`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":0,`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"0":0,`, ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"0":0,`, ""), ""}, |
| | }, |
| | wantOffset: len(`{"0":0`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingColon"), |
| | in: ` { "fizz" "buzz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("\"", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError("\"", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {0, zeroValue, newInvalidCharacterError("\"", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | }, |
| | wantOffset: len(` { "fizz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingColon/GotComma"), |
| | in: ` { "fizz" , "buzz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | }, |
| | wantOffset: len(` { "fizz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingColon/GotHash"), |
| | in: ` { "fizz" # "buzz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("#", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError("#", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | {0, zeroValue, newInvalidCharacterError("#", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""}, |
| | }, |
| | wantOffset: len(` { "fizz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingComma"), |
| | in: ` { "fizz" : "buzz" "gazz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("\"", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {'"', String("buzz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError("\"", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError("\"", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | }, |
| | wantOffset: len(` { "fizz" : "buzz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingComma/GotColon"), |
| | in: ` { "fizz" : "buzz" : "gazz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {'"', String("buzz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | }, |
| | wantOffset: len(` { "fizz" : "buzz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MissingComma/GotHash"), |
| | in: ` { "fizz" : "buzz" # "gazz" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("#", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {'"', String("buzz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError("#", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError("#", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | }, |
| | wantOffset: len(` { "fizz" : "buzz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/ExtraComma/AfterStart"), |
| | in: ` { , } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(",", `at start of string (expecting '"')`).withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", `at start of value`).withPos(` { `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", `at start of value`).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/ExtraComma/AfterValue"), |
| | in: ` { "fizz" : "buzz" , } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("}", `at start of string (expecting '"')`).withPos(` { "fizz" : "buzz" , `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {'"', String("buzz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", `at start of value`).withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", `at start of value`).withPos(` { "fizz" : "buzz" `, ""), ""}, |
| | }, |
| | wantOffset: len(` { "fizz" : "buzz"`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotNull"), |
| | in: ` { null : null } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("n", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'n', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'n', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotFalse"), |
| | in: ` { false : false } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("f", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'f', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'f', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotTrue"), |
| | in: ` { true : true } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("t", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'t', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'t', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotNumber"), |
| | in: ` { 0 : 0 } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("0", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'0', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'0', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotObject"), |
| | in: ` { {} : {} } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("{", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'{', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'{', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/InvalidName/GotArray"), |
| | in: ` { [] : [] } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("[", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'[', zeroToken, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | {'[', zeroValue, E(ErrNonStringName).withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/MismatchingDelim"), |
| | in: ` { ] `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError("]", "at start of string (expecting '\"')").withPos(` { `, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {']', zeroToken, newInvalidCharacterError("]", "at start of value").withPos(` { `, ""), ""}, |
| | {']', zeroValue, newInvalidCharacterError("]", "at start of value").withPos(` { `, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("ValidObject/InvalidValue"), |
| | in: ` { } `, |
| | calls: []decoderMethodCall{ |
| | {'{', BeginObject, nil, ""}, |
| | {'}', zeroValue, newInvalidCharacterError("}", "at start of value").withPos(" { ", ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("ValidObject/UniqueNames"), |
| | in: `{"0":0,"1":1} `, |
| | calls: []decoderMethodCall{ |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {'"', String("1"), nil, ""}, |
| | {'0', Uint(1), nil, ""}, |
| | {'}', EndObject, nil, ""}, |
| | }, |
| | wantOffset: len(`{"0":0,"1":1}`), |
| | }, { |
| | name: jsontest.Name("ValidObject/DuplicateNames"), |
| | opts: []Options{AllowDuplicateNames(true)}, |
| | in: `{"0":0,"0":0} `, |
| | calls: []decoderMethodCall{ |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {'"', String("0"), nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {'}', EndObject, nil, ""}, |
| | }, |
| | wantOffset: len(`{"0":0,"0":0}`), |
| | }, { |
| | name: jsontest.Name("InvalidObject/DuplicateNames"), |
| | in: `{"X":{},"Y":{},"X":{}} `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/X"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("X"), nil, ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'}', EndObject, nil, ""}, |
| | {'"', String("Y"), nil, ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'}', EndObject, nil, ""}, |
| | {'"', zeroToken, E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/X"), "/Y"}, |
| | {'"', zeroValue, E(ErrDuplicateName).withPos(`{"0":{},"Y":{},`, "/X"), "/Y"}, |
| | }, |
| | wantOffset: len(`{"0":{},"1":{}`), |
| | }, { |
| | name: jsontest.Name("TruncatedArray/AfterStart"), |
| | in: `[`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(io.ErrUnexpectedEOF).withPos("[", ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos("[", ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos("[", ""), ""}, |
| | }, |
| | wantOffset: len(`[`), |
| | }, { |
| | name: jsontest.Name("TruncatedArray/AfterValue"), |
| | in: `[0`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(io.ErrUnexpectedEOF).withPos("[0", ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos("[0", ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos("[0", ""), ""}, |
| | }, |
| | wantOffset: len(`[0`), |
| | }, { |
| | name: jsontest.Name("TruncatedArray/AfterComma"), |
| | in: `[0,`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(io.ErrUnexpectedEOF).withPos("[0,", ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'0', Uint(0), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos("[0,", ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos("[0,", ""), ""}, |
| | }, |
| | wantOffset: len(`[0`), |
| | }, { |
| | name: jsontest.Name("InvalidArray/MissingComma"), |
| | in: ` [ "fizz" "buzz" ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, newInvalidCharacterError("\"", "after array element (expecting ',' or ']')").withPos(` [ "fizz" `, ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("fizz"), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError("\"", "after array element (expecting ',' or ']')").withPos(` [ "fizz" `, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError("\"", "after array element (expecting ',' or ']')").withPos(` [ "fizz" `, ""), ""}, |
| | }, |
| | wantOffset: len(` [ "fizz"`), |
| | }, { |
| | name: jsontest.Name("InvalidArray/MismatchingDelim"), |
| | in: ` [ } `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, newInvalidCharacterError("}", "at start of value").withPos(` [ `, "/0"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'}', zeroToken, newInvalidCharacterError("}", "at start of value").withPos(` [ `, "/0"), ""}, |
| | {'}', zeroValue, newInvalidCharacterError("}", "at start of value").withPos(` [ `, "/0"), ""}, |
| | }, |
| | wantOffset: len(` [`), |
| | }, { |
| | name: jsontest.Name("ValidArray/InvalidValue"), |
| | in: ` [ ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', BeginArray, nil, ""}, |
| | {']', zeroValue, newInvalidCharacterError("]", "at start of value").withPos(" [ ", "/0"), ""}, |
| | }, |
| | wantOffset: len(` [`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterTopLevel"), |
| | in: `"",`, |
| | calls: []decoderMethodCall{ |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", "at start of value").withPos(`""`, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", "at start of value").withPos(`""`, ""), ""}, |
| | }, |
| | wantOffset: len(`""`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterBeginObject"), |
| | in: `{:`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(":", `at start of string (expecting '"')`).withPos(`{`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(":", "at start of value").withPos(`{`, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(":", "at start of value").withPos(`{`, ""), ""}, |
| | }, |
| | wantOffset: len(`{`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterObjectName"), |
| | in: `{"",`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(`{""`, "/"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(`{""`, "/"), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", "after object name (expecting ':')").withPos(`{""`, "/"), ""}, |
| | }, |
| | wantOffset: len(`{""`), |
| | }, { |
| | name: jsontest.Name("ValidDelim/AfterObjectName"), |
| | in: `{"":`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"":`, "/"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"":`, "/"), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"":`, "/"), ""}, |
| | }, |
| | wantOffset: len(`{""`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterObjectValue"), |
| | in: `{"":"":`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(`{"":""`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(`{"":""`, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(`{"":""`, ""), ""}, |
| | }, |
| | wantOffset: len(`{"":""`), |
| | }, { |
| | name: jsontest.Name("ValidDelim/AfterObjectValue"), |
| | in: `{"":"",`, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"":"",`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`{"":"",`, ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`{"":"",`, ""), ""}, |
| | }, |
| | wantOffset: len(`{"":""`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterBeginArray"), |
| | in: `[,`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, newInvalidCharacterError(",", "at start of value").withPos(`[`, "/0"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(",", "at start of value").withPos(`[`, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(",", "at start of value").withPos(`[`, ""), ""}, |
| | }, |
| | wantOffset: len(`[`), |
| | }, { |
| | name: jsontest.Name("InvalidDelim/AfterArrayValue"), |
| | in: `["":`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, newInvalidCharacterError(":", "after array element (expecting ',' or ']')").withPos(`[""`, ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, newInvalidCharacterError(":", "after array element (expecting ',' or ']')").withPos(`[""`, ""), ""}, |
| | {0, zeroValue, newInvalidCharacterError(":", "after array element (expecting ',' or ']')").withPos(`[""`, ""), ""}, |
| | }, |
| | wantOffset: len(`[""`), |
| | }, { |
| | name: jsontest.Name("ValidDelim/AfterArrayValue"), |
| | in: `["",`, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(io.ErrUnexpectedEOF).withPos(`["",`, ""), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String(""), nil, ""}, |
| | {0, zeroToken, E(io.ErrUnexpectedEOF).withPos(`["",`, ""), ""}, |
| | {0, zeroValue, E(io.ErrUnexpectedEOF).withPos(`["",`, ""), ""}, |
| | }, |
| | wantOffset: len(`[""`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition"), |
| | in: ` "a` + "\xff" + `0" `, |
| | calls: []decoderMethodCall{ |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` "a`, ""), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` "a`, ""), ""}, |
| | }, |
| | }, { |
| | name: jsontest.Name("ErrorPosition/0"), |
| | in: ` [ "a` + "\xff" + `1" ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a`, "/0"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a`, "/0"), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a`, "/0"), ""}, |
| | }, |
| | wantOffset: len(` [`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/1"), |
| | in: ` [ "a1" , "b` + "\xff" + `1" ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , "b`, "/1"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("a1"), nil, ""}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , "b`, "/1"), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , "b`, "/1"), ""}, |
| | }, |
| | wantOffset: len(` [ "a1"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/0/0"), |
| | in: ` [ [ "a` + "\xff" + `2" ] ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a`, "/0/0"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a`, "/0/0"), ""}, |
| | {'[', BeginArray, nil, "/0"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a`, "/0/0"), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a`, "/0/0"), ""}, |
| | }, |
| | wantOffset: len(` [ [`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/1/0"), |
| | in: ` [ "a1" , [ "a` + "\xff" + `2" ] ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a`, "/1/0"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("a1"), nil, "/0"}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a`, "/1/0"), "/0"}, |
| | {'[', BeginArray, nil, "/1"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a`, "/1/0"), "/1"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a`, "/1/0"), "/1"}, |
| | }, |
| | wantOffset: len(` [ "a1" , [`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/0/1"), |
| | in: ` [ [ "a2" , "b` + "\xff" + `2" ] ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a2" , "b`, "/0/1"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a2" , "b`, "/0/1"), ""}, |
| | {'[', BeginArray, nil, "/0"}, |
| | {'"', String("a2"), nil, "/0/0"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a2" , "b`, "/0/1"), "/0/0"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a2" , "b`, "/0/1"), "/0/0"}, |
| | }, |
| | wantOffset: len(` [ [ "a2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/1/1"), |
| | in: ` [ "a1" , [ "a2" , "b` + "\xff" + `2" ] ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a2" , "b`, "/1/1"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("a1"), nil, "/0"}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a2" , "b`, "/1/1"), ""}, |
| | {'[', BeginArray, nil, "/1"}, |
| | {'"', String("a2"), nil, "/1/0"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a2" , "b`, "/1/1"), "/1/0"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a2" , "b`, "/1/1"), "/1/0"}, |
| | }, |
| | wantOffset: len(` [ "a1" , [ "a2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1-"), |
| | in: ` { "a` + "\xff" + `1" : "b1" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a`, ""), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a`, ""), ""}, |
| | }, |
| | wantOffset: len(` {`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1"), |
| | in: ` { "a1" : "b` + "\xff" + `1" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b`, "/a1"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b`, "/a1"), ""}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b`, "/a1"), ""}, |
| | }, |
| | wantOffset: len(` { "a1"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/c1-"), |
| | in: ` { "a1" : "b1" , "c` + "\xff" + `1" : "d1" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c`, ""), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'"', String("b1"), nil, "/a1"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" : "c`, ""), "/a1"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" : "c`, ""), "/a1"}, |
| | }, |
| | wantOffset: len(` { "a1" : "b1"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/c1"), |
| | in: ` { "a1" : "b1" , "c1" : "d` + "\xff" + `1" } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : "d`, "/c1"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'"', String("b1"), nil, "/a1"}, |
| | {'"', String("c1"), nil, "/c1"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" : "c1" : "d`, "/c1"), "/c1"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" : "c1" : "d`, "/c1"), "/c1"}, |
| | }, |
| | wantOffset: len(` { "a1" : "b1" , "c1"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1/a2-"), |
| | in: ` { "a1" : { "a` + "\xff" + `2" : "b2" } } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a`, "/a1"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a`, "/a1"), ""}, |
| | {'{', BeginObject, nil, "/a1"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a`, "/a1"), "/a1"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a`, "/a1"), "/a1"}, |
| | }, |
| | wantOffset: len(` { "a1" : {`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1/a2"), |
| | in: ` { "a1" : { "a2" : "b` + "\xff" + `2" } } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b`, "/a1/a2"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b`, "/a1/a2"), ""}, |
| | {'{', BeginObject, nil, "/a1"}, |
| | {'"', String("a2"), nil, "/a1/a2"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b`, "/a1/a2"), "/a1/a2"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b`, "/a1/a2"), "/a1/a2"}, |
| | }, |
| | wantOffset: len(` { "a1" : { "a2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1/c2-"), |
| | in: ` { "a1" : { "a2" : "b2" , "c` + "\xff" + `2" : "d2" } } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c`, "/a1"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'{', BeginObject, nil, "/a1"}, |
| | {'"', String("a2"), nil, "/a1/a2"}, |
| | {'"', String("b2"), nil, "/a1/a2"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c`, "/a1"), "/a1/a2"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c`, "/a1"), "/a1/a2"}, |
| | }, |
| | wantOffset: len(` { "a1" : { "a2" : "b2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/a1/c2"), |
| | in: ` { "a1" : { "a2" : "b2" , "c2" : "d` + "\xff" + `2" } } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c2" : "d`, "/a1/c2"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c2" : "d`, "/a1/c2"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a2"), nil, "/a1/a2"}, |
| | {'"', String("b2"), nil, "/a1/a2"}, |
| | {'"', String("c2"), nil, "/a1/c2"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c2" : "d`, "/a1/c2"), "/a1/c2"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c2" : "d`, "/a1/c2"), "/a1/c2"}, |
| | }, |
| | wantOffset: len(` { "a1" : { "a2" : "b2" , "c2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/1/a2"), |
| | in: ` [ "a1" , { "a2" : "b` + "\xff" + `2" } ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , { "a2" : "b`, "/1/a2"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("a1"), nil, "/0"}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , { "a2" : "b`, "/1/a2"), ""}, |
| | {'{', BeginObject, nil, "/1"}, |
| | {'"', String("a2"), nil, "/1/a2"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , { "a2" : "b`, "/1/a2"), "/1/a2"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , { "a2" : "b`, "/1/a2"), "/1/a2"}, |
| | }, |
| | wantOffset: len(` [ "a1" , { "a2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/c1/1"), |
| | in: ` { "a1" : "b1" , "c1" : [ "a2" , "b` + "\xff" + `2" ] } `, |
| | calls: []decoderMethodCall{ |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : [ "a2" , "b`, "/c1/1"), ""}, |
| | {'{', BeginObject, nil, ""}, |
| | {'"', String("a1"), nil, "/a1"}, |
| | {'"', String("b1"), nil, "/a1"}, |
| | {'"', String("c1"), nil, "/c1"}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : [ "a2" , "b`, "/c1/1"), ""}, |
| | {'[', BeginArray, nil, "/c1"}, |
| | {'"', String("a2"), nil, "/c1/0"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : [ "a2" , "b`, "/c1/1"), "/c1/0"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : [ "a2" , "b`, "/c1/1"), "/c1/0"}, |
| | }, |
| | wantOffset: len(` { "a1" : "b1" , "c1" : [ "a2"`), |
| | }, { |
| | name: jsontest.Name("ErrorPosition/0/a1/1/c3/1"), |
| | in: ` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b` + "\xff" + `4" ] } ] } ] `, |
| | calls: []decoderMethodCall{ |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""}, |
| | {'{', BeginObject, nil, "/0"}, |
| | {'"', String("a1"), nil, "/0/a1"}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""}, |
| | {'[', BeginArray, nil, ""}, |
| | {'"', String("a2"), nil, "/0/a1/0"}, |
| | {'{', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""}, |
| | {'{', BeginObject, nil, "/0/a1/1"}, |
| | {'"', String("a3"), nil, "/0/a1/1/a3"}, |
| | {'"', String("b3"), nil, "/0/a1/1/a3"}, |
| | {'"', String("c3"), nil, "/0/a1/1/c3"}, |
| | {'[', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""}, |
| | {'[', BeginArray, nil, "/0/a1/1/c3"}, |
| | {'"', String("a4"), nil, "/0/a1/1/c3/0"}, |
| | {'"', zeroValue, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), "/0/a1/1/c3/0"}, |
| | {'"', zeroToken, E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), "/0/a1/1/c3/0"}, |
| | }, |
| | wantOffset: len(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4"`), |
| | }} |
| |
|
| | |
| | |
| | func TestDecoderErrors(t *testing.T) { |
| | for _, td := range decoderErrorTestdata { |
| | t.Run(path.Join(td.name.Name), func(t *testing.T) { |
| | testDecoderErrors(t, td.name.Where, td.opts, td.in, td.calls, td.wantOffset) |
| | }) |
| | } |
| | } |
| | func testDecoderErrors(t *testing.T, where jsontest.CasePos, opts []Options, in string, calls []decoderMethodCall, wantOffset int) { |
| | src := bytes.NewBufferString(in) |
| | dec := NewDecoder(src, opts...) |
| | for i, call := range calls { |
| | gotKind := dec.PeekKind() |
| | if gotKind != call.wantKind { |
| | t.Fatalf("%s: %d: Decoder.PeekKind = %v, want %v", where, i, gotKind, call.wantKind) |
| | } |
| |
|
| | var gotErr error |
| | switch wantOut := call.wantOut.(type) { |
| | case Token: |
| | var gotOut Token |
| | gotOut, gotErr = dec.ReadToken() |
| | if gotOut.String() != wantOut.String() { |
| | t.Fatalf("%s: %d: Decoder.ReadToken = %v, want %v", where, i, gotOut, wantOut) |
| | } |
| | case Value: |
| | var gotOut Value |
| | gotOut, gotErr = dec.ReadValue() |
| | if string(gotOut) != string(wantOut) { |
| | t.Fatalf("%s: %d: Decoder.ReadValue = %s, want %s", where, i, gotOut, wantOut) |
| | } |
| | } |
| | if !equalError(gotErr, call.wantErr) { |
| | t.Fatalf("%s: %d: error mismatch:\ngot %v\nwant %v", where, i, gotErr, call.wantErr) |
| | } |
| | if call.wantPointer != "" { |
| | gotPointer := dec.StackPointer() |
| | if gotPointer != call.wantPointer { |
| | t.Fatalf("%s: %d: Decoder.StackPointer = %s, want %s", where, i, gotPointer, call.wantPointer) |
| | } |
| | } |
| | } |
| | gotOffset := int(dec.InputOffset()) |
| | if gotOffset != wantOffset { |
| | t.Fatalf("%s: Decoder.InputOffset = %v, want %v", where, gotOffset, wantOffset) |
| | } |
| | gotUnread := string(dec.s.unreadBuffer()) |
| | wantUnread := in[wantOffset:] |
| | if !strings.HasPrefix(wantUnread, gotUnread) { |
| | t.Fatalf("%s: Decoder.UnreadBuffer = %v, want %v", where, gotUnread, wantUnread) |
| | } |
| | } |
| |
|
| | |
| | func TestBufferDecoder(t *testing.T) { |
| | bb := bytes.NewBufferString("[null, false, true]") |
| | dec := NewDecoder(bb) |
| | var err error |
| | for { |
| | if _, err = dec.ReadToken(); err != nil { |
| | break |
| | } |
| | bb.WriteByte(' ') |
| | } |
| | want := &ioError{action: "read", err: errBufferWriteAfterNext} |
| | if !equalError(err, want) { |
| | t.Fatalf("error mismatch: got %v, want %v", err, want) |
| | } |
| | } |
| |
|
| | var resumableDecoderTestdata = []string{ |
| | `0`, |
| | `123456789`, |
| | `0.0`, |
| | `0.123456789`, |
| | `0e0`, |
| | `0e+0`, |
| | `0e123456789`, |
| | `0e+123456789`, |
| | `123456789.123456789e+123456789`, |
| | `-0`, |
| | `-123456789`, |
| | `-0.0`, |
| | `-0.123456789`, |
| | `-0e0`, |
| | `-0e-0`, |
| | `-0e123456789`, |
| | `-0e-123456789`, |
| | `-123456789.123456789e-123456789`, |
| |
|
| | `""`, |
| | `"a"`, |
| | `"ab"`, |
| | `"abc"`, |
| | `"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"`, |
| | `"\"\\\/\b\f\n\r\t"`, |
| | `"\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009"`, |
| | `"\ud800\udead"`, |
| | "\"\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602\"", |
| | `"\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\ud83d\ude02"`, |
| | } |
| |
|
| | |
| | |
| | func TestResumableDecoder(t *testing.T) { |
| | for _, want := range resumableDecoderTestdata { |
| | t.Run("", func(t *testing.T) { |
| | dec := NewDecoder(iotest.OneByteReader(strings.NewReader(want))) |
| | got, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("Decoder.ReadValue error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Fatalf("Decoder.ReadValue = %s, want %s", got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func TestBlockingDecoder(t *testing.T) { |
| | values := []string{"null", "false", "true", `""`, `{}`, `[]`} |
| |
|
| | r, w := net.Pipe() |
| | defer r.Close() |
| | defer w.Close() |
| |
|
| | enc := NewEncoder(w, jsonflags.OmitTopLevelNewline|1) |
| | dec := NewDecoder(r) |
| |
|
| | errCh := make(chan error) |
| |
|
| | |
| | for _, want := range values { |
| | go func() { |
| | errCh <- enc.WriteValue(Value(want)) |
| | }() |
| |
|
| | tok, err := dec.ReadToken() |
| | if err != nil { |
| | t.Fatalf("Decoder.ReadToken error: %v", err) |
| | } |
| | got := tok.String() |
| | switch tok.Kind() { |
| | case '"': |
| | got = `"` + got + `"` |
| | case '{', '[': |
| | tok, err := dec.ReadToken() |
| | if err != nil { |
| | t.Fatalf("Decoder.ReadToken error: %v", err) |
| | } |
| | got += tok.String() |
| | } |
| | if got != want { |
| | t.Fatalf("ReadTokens = %s, want %s", got, want) |
| | } |
| |
|
| | if err := <-errCh; err != nil { |
| | t.Fatalf("Encoder.WriteValue error: %v", err) |
| | } |
| | } |
| |
|
| | |
| | for _, want := range values { |
| | go func() { |
| | errCh <- enc.WriteValue(Value(want)) |
| | }() |
| |
|
| | got, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("Decoder.ReadValue error: %v", err) |
| | } |
| | if string(got) != want { |
| | t.Fatalf("ReadValue = %s, want %s", got, want) |
| | } |
| |
|
| | if err := <-errCh; err != nil { |
| | t.Fatalf("Encoder.WriteValue error: %v", err) |
| | } |
| | } |
| | } |
| |
|
| | func TestPeekableDecoder(t *testing.T) { |
| | type operation any |
| | type PeekKind struct { |
| | want Kind |
| | } |
| | type ReadToken struct { |
| | wantKind Kind |
| | wantErr error |
| | } |
| | type ReadValue struct { |
| | wantKind Kind |
| | wantErr error |
| | } |
| | type WriteString struct { |
| | in string |
| | } |
| | ops := []operation{ |
| | PeekKind{0}, |
| | WriteString{"[ "}, |
| | ReadToken{0, io.EOF}, |
| | ReadToken{'[', nil}, |
| |
|
| | PeekKind{0}, |
| | WriteString{"] "}, |
| | ReadValue{0, E(io.ErrUnexpectedEOF).withPos("[ ", "")}, |
| | ReadValue{0, newInvalidCharacterError("]", "at start of value").withPos("[ ", "/0")}, |
| | ReadToken{']', nil}, |
| |
|
| | WriteString{"[ "}, |
| | ReadToken{'[', nil}, |
| |
|
| | WriteString{" null "}, |
| | PeekKind{'n'}, |
| | PeekKind{'n'}, |
| | ReadToken{'n', nil}, |
| |
|
| | WriteString{", "}, |
| | PeekKind{0}, |
| | WriteString{"fal"}, |
| | PeekKind{'f'}, |
| | ReadValue{0, E(io.ErrUnexpectedEOF).withPos("[ ] [ null , fal", "/1")}, |
| | WriteString{"se "}, |
| | ReadValue{'f', nil}, |
| |
|
| | PeekKind{0}, |
| | WriteString{" , "}, |
| | PeekKind{0}, |
| | WriteString{` "" `}, |
| | ReadValue{0, E(io.ErrUnexpectedEOF).withPos("[ ] [ null , false , ", "")}, |
| | ReadValue{'"', nil}, |
| |
|
| | WriteString{" , 0"}, |
| | PeekKind{'0'}, |
| | ReadToken{'0', nil}, |
| |
|
| | WriteString{" , {} , []"}, |
| | PeekKind{'{'}, |
| | ReadValue{'{', nil}, |
| | ReadValue{'[', nil}, |
| |
|
| | WriteString{"]"}, |
| | ReadToken{']', nil}, |
| | } |
| |
|
| | bb := struct{ *bytes.Buffer }{new(bytes.Buffer)} |
| | d := NewDecoder(bb) |
| | for i, op := range ops { |
| | switch op := op.(type) { |
| | case PeekKind: |
| | if got := d.PeekKind(); got != op.want { |
| | t.Fatalf("%d: Decoder.PeekKind() = %v, want %v", i, got, op.want) |
| | } |
| | case ReadToken: |
| | gotTok, gotErr := d.ReadToken() |
| | gotKind := gotTok.Kind() |
| | if gotKind != op.wantKind || !equalError(gotErr, op.wantErr) { |
| | t.Fatalf("%d: Decoder.ReadToken() = (%v, %v), want (%v, %v)", i, gotKind, gotErr, op.wantKind, op.wantErr) |
| | } |
| | case ReadValue: |
| | gotVal, gotErr := d.ReadValue() |
| | gotKind := gotVal.Kind() |
| | if gotKind != op.wantKind || !equalError(gotErr, op.wantErr) { |
| | t.Fatalf("%d: Decoder.ReadValue() = (%v, %v), want (%v, %v)", i, gotKind, gotErr, op.wantKind, op.wantErr) |
| | } |
| | case WriteString: |
| | bb.WriteString(op.in) |
| | default: |
| | panic(fmt.Sprintf("unknown operation: %T", op)) |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func TestDecoderReset(t *testing.T) { |
| | |
| | largeJSON := `{"key1":"value1","key2":"value2","key3":"value3","key4":"value4","key5":"value5"}` |
| | dec := NewDecoder(strings.NewReader(largeJSON)) |
| |
|
| | t.Run("Test capacity preservation", func(t *testing.T) { |
| | |
| | val1, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("first ReadValue failed: %v", err) |
| | } |
| | if string(val1) != largeJSON { |
| | t.Fatalf("first ReadValue = %q, want %q", val1, largeJSON) |
| | } |
| |
|
| | |
| | initialCapacity := cap(dec.s.buf) |
| | if initialCapacity == 0 { |
| | t.Fatalf("expected non-zero buffer capacity after first use") |
| | } |
| |
|
| | |
| | dec.Reset(strings.NewReader(largeJSON)) |
| |
|
| | |
| | preservedCapacity := cap(dec.s.buf) |
| | if preservedCapacity < initialCapacity { |
| | t.Fatalf("buffer capacity reduced after Reset: got %d, want at least %d", preservedCapacity, initialCapacity) |
| | } |
| |
|
| | |
| | val2, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("second ReadValue failed: %v", err) |
| | } |
| | if string(val2) != largeJSON { |
| | t.Fatalf("second ReadValue = %q, want %q", val2, largeJSON) |
| | } |
| | }) |
| |
|
| | var bbBuf []byte |
| | t.Run("Test aliasing with bytes.Buffer", func(t *testing.T) { |
| | |
| | bb := bytes.NewBufferString(largeJSON) |
| | dec.Reset(bb) |
| | bbBuf = bb.Bytes() |
| |
|
| | |
| | val3, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("fourth ReadValue failed: %v", err) |
| | } |
| | if string(val3) != largeJSON { |
| | t.Fatalf("fourth ReadValue = %q, want %q", val3, largeJSON) |
| | } |
| | |
| | if len(dec.s.buf) == 0 || len(bbBuf) == 0 || &dec.s.buf[0] != &bbBuf[0] { |
| | t.Fatalf("decoder buffer does not alias bytes.Buffer") |
| | } |
| | }) |
| |
|
| | t.Run("Test aliasing removed after Reset", func(t *testing.T) { |
| | |
| | dec.Reset(strings.NewReader(largeJSON)) |
| | val4, err := dec.ReadValue() |
| | if err != nil { |
| | t.Fatalf("fifth ReadValue failed: %v", err) |
| | } |
| | if string(val4) != largeJSON { |
| | t.Fatalf("fourth ReadValue = %q, want %q", val4, largeJSON) |
| | } |
| |
|
| | |
| | if len(dec.s.buf) == 0 || len(bbBuf) == 0 || &dec.s.buf[0] == &bbBuf[0] { |
| | t.Fatalf("decoder buffer aliases bytes.Buffer") |
| | } |
| | }) |
| | } |
| |
|