github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/encoding/json/stream_test.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // Test values for the stream test. 21 // One of each JSON kind. 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, // another value to make sure something can follow map 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 // Check that enc.SetIndent("", "") turns off indentation. 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 // Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test. 65 percent := debug.SetGCPercent(-1) 66 defer debug.SetGCPercent(percent) 67 68 // Trigger an error in Marshal with cyclic data. 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 // This case is particularly interesting, as we force the encoder to 151 // take the address of the Ptr field to use its MarshalJSON method. This 152 // is why the '&' is important. 153 marshalerStruct := &struct { 154 NonPtr strMarshaler 155 Ptr strPtrMarshaler 156 }{`"<str>"`, `"<str>"`} 157 158 // https://golang.org/issue/34154 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 // Use stream without newlines as input, 213 // just to stress the decoder even more. 214 // Our test input does not include back-to-back numbers. 215 // Otherwise stripping the newlines would 216 // merge two adjacent JSON values. 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 // If Decode reads beyond what w.Write writes above, 341 // it will block, and the test will deadlock. 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 // streaming token cases 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 // streaming tokens with intermittent Decode() 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 // Test from golang.org/issue/11893 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 // make sure we get the EOF the second time 493 err = d.Decode(&foo) 494 if err != io.EOF { 495 t.Errorf("err = %v; want io.EOF", err) 496 } 497 }