|  | // 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) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |