github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/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 "github.com/x04/go/src/bytes" 9 "github.com/x04/go/src/io" 10 "github.com/x04/go/src/io/ioutil" 11 "github.com/x04/go/src/log" 12 "github.com/x04/go/src/net" 13 "github.com/x04/go/src/net/http" 14 "github.com/x04/go/src/net/http/httptest" 15 "github.com/x04/go/src/reflect" 16 "github.com/x04/go/src/strings" 17 "github.com/x04/go/src/testing" 18 ) 19 20 // Test values for the stream test. 21 // One of each JSON kind. 22 var streamTest = []interface{}{ 23 0.1, 24 "hello", 25 nil, 26 true, 27 false, 28 []interface{}{"a", "b", "c"}, 29 map[string]interface{}{"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 bytes.Buffer 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 var streamEncodedIndent = `0.1 64 "hello" 65 null 66 true 67 false 68 [ 69 >."a", 70 >."b", 71 >."c" 72 >] 73 { 74 >."ß": "long s", 75 >."K": "Kelvin" 76 >} 77 3.14 78 ` 79 80 func TestEncoderIndent(t *testing.T) { 81 var buf bytes.Buffer 82 enc := NewEncoder(&buf) 83 enc.SetIndent(">", ".") 84 for _, v := range streamTest { 85 enc.Encode(v) 86 } 87 if have, want := buf.String(), streamEncodedIndent; have != want { 88 t.Error("indented encoding mismatch") 89 diff(t, []byte(have), []byte(want)) 90 } 91 } 92 93 type strMarshaler string 94 95 func (s strMarshaler) MarshalJSON() ([]byte, error) { 96 return []byte(s), nil 97 } 98 99 type strPtrMarshaler string 100 101 func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) { 102 return []byte(*s), nil 103 } 104 105 func TestEncoderSetEscapeHTML(t *testing.T) { 106 var c C 107 var ct CText 108 var tagStruct struct { 109 Valid int `json:"<>&#! "` 110 Invalid int `json:"\\"` 111 } 112 113 // This case is particularly interesting, as we force the encoder to 114 // take the address of the Ptr field to use its MarshalJSON method. This 115 // is why the '&' is important. 116 marshalerStruct := &struct { 117 NonPtr strMarshaler 118 Ptr strPtrMarshaler 119 }{`"<str>"`, `"<str>"`} 120 121 // https://golang.org/issue/34154 122 stringOption := struct { 123 Bar string `json:"bar,string"` 124 }{`<html>foobar</html>`} 125 126 for _, tt := range []struct { 127 name string 128 v interface{} 129 wantEscape string 130 want string 131 }{ 132 {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`}, 133 {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, 134 {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, 135 { 136 "tagStruct", tagStruct, 137 `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, 138 `{"<>&#! ":0,"Invalid":0}`, 139 }, 140 { 141 `"<str>"`, marshalerStruct, 142 `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`, 143 `{"NonPtr":"<str>","Ptr":"<str>"}`, 144 }, 145 { 146 "stringOption", stringOption, 147 `{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`, 148 `{"bar":"\"<html>foobar</html>\""}`, 149 }, 150 } { 151 var buf bytes.Buffer 152 enc := NewEncoder(&buf) 153 if err := enc.Encode(tt.v); err != nil { 154 t.Fatalf("Encode(%s): %s", tt.name, err) 155 } 156 if got := strings.TrimSpace(buf.String()); got != tt.wantEscape { 157 t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape) 158 } 159 buf.Reset() 160 enc.SetEscapeHTML(false) 161 if err := enc.Encode(tt.v); err != nil { 162 t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err) 163 } 164 if got := strings.TrimSpace(buf.String()); got != tt.want { 165 t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q", 166 tt.name, got, tt.want) 167 } 168 } 169 } 170 171 func TestDecoder(t *testing.T) { 172 for i := 0; i <= len(streamTest); i++ { 173 // Use stream without newlines as input, 174 // just to stress the decoder even more. 175 // Our test input does not include back-to-back numbers. 176 // Otherwise stripping the newlines would 177 // merge two adjacent JSON values. 178 var buf bytes.Buffer 179 for _, c := range nlines(streamEncoded, i) { 180 if c != '\n' { 181 buf.WriteRune(c) 182 } 183 } 184 out := make([]interface{}, i) 185 dec := NewDecoder(&buf) 186 for j := range out { 187 if err := dec.Decode(&out[j]); err != nil { 188 t.Fatalf("decode #%d/%d: %v", j, i, err) 189 } 190 } 191 if !reflect.DeepEqual(out, streamTest[0:i]) { 192 t.Errorf("decoding %d items: mismatch", i) 193 for j := range out { 194 if !reflect.DeepEqual(out[j], streamTest[j]) { 195 t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j]) 196 } 197 } 198 break 199 } 200 } 201 } 202 203 func TestDecoderBuffered(t *testing.T) { 204 r := strings.NewReader(`{"Name": "Gopher"} extra `) 205 var m struct { 206 Name string 207 } 208 d := NewDecoder(r) 209 err := d.Decode(&m) 210 if err != nil { 211 t.Fatal(err) 212 } 213 if m.Name != "Gopher" { 214 t.Errorf("Name = %q; want Gopher", m.Name) 215 } 216 rest, err := ioutil.ReadAll(d.Buffered()) 217 if err != nil { 218 t.Fatal(err) 219 } 220 if g, w := string(rest), " extra "; g != w { 221 t.Errorf("Remaining = %q; want %q", g, w) 222 } 223 } 224 225 func nlines(s string, n int) string { 226 if n <= 0 { 227 return "" 228 } 229 for i, c := range s { 230 if c == '\n' { 231 if n--; n == 0 { 232 return s[0 : i+1] 233 } 234 } 235 } 236 return s 237 } 238 239 func TestRawMessage(t *testing.T) { 240 var data struct { 241 X float64 242 Id RawMessage 243 Y float32 244 } 245 const raw = `["\u0056",null]` 246 const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}` 247 err := Unmarshal([]byte(msg), &data) 248 if err != nil { 249 t.Fatalf("Unmarshal: %v", err) 250 } 251 if string([]byte(data.Id)) != raw { 252 t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw) 253 } 254 b, err := Marshal(&data) 255 if err != nil { 256 t.Fatalf("Marshal: %v", err) 257 } 258 if string(b) != msg { 259 t.Fatalf("Marshal: have %#q want %#q", b, msg) 260 } 261 } 262 263 func TestNullRawMessage(t *testing.T) { 264 var data struct { 265 X float64 266 Id RawMessage 267 IdPtr *RawMessage 268 Y float32 269 } 270 const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` 271 err := Unmarshal([]byte(msg), &data) 272 if err != nil { 273 t.Fatalf("Unmarshal: %v", err) 274 } 275 if want, got := "null", string(data.Id); want != got { 276 t.Fatalf("Raw mismatch: have %q, want %q", got, want) 277 } 278 if data.IdPtr != nil { 279 t.Fatalf("Raw pointer mismatch: have non-nil, want nil") 280 } 281 b, err := Marshal(&data) 282 if err != nil { 283 t.Fatalf("Marshal: %v", err) 284 } 285 if string(b) != msg { 286 t.Fatalf("Marshal: have %#q want %#q", b, msg) 287 } 288 } 289 290 var blockingTests = []string{ 291 `{"x": 1}`, 292 `[1, 2, 3]`, 293 } 294 295 func TestBlocking(t *testing.T) { 296 for _, enc := range blockingTests { 297 r, w := net.Pipe() 298 go w.Write([]byte(enc)) 299 var val interface{} 300 301 // If Decode reads beyond what w.Write writes above, 302 // it will block, and the test will deadlock. 303 if err := NewDecoder(r).Decode(&val); err != nil { 304 t.Errorf("decoding %s: %v", enc, err) 305 } 306 r.Close() 307 w.Close() 308 } 309 } 310 311 func BenchmarkEncoderEncode(b *testing.B) { 312 b.ReportAllocs() 313 type T struct { 314 X, Y string 315 } 316 v := &T{"foo", "bar"} 317 b.RunParallel(func(pb *testing.PB) { 318 for pb.Next() { 319 if err := NewEncoder(ioutil.Discard).Encode(v); err != nil { 320 b.Fatal(err) 321 } 322 } 323 }) 324 } 325 326 type tokenStreamCase struct { 327 json string 328 expTokens []interface{} 329 } 330 331 type decodeThis struct { 332 v interface{} 333 } 334 335 var tokenStreamCases = []tokenStreamCase{ 336 // streaming token cases 337 {json: `10`, expTokens: []interface{}{float64(10)}}, 338 {json: ` [10] `, expTokens: []interface{}{ 339 Delim('['), float64(10), Delim(']')}}, 340 {json: ` [false,10,"b"] `, expTokens: []interface{}{ 341 Delim('['), false, float64(10), "b", Delim(']')}}, 342 {json: `{ "a": 1 }`, expTokens: []interface{}{ 343 Delim('{'), "a", float64(1), Delim('}')}}, 344 {json: `{"a": 1, "b":"3"}`, expTokens: []interface{}{ 345 Delim('{'), "a", float64(1), "b", "3", Delim('}')}}, 346 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ 347 Delim('['), 348 Delim('{'), "a", float64(1), Delim('}'), 349 Delim('{'), "a", float64(2), Delim('}'), 350 Delim(']')}}, 351 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ 352 Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'), 353 Delim('}')}}, 354 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ 355 Delim('{'), "obj", Delim('['), 356 Delim('{'), "a", float64(1), Delim('}'), 357 Delim(']'), Delim('}')}}, 358 359 // streaming tokens with intermittent Decode() 360 {json: `{ "a": 1 }`, expTokens: []interface{}{ 361 Delim('{'), "a", 362 decodeThis{float64(1)}, 363 Delim('}')}}, 364 {json: ` [ { "a" : 1 } ] `, expTokens: []interface{}{ 365 Delim('['), 366 decodeThis{map[string]interface{}{"a": float64(1)}}, 367 Delim(']')}}, 368 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ 369 Delim('['), 370 decodeThis{map[string]interface{}{"a": float64(1)}}, 371 decodeThis{map[string]interface{}{"a": float64(2)}}, 372 Delim(']')}}, 373 {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []interface{}{ 374 Delim('{'), "obj", Delim('['), 375 decodeThis{map[string]interface{}{"a": float64(1)}}, 376 Delim(']'), Delim('}')}}, 377 378 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ 379 Delim('{'), "obj", 380 decodeThis{map[string]interface{}{"a": float64(1)}}, 381 Delim('}')}}, 382 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ 383 Delim('{'), "obj", 384 decodeThis{[]interface{}{ 385 map[string]interface{}{"a": float64(1)}, 386 }}, 387 Delim('}')}}, 388 {json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{ 389 Delim('['), 390 decodeThis{map[string]interface{}{"a": float64(1)}}, 391 decodeThis{&SyntaxError{"expected comma after array element", 11}}, 392 }}, 393 {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []interface{}{ 394 Delim('{'), strings.Repeat("a", 513), 395 decodeThis{&SyntaxError{"expected colon after object key", 518}}, 396 }}, 397 {json: `{ "\a" }`, expTokens: []interface{}{ 398 Delim('{'), 399 &SyntaxError{"invalid character 'a' in string escape code", 3}, 400 }}, 401 {json: ` \a`, expTokens: []interface{}{ 402 &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1}, 403 }}, 404 } 405 406 func TestDecodeInStream(t *testing.T) { 407 for ci, tcase := range tokenStreamCases { 408 409 dec := NewDecoder(strings.NewReader(tcase.json)) 410 for i, etk := range tcase.expTokens { 411 412 var tk interface{} 413 var err error 414 415 if dt, ok := etk.(decodeThis); ok { 416 etk = dt.v 417 err = dec.Decode(&tk) 418 } else { 419 tk, err = dec.Token() 420 } 421 if experr, ok := etk.(error); ok { 422 if err == nil || !reflect.DeepEqual(err, experr) { 423 t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err) 424 } 425 break 426 } else if err == io.EOF { 427 t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json) 428 break 429 } else if err != nil { 430 t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json) 431 break 432 } 433 if !reflect.DeepEqual(tk, etk) { 434 t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk) 435 break 436 } 437 } 438 } 439 } 440 441 // Test from golang.org/issue/11893 442 func TestHTTPDecoding(t *testing.T) { 443 const raw = `{ "foo": "bar" }` 444 445 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 446 w.Write([]byte(raw)) 447 })) 448 defer ts.Close() 449 res, err := http.Get(ts.URL) 450 if err != nil { 451 log.Fatalf("GET failed: %v", err) 452 } 453 defer res.Body.Close() 454 455 foo := struct { 456 Foo string 457 }{} 458 459 d := NewDecoder(res.Body) 460 err = d.Decode(&foo) 461 if err != nil { 462 t.Fatalf("Decode: %v", err) 463 } 464 if foo.Foo != "bar" { 465 t.Errorf("decoded %q; want \"bar\"", foo.Foo) 466 } 467 468 // make sure we get the EOF the second time 469 err = d.Decode(&foo) 470 if err != io.EOF { 471 t.Errorf("err = %v; want io.EOF", err) 472 } 473 }