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