1
2
3
4
5 package json
6
7 import (
8 "bytes"
9 "io"
10 "log"
11 "net"
12 "net/http"
13 "net/http/httptest"
14 "reflect"
15 "strings"
16 "testing"
17 )
18
19
20
21 var streamTest = []interface{}{
22 0.1,
23 "hello",
24 nil,
25 true,
26 false,
27 []interface{}{"a", "b", "c"},
28 map[string]interface{}{"K": "Kelvin", "ß": "long s"},
29 3.14,
30 }
31
32 var streamEncoded = `0.1
33 "hello"
34 null
35 true
36 false
37 ["a","b","c"]
38 {"ß":"long s","K":"Kelvin"}
39 3.14
40 `
41
42 func TestEncoder(t *testing.T) {
43 for i := 0; i <= len(streamTest); i++ {
44 var buf bytes.Buffer
45 enc := NewEncoder(&buf)
46
47 enc.SetIndent(">", ".")
48 enc.SetIndent("", "")
49 for j, v := range streamTest[0:i] {
50 if err := enc.Encode(v); err != nil {
51 t.Fatalf("encode #%d: %v", j, err)
52 }
53 }
54 if have, want := buf.String(), nlines(streamEncoded, i); have != want {
55 t.Errorf("encoding %d items: mismatch", i)
56 diff(t, []byte(have), []byte(want))
57 break
58 }
59 }
60 }
61
62 var streamEncodedIndent = `0.1
63 "hello"
64 null
65 true
66 false
67 [
68 >."a",
69 >."b",
70 >."c"
71 >]
72 {
73 >."ß": "long s",
74 >."K": "Kelvin"
75 >}
76 3.14
77 `
78
79 func TestEncoderIndent(t *testing.T) {
80 var buf bytes.Buffer
81 enc := NewEncoder(&buf)
82 enc.SetIndent(">", ".")
83 for _, v := range streamTest {
84 enc.Encode(v)
85 }
86 if have, want := buf.String(), streamEncodedIndent; have != want {
87 t.Error("indented encoding mismatch")
88 diff(t, []byte(have), []byte(want))
89 }
90 }
91
92 type strMarshaler string
93
94 func (s strMarshaler) MarshalJSON() ([]byte, error) {
95 return []byte(s), nil
96 }
97
98 type strPtrMarshaler string
99
100 func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) {
101 return []byte(*s), nil
102 }
103
104 func TestEncoderSetEscapeHTML(t *testing.T) {
105 var c C
106 var ct CText
107 var tagStruct struct {
108 Valid int `json:"<>&#! "`
109 Invalid int `json:"\\"`
110 }
111
112
113
114
115 marshalerStruct := &struct {
116 NonPtr strMarshaler
117 Ptr strPtrMarshaler
118 }{`"<str>"`, `"<str>"`}
119
120
121 stringOption := struct {
122 Bar string `json:"bar,string"`
123 }{`<html>foobar</html>`}
124
125 for _, tt := range []struct {
126 name string
127 v interface{}
128 wantEscape string
129 want string
130 }{
131 {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`},
132 {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
133 {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
134 {
135 "tagStruct", tagStruct,
136 `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`,
137 `{"<>&#! ":0,"Invalid":0}`,
138 },
139 {
140 `"<str>"`, marshalerStruct,
141 `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
142 `{"NonPtr":"<str>","Ptr":"<str>"}`,
143 },
144 {
145 "stringOption", stringOption,
146 `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
147 `{"bar":"\"<html>foobar</html>\""}`,
148 },
149 } {
150 var buf bytes.Buffer
151 enc := NewEncoder(&buf)
152 if err := enc.Encode(tt.v); err != nil {
153 t.Errorf("Encode(%s): %s", tt.name, err)
154 continue
155 }
156 if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
157 t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
158 }
159 buf.Reset()
160 enc.SetEscapeHTML(false)
161 if err := enc.Encode(tt.v); err != nil {
162 t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
163 continue
164 }
165 if got := strings.TrimSpace(buf.String()); got != tt.want {
166 t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
167 tt.name, got, tt.want)
168 }
169 }
170 }
171
172 func TestDecoder(t *testing.T) {
173 for i := 0; i <= len(streamTest); i++ {
174
175
176
177
178
179 var buf bytes.Buffer
180 for _, c := range nlines(streamEncoded, i) {
181 if c != '\n' {
182 buf.WriteRune(c)
183 }
184 }
185 out := make([]interface{}, i)
186 dec := NewDecoder(&buf)
187 for j := range out {
188 if err := dec.Decode(&out[j]); err != nil {
189 t.Fatalf("decode #%d/%d: %v", j, i, err)
190 }
191 }
192 if !reflect.DeepEqual(out, streamTest[0:i]) {
193 t.Errorf("decoding %d items: mismatch", i)
194 for j := range out {
195 if !reflect.DeepEqual(out[j], streamTest[j]) {
196 t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j])
197 }
198 }
199 break
200 }
201 }
202 }
203
204 func TestDecoderBuffered(t *testing.T) {
205 r := strings.NewReader(`{"Name": "Gopher"} extra `)
206 var m struct {
207 Name string
208 }
209 d := NewDecoder(r)
210 err := d.Decode(&m)
211 if err != nil {
212 t.Fatal(err)
213 }
214 if m.Name != "Gopher" {
215 t.Errorf("Name = %q; want Gopher", m.Name)
216 }
217 rest, err := io.ReadAll(d.Buffered())
218 if err != nil {
219 t.Fatal(err)
220 }
221 if g, w := string(rest), " extra "; g != w {
222 t.Errorf("Remaining = %q; want %q", g, w)
223 }
224 }
225
226 func nlines(s string, n int) string {
227 if n <= 0 {
228 return ""
229 }
230 for i, c := range s {
231 if c == '\n' {
232 if n--; n == 0 {
233 return s[0 : i+1]
234 }
235 }
236 }
237 return s
238 }
239
240 func TestRawMessage(t *testing.T) {
241 var data struct {
242 X float64
243 Id RawMessage
244 Y float32
245 }
246 const raw = `["\u0056",null]`
247 const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
248 err := Unmarshal([]byte(msg), &data)
249 if err != nil {
250 t.Fatalf("Unmarshal: %v", err)
251 }
252 if string([]byte(data.Id)) != raw {
253 t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw)
254 }
255 b, err := Marshal(&data)
256 if err != nil {
257 t.Fatalf("Marshal: %v", err)
258 }
259 if string(b) != msg {
260 t.Fatalf("Marshal: have %#q want %#q", b, msg)
261 }
262 }
263
264 func TestNullRawMessage(t *testing.T) {
265 var data struct {
266 X float64
267 Id RawMessage
268 IdPtr *RawMessage
269 Y float32
270 }
271 const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
272 err := Unmarshal([]byte(msg), &data)
273 if err != nil {
274 t.Fatalf("Unmarshal: %v", err)
275 }
276 if want, got := "null", string(data.Id); want != got {
277 t.Fatalf("Raw mismatch: have %q, want %q", got, want)
278 }
279 if data.IdPtr != nil {
280 t.Fatalf("Raw pointer mismatch: have non-nil, want nil")
281 }
282 b, err := Marshal(&data)
283 if err != nil {
284 t.Fatalf("Marshal: %v", err)
285 }
286 if string(b) != msg {
287 t.Fatalf("Marshal: have %#q want %#q", b, msg)
288 }
289 }
290
291 var blockingTests = []string{
292 `{"x": 1}`,
293 `[1, 2, 3]`,
294 }
295
296 func TestBlocking(t *testing.T) {
297 for _, enc := range blockingTests {
298 r, w := net.Pipe()
299 go w.Write([]byte(enc))
300 var val interface{}
301
302
303
304 if err := NewDecoder(r).Decode(&val); err != nil {
305 t.Errorf("decoding %s: %v", enc, err)
306 }
307 r.Close()
308 w.Close()
309 }
310 }
311
312 func BenchmarkEncoderEncode(b *testing.B) {
313 b.ReportAllocs()
314 type T struct {
315 X, Y string
316 }
317 v := &T{"foo", "bar"}
318 b.RunParallel(func(pb *testing.PB) {
319 for pb.Next() {
320 if err := NewEncoder(io.Discard).Encode(v); err != nil {
321 b.Fatal(err)
322 }
323 }
324 })
325 }
326
327 type tokenStreamCase struct {
328 json string
329 expTokens []interface{}
330 }
331
332 type decodeThis struct {
333 v interface{}
334 }
335
336 var tokenStreamCases = []tokenStreamCase{
337
338 {json: `10`, expTokens: []interface{}{float64(10)}},
339 {json: ` [10] `, expTokens: []interface{}{
340 Delim('['), float64(10), Delim(']')}},
341 {json: ` [false,10,"b"] `, expTokens: []interface{}{
342 Delim('['), false, float64(10), "b", Delim(']')}},
343 {json: `{ "a": 1 }`, expTokens: []interface{}{
344 Delim('{'), "a", float64(1), Delim('}')}},
345 {json: `{"a": 1, "b":"3"}`, expTokens: []interface{}{
346 Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
347 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{
348 Delim('['),
349 Delim('{'), "a", float64(1), Delim('}'),
350 Delim('{'), "a", float64(2), Delim('}'),
351 Delim(']')}},
352 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{
353 Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
354 Delim('}')}},
355 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{
356 Delim('{'), "obj", Delim('['),
357 Delim('{'), "a", float64(1), Delim('}'),
358 Delim(']'), Delim('}')}},
359
360
361 {json: `{ "a": 1 }`, expTokens: []interface{}{
362 Delim('{'), "a",
363 decodeThis{float64(1)},
364 Delim('}')}},
365 {json: ` [ { "a" : 1 } ] `, expTokens: []interface{}{
366 Delim('['),
367 decodeThis{map[string]interface{}{"a": float64(1)}},
368 Delim(']')}},
369 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{
370 Delim('['),
371 decodeThis{map[string]interface{}{"a": float64(1)}},
372 decodeThis{map[string]interface{}{"a": float64(2)}},
373 Delim(']')}},
374 {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []interface{}{
375 Delim('{'), "obj", Delim('['),
376 decodeThis{map[string]interface{}{"a": float64(1)}},
377 Delim(']'), Delim('}')}},
378
379 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{
380 Delim('{'), "obj",
381 decodeThis{map[string]interface{}{"a": float64(1)}},
382 Delim('}')}},
383 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{
384 Delim('{'), "obj",
385 decodeThis{[]interface{}{
386 map[string]interface{}{"a": float64(1)},
387 }},
388 Delim('}')}},
389 {json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{
390 Delim('['),
391 decodeThis{map[string]interface{}{"a": float64(1)}},
392 decodeThis{&SyntaxError{"expected comma after array element", 11}},
393 }},
394 {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []interface{}{
395 Delim('{'), strings.Repeat("a", 513),
396 decodeThis{&SyntaxError{"expected colon after object key", 518}},
397 }},
398 {json: `{ "\a" }`, expTokens: []interface{}{
399 Delim('{'),
400 &SyntaxError{"invalid character 'a' in string escape code", 3},
401 }},
402 {json: ` \a`, expTokens: []interface{}{
403 &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1},
404 }},
405 }
406
407 func TestDecodeInStream(t *testing.T) {
408 for ci, tcase := range tokenStreamCases {
409
410 dec := NewDecoder(strings.NewReader(tcase.json))
411 for i, etk := range tcase.expTokens {
412
413 var tk interface{}
414 var err error
415
416 if dt, ok := etk.(decodeThis); ok {
417 etk = dt.v
418 err = dec.Decode(&tk)
419 } else {
420 tk, err = dec.Token()
421 }
422 if experr, ok := etk.(error); ok {
423 if err == nil || !reflect.DeepEqual(err, experr) {
424 t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err)
425 }
426 break
427 } else if err == io.EOF {
428 t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json)
429 break
430 } else if err != nil {
431 t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json)
432 break
433 }
434 if !reflect.DeepEqual(tk, etk) {
435 t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk)
436 break
437 }
438 }
439 }
440 }
441
442
443 func TestHTTPDecoding(t *testing.T) {
444 const raw = `{ "foo": "bar" }`
445
446 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
447 w.Write([]byte(raw))
448 }))
449 defer ts.Close()
450 res, err := http.Get(ts.URL)
451 if err != nil {
452 log.Fatalf("GET failed: %v", err)
453 }
454 defer res.Body.Close()
455
456 foo := struct {
457 Foo string
458 }{}
459
460 d := NewDecoder(res.Body)
461 err = d.Decode(&foo)
462 if err != nil {
463 t.Fatalf("Decode: %v", err)
464 }
465 if foo.Foo != "bar" {
466 t.Errorf("decoded %q; want \"bar\"", foo.Foo)
467 }
468
469
470 err = d.Decode(&foo)
471 if err != io.EOF {
472 t.Errorf("err = %v; want io.EOF", err)
473 }
474 }
475
View as plain text