xref: /aosp_15_r20/external/golang-protobuf/internal/encoding/json/encode_test.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package json_test
6
7import (
8	"math"
9	"strings"
10	"testing"
11
12	"github.com/google/go-cmp/cmp"
13	"github.com/google/go-cmp/cmp/cmpopts"
14
15	"google.golang.org/protobuf/internal/detrand"
16	"google.golang.org/protobuf/internal/encoding/json"
17)
18
19// Disable detrand to enable direct comparisons on outputs.
20func init() { detrand.Disable() }
21
22// splitLines is a cmpopts.Option for comparing strings with line breaks.
23var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
24	return strings.Split(s, "\n")
25})
26
27func TestEncoder(t *testing.T) {
28	tests := []struct {
29		desc          string
30		write         func(*json.Encoder)
31		wantOut       string
32		wantOutIndent string
33	}{
34		{
35			desc: "null",
36			write: func(e *json.Encoder) {
37				e.WriteNull()
38			},
39			wantOut: `null`,
40		},
41		{
42			desc: "true",
43			write: func(e *json.Encoder) {
44				e.WriteBool(true)
45			},
46			wantOut: `true`,
47		},
48		{
49			desc: "false",
50			write: func(e *json.Encoder) {
51				e.WriteBool(false)
52			},
53			wantOut: `false`,
54		},
55		{
56			desc: "string",
57			write: func(e *json.Encoder) {
58				e.WriteString("hello world")
59			},
60			wantOut: `"hello world"`,
61		},
62		{
63			desc: "string contains escaped characters",
64			write: func(e *json.Encoder) {
65				e.WriteString("\u0000\"\\/\b\f\n\r\t")
66			},
67			wantOut: `"\u0000\"\\/\b\f\n\r\t"`,
68		},
69		{
70			desc: "float64",
71			write: func(e *json.Encoder) {
72				e.WriteFloat(1.0199999809265137, 64)
73			},
74			wantOut: `1.0199999809265137`,
75		},
76		{
77			desc: "float64 max value",
78			write: func(e *json.Encoder) {
79				e.WriteFloat(math.MaxFloat64, 64)
80			},
81			wantOut: `1.7976931348623157e+308`,
82		},
83		{
84			desc: "float64 min value",
85			write: func(e *json.Encoder) {
86				e.WriteFloat(-math.MaxFloat64, 64)
87			},
88			wantOut: `-1.7976931348623157e+308`,
89		},
90		{
91			desc: "float64 NaN",
92			write: func(e *json.Encoder) {
93				e.WriteFloat(math.NaN(), 64)
94			},
95			wantOut: `"NaN"`,
96		},
97		{
98			desc: "float64 Infinity",
99			write: func(e *json.Encoder) {
100				e.WriteFloat(math.Inf(+1), 64)
101			},
102			wantOut: `"Infinity"`,
103		},
104		{
105			desc: "float64 -Infinity",
106			write: func(e *json.Encoder) {
107				e.WriteFloat(math.Inf(-1), 64)
108			},
109			wantOut: `"-Infinity"`,
110		},
111		{
112			desc: "float64 negative zero",
113			write: func(e *json.Encoder) {
114				e.WriteFloat(math.Copysign(0, -1), 64)
115			},
116			wantOut: `-0`,
117		},
118		{
119			desc: "float32",
120			write: func(e *json.Encoder) {
121				e.WriteFloat(1.02, 32)
122			},
123			wantOut: `1.02`,
124		},
125		{
126			desc: "float32 max value",
127			write: func(e *json.Encoder) {
128				e.WriteFloat(math.MaxFloat32, 32)
129			},
130			wantOut: `3.4028235e+38`,
131		},
132		{
133			desc: "float32 min value",
134			write: func(e *json.Encoder) {
135				e.WriteFloat(-math.MaxFloat32, 32)
136			},
137			wantOut: `-3.4028235e+38`,
138		},
139		{
140			desc: "float32 negative zero",
141			write: func(e *json.Encoder) {
142				e.WriteFloat(math.Copysign(0, -1), 32)
143			},
144			wantOut: `-0`,
145		},
146		{
147			desc: "int",
148			write: func(e *json.Encoder) {
149				e.WriteInt(-math.MaxInt64)
150			},
151			wantOut: `-9223372036854775807`,
152		},
153		{
154			desc: "uint",
155			write: func(e *json.Encoder) {
156				e.WriteUint(math.MaxUint64)
157			},
158			wantOut: `18446744073709551615`,
159		},
160		{
161			desc: "empty object",
162			write: func(e *json.Encoder) {
163				e.StartObject()
164				e.EndObject()
165			},
166			wantOut: `{}`,
167		},
168		{
169			desc: "empty array",
170			write: func(e *json.Encoder) {
171				e.StartArray()
172				e.EndArray()
173			},
174			wantOut: `[]`,
175		},
176		{
177			desc: "object with one member",
178			write: func(e *json.Encoder) {
179				e.StartObject()
180				e.WriteName("hello")
181				e.WriteString("world")
182				e.EndObject()
183			},
184			wantOut: `{"hello":"world"}`,
185			wantOutIndent: `{
186	"hello": "world"
187}`,
188		},
189		{
190			desc: "array with one member",
191			write: func(e *json.Encoder) {
192				e.StartArray()
193				e.WriteNull()
194				e.EndArray()
195			},
196			wantOut: `[null]`,
197			wantOutIndent: `[
198	null
199]`,
200		},
201		{
202			desc: "simple object",
203			write: func(e *json.Encoder) {
204				e.StartObject()
205				{
206					e.WriteName("null")
207					e.WriteNull()
208				}
209				{
210					e.WriteName("bool")
211					e.WriteBool(true)
212				}
213				{
214					e.WriteName("string")
215					e.WriteString("hello")
216				}
217				{
218					e.WriteName("float")
219					e.WriteFloat(6.28318, 64)
220				}
221				{
222					e.WriteName("int")
223					e.WriteInt(42)
224				}
225				{
226					e.WriteName("uint")
227					e.WriteUint(47)
228				}
229				e.EndObject()
230			},
231			wantOut: `{"null":null,"bool":true,"string":"hello","float":6.28318,"int":42,"uint":47}`,
232			wantOutIndent: `{
233	"null": null,
234	"bool": true,
235	"string": "hello",
236	"float": 6.28318,
237	"int": 42,
238	"uint": 47
239}`,
240		},
241		{
242			desc: "simple array",
243			write: func(e *json.Encoder) {
244				e.StartArray()
245				{
246					e.WriteString("hello")
247					e.WriteFloat(6.28318, 32)
248					e.WriteInt(42)
249					e.WriteUint(47)
250					e.WriteBool(true)
251					e.WriteNull()
252				}
253				e.EndArray()
254			},
255			wantOut: `["hello",6.28318,42,47,true,null]`,
256			wantOutIndent: `[
257	"hello",
258	6.28318,
259	42,
260	47,
261	true,
262	null
263]`,
264		},
265		{
266			desc: "fancy object",
267			write: func(e *json.Encoder) {
268				e.StartObject()
269				{
270					e.WriteName("object0")
271					e.StartObject()
272					e.EndObject()
273				}
274				{
275					e.WriteName("array0")
276					e.StartArray()
277					e.EndArray()
278				}
279				{
280					e.WriteName("object1")
281					e.StartObject()
282					{
283						e.WriteName("null")
284						e.WriteNull()
285					}
286					{
287						e.WriteName("object1-1")
288						e.StartObject()
289						{
290							e.WriteName("bool")
291							e.WriteBool(false)
292						}
293						{
294							e.WriteName("float")
295							e.WriteFloat(3.14159, 32)
296						}
297						e.EndObject()
298					}
299					e.EndObject()
300				}
301				{
302					e.WriteName("array1")
303					e.StartArray()
304					{
305						e.WriteNull()
306						e.StartObject()
307						e.EndObject()
308						e.StartObject()
309						{
310							e.WriteName("hello")
311							e.WriteString("world")
312						}
313						{
314							e.WriteName("hola")
315							e.WriteString("mundo")
316						}
317						e.EndObject()
318						e.StartArray()
319						{
320							e.WriteUint(1)
321							e.WriteUint(0)
322							e.WriteUint(1)
323						}
324						e.EndArray()
325					}
326					e.EndArray()
327				}
328				e.EndObject()
329			},
330			wantOutIndent: `{
331	"object0": {},
332	"array0": [],
333	"object1": {
334		"null": null,
335		"object1-1": {
336			"bool": false,
337			"float": 3.14159
338		}
339	},
340	"array1": [
341		null,
342		{},
343		{
344			"hello": "world",
345			"hola": "mundo"
346		},
347		[
348			1,
349			0,
350			1
351		]
352	]
353}`,
354		}}
355
356	for _, tc := range tests {
357		t.Run(tc.desc, func(t *testing.T) {
358			if tc.wantOut != "" {
359				enc, err := json.NewEncoder("")
360				if err != nil {
361					t.Fatalf("NewEncoder() returned error: %v", err)
362				}
363				tc.write(enc)
364				got := string(enc.Bytes())
365				if got != tc.wantOut {
366					t.Errorf("%s:\n<got>:\n%v\n<want>\n%v\n", tc.desc, got, tc.wantOut)
367				}
368			}
369			if tc.wantOutIndent != "" {
370				enc, err := json.NewEncoder("\t")
371				if err != nil {
372					t.Fatalf("NewEncoder() returned error: %v", err)
373				}
374				tc.write(enc)
375				got, want := string(enc.Bytes()), tc.wantOutIndent
376				if got != want {
377					t.Errorf("%s(indent):\n<got>:\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
378						tc.desc, got, want, cmp.Diff(want, got, splitLines))
379				}
380			}
381		})
382	}
383}
384
385func TestWriteStringError(t *testing.T) {
386	tests := []string{"abc\xff"}
387
388	for _, in := range tests {
389		t.Run(in, func(t *testing.T) {
390			enc, err := json.NewEncoder("")
391			if err != nil {
392				t.Fatalf("NewEncoder() returned error: %v", err)
393			}
394			if err := enc.WriteString(in); err == nil {
395				t.Errorf("WriteString(%v): got nil error, want error", in)
396			}
397		})
398	}
399}
400