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
View as plain text