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