blob: 3f85f65b8c94222d1141024de61265063c4e2fc8 [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"
Damien Neile89e6242019-05-13 23:55:40 -070014 "google.golang.org/protobuf/internal/encoding/json"
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080015)
16
17// splitLines is a cmpopts.Option for comparing strings with line breaks.
18var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
19 return strings.Split(s, "\n")
20})
21
22func TestEncoder(t *testing.T) {
23 tests := []struct {
24 desc string
25 write func(*json.Encoder)
26 wantOut string
27 wantOutIndent string
28 }{
29 {
30 desc: "null",
31 write: func(e *json.Encoder) {
32 e.WriteNull()
33 },
34 wantOut: `null`,
35 wantOutIndent: `null`,
36 },
37 {
38 desc: "true",
39 write: func(e *json.Encoder) {
40 e.WriteBool(true)
41 },
42 wantOut: `true`,
43 wantOutIndent: `true`,
44 },
45 {
46 desc: "false",
47 write: func(e *json.Encoder) {
48 e.WriteBool(false)
49 },
50 wantOut: `false`,
51 wantOutIndent: `false`,
52 },
53 {
54 desc: "string",
55 write: func(e *json.Encoder) {
56 e.WriteString("hello world")
57 },
58 wantOut: `"hello world"`,
59 wantOutIndent: `"hello world"`,
60 },
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 },
73 wantOut: `1.0199999809265137`,
74 wantOutIndent: `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 wantOutIndent: `1.7976931348623157e+308`,
83 },
84 {
85 desc: "float64 min value",
86 write: func(e *json.Encoder) {
87 e.WriteFloat(-math.MaxFloat64, 64)
88 },
89 wantOut: `-1.7976931348623157e+308`,
90 wantOutIndent: `-1.7976931348623157e+308`,
91 },
92 {
93 desc: "float64 NaN",
94 write: func(e *json.Encoder) {
95 e.WriteFloat(math.NaN(), 64)
96 },
97 wantOut: `"NaN"`,
98 wantOutIndent: `"NaN"`,
99 },
100 {
101 desc: "float64 Infinity",
102 write: func(e *json.Encoder) {
103 e.WriteFloat(math.Inf(+1), 64)
104 },
105 wantOut: `"Infinity"`,
106 wantOutIndent: `"Infinity"`,
107 },
108 {
109 desc: "float64 -Infinity",
110 write: func(e *json.Encoder) {
111 e.WriteFloat(math.Inf(-1), 64)
112 },
113 wantOut: `"-Infinity"`,
114 wantOutIndent: `"-Infinity"`,
115 },
116 {
117 desc: "float32",
118 write: func(e *json.Encoder) {
119 e.WriteFloat(1.02, 32)
120 },
121 wantOut: `1.02`,
122 wantOutIndent: `1.02`,
123 },
124 {
125 desc: "float32 max value",
126 write: func(e *json.Encoder) {
127 e.WriteFloat(math.MaxFloat32, 32)
128 },
129 wantOut: `3.4028235e+38`,
130 wantOutIndent: `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 wantOutIndent: `-3.4028235e+38`,
139 },
140 {
141 desc: "int",
142 write: func(e *json.Encoder) {
143 e.WriteInt(-math.MaxInt64)
144 },
145 wantOut: `-9223372036854775807`,
146 wantOutIndent: `-9223372036854775807`,
147 },
148 {
149 desc: "uint",
150 write: func(e *json.Encoder) {
151 e.WriteUint(math.MaxUint64)
152 },
153 wantOut: `18446744073709551615`,
154 wantOutIndent: `18446744073709551615`,
155 },
156 {
157 desc: "empty object",
158 write: func(e *json.Encoder) {
159 e.StartObject()
160 e.EndObject()
161 },
162 wantOut: `{}`,
163 wantOutIndent: `{}`,
164 },
165 {
166 desc: "empty array",
167 write: func(e *json.Encoder) {
168 e.StartArray()
169 e.EndArray()
170 },
171 wantOut: `[]`,
172 wantOutIndent: `[]`,
173 },
174 {
175 desc: "object with one member",
176 write: func(e *json.Encoder) {
177 e.StartObject()
178 e.WriteName("hello")
179 e.WriteString("world")
180 e.EndObject()
181 },
182 wantOut: `{"hello":"world"}`,
183 wantOutIndent: `{
184 "hello": "world"
185}`,
186 },
187 {
188 desc: "array with one member",
189 write: func(e *json.Encoder) {
190 e.StartArray()
191 e.WriteNull()
192 e.EndArray()
193 },
194 wantOut: `[null]`,
195 wantOutIndent: `[
196 null
197]`,
198 },
199 {
200 desc: "simple object",
201 write: func(e *json.Encoder) {
202 e.StartObject()
203 {
204 e.WriteName("null")
205 e.WriteNull()
206 }
207 {
208 e.WriteName("bool")
209 e.WriteBool(true)
210 }
211 {
212 e.WriteName("string")
213 e.WriteString("hello")
214 }
215 {
216 e.WriteName("float")
217 e.WriteFloat(6.28318, 64)
218 }
219 {
220 e.WriteName("int")
221 e.WriteInt(42)
222 }
223 {
224 e.WriteName("uint")
225 e.WriteUint(47)
226 }
227 e.EndObject()
228 },
229 wantOut: `{"null":null,"bool":true,"string":"hello","float":6.28318,"int":42,"uint":47}`,
230 wantOutIndent: `{
231 "null": null,
232 "bool": true,
233 "string": "hello",
234 "float": 6.28318,
235 "int": 42,
236 "uint": 47
237}`,
238 },
239 {
240 desc: "simple array",
241 write: func(e *json.Encoder) {
242 e.StartArray()
243 {
244 e.WriteString("hello")
245 e.WriteFloat(6.28318, 32)
246 e.WriteInt(42)
247 e.WriteUint(47)
248 e.WriteBool(true)
249 e.WriteNull()
250 }
251 e.EndArray()
252 },
253 wantOut: `["hello",6.28318,42,47,true,null]`,
254 wantOutIndent: `[
255 "hello",
256 6.28318,
257 42,
258 47,
259 true,
260 null
261]`,
262 },
263 {
264 desc: "fancy object",
265 write: func(e *json.Encoder) {
266 e.StartObject()
267 {
268 e.WriteName("object0")
269 e.StartObject()
270 e.EndObject()
271 }
272 {
273 e.WriteName("array0")
274 e.StartArray()
275 e.EndArray()
276 }
277 {
278 e.WriteName("object1")
279 e.StartObject()
280 {
281 e.WriteName("null")
282 e.WriteNull()
283 }
284 {
285 e.WriteName("object1-1")
286 e.StartObject()
287 {
288 e.WriteName("bool")
289 e.WriteBool(false)
290 }
291 {
292 e.WriteName("float")
293 e.WriteFloat(3.14159, 32)
294 }
295 e.EndObject()
296 }
297 e.EndObject()
298 }
299 {
300 e.WriteName("array1")
301 e.StartArray()
302 {
303 e.WriteNull()
304 e.StartObject()
305 e.EndObject()
306 e.StartObject()
307 {
308 e.WriteName("hello")
309 e.WriteString("world")
310 }
311 {
312 e.WriteName("hola")
313 e.WriteString("mundo")
314 }
315 e.EndObject()
316 e.StartArray()
317 {
318 e.WriteUint(1)
319 e.WriteUint(0)
320 e.WriteUint(1)
321 }
322 e.EndArray()
323 }
324 e.EndArray()
325 }
326 e.EndObject()
327 },
328 wantOutIndent: `{
329 "object0": {},
330 "array0": [],
331 "object1": {
332 "null": null,
333 "object1-1": {
334 "bool": false,
335 "float": 3.14159
336 }
337 },
338 "array1": [
339 null,
340 {},
341 {
342 "hello": "world",
343 "hola": "mundo"
344 },
345 [
346 1,
347 0,
348 1
349 ]
350 ]
351}`,
352 },
353 {
354 desc: "string contains rune error",
355 write: func(e *json.Encoder) {
356 // WriteString returns non-fatal error for invalid UTF sequence, but
357 // should still output the written value. See TestWriteStringError
358 // below that checks for this.
359 e.StartObject()
360 e.WriteName("invalid rune")
361 e.WriteString("abc\xff")
362 e.EndObject()
363 },
364 wantOut: "{\"invalid rune\":\"abc\xff\"}",
365 }}
366
367 for _, tc := range tests {
368 t.Run(tc.desc, func(t *testing.T) {
369 if tc.wantOut != "" {
370 enc, err := json.NewEncoder("")
371 if err != nil {
372 t.Fatalf("NewEncoder() returned error: %v", err)
373 }
374 tc.write(enc)
375 got := string(enc.Bytes())
376 if got != tc.wantOut {
377 t.Errorf("%s:\n<got>:\n%v\n<want>\n%v\n", tc.desc, got, tc.wantOut)
378 }
379 }
380 if tc.wantOutIndent != "" {
381 enc, err := json.NewEncoder("\t")
382 if err != nil {
383 t.Fatalf("NewEncoder() returned error: %v", err)
384 }
385 tc.write(enc)
386 got, want := string(enc.Bytes()), tc.wantOutIndent
387 if got != want {
388 t.Errorf("%s(indent):\n<got>:\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
389 tc.desc, got, want, cmp.Diff(want, got, splitLines))
390 }
391 }
392 })
393 }
394}
395
396func TestWriteStringError(t *testing.T) {
397 tests := []string{"abc\xff"}
398
399 for _, in := range tests {
400 t.Run(in, func(t *testing.T) {
401 enc, err := json.NewEncoder("")
402 if err != nil {
403 t.Fatalf("NewEncoder() returned error: %v", err)
404 }
405 if err := enc.WriteString(in); err == nil {
406 t.Errorf("WriteString(%v): got nil error, want error", in)
407 }
408 })
409 }
410}