mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-30 03:32:49 +00:00
05cbe34333
Adds MarshalAppend methods to allow for byte slices to be reused. Copies signature from the binary encoding. Small changes to internal json and text libraries to use strconv AppendInt and AppendUint for number encoding. Change-Id: Ife7c8979c1c153a0a0bf9b70b296b8158d38dffc Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/489615 Reviewed-by: Edward McFarlane <emcfarlane000@gmail.com> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Lasse Folger <lassefolger@google.com> Reviewed-by: Damien Neil <dneil@google.com>
400 lines
7.4 KiB
Go
400 lines
7.4 KiB
Go
// Copyright 2019 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 json_test
|
|
|
|
import (
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
|
|
"google.golang.org/protobuf/internal/detrand"
|
|
"google.golang.org/protobuf/internal/encoding/json"
|
|
)
|
|
|
|
// Disable detrand to enable direct comparisons on outputs.
|
|
func init() { detrand.Disable() }
|
|
|
|
// splitLines is a cmpopts.Option for comparing strings with line breaks.
|
|
var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
|
|
return strings.Split(s, "\n")
|
|
})
|
|
|
|
func TestEncoder(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
write func(*json.Encoder)
|
|
wantOut string
|
|
wantOutIndent string
|
|
}{
|
|
{
|
|
desc: "null",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteNull()
|
|
},
|
|
wantOut: `null`,
|
|
},
|
|
{
|
|
desc: "true",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteBool(true)
|
|
},
|
|
wantOut: `true`,
|
|
},
|
|
{
|
|
desc: "false",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteBool(false)
|
|
},
|
|
wantOut: `false`,
|
|
},
|
|
{
|
|
desc: "string",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteString("hello world")
|
|
},
|
|
wantOut: `"hello world"`,
|
|
},
|
|
{
|
|
desc: "string contains escaped characters",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteString("\u0000\"\\/\b\f\n\r\t")
|
|
},
|
|
wantOut: `"\u0000\"\\/\b\f\n\r\t"`,
|
|
},
|
|
{
|
|
desc: "float64",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(1.0199999809265137, 64)
|
|
},
|
|
wantOut: `1.0199999809265137`,
|
|
},
|
|
{
|
|
desc: "float64 max value",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.MaxFloat64, 64)
|
|
},
|
|
wantOut: `1.7976931348623157e+308`,
|
|
},
|
|
{
|
|
desc: "float64 min value",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(-math.MaxFloat64, 64)
|
|
},
|
|
wantOut: `-1.7976931348623157e+308`,
|
|
},
|
|
{
|
|
desc: "float64 NaN",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.NaN(), 64)
|
|
},
|
|
wantOut: `"NaN"`,
|
|
},
|
|
{
|
|
desc: "float64 Infinity",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.Inf(+1), 64)
|
|
},
|
|
wantOut: `"Infinity"`,
|
|
},
|
|
{
|
|
desc: "float64 -Infinity",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.Inf(-1), 64)
|
|
},
|
|
wantOut: `"-Infinity"`,
|
|
},
|
|
{
|
|
desc: "float64 negative zero",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.Copysign(0, -1), 64)
|
|
},
|
|
wantOut: `-0`,
|
|
},
|
|
{
|
|
desc: "float32",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(1.02, 32)
|
|
},
|
|
wantOut: `1.02`,
|
|
},
|
|
{
|
|
desc: "float32 max value",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.MaxFloat32, 32)
|
|
},
|
|
wantOut: `3.4028235e+38`,
|
|
},
|
|
{
|
|
desc: "float32 min value",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(-math.MaxFloat32, 32)
|
|
},
|
|
wantOut: `-3.4028235e+38`,
|
|
},
|
|
{
|
|
desc: "float32 negative zero",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteFloat(math.Copysign(0, -1), 32)
|
|
},
|
|
wantOut: `-0`,
|
|
},
|
|
{
|
|
desc: "int",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteInt(-math.MaxInt64)
|
|
},
|
|
wantOut: `-9223372036854775807`,
|
|
},
|
|
{
|
|
desc: "uint",
|
|
write: func(e *json.Encoder) {
|
|
e.WriteUint(math.MaxUint64)
|
|
},
|
|
wantOut: `18446744073709551615`,
|
|
},
|
|
{
|
|
desc: "empty object",
|
|
write: func(e *json.Encoder) {
|
|
e.StartObject()
|
|
e.EndObject()
|
|
},
|
|
wantOut: `{}`,
|
|
},
|
|
{
|
|
desc: "empty array",
|
|
write: func(e *json.Encoder) {
|
|
e.StartArray()
|
|
e.EndArray()
|
|
},
|
|
wantOut: `[]`,
|
|
},
|
|
{
|
|
desc: "object with one member",
|
|
write: func(e *json.Encoder) {
|
|
e.StartObject()
|
|
e.WriteName("hello")
|
|
e.WriteString("world")
|
|
e.EndObject()
|
|
},
|
|
wantOut: `{"hello":"world"}`,
|
|
wantOutIndent: `{
|
|
"hello": "world"
|
|
}`,
|
|
},
|
|
{
|
|
desc: "array with one member",
|
|
write: func(e *json.Encoder) {
|
|
e.StartArray()
|
|
e.WriteNull()
|
|
e.EndArray()
|
|
},
|
|
wantOut: `[null]`,
|
|
wantOutIndent: `[
|
|
null
|
|
]`,
|
|
},
|
|
{
|
|
desc: "simple object",
|
|
write: func(e *json.Encoder) {
|
|
e.StartObject()
|
|
{
|
|
e.WriteName("null")
|
|
e.WriteNull()
|
|
}
|
|
{
|
|
e.WriteName("bool")
|
|
e.WriteBool(true)
|
|
}
|
|
{
|
|
e.WriteName("string")
|
|
e.WriteString("hello")
|
|
}
|
|
{
|
|
e.WriteName("float")
|
|
e.WriteFloat(6.28318, 64)
|
|
}
|
|
{
|
|
e.WriteName("int")
|
|
e.WriteInt(42)
|
|
}
|
|
{
|
|
e.WriteName("uint")
|
|
e.WriteUint(47)
|
|
}
|
|
e.EndObject()
|
|
},
|
|
wantOut: `{"null":null,"bool":true,"string":"hello","float":6.28318,"int":42,"uint":47}`,
|
|
wantOutIndent: `{
|
|
"null": null,
|
|
"bool": true,
|
|
"string": "hello",
|
|
"float": 6.28318,
|
|
"int": 42,
|
|
"uint": 47
|
|
}`,
|
|
},
|
|
{
|
|
desc: "simple array",
|
|
write: func(e *json.Encoder) {
|
|
e.StartArray()
|
|
{
|
|
e.WriteString("hello")
|
|
e.WriteFloat(6.28318, 32)
|
|
e.WriteInt(42)
|
|
e.WriteUint(47)
|
|
e.WriteBool(true)
|
|
e.WriteNull()
|
|
}
|
|
e.EndArray()
|
|
},
|
|
wantOut: `["hello",6.28318,42,47,true,null]`,
|
|
wantOutIndent: `[
|
|
"hello",
|
|
6.28318,
|
|
42,
|
|
47,
|
|
true,
|
|
null
|
|
]`,
|
|
},
|
|
{
|
|
desc: "fancy object",
|
|
write: func(e *json.Encoder) {
|
|
e.StartObject()
|
|
{
|
|
e.WriteName("object0")
|
|
e.StartObject()
|
|
e.EndObject()
|
|
}
|
|
{
|
|
e.WriteName("array0")
|
|
e.StartArray()
|
|
e.EndArray()
|
|
}
|
|
{
|
|
e.WriteName("object1")
|
|
e.StartObject()
|
|
{
|
|
e.WriteName("null")
|
|
e.WriteNull()
|
|
}
|
|
{
|
|
e.WriteName("object1-1")
|
|
e.StartObject()
|
|
{
|
|
e.WriteName("bool")
|
|
e.WriteBool(false)
|
|
}
|
|
{
|
|
e.WriteName("float")
|
|
e.WriteFloat(3.14159, 32)
|
|
}
|
|
e.EndObject()
|
|
}
|
|
e.EndObject()
|
|
}
|
|
{
|
|
e.WriteName("array1")
|
|
e.StartArray()
|
|
{
|
|
e.WriteNull()
|
|
e.StartObject()
|
|
e.EndObject()
|
|
e.StartObject()
|
|
{
|
|
e.WriteName("hello")
|
|
e.WriteString("world")
|
|
}
|
|
{
|
|
e.WriteName("hola")
|
|
e.WriteString("mundo")
|
|
}
|
|
e.EndObject()
|
|
e.StartArray()
|
|
{
|
|
e.WriteUint(1)
|
|
e.WriteUint(0)
|
|
e.WriteUint(1)
|
|
}
|
|
e.EndArray()
|
|
}
|
|
e.EndArray()
|
|
}
|
|
e.EndObject()
|
|
},
|
|
wantOutIndent: `{
|
|
"object0": {},
|
|
"array0": [],
|
|
"object1": {
|
|
"null": null,
|
|
"object1-1": {
|
|
"bool": false,
|
|
"float": 3.14159
|
|
}
|
|
},
|
|
"array1": [
|
|
null,
|
|
{},
|
|
{
|
|
"hello": "world",
|
|
"hola": "mundo"
|
|
},
|
|
[
|
|
1,
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
}`,
|
|
}}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
if tc.wantOut != "" {
|
|
enc, err := json.NewEncoder(nil, "")
|
|
if err != nil {
|
|
t.Fatalf("NewEncoder() returned error: %v", err)
|
|
}
|
|
tc.write(enc)
|
|
got := string(enc.Bytes())
|
|
if got != tc.wantOut {
|
|
t.Errorf("%s:\n<got>:\n%v\n<want>\n%v\n", tc.desc, got, tc.wantOut)
|
|
}
|
|
}
|
|
if tc.wantOutIndent != "" {
|
|
enc, err := json.NewEncoder(nil, "\t")
|
|
if err != nil {
|
|
t.Fatalf("NewEncoder() returned error: %v", err)
|
|
}
|
|
tc.write(enc)
|
|
got, want := string(enc.Bytes()), tc.wantOutIndent
|
|
if got != want {
|
|
t.Errorf("%s(indent):\n<got>:\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
|
|
tc.desc, got, want, cmp.Diff(want, got, splitLines))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWriteStringError(t *testing.T) {
|
|
tests := []string{"abc\xff"}
|
|
|
|
for _, in := range tests {
|
|
t.Run(in, func(t *testing.T) {
|
|
enc, err := json.NewEncoder(nil, "")
|
|
if err != nil {
|
|
t.Fatalf("NewEncoder() returned error: %v", err)
|
|
}
|
|
if err := enc.WriteString(in); err == nil {
|
|
t.Errorf("WriteString(%v): got nil error, want error", in)
|
|
}
|
|
})
|
|
}
|
|
}
|