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 }
443 for _, tt := range tests {
444 t.Run(tt.Name, func(t *testing.T) {
445 dec := NewDecoder(strings.NewReader(tt.json))
446 for i, want := range tt.expTokens {
447 var got any
448 var err error
449
450 if dt, ok := want.(decodeThis); ok {
451 want = dt.v
452 err = dec.Decode(&got)
453 } else {
454 got, err = dec.Token()
455 }
456 if errWant, ok := want.(error); ok {
457 if err == nil || !reflect.DeepEqual(err, errWant) {
458 t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: %v", tt.Where, tt.json, err, errWant)
459 }
460 break
461 } else if err != nil {
462 t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: nil", tt.Where, tt.json, err)
463 }
464 if !reflect.DeepEqual(got, want) {
465 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)
466 }
467 }
468 })
469 }
470 }
471
472
473 func TestHTTPDecoding(t *testing.T) {
474 const raw = `{ "foo": "bar" }`
475
476 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
477 w.Write([]byte(raw))
478 }))
479 defer ts.Close()
480 res, err := http.Get(ts.URL)
481 if err != nil {
482 log.Fatalf("http.Get error: %v", err)
483 }
484 defer res.Body.Close()
485
486 foo := struct {
487 Foo string
488 }{}
489
490 d := NewDecoder(res.Body)
491 err = d.Decode(&foo)
492 if err != nil {
493 t.Fatalf("Decode error: %v", err)
494 }
495 if foo.Foo != "bar" {
496 t.Errorf(`Decode: got %q, want "bar"`, foo.Foo)
497 }
498
499
500 err = d.Decode(&foo)
501 if err != io.EOF {
502 t.Errorf("Decode error:\n\tgot: %v\n\twant: io.EOF", err)
503 }
504 }
505
506 func TestTokenTruncation(t *testing.T) {
507 tests := []struct {
508 in string
509 err error
510 }{
511 {in: ``, err: io.EOF},
512 {in: `{`, err: io.EOF},
513 {in: `{"`, err: io.ErrUnexpectedEOF},
514 {in: `{"k"`, err: io.EOF},
515 {in: `{"k":`, err: io.EOF},
516 {in: `{"k",`, err: &SyntaxError{"invalid character ',' after object key", int64(len(`{"k"`))}},
517 {in: `{"k"}`, err: &SyntaxError{"invalid character '}' after object key", int64(len(`{"k"`))}},
518 {in: ` [0`, err: io.EOF},
519 {in: `[0.`, err: io.ErrUnexpectedEOF},
520 {in: `[0. `, err: &SyntaxError{"invalid character ' ' in numeric literal", int64(len(`[0.`))}},
521 {in: `[0,`, err: io.EOF},
522 {in: `[0:`, err: &SyntaxError{"invalid character ':' after array element", int64(len(`[0`))}},
523 {in: `n`, err: io.ErrUnexpectedEOF},
524 {in: `nul`, err: io.ErrUnexpectedEOF},
525 {in: `fal `, err: &SyntaxError{"invalid character ' ' in literal false (expecting 's')", int64(len(`fal`))}},
526 {in: `false`, err: io.EOF},
527 }
528 for _, tt := range tests {
529 d := NewDecoder(strings.NewReader(tt.in))
530 for i := 0; true; i++ {
531 if _, err := d.Token(); err != nil {
532 if !reflect.DeepEqual(err, tt.err) {
533 t.Errorf("`%s`: %d.Token error = %#v, want %v", tt.in, i, err, tt.err)
534 }
535 break
536 }
537 }
538 }
539 }
540
541 func TestDecoderInputOffset(t *testing.T) {
542 const input = ` [
543 [ ] , [ "one" ] , [ "one" , "two" ] ,
544 { } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
545 ] `
546 wantOffsets := []int64{
547 0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
548 38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
549 76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
550 117, 117,
551 }
552 wantMores := []bool{
553 true, true, false, true, true, false, true, true, true, false,
554 true, false, true, true, true, false, true, true, true, true,
555 true, false, false, false, false,
556 }
557
558 d := NewDecoder(strings.NewReader(input))
559 checkOffset := func() {
560 t.Helper()
561 got := d.InputOffset()
562 if len(wantOffsets) == 0 {
563 t.Fatalf("InputOffset = %d, want nil", got)
564 }
565 want := wantOffsets[0]
566 if got != want {
567 t.Fatalf("InputOffset = %d, want %d", got, want)
568 }
569 wantOffsets = wantOffsets[1:]
570 }
571 checkMore := func() {
572 t.Helper()
573 got := d.More()
574 if len(wantMores) == 0 {
575 t.Fatalf("More = %v, want nil", got)
576 }
577 want := wantMores[0]
578 if got != want {
579 t.Fatalf("More = %v, want %v", got, want)
580 }
581 wantMores = wantMores[1:]
582 }
583 checkOffset()
584 checkMore()
585 checkOffset()
586 for {
587 if _, err := d.Token(); err == io.EOF {
588 break
589 } else if err != nil {
590 t.Fatalf("Token error: %v", err)
591 }
592 checkOffset()
593 checkMore()
594 checkOffset()
595 }
596 checkOffset()
597 checkMore()
598 checkOffset()
599
600 if len(wantOffsets)+len(wantMores) > 0 {
601 t.Fatal("unconsumed testdata")
602 }
603 }
604
View as plain text