1
2
3
4
5
6
7 package json
8
9 import (
10 "bytes"
11 "fmt"
12 "io"
13 "log"
14 "net"
15 "net/http"
16 "net/http/httptest"
17 "path"
18 "reflect"
19 "runtime"
20 "runtime/debug"
21 "strings"
22 "testing"
23 )
24
25
26
27
28 type CaseName struct {
29 Name string
30 Where CasePos
31 }
32
33
34 func Name(s string) (c CaseName) {
35 c.Name = s
36 runtime.Callers(2, c.Where.pc[:])
37 return c
38 }
39
40
41 type CasePos struct{ pc [1]uintptr }
42
43 func (pos CasePos) String() string {
44 frames := runtime.CallersFrames(pos.pc[:])
45 frame, _ := frames.Next()
46 return fmt.Sprintf("%s:%d", path.Base(frame.File), frame.Line)
47 }
48
49
50
51 var streamTest = []any{
52 0.1,
53 "hello",
54 nil,
55 true,
56 false,
57 []any{"a", "b", "c"},
58 map[string]any{"K": "Kelvin", "ß": "long s"},
59 3.14,
60 }
61
62 var streamEncoded = `0.1
63 "hello"
64 null
65 true
66 false
67 ["a","b","c"]
68 {"ß":"long s","K":"Kelvin"}
69 3.14
70 `
71
72 func TestEncoder(t *testing.T) {
73 for i := 0; i <= len(streamTest); i++ {
74 var buf strings.Builder
75 enc := NewEncoder(&buf)
76
77 enc.SetIndent(">", ".")
78 enc.SetIndent("", "")
79 for j, v := range streamTest[0:i] {
80 if err := enc.Encode(v); err != nil {
81 t.Fatalf("#%d.%d Encode error: %v", i, j, err)
82 }
83 }
84 if got, want := buf.String(), nlines(streamEncoded, i); got != want {
85 t.Errorf("encoding %d items: mismatch:", i)
86 diff(t, []byte(got), []byte(want))
87 break
88 }
89 }
90 }
91
92 func TestEncoderErrorAndReuseEncodeState(t *testing.T) {
93
94 percent := debug.SetGCPercent(-1)
95 defer debug.SetGCPercent(percent)
96
97
98 type Dummy struct {
99 Name string
100 Next *Dummy
101 }
102 dummy := Dummy{Name: "Dummy"}
103 dummy.Next = &dummy
104
105 var buf bytes.Buffer
106 enc := NewEncoder(&buf)
107 if err := enc.Encode(dummy); err == nil {
108 t.Errorf("Encode(dummy) error: got nil, want non-nil")
109 }
110
111 type Data struct {
112 A string
113 I int
114 }
115 want := Data{A: "a", I: 1}
116 if err := enc.Encode(want); err != nil {
117 t.Errorf("Marshal error: %v", err)
118 }
119
120 var got Data
121 if err := Unmarshal(buf.Bytes(), &got); err != nil {
122 t.Errorf("Unmarshal error: %v", err)
123 }
124 if got != want {
125 t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %v\n\twant: %v", got, want)
126 }
127 }
128
129 var streamEncodedIndent = `0.1
130 "hello"
131 null
132 true
133 false
134 [
135 >."a",
136 >."b",
137 >."c"
138 >]
139 {
140 >."ß": "long s",
141 >."K": "Kelvin"
142 >}
143 3.14
144 `
145
146 func TestEncoderIndent(t *testing.T) {
147 var buf strings.Builder
148 enc := NewEncoder(&buf)
149 enc.SetIndent(">", ".")
150 for _, v := range streamTest {
151 enc.Encode(v)
152 }
153 if got, want := buf.String(), streamEncodedIndent; got != want {
154 t.Errorf("Encode mismatch:\ngot:\n%s\n\nwant:\n%s", got, want)
155 diff(t, []byte(got), []byte(want))
156 }
157 }
158
159 type strMarshaler string
160
161 func (s strMarshaler) MarshalJSON() ([]byte, error) {
162 return []byte(s), nil
163 }
164
165 type strPtrMarshaler string
166
167 func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) {
168 return []byte(*s), nil
169 }
170
171 func TestEncoderSetEscapeHTML(t *testing.T) {
172 var c C
173 var ct CText
174 var tagStruct struct {
175 Valid int `json:"<>&#! "`
176 Invalid int `json:"\\"`
177 }
178
179
180
181
182 marshalerStruct := &struct {
183 NonPtr strMarshaler
184 Ptr strPtrMarshaler
185 }{`"<str>"`, `"<str>"`}
186
187
188 stringOption := struct {
189 Bar string `json:"bar,string"`
190 }{`<html>foobar</html>`}
191
192 tests := []struct {
193 CaseName
194 v any
195 wantEscape string
196 want string
197 }{
198 {Name("c"), c, `"\u003c\u0026\u003e"`, `"<&>"`},
199 {Name("ct"), ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
200 {Name(`"<&>"`), "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
201 {
202 Name("tagStruct"), tagStruct,
203 `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`,
204 `{"<>&#! ":0,"Invalid":0}`,
205 },
206 {
207 Name(`"<str>"`), marshalerStruct,
208 `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
209 `{"NonPtr":"<str>","Ptr":"<str>"}`,
210 },
211 {
212 Name("stringOption"), stringOption,
213 `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
214 `{"bar":"\"<html>foobar</html>\""}`,
215 },
216 }
217 for _, tt := range tests {
218 t.Run(tt.Name, func(t *testing.T) {
219 var buf strings.Builder
220 enc := NewEncoder(&buf)
221 if err := enc.Encode(tt.v); err != nil {
222 t.Fatalf("%s: Encode(%s) error: %s", tt.Where, tt.Name, err)
223 }
224 if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
225 t.Errorf("%s: Encode(%s):\n\tgot: %s\n\twant: %s", tt.Where, tt.Name, got, tt.wantEscape)
226 }
227 buf.Reset()
228 enc.SetEscapeHTML(false)
229 if err := enc.Encode(tt.v); err != nil {
230 t.Fatalf("%s: SetEscapeHTML(false) Encode(%s) error: %s", tt.Where, tt.Name, err)
231 }
232 if got := strings.TrimSpace(buf.String()); got != tt.want {
233 t.Errorf("%s: SetEscapeHTML(false) Encode(%s):\n\tgot: %s\n\twant: %s",
234 tt.Where, tt.Name, got, tt.want)
235 }
236 })
237 }
238 }
239
240 func TestDecoder(t *testing.T) {
241 for i := 0; i <= len(streamTest); i++ {
242
243
244
245
246
247 var buf bytes.Buffer
248 for _, c := range nlines(streamEncoded, i) {
249 if c != '\n' {
250 buf.WriteRune(c)
251 }
252 }
253 out := make([]any, i)
254 dec := NewDecoder(&buf)
255 for j := range out {
256 if err := dec.Decode(&out[j]); err != nil {
257 t.Fatalf("decode #%d/%d error: %v", j, i, err)
258 }
259 }
260 if !reflect.DeepEqual(out, streamTest[0:i]) {
261 t.Errorf("decoding %d items: mismatch:", i)
262 for j := range out {
263 if !reflect.DeepEqual(out[j], streamTest[j]) {
264 t.Errorf("#%d:\n\tgot: %v\n\twant: %v", j, out[j], streamTest[j])
265 }
266 }
267 break
268 }
269 }
270 }
271
272 func TestDecoderBuffered(t *testing.T) {
273 r := strings.NewReader(`{"Name": "Gopher"} extra `)
274 var m struct {
275 Name string
276 }
277 d := NewDecoder(r)
278 err := d.Decode(&m)
279 if err != nil {
280 t.Fatal(err)
281 }
282 if m.Name != "Gopher" {
283 t.Errorf("Name = %s, want Gopher", m.Name)
284 }
285 rest, err := io.ReadAll(d.Buffered())
286 if err != nil {
287 t.Fatal(err)
288 }
289 if got, want := string(rest), " extra "; got != want {
290 t.Errorf("Remaining = %s, want %s", got, want)
291 }
292 }
293
294 func nlines(s string, n int) string {
295 if n <= 0 {
296 return ""
297 }
298 for i, c := range s {
299 if c == '\n' {
300 if n--; n == 0 {
301 return s[0 : i+1]
302 }
303 }
304 }
305 return s
306 }
307
308 func TestRawMessage(t *testing.T) {
309 var data struct {
310 X float64
311 Id RawMessage
312 Y float32
313 }
314 const raw = `["\u0056",null]`
315 const want = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
316 err := Unmarshal([]byte(want), &data)
317 if err != nil {
318 t.Fatalf("Unmarshal error: %v", err)
319 }
320 if string([]byte(data.Id)) != raw {
321 t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", []byte(data.Id), raw)
322 }
323 got, err := Marshal(&data)
324 if err != nil {
325 t.Fatalf("Marshal error: %v", err)
326 }
327 if string(got) != want {
328 t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
329 }
330 }
331
332 func TestNullRawMessage(t *testing.T) {
333 var data struct {
334 X float64
335 Id RawMessage
336 IdPtr *RawMessage
337 Y float32
338 }
339 const want = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
340 err := Unmarshal([]byte(want), &data)
341 if err != nil {
342 t.Fatalf("Unmarshal error: %v", err)
343 }
344 if want, got := "null", string(data.Id); want != got {
345 t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", got, want)
346 }
347 if data.IdPtr != nil {
348 t.Fatalf("pointer mismatch: got non-nil, want nil")
349 }
350 got, err := Marshal(&data)
351 if err != nil {
352 t.Fatalf("Marshal error: %v", err)
353 }
354 if string(got) != want {
355 t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want)
356 }
357 }
358
359 func TestBlocking(t *testing.T) {
360 tests := []struct {
361 CaseName
362 in string
363 }{
364 {Name(""), `{"x": 1}`},
365 {Name(""), `[1, 2, 3]`},
366 }
367 for _, tt := range tests {
368 t.Run(tt.Name, func(t *testing.T) {
369 r, w := net.Pipe()
370 go w.Write([]byte(tt.in))
371 var val any
372
373
374
375 if err := NewDecoder(r).Decode(&val); err != nil {
376 t.Errorf("%s: NewDecoder(%s).Decode error: %v", tt.Where, tt.in, err)
377 }
378 r.Close()
379 w.Close()
380 })
381 }
382 }
383
384 type decodeThis struct {
385 v any
386 }
387
388 func TestDecodeInStream(t *testing.T) {
389 tests := []struct {
390 CaseName
391 json string
392 expTokens []any
393 }{
394
395 {CaseName: Name(""), json: `10`, expTokens: []any{float64(10)}},
396 {CaseName: Name(""), json: ` [10] `, expTokens: []any{
397 Delim('['), float64(10), Delim(']')}},
398 {CaseName: Name(""), json: ` [false,10,"b"] `, expTokens: []any{
399 Delim('['), false, float64(10), "b", Delim(']')}},
400 {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
401 Delim('{'), "a", float64(1), Delim('}')}},
402 {CaseName: Name(""), json: `{"a": 1, "b":"3"}`, expTokens: []any{
403 Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
404 {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
405 Delim('['),
406 Delim('{'), "a", float64(1), Delim('}'),
407 Delim('{'), "a", float64(2), Delim('}'),
408 Delim(']')}},
409 {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
410 Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
411 Delim('}')}},
412 {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
413 Delim('{'), "obj", Delim('['),
414 Delim('{'), "a", float64(1), Delim('}'),
415 Delim(']'), Delim('}')}},
416
417
418 {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
419 Delim('{'), "a",
420 decodeThis{float64(1)},
421 Delim('}')}},
422 {CaseName: Name(""), json: ` [ { "a" : 1 } ] `, expTokens: []any{
423 Delim('['),
424 decodeThis{map[string]any{"a": float64(1)}},
425 Delim(']')}},
426 {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
427 Delim('['),
428 decodeThis{map[string]any{"a": float64(1)}},
429 decodeThis{map[string]any{"a": float64(2)}},
430 Delim(']')}},
431 {CaseName: Name(""), json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{
432 Delim('{'), "obj", Delim('['),
433 decodeThis{map[string]any{"a": float64(1)}},
434 Delim(']'), Delim('}')}},
435
436 {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
437 Delim('{'), "obj",
438 decodeThis{map[string]any{"a": float64(1)}},
439 Delim('}')}},
440 {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
441 Delim('{'), "obj",
442 decodeThis{[]any{
443 map[string]any{"a": float64(1)},
444 }},
445 Delim('}')}},
446 {CaseName: Name(""), json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{
447 Delim('['),
448 decodeThis{map[string]any{"a": float64(1)}},
449 decodeThis{&SyntaxError{"expected comma after array element", len64(` [{"a": 1} `)}},
450 }},
451 {CaseName: Name(""), json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{
452 Delim('{'), strings.Repeat("a", 513),
453 decodeThis{&SyntaxError{"expected colon after object key", len64(`{ "`) + 513 + len64(`" `)}},
454 }},
455 {CaseName: Name(""), json: `{ "\a" }`, expTokens: []any{
456 Delim('{'),
457 &SyntaxError{"invalid character 'a' in string escape code", len64(`{ "`)},
458 }},
459 {CaseName: Name(""), json: ` \a`, expTokens: []any{
460 &SyntaxError{"invalid character '\\\\' looking for beginning of value", len64(` `)},
461 }},
462 {CaseName: Name(""), json: `,`, expTokens: []any{
463 &SyntaxError{"invalid character ',' looking for beginning of value", 0},
464 }},
465 }
466 for _, tt := range tests {
467 t.Run(tt.Name, func(t *testing.T) {
468 dec := NewDecoder(strings.NewReader(tt.json))
469 for i, want := range tt.expTokens {
470 var got any
471 var err error
472
473 wantMore := true
474 switch want {
475 case Delim(']'), Delim('}'):
476 wantMore = false
477 }
478 if got := dec.More(); got != wantMore {
479 t.Fatalf("%s:\n\tinput: %s\n\tdec.More() = %v, want %v (next token: %T(%v))", tt.Where, tt.json, got, wantMore, want, want)
480 }
481
482 if dt, ok := want.(decodeThis); ok {
483 want = dt.v
484 err = dec.Decode(&got)
485 } else {
486 got, err = dec.Token()
487 }
488 if errWant, ok := want.(error); ok {
489 if err == nil || !reflect.DeepEqual(err, errWant) {
490 t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: %v", tt.Where, tt.json, err, errWant)
491 }
492 break
493 } else if err != nil {
494 t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: nil", tt.Where, tt.json, err)
495 }
496 if !reflect.DeepEqual(got, want) {
497 t.Fatalf("%s: token %d:\n\tinput: %s\n\tgot: %T(%v)\n\twant: %T(%v)", tt.Where, i, tt.json, got, got, want, want)
498 }
499 }
500 })
501 }
502 }
503
504
505 func TestHTTPDecoding(t *testing.T) {
506 const raw = `{ "foo": "bar" }`
507
508 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
509 w.Write([]byte(raw))
510 }))
511 defer ts.Close()
512 res, err := http.Get(ts.URL)
513 if err != nil {
514 log.Fatalf("http.Get error: %v", err)
515 }
516 defer res.Body.Close()
517
518 foo := struct {
519 Foo string
520 }{}
521
522 d := NewDecoder(res.Body)
523 err = d.Decode(&foo)
524 if err != nil {
525 t.Fatalf("Decode error: %v", err)
526 }
527 if foo.Foo != "bar" {
528 t.Errorf(`Decode: got %q, want "bar"`, foo.Foo)
529 }
530
531
532 err = d.Decode(&foo)
533 if err != io.EOF {
534 t.Errorf("Decode error:\n\tgot: %v\n\twant: io.EOF", err)
535 }
536 }
537
538
539 type readerFunc func([]byte) (int, error)
540
541 func (f readerFunc) Read(b []byte) (int, error) {
542 return f(b)
543 }
544
545 func TestTokenError(t *testing.T) {
546 tests := []struct {
547 in string
548 inErr error
549 err error
550 }{
551 {in: ``, err: io.EOF},
552 {in: `{`, err: io.EOF},
553 {in: `{"`, err: io.ErrUnexpectedEOF},
554 {in: `{"k"`, err: io.EOF},
555 {in: `{"k":`, err: io.EOF},
556 {in: `{"k",`, err: &SyntaxError{"invalid character ',' after object key", len64(`{"k"`)}},
557 {in: `{"k"}`, err: &SyntaxError{"invalid character '}' after object key", len64(`{"k"`)}},
558 {in: ` [0`, err: io.EOF},
559 {in: `[0.`, err: io.ErrUnexpectedEOF},
560 {in: `[0. `, err: &SyntaxError{"invalid character ' ' after decimal point in numeric literal", len64(`[0.`)}},
561 {in: `[0,`, err: io.EOF},
562 {in: `[0:`, err: &SyntaxError{"invalid character ':' after array element", len64(`[0`)}},
563 {in: `n`, err: io.ErrUnexpectedEOF},
564 {in: `nul`, err: io.ErrUnexpectedEOF},
565 {in: `fal `, err: &SyntaxError{"invalid character ' ' in literal false (expecting 's')", len64(`fal `)}},
566 {in: `false`, err: io.EOF},
567 {in: ` 1e1000`, err: &UnmarshalTypeError{Value: "number 1e1000", Type: reflect.TypeFor[float64](), Offset: len64(` 1e100`)}},
568 {in: `{"foo":1}{"bar":2}`, err: io.EOF},
569 {in: `{"foo":1}{"bar":2}`, inErr: io.ErrUnexpectedEOF, err: io.ErrUnexpectedEOF},
570 {in: `{"foo":1}{"bar":2}`, inErr: fmt.Errorf("wrap: %w", io.ErrUnexpectedEOF), err: fmt.Errorf("wrap: %w", io.ErrUnexpectedEOF)},
571 }
572 for _, tt := range tests {
573 r := strings.NewReader(tt.in)
574 d := NewDecoder(readerFunc(func(b []byte) (int, error) {
575 n, err := r.Read(b)
576 if err == io.EOF && tt.inErr != nil {
577 return n, tt.inErr
578 }
579 return n, err
580 }))
581 for i := 0; true; i++ {
582 if _, err := d.Token(); err != nil {
583 if !reflect.DeepEqual(err, tt.err) {
584 t.Errorf("`%s`: %d.Token error = %#v, want %#v", tt.in, i, err, tt.err)
585 }
586 break
587 }
588 }
589 }
590 }
591
592 func TestDecoderInputOffset(t *testing.T) {
593 const input = ` [
594 [ ] , [ "one" ] , [ "one" , "two" ] ,
595 { } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
596 ] `
597 wantOffsets := []int64{
598 0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
599 38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
600 76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
601 117, 117,
602 }
603 wantMores := []bool{
604 true, true, false, true, true, false, true, true, true, false,
605 true, false, true, true, true, false, true, true, true, true,
606 true, false, false, false, false,
607 }
608
609 d := NewDecoder(strings.NewReader(input))
610 checkOffset := func() {
611 t.Helper()
612 got := d.InputOffset()
613 if len(wantOffsets) == 0 {
614 t.Fatalf("InputOffset = %d, want nil", got)
615 }
616 want := wantOffsets[0]
617 if got != want {
618 t.Fatalf("InputOffset = %d, want %d", got, want)
619 }
620 wantOffsets = wantOffsets[1:]
621 }
622 checkMore := func() {
623 t.Helper()
624 got := d.More()
625 if len(wantMores) == 0 {
626 t.Fatalf("More = %v, want nil", got)
627 }
628 want := wantMores[0]
629 if got != want {
630 t.Fatalf("More = %v, want %v", got, want)
631 }
632 wantMores = wantMores[1:]
633 }
634 checkOffset()
635 checkMore()
636 checkOffset()
637 for {
638 if _, err := d.Token(); err == io.EOF {
639 break
640 } else if err != nil {
641 t.Fatalf("Token error: %v", err)
642 }
643 checkOffset()
644 checkMore()
645 checkOffset()
646 }
647 checkOffset()
648 checkMore()
649 checkOffset()
650
651 if len(wantOffsets)+len(wantMores) > 0 {
652 t.Fatal("unconsumed testdata")
653 }
654
655 t.Run("ArrayEOF", func(t *testing.T) {
656 d := NewDecoder(strings.NewReader(` [ "fizz" , `))
657 for {
658 if _, err := d.Token(); err == io.EOF {
659 break
660 } else if err != nil {
661 t.Fatalf("Token error: %v", err)
662 }
663 }
664 got := d.InputOffset()
665 want := len64(` [ "fizz" ,`)
666 if got != want {
667 t.Errorf("InputOffset = %v, want %v", got, want)
668 }
669 })
670
671 t.Run("ObjectEOF", func(t *testing.T) {
672 d := NewDecoder(strings.NewReader(` { "fizz" : `))
673 for {
674 if _, err := d.Token(); err == io.EOF {
675 break
676 } else if err != nil {
677 t.Fatalf("Token error: %v", err)
678 }
679 }
680 got := d.InputOffset()
681 want := len64(` { "fizz" :`)
682 if got != want {
683 t.Errorf("InputOffset = %v, want %v", got, want)
684 }
685 })
686 }
687
View as plain text