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