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