1
2
3
4
5 package json
6
7 import (
8 "bytes"
9 "io"
10 "log"
11 "net"
12 "net/http"
13 "net/http/httptest"
14 "reflect"
15 "runtime/debug"
16 "strings"
17 "testing"
18 )
19
20
21
22 var streamTest = []any{
23 0.1,
24 "hello",
25 nil,
26 true,
27 false,
28 []any{"a", "b", "c"},
29 map[string]any{"K": "Kelvin", "ß": "long s"},
30 3.14,
31 }
32
33 var streamEncoded = `0.1
34 "hello"
35 null
36 true
37 false
38 ["a","b","c"]
39 {"ß":"long s","K":"Kelvin"}
40 3.14
41 `
42
43 func TestEncoder(t *testing.T) {
44 for i := 0; i <= len(streamTest); i++ {
45 var buf strings.Builder
46 enc := NewEncoder(&buf)
47
48 enc.SetIndent(">", ".")
49 enc.SetIndent("", "")
50 for j, v := range streamTest[0:i] {
51 if err := enc.Encode(v); err != nil {
52 t.Fatalf("encode #%d: %v", j, err)
53 }
54 }
55 if have, want := buf.String(), nlines(streamEncoded, i); have != want {
56 t.Errorf("encoding %d items: mismatch", i)
57 diff(t, []byte(have), []byte(want))
58 break
59 }
60 }
61 }
62
63 func TestEncoderErrorAndReuseEncodeState(t *testing.T) {
64
65 percent := debug.SetGCPercent(-1)
66 defer debug.SetGCPercent(percent)
67
68
69 type Dummy struct {
70 Name string
71 Next *Dummy
72 }
73 dummy := Dummy{Name: "Dummy"}
74 dummy.Next = &dummy
75
76 var buf bytes.Buffer
77 enc := NewEncoder(&buf)
78 if err := enc.Encode(dummy); err == nil {
79 t.Errorf("Encode(dummy) == nil; want error")
80 }
81
82 type Data struct {
83 A string
84 I int
85 }
86 data := Data{A: "a", I: 1}
87 if err := enc.Encode(data); err != nil {
88 t.Errorf("Marshal(%v) = %v", data, err)
89 }
90
91 var data2 Data
92 if err := Unmarshal(buf.Bytes(), &data2); err != nil {
93 t.Errorf("Unmarshal(%v) = %v", data2, err)
94 }
95 if data2 != data {
96 t.Errorf("expect: %v, but get: %v", data, data2)
97 }
98 }
99
100 var streamEncodedIndent = `0.1
101 "hello"
102 null
103 true
104 false
105 [
106 >."a",
107 >."b",
108 >."c"
109 >]
110 {
111 >."ß": "long s",
112 >."K": "Kelvin"
113 >}
114 3.14
115 `
116
117 func TestEncoderIndent(t *testing.T) {
118 var buf strings.Builder
119 enc := NewEncoder(&buf)
120 enc.SetIndent(">", ".")
121 for _, v := range streamTest {
122 enc.Encode(v)
123 }
124 if have, want := buf.String(), streamEncodedIndent; have != want {
125 t.Error("indented encoding mismatch")
126 diff(t, []byte(have), []byte(want))
127 }
128 }
129
130 type strMarshaler string
131
132 func (s strMarshaler) MarshalJSON() ([]byte, error) {
133 return []byte(s), nil
134 }
135
136 type strPtrMarshaler string
137
138 func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) {
139 return []byte(*s), nil
140 }
141
142 func TestEncoderSetEscapeHTML(t *testing.T) {
143 var c C
144 var ct CText
145 var tagStruct struct {
146 Valid int `json:"<>&#! "`
147 Invalid int `json:"\\"`
148 }
149
150
151
152
153 marshalerStruct := &struct {
154 NonPtr strMarshaler
155 Ptr strPtrMarshaler
156 }{`"<str>"`, `"<str>"`}
157
158
159 stringOption := struct {
160 Bar string `json:"bar,string"`
161 }{`<html>foobar</html>`}
162
163 for _, tt := range []struct {
164 name string
165 v any
166 wantEscape string
167 want string
168 }{
169 {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`},
170 {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
171 {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
172 {
173 "tagStruct", tagStruct,
174 `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`,
175 `{"<>&#! ":0,"Invalid":0}`,
176 },
177 {
178 `"<str>"`, marshalerStruct,
179 `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
180 `{"NonPtr":"<str>","Ptr":"<str>"}`,
181 },
182 {
183 "stringOption", stringOption,
184 `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
185 `{"bar":"\"<html>foobar</html>\""}`,
186 },
187 } {
188 var buf strings.Builder
189 enc := NewEncoder(&buf)
190 if err := enc.Encode(tt.v); err != nil {
191 t.Errorf("Encode(%s): %s", tt.name, err)
192 continue
193 }
194 if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
195 t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
196 }
197 buf.Reset()
198 enc.SetEscapeHTML(false)
199 if err := enc.Encode(tt.v); err != nil {
200 t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
201 continue
202 }
203 if got := strings.TrimSpace(buf.String()); got != tt.want {
204 t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
205 tt.name, got, tt.want)
206 }
207 }
208 }
209
210 func TestDecoder(t *testing.T) {
211 for i := 0; i <= len(streamTest); i++ {
212
213
214
215
216
217 var buf bytes.Buffer
218 for _, c := range nlines(streamEncoded, i) {
219 if c != '\n' {
220 buf.WriteRune(c)
221 }
222 }
223 out := make([]any, i)
224 dec := NewDecoder(&buf)
225 for j := range out {
226 if err := dec.Decode(&out[j]); err != nil {
227 t.Fatalf("decode #%d/%d: %v", j, i, err)
228 }
229 }
230 if !reflect.DeepEqual(out, streamTest[0:i]) {
231 t.Errorf("decoding %d items: mismatch", i)
232 for j := range out {
233 if !reflect.DeepEqual(out[j], streamTest[j]) {
234 t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j])
235 }
236 }
237 break
238 }
239 }
240 }
241
242 func TestDecoderBuffered(t *testing.T) {
243 r := strings.NewReader(`{"Name": "Gopher"} extra `)
244 var m struct {
245 Name string
246 }
247 d := NewDecoder(r)
248 err := d.Decode(&m)
249 if err != nil {
250 t.Fatal(err)
251 }
252 if m.Name != "Gopher" {
253 t.Errorf("Name = %q; want Gopher", m.Name)
254 }
255 rest, err := io.ReadAll(d.Buffered())
256 if err != nil {
257 t.Fatal(err)
258 }
259 if g, w := string(rest), " extra "; g != w {
260 t.Errorf("Remaining = %q; want %q", g, w)
261 }
262 }
263
264 func nlines(s string, n int) string {
265 if n <= 0 {
266 return ""
267 }
268 for i, c := range s {
269 if c == '\n' {
270 if n--; n == 0 {
271 return s[0 : i+1]
272 }
273 }
274 }
275 return s
276 }
277
278 func TestRawMessage(t *testing.T) {
279 var data struct {
280 X float64
281 Id RawMessage
282 Y float32
283 }
284 const raw = `["\u0056",null]`
285 const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
286 err := Unmarshal([]byte(msg), &data)
287 if err != nil {
288 t.Fatalf("Unmarshal: %v", err)
289 }
290 if string([]byte(data.Id)) != raw {
291 t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw)
292 }
293 b, err := Marshal(&data)
294 if err != nil {
295 t.Fatalf("Marshal: %v", err)
296 }
297 if string(b) != msg {
298 t.Fatalf("Marshal: have %#q want %#q", b, msg)
299 }
300 }
301
302 func TestNullRawMessage(t *testing.T) {
303 var data struct {
304 X float64
305 Id RawMessage
306 IdPtr *RawMessage
307 Y float32
308 }
309 const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
310 err := Unmarshal([]byte(msg), &data)
311 if err != nil {
312 t.Fatalf("Unmarshal: %v", err)
313 }
314 if want, got := "null", string(data.Id); want != got {
315 t.Fatalf("Raw mismatch: have %q, want %q", got, want)
316 }
317 if data.IdPtr != nil {
318 t.Fatalf("Raw pointer mismatch: have non-nil, want nil")
319 }
320 b, err := Marshal(&data)
321 if err != nil {
322 t.Fatalf("Marshal: %v", err)
323 }
324 if string(b) != msg {
325 t.Fatalf("Marshal: have %#q want %#q", b, msg)
326 }
327 }
328
329 var blockingTests = []string{
330 `{"x": 1}`,
331 `[1, 2, 3]`,
332 }
333
334 func TestBlocking(t *testing.T) {
335 for _, enc := range blockingTests {
336 r, w := net.Pipe()
337 go w.Write([]byte(enc))
338 var val any
339
340
341
342 if err := NewDecoder(r).Decode(&val); err != nil {
343 t.Errorf("decoding %s: %v", enc, err)
344 }
345 r.Close()
346 w.Close()
347 }
348 }
349
350 type tokenStreamCase struct {
351 json string
352 expTokens []any
353 }
354
355 type decodeThis struct {
356 v any
357 }
358
359 var tokenStreamCases = []tokenStreamCase{
360
361 {json: `10`, expTokens: []any{float64(10)}},
362 {json: ` [10] `, expTokens: []any{
363 Delim('['), float64(10), Delim(']')}},
364 {json: ` [false,10,"b"] `, expTokens: []any{
365 Delim('['), false, float64(10), "b", Delim(']')}},
366 {json: `{ "a": 1 }`, expTokens: []any{
367 Delim('{'), "a", float64(1), Delim('}')}},
368 {json: `{"a": 1, "b":"3"}`, expTokens: []any{
369 Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
370 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
371 Delim('['),
372 Delim('{'), "a", float64(1), Delim('}'),
373 Delim('{'), "a", float64(2), Delim('}'),
374 Delim(']')}},
375 {json: `{"obj": {"a": 1}}`, expTokens: []any{
376 Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
377 Delim('}')}},
378 {json: `{"obj": [{"a": 1}]}`, expTokens: []any{
379 Delim('{'), "obj", Delim('['),
380 Delim('{'), "a", float64(1), Delim('}'),
381 Delim(']'), Delim('}')}},
382
383
384 {json: `{ "a": 1 }`, expTokens: []any{
385 Delim('{'), "a",
386 decodeThis{float64(1)},
387 Delim('}')}},
388 {json: ` [ { "a" : 1 } ] `, expTokens: []any{
389 Delim('['),
390 decodeThis{map[string]any{"a": float64(1)}},
391 Delim(']')}},
392 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
393 Delim('['),
394 decodeThis{map[string]any{"a": float64(1)}},
395 decodeThis{map[string]any{"a": float64(2)}},
396 Delim(']')}},
397 {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{
398 Delim('{'), "obj", Delim('['),
399 decodeThis{map[string]any{"a": float64(1)}},
400 Delim(']'), Delim('}')}},
401
402 {json: `{"obj": {"a": 1}}`, expTokens: []any{
403 Delim('{'), "obj",
404 decodeThis{map[string]any{"a": float64(1)}},
405 Delim('}')}},
406 {json: `{"obj": [{"a": 1}]}`, expTokens: []any{
407 Delim('{'), "obj",
408 decodeThis{[]any{
409 map[string]any{"a": float64(1)},
410 }},
411 Delim('}')}},
412 {json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{
413 Delim('['),
414 decodeThis{map[string]any{"a": float64(1)}},
415 decodeThis{&SyntaxError{"expected comma after array element", 11}},
416 }},
417 {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{
418 Delim('{'), strings.Repeat("a", 513),
419 decodeThis{&SyntaxError{"expected colon after object key", 518}},
420 }},
421 {json: `{ "\a" }`, expTokens: []any{
422 Delim('{'),
423 &SyntaxError{"invalid character 'a' in string escape code", 3},
424 }},
425 {json: ` \a`, expTokens: []any{
426 &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1},
427 }},
428 }
429
430 func TestDecodeInStream(t *testing.T) {
431 for ci, tcase := range tokenStreamCases {
432
433 dec := NewDecoder(strings.NewReader(tcase.json))
434 for i, etk := range tcase.expTokens {
435
436 var tk any
437 var err error
438
439 if dt, ok := etk.(decodeThis); ok {
440 etk = dt.v
441 err = dec.Decode(&tk)
442 } else {
443 tk, err = dec.Token()
444 }
445 if experr, ok := etk.(error); ok {
446 if err == nil || !reflect.DeepEqual(err, experr) {
447 t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err)
448 }
449 break
450 } else if err == io.EOF {
451 t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json)
452 break
453 } else if err != nil {
454 t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json)
455 break
456 }
457 if !reflect.DeepEqual(tk, etk) {
458 t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk)
459 break
460 }
461 }
462 }
463 }
464
465
466 func TestHTTPDecoding(t *testing.T) {
467 const raw = `{ "foo": "bar" }`
468
469 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
470 w.Write([]byte(raw))
471 }))
472 defer ts.Close()
473 res, err := http.Get(ts.URL)
474 if err != nil {
475 log.Fatalf("GET failed: %v", err)
476 }
477 defer res.Body.Close()
478
479 foo := struct {
480 Foo string
481 }{}
482
483 d := NewDecoder(res.Body)
484 err = d.Decode(&foo)
485 if err != nil {
486 t.Fatalf("Decode: %v", err)
487 }
488 if foo.Foo != "bar" {
489 t.Errorf("decoded %q; want \"bar\"", foo.Foo)
490 }
491
492
493 err = d.Decode(&foo)
494 if err != io.EOF {
495 t.Errorf("err = %v; want io.EOF", err)
496 }
497 }
498
View as plain text