blob: d6616c71547dfd1d8193007b97c6f8c65750b029 [file] [log] [blame]
Herbie Ongd3f8f2d2019-03-06 00:28:23 -08001// 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
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080012 "github.com/google/go-cmp/cmp"
13 "github.com/google/go-cmp/cmp/cmpopts"
Herbie Ong582ab3d2019-09-06 15:56:09 -070014 "google.golang.org/protobuf/internal/detrand"
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/internal/encoding/json"
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080016)
17
Herbie Ong582ab3d2019-09-06 15:56:09 -070018// Disable detrand to enable direct comparisons on outputs.
19func init() { detrand.Disable() }
20
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080021// splitLines is a cmpopts.Option for comparing strings with line breaks.
22var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
23 return strings.Split(s, "\n")
24})
25
26func TestEncoder(t *testing.T) {
27 tests := []struct {
28 desc string
29 write func(*json.Encoder)
30 wantOut string
31 wantOutIndent string
32 }{
33 {
34 desc: "null",
35 write: func(e *json.Encoder) {
36 e.WriteNull()
37 },
Herbie Ong886c3262020-02-03 14:33:01 -080038 wantOut: `null`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080039 },
40 {
41 desc: "true",
42 write: func(e *json.Encoder) {
43 e.WriteBool(true)
44 },
Herbie Ong886c3262020-02-03 14:33:01 -080045 wantOut: `true`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080046 },
47 {
48 desc: "false",
49 write: func(e *json.Encoder) {
50 e.WriteBool(false)
51 },
Herbie Ong886c3262020-02-03 14:33:01 -080052 wantOut: `false`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080053 },
54 {
55 desc: "string",
56 write: func(e *json.Encoder) {
57 e.WriteString("hello world")
58 },
Herbie Ong886c3262020-02-03 14:33:01 -080059 wantOut: `"hello world"`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080060 },
61 {
62 desc: "string contains escaped characters",
63 write: func(e *json.Encoder) {
64 e.WriteString("\u0000\"\\/\b\f\n\r\t")
65 },
66 wantOut: `"\u0000\"\\/\b\f\n\r\t"`,
67 },
68 {
69 desc: "float64",
70 write: func(e *json.Encoder) {
71 e.WriteFloat(1.0199999809265137, 64)
72 },
Herbie Ong886c3262020-02-03 14:33:01 -080073 wantOut: `1.0199999809265137`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080074 },
75 {
76 desc: "float64 max value",
77 write: func(e *json.Encoder) {
78 e.WriteFloat(math.MaxFloat64, 64)
79 },
Herbie Ong886c3262020-02-03 14:33:01 -080080 wantOut: `1.7976931348623157e+308`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080081 },
82 {
83 desc: "float64 min value",
84 write: func(e *json.Encoder) {
85 e.WriteFloat(-math.MaxFloat64, 64)
86 },
Herbie Ong886c3262020-02-03 14:33:01 -080087 wantOut: `-1.7976931348623157e+308`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080088 },
89 {
90 desc: "float64 NaN",
91 write: func(e *json.Encoder) {
92 e.WriteFloat(math.NaN(), 64)
93 },
Herbie Ong886c3262020-02-03 14:33:01 -080094 wantOut: `"NaN"`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080095 },
96 {
97 desc: "float64 Infinity",
98 write: func(e *json.Encoder) {
99 e.WriteFloat(math.Inf(+1), 64)
100 },
Herbie Ong886c3262020-02-03 14:33:01 -0800101 wantOut: `"Infinity"`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800102 },
103 {
104 desc: "float64 -Infinity",
105 write: func(e *json.Encoder) {
106 e.WriteFloat(math.Inf(-1), 64)
107 },
Herbie Ong886c3262020-02-03 14:33:01 -0800108 wantOut: `"-Infinity"`,
109 },
110 {
111 desc: "float64 negative zero",
112 write: func(e *json.Encoder) {
113 e.WriteFloat(math.Copysign(0, -1), 64)
114 },
115 wantOut: `-0`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800116 },
117 {
118 desc: "float32",
119 write: func(e *json.Encoder) {
120 e.WriteFloat(1.02, 32)
121 },
Herbie Ong886c3262020-02-03 14:33:01 -0800122 wantOut: `1.02`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800123 },
124 {
125 desc: "float32 max value",
126 write: func(e *json.Encoder) {
127 e.WriteFloat(math.MaxFloat32, 32)
128 },
Herbie Ong886c3262020-02-03 14:33:01 -0800129 wantOut: `3.4028235e+38`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800130 },
131 {
132 desc: "float32 min value",
133 write: func(e *json.Encoder) {
134 e.WriteFloat(-math.MaxFloat32, 32)
135 },
Herbie Ong886c3262020-02-03 14:33:01 -0800136 wantOut: `-3.4028235e+38`,
137 },
138 {
139 desc: "float32 negative zero",
140 write: func(e *json.Encoder) {
141 e.WriteFloat(math.Copysign(0, -1), 32)
142 },
143 wantOut: `-0`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800144 },
145 {
146 desc: "int",
147 write: func(e *json.Encoder) {
148 e.WriteInt(-math.MaxInt64)
149 },
Herbie Ong886c3262020-02-03 14:33:01 -0800150 wantOut: `-9223372036854775807`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800151 },
152 {
153 desc: "uint",
154 write: func(e *json.Encoder) {
155 e.WriteUint(math.MaxUint64)
156 },
Herbie Ong886c3262020-02-03 14:33:01 -0800157 wantOut: `18446744073709551615`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800158 },
159 {
160 desc: "empty object",
161 write: func(e *json.Encoder) {
162 e.StartObject()
163 e.EndObject()
164 },
Herbie Ong886c3262020-02-03 14:33:01 -0800165 wantOut: `{}`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800166 },
167 {
168 desc: "empty array",
169 write: func(e *json.Encoder) {
170 e.StartArray()
171 e.EndArray()
172 },
Herbie Ong886c3262020-02-03 14:33:01 -0800173 wantOut: `[]`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800174 },
175 {
176 desc: "object with one member",
177 write: func(e *json.Encoder) {
178 e.StartObject()
179 e.WriteName("hello")
180 e.WriteString("world")
181 e.EndObject()
182 },
183 wantOut: `{"hello":"world"}`,
184 wantOutIndent: `{
185 "hello": "world"
186}`,
187 },
188 {
189 desc: "array with one member",
190 write: func(e *json.Encoder) {
191 e.StartArray()
192 e.WriteNull()
193 e.EndArray()
194 },
195 wantOut: `[null]`,
196 wantOutIndent: `[
197 null
198]`,
199 },
200 {
201 desc: "simple object",
202 write: func(e *json.Encoder) {
203 e.StartObject()
204 {
205 e.WriteName("null")
206 e.WriteNull()
207 }
208 {
209 e.WriteName("bool")
210 e.WriteBool(true)
211 }
212 {
213 e.WriteName("string")
214 e.WriteString("hello")
215 }
216 {
217 e.WriteName("float")
218 e.WriteFloat(6.28318, 64)
219 }
220 {
221 e.WriteName("int")
222 e.WriteInt(42)
223 }
224 {
225 e.WriteName("uint")
226 e.WriteUint(47)
227 }
228 e.EndObject()
229 },
230 wantOut: `{"null":null,"bool":true,"string":"hello","float":6.28318,"int":42,"uint":47}`,
231 wantOutIndent: `{
232 "null": null,
233 "bool": true,
234 "string": "hello",
235 "float": 6.28318,
236 "int": 42,
237 "uint": 47
238}`,
239 },
240 {
241 desc: "simple array",
242 write: func(e *json.Encoder) {
243 e.StartArray()
244 {
245 e.WriteString("hello")
246 e.WriteFloat(6.28318, 32)
247 e.WriteInt(42)
248 e.WriteUint(47)
249 e.WriteBool(true)
250 e.WriteNull()
251 }
252 e.EndArray()
253 },
254 wantOut: `["hello",6.28318,42,47,true,null]`,
255 wantOutIndent: `[
256 "hello",
257 6.28318,
258 42,
259 47,
260 true,
261 null
262]`,
263 },
264 {
265 desc: "fancy object",
266 write: func(e *json.Encoder) {
267 e.StartObject()
268 {
269 e.WriteName("object0")
270 e.StartObject()
271 e.EndObject()
272 }
273 {
274 e.WriteName("array0")
275 e.StartArray()
276 e.EndArray()
277 }
278 {
279 e.WriteName("object1")
280 e.StartObject()
281 {
282 e.WriteName("null")
283 e.WriteNull()
284 }
285 {
286 e.WriteName("object1-1")
287 e.StartObject()
288 {
289 e.WriteName("bool")
290 e.WriteBool(false)
291 }
292 {
293 e.WriteName("float")
294 e.WriteFloat(3.14159, 32)
295 }
296 e.EndObject()
297 }
298 e.EndObject()
299 }
300 {
301 e.WriteName("array1")
302 e.StartArray()
303 {
304 e.WriteNull()
305 e.StartObject()
306 e.EndObject()
307 e.StartObject()
308 {
309 e.WriteName("hello")
310 e.WriteString("world")
311 }
312 {
313 e.WriteName("hola")
314 e.WriteString("mundo")
315 }
316 e.EndObject()
317 e.StartArray()
318 {
319 e.WriteUint(1)
320 e.WriteUint(0)
321 e.WriteUint(1)
322 }
323 e.EndArray()
324 }
325 e.EndArray()
326 }
327 e.EndObject()
328 },
329 wantOutIndent: `{
330 "object0": {},
331 "array0": [],
332 "object1": {
333 "null": null,
334 "object1-1": {
335 "bool": false,
336 "float": 3.14159
337 }
338 },
339 "array1": [
340 null,
341 {},
342 {
343 "hello": "world",
344 "hola": "mundo"
345 },
346 [
347 1,
348 0,
349 1
350 ]
351 ]
352}`,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800353 }}
354
355 for _, tc := range tests {
356 t.Run(tc.desc, func(t *testing.T) {
357 if tc.wantOut != "" {
358 enc, err := json.NewEncoder("")
359 if err != nil {
360 t.Fatalf("NewEncoder() returned error: %v", err)
361 }
362 tc.write(enc)
363 got := string(enc.Bytes())
364 if got != tc.wantOut {
365 t.Errorf("%s:\n<got>:\n%v\n<want>\n%v\n", tc.desc, got, tc.wantOut)
366 }
367 }
368 if tc.wantOutIndent != "" {
369 enc, err := json.NewEncoder("\t")
370 if err != nil {
371 t.Fatalf("NewEncoder() returned error: %v", err)
372 }
373 tc.write(enc)
374 got, want := string(enc.Bytes()), tc.wantOutIndent
375 if got != want {
376 t.Errorf("%s(indent):\n<got>:\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
377 tc.desc, got, want, cmp.Diff(want, got, splitLines))
378 }
379 }
380 })
381 }
382}
383
384func TestWriteStringError(t *testing.T) {
385 tests := []string{"abc\xff"}
386
387 for _, in := range tests {
388 t.Run(in, func(t *testing.T) {
389 enc, err := json.NewEncoder("")
390 if err != nil {
391 t.Fatalf("NewEncoder() returned error: %v", err)
392 }
393 if err := enc.WriteString(in); err == nil {
394 t.Errorf("WriteString(%v): got nil error, want error", in)
395 }
396 })
397 }
398}