k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/coder_test.go (about) 1 // Copyright 2020 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 "errors" 10 "io" 11 "math" 12 "math/rand" 13 "path" 14 "strings" 15 "testing" 16 ) 17 18 var ( 19 zeroToken Token 20 zeroValue RawValue 21 ) 22 23 // tokOrVal is either a Token or a RawValue. 24 type tokOrVal interface{ Kind() Kind } 25 26 type coderTestdataEntry struct { 27 name testName 28 in string 29 outCompacted string 30 outEscaped string // outCompacted if empty; escapes all runes in a string 31 outIndented string // outCompacted if empty; uses " " for indent prefix and "\t" for indent 32 outCanonicalized string // outCompacted if empty 33 tokens []Token 34 pointers []string 35 } 36 37 var coderTestdata = []coderTestdataEntry{{ 38 name: name("Null"), 39 in: ` null `, 40 outCompacted: `null`, 41 tokens: []Token{Null}, 42 pointers: []string{""}, 43 }, { 44 name: name("False"), 45 in: ` false `, 46 outCompacted: `false`, 47 tokens: []Token{False}, 48 }, { 49 name: name("True"), 50 in: ` true `, 51 outCompacted: `true`, 52 tokens: []Token{True}, 53 }, { 54 name: name("EmptyString"), 55 in: ` "" `, 56 outCompacted: `""`, 57 tokens: []Token{String("")}, 58 }, { 59 name: name("SimpleString"), 60 in: ` "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" `, 61 outCompacted: `"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"`, 62 outEscaped: `"\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a"`, 63 tokens: []Token{String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")}, 64 }, { 65 name: name("ComplicatedString"), 66 in: " \"Hello, δΈη πβ ββ©π " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + ` \ud800\udead \"\\\/\b\f\n\r\t \u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009" `, 67 outCompacted: "\"Hello, δΈη πβ ββ©π " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + " π \\\"\\\\/\\b\\f\\n\\r\\t \\\"\\\\/\\b\\f\\n\\r\\t\"", 68 outEscaped: `"\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u4e16\u754c\u0020\ud83c\udf1f\u2605\u2606\u2729\ud83c\udf20\u0020\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\ud83d\ude02\u0020\ud800\udead\u0020\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009\u0020\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009"`, 69 outCanonicalized: `"Hello, δΈη πβ ββ©π ΒΓΆβ¬νξדּ�π π \"\\/\b\f\n\r\t \"\\/\b\f\n\r\t"`, 70 tokens: []Token{rawToken("\"Hello, δΈη πβ ββ©π " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + " π \\\"\\\\/\\b\\f\\n\\r\\t \\\"\\\\/\\b\\f\\n\\r\\t\"")}, 71 }, { 72 name: name("ZeroNumber"), 73 in: ` 0 `, 74 outCompacted: `0`, 75 tokens: []Token{Uint(0)}, 76 }, { 77 name: name("SimpleNumber"), 78 in: ` 123456789 `, 79 outCompacted: `123456789`, 80 tokens: []Token{Uint(123456789)}, 81 }, { 82 name: name("NegativeNumber"), 83 in: ` -123456789 `, 84 outCompacted: `-123456789`, 85 tokens: []Token{Int(-123456789)}, 86 }, { 87 name: name("FractionalNumber"), 88 in: " 0.123456789 ", 89 outCompacted: `0.123456789`, 90 tokens: []Token{Float(0.123456789)}, 91 }, { 92 name: name("ExponentNumber"), 93 in: " 0e12456789 ", 94 outCompacted: `0e12456789`, 95 outCanonicalized: `0`, 96 tokens: []Token{rawToken(`0e12456789`)}, 97 }, { 98 name: name("ExponentNumberP"), 99 in: " 0e+12456789 ", 100 outCompacted: `0e+12456789`, 101 outCanonicalized: `0`, 102 tokens: []Token{rawToken(`0e+12456789`)}, 103 }, { 104 name: name("ExponentNumberN"), 105 in: " 0e-12456789 ", 106 outCompacted: `0e-12456789`, 107 outCanonicalized: `0`, 108 tokens: []Token{rawToken(`0e-12456789`)}, 109 }, { 110 name: name("ComplicatedNumber"), 111 in: ` -123456789.987654321E+0123456789 `, 112 outCompacted: `-123456789.987654321E+0123456789`, 113 outCanonicalized: `-1.7976931348623157e+308`, 114 tokens: []Token{rawToken(`-123456789.987654321E+0123456789`)}, 115 }, { 116 name: name("Numbers"), 117 in: ` [ 118 0, -0, 0.0, -0.0, 1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001, 1e1000, 119 -5e-324, 1e+100, 1.7976931348623157e+308, 120 9007199254740990, 9007199254740991, 9007199254740992, 9007199254740993, 9007199254740994, 121 -9223372036854775808, 9223372036854775807, 0, 18446744073709551615 122 ] `, 123 outCompacted: "[0,-0,0.0,-0.0,1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,1e1000,-5e-324,1e+100,1.7976931348623157e+308,9007199254740990,9007199254740991,9007199254740992,9007199254740993,9007199254740994,-9223372036854775808,9223372036854775807,0,18446744073709551615]", 124 outIndented: `[ 125 0, 126 -0, 127 0.0, 128 -0.0, 129 1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001, 130 1e1000, 131 -5e-324, 132 1e+100, 133 1.7976931348623157e+308, 134 9007199254740990, 135 9007199254740991, 136 9007199254740992, 137 9007199254740993, 138 9007199254740994, 139 -9223372036854775808, 140 9223372036854775807, 141 0, 142 18446744073709551615 143 ]`, 144 outCanonicalized: `[0,0,0,0,1,1.7976931348623157e+308,-5e-324,1e+100,1.7976931348623157e+308,9007199254740990,9007199254740991,9007199254740992,9007199254740992,9007199254740994,-9223372036854776000,9223372036854776000,0,18446744073709552000]`, 145 tokens: []Token{ 146 ArrayStart, 147 Float(0), Float(math.Copysign(0, -1)), rawToken(`0.0`), rawToken(`-0.0`), rawToken(`1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001`), rawToken(`1e1000`), 148 Float(-5e-324), Float(1e100), Float(1.7976931348623157e+308), 149 Float(9007199254740990), Float(9007199254740991), Float(9007199254740992), rawToken(`9007199254740993`), rawToken(`9007199254740994`), 150 Int(minInt64), Int(maxInt64), Uint(minUint64), Uint(maxUint64), 151 ArrayEnd, 152 }, 153 pointers: []string{ 154 "", "/0", "/1", "/2", "/3", "/4", "/5", "/6", "/7", "/8", "/9", "/10", "/11", "/12", "/13", "/14", "/15", "/16", "/17", "", 155 }, 156 }, { 157 name: name("ObjectN0"), 158 in: ` { } `, 159 outCompacted: `{}`, 160 tokens: []Token{ObjectStart, ObjectEnd}, 161 pointers: []string{"", ""}, 162 }, { 163 name: name("ObjectN1"), 164 in: ` { "0" : 0 } `, 165 outCompacted: `{"0":0}`, 166 outEscaped: `{"\u0030":0}`, 167 outIndented: `{ 168 "0": 0 169 }`, 170 tokens: []Token{ObjectStart, String("0"), Uint(0), ObjectEnd}, 171 pointers: []string{"", "/0", "/0", ""}, 172 }, { 173 name: name("ObjectN2"), 174 in: ` { "0" : 0 , "1" : 1 } `, 175 outCompacted: `{"0":0,"1":1}`, 176 outEscaped: `{"\u0030":0,"\u0031":1}`, 177 outIndented: `{ 178 "0": 0, 179 "1": 1 180 }`, 181 tokens: []Token{ObjectStart, String("0"), Uint(0), String("1"), Uint(1), ObjectEnd}, 182 pointers: []string{"", "/0", "/0", "/1", "/1", ""}, 183 }, { 184 name: name("ObjectNested"), 185 in: ` { "0" : { "1" : { "2" : { "3" : { "4" : { } } } } } } `, 186 outCompacted: `{"0":{"1":{"2":{"3":{"4":{}}}}}}`, 187 outEscaped: `{"\u0030":{"\u0031":{"\u0032":{"\u0033":{"\u0034":{}}}}}}`, 188 outIndented: `{ 189 "0": { 190 "1": { 191 "2": { 192 "3": { 193 "4": {} 194 } 195 } 196 } 197 } 198 }`, 199 tokens: []Token{ObjectStart, String("0"), ObjectStart, String("1"), ObjectStart, String("2"), ObjectStart, String("3"), ObjectStart, String("4"), ObjectStart, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd}, 200 pointers: []string{ 201 "", 202 "/0", "/0", 203 "/0/1", "/0/1", 204 "/0/1/2", "/0/1/2", 205 "/0/1/2/3", "/0/1/2/3", 206 "/0/1/2/3/4", "/0/1/2/3/4", 207 "/0/1/2/3/4", 208 "/0/1/2/3", 209 "/0/1/2", 210 "/0/1", 211 "/0", 212 "", 213 }, 214 }, { 215 name: name("ObjectSuperNested"), 216 in: `{"": { 217 "44444": { 218 "6666666": "ccccccc", 219 "77777777": "bb", 220 "555555": "aaaa" 221 }, 222 "0": { 223 "3333": "bbb", 224 "11": "", 225 "222": "aaaaa" 226 } 227 }}`, 228 outCompacted: `{"":{"44444":{"6666666":"ccccccc","77777777":"bb","555555":"aaaa"},"0":{"3333":"bbb","11":"","222":"aaaaa"}}}`, 229 outEscaped: `{"":{"\u0034\u0034\u0034\u0034\u0034":{"\u0036\u0036\u0036\u0036\u0036\u0036\u0036":"\u0063\u0063\u0063\u0063\u0063\u0063\u0063","\u0037\u0037\u0037\u0037\u0037\u0037\u0037\u0037":"\u0062\u0062","\u0035\u0035\u0035\u0035\u0035\u0035":"\u0061\u0061\u0061\u0061"},"\u0030":{"\u0033\u0033\u0033\u0033":"\u0062\u0062\u0062","\u0031\u0031":"","\u0032\u0032\u0032":"\u0061\u0061\u0061\u0061\u0061"}}}`, 230 outIndented: `{ 231 "": { 232 "44444": { 233 "6666666": "ccccccc", 234 "77777777": "bb", 235 "555555": "aaaa" 236 }, 237 "0": { 238 "3333": "bbb", 239 "11": "", 240 "222": "aaaaa" 241 } 242 } 243 }`, 244 outCanonicalized: `{"":{"0":{"11":"","222":"aaaaa","3333":"bbb"},"44444":{"555555":"aaaa","6666666":"ccccccc","77777777":"bb"}}}`, 245 tokens: []Token{ 246 ObjectStart, 247 String(""), 248 ObjectStart, 249 String("44444"), 250 ObjectStart, 251 String("6666666"), String("ccccccc"), 252 String("77777777"), String("bb"), 253 String("555555"), String("aaaa"), 254 ObjectEnd, 255 String("0"), 256 ObjectStart, 257 String("3333"), String("bbb"), 258 String("11"), String(""), 259 String("222"), String("aaaaa"), 260 ObjectEnd, 261 ObjectEnd, 262 ObjectEnd, 263 }, 264 pointers: []string{ 265 "", 266 "/", "/", 267 "//44444", "//44444", 268 "//44444/6666666", "//44444/6666666", 269 "//44444/77777777", "//44444/77777777", 270 "//44444/555555", "//44444/555555", 271 "//44444", 272 "//0", "//0", 273 "//0/3333", "//0/3333", 274 "//0/11", "//0/11", 275 "//0/222", "//0/222", 276 "//0", 277 "/", 278 "", 279 }, 280 }, { 281 name: name("ArrayN0"), 282 in: ` [ ] `, 283 outCompacted: `[]`, 284 tokens: []Token{ArrayStart, ArrayEnd}, 285 pointers: []string{"", ""}, 286 }, { 287 name: name("ArrayN1"), 288 in: ` [ 0 ] `, 289 outCompacted: `[0]`, 290 outIndented: `[ 291 0 292 ]`, 293 tokens: []Token{ArrayStart, Uint(0), ArrayEnd}, 294 pointers: []string{"", "/0", ""}, 295 }, { 296 name: name("ArrayN2"), 297 in: ` [ 0 , 1 ] `, 298 outCompacted: `[0,1]`, 299 outIndented: `[ 300 0, 301 1 302 ]`, 303 tokens: []Token{ArrayStart, Uint(0), Uint(1), ArrayEnd}, 304 }, { 305 name: name("ArrayNested"), 306 in: ` [ [ [ [ [ ] ] ] ] ] `, 307 outCompacted: `[[[[[]]]]]`, 308 outIndented: `[ 309 [ 310 [ 311 [ 312 [] 313 ] 314 ] 315 ] 316 ]`, 317 tokens: []Token{ArrayStart, ArrayStart, ArrayStart, ArrayStart, ArrayStart, ArrayEnd, ArrayEnd, ArrayEnd, ArrayEnd, ArrayEnd}, 318 pointers: []string{ 319 "", 320 "/0", 321 "/0/0", 322 "/0/0/0", 323 "/0/0/0/0", 324 "/0/0/0/0", 325 "/0/0/0", 326 "/0/0", 327 "/0", 328 "", 329 }, 330 }, { 331 name: name("Everything"), 332 in: ` { 333 "literals" : [ null , false , true ], 334 "string" : "Hello, δΈη" , 335 "number" : 3.14159 , 336 "arrayN0" : [ ] , 337 "arrayN1" : [ 0 ] , 338 "arrayN2" : [ 0 , 1 ] , 339 "objectN0" : { } , 340 "objectN1" : { "0" : 0 } , 341 "objectN2" : { "0" : 0 , "1" : 1 } 342 } `, 343 outCompacted: `{"literals":[null,false,true],"string":"Hello, δΈη","number":3.14159,"arrayN0":[],"arrayN1":[0],"arrayN2":[0,1],"objectN0":{},"objectN1":{"0":0},"objectN2":{"0":0,"1":1}}`, 344 outEscaped: `{"\u006c\u0069\u0074\u0065\u0072\u0061\u006c\u0073":[null,false,true],"\u0073\u0074\u0072\u0069\u006e\u0067":"\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u4e16\u754c","\u006e\u0075\u006d\u0062\u0065\u0072":3.14159,"\u0061\u0072\u0072\u0061\u0079\u004e\u0030":[],"\u0061\u0072\u0072\u0061\u0079\u004e\u0031":[0],"\u0061\u0072\u0072\u0061\u0079\u004e\u0032":[0,1],"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0030":{},"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0031":{"\u0030":0},"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0032":{"\u0030":0,"\u0031":1}}`, 345 outIndented: `{ 346 "literals": [ 347 null, 348 false, 349 true 350 ], 351 "string": "Hello, δΈη", 352 "number": 3.14159, 353 "arrayN0": [], 354 "arrayN1": [ 355 0 356 ], 357 "arrayN2": [ 358 0, 359 1 360 ], 361 "objectN0": {}, 362 "objectN1": { 363 "0": 0 364 }, 365 "objectN2": { 366 "0": 0, 367 "1": 1 368 } 369 }`, 370 outCanonicalized: `{"arrayN0":[],"arrayN1":[0],"arrayN2":[0,1],"literals":[null,false,true],"number":3.14159,"objectN0":{},"objectN1":{"0":0},"objectN2":{"0":0,"1":1},"string":"Hello, δΈη"}`, 371 tokens: []Token{ 372 ObjectStart, 373 String("literals"), ArrayStart, Null, False, True, ArrayEnd, 374 String("string"), String("Hello, δΈη"), 375 String("number"), Float(3.14159), 376 String("arrayN0"), ArrayStart, ArrayEnd, 377 String("arrayN1"), ArrayStart, Uint(0), ArrayEnd, 378 String("arrayN2"), ArrayStart, Uint(0), Uint(1), ArrayEnd, 379 String("objectN0"), ObjectStart, ObjectEnd, 380 String("objectN1"), ObjectStart, String("0"), Uint(0), ObjectEnd, 381 String("objectN2"), ObjectStart, String("0"), Uint(0), String("1"), Uint(1), ObjectEnd, 382 ObjectEnd, 383 }, 384 pointers: []string{ 385 "", 386 "/literals", "/literals", 387 "/literals/0", 388 "/literals/1", 389 "/literals/2", 390 "/literals", 391 "/string", "/string", 392 "/number", "/number", 393 "/arrayN0", "/arrayN0", "/arrayN0", 394 "/arrayN1", "/arrayN1", 395 "/arrayN1/0", 396 "/arrayN1", 397 "/arrayN2", "/arrayN2", 398 "/arrayN2/0", 399 "/arrayN2/1", 400 "/arrayN2", 401 "/objectN0", "/objectN0", "/objectN0", 402 "/objectN1", "/objectN1", 403 "/objectN1/0", "/objectN1/0", 404 "/objectN1", 405 "/objectN2", "/objectN2", 406 "/objectN2/0", "/objectN2/0", 407 "/objectN2/1", "/objectN2/1", 408 "/objectN2", 409 "", 410 }, 411 }} 412 413 // TestCoderInterleaved tests that we can interleave calls that operate on 414 // tokens and raw values. The only error condition is trying to operate on a 415 // raw value when the next token is an end of object or array. 416 func TestCoderInterleaved(t *testing.T) { 417 for _, td := range coderTestdata { 418 // In TokenFirst and ValueFirst, alternate between tokens and values. 419 // In TokenDelims, only use tokens for object and array delimiters. 420 for _, modeName := range []string{"TokenFirst", "ValueFirst", "TokenDelims"} { 421 t.Run(path.Join(td.name.name, modeName), func(t *testing.T) { 422 testCoderInterleaved(t, td.name.where, modeName, td) 423 }) 424 } 425 } 426 } 427 func testCoderInterleaved(t *testing.T, where pc, modeName string, td coderTestdataEntry) { 428 src := strings.NewReader(td.in) 429 dst := new(bytes.Buffer) 430 dec := NewDecoder(src) 431 enc := NewEncoder(dst) 432 tickTock := modeName == "TokenFirst" 433 for { 434 if modeName == "TokenDelims" { 435 switch dec.PeekKind() { 436 case '{', '}', '[', ']': 437 tickTock = true // as token 438 default: 439 tickTock = false // as value 440 } 441 } 442 if tickTock { 443 tok, err := dec.ReadToken() 444 if err != nil { 445 if err == io.EOF { 446 break 447 } 448 t.Fatalf("%s: Decoder.ReadToken error: %v", where, err) 449 } 450 if err := enc.WriteToken(tok); err != nil { 451 t.Fatalf("%s: Encoder.WriteToken error: %v", where, err) 452 } 453 } else { 454 val, err := dec.ReadValue() 455 if err != nil { 456 // It is a syntactic error to call ReadValue 457 // at the end of an object or array. 458 // Retry as a ReadToken call. 459 expectError := dec.PeekKind() == '}' || dec.PeekKind() == ']' 460 if expectError { 461 if !errors.As(err, new(*SyntacticError)) { 462 t.Fatalf("%s: Decoder.ReadToken error is %T, want %T", where, err, new(SyntacticError)) 463 } 464 tickTock = !tickTock 465 continue 466 } 467 468 if err == io.EOF { 469 break 470 } 471 t.Fatalf("%s: Decoder.ReadValue error: %v", where, err) 472 } 473 if err := enc.WriteValue(val); err != nil { 474 t.Fatalf("%s: Encoder.WriteValue error: %v", where, err) 475 } 476 } 477 tickTock = !tickTock 478 } 479 480 got := dst.String() 481 want := td.outCompacted + "\n" 482 if got != want { 483 t.Fatalf("%s: output mismatch:\ngot %q\nwant %q", where, got, want) 484 } 485 } 486 487 func TestCoderStackPointer(t *testing.T) { 488 tests := []struct { 489 token Token 490 wantWithRejectDuplicateNames string 491 wantWithAllowDuplicateNames string 492 }{ 493 {Null, "", ""}, 494 495 {ArrayStart, "", ""}, 496 {ArrayEnd, "", ""}, 497 498 {ArrayStart, "", ""}, 499 {Bool(true), "/0", "/0"}, 500 {ArrayEnd, "", ""}, 501 502 {ArrayStart, "", ""}, 503 {String("hello"), "/0", "/0"}, 504 {String("goodbye"), "/1", "/1"}, 505 {ArrayEnd, "", ""}, 506 507 {ObjectStart, "", ""}, 508 {ObjectEnd, "", ""}, 509 510 {ObjectStart, "", ""}, 511 {String("hello"), "/hello", "/0"}, 512 {String("goodbye"), "/hello", "/0"}, 513 {ObjectEnd, "", ""}, 514 515 {ObjectStart, "", ""}, 516 {String(""), "/", "/0"}, 517 {Null, "/", "/0"}, 518 {String("0"), "/0", "/1"}, 519 {Null, "/0", "/1"}, 520 {String("~"), "/~0", "/2"}, 521 {Null, "/~0", "/2"}, 522 {String("/"), "/~1", "/3"}, 523 {Null, "/~1", "/3"}, 524 {String("a//b~/c/~d~~e"), "/a~1~1b~0~1c~1~0d~0~0e", "/4"}, 525 {Null, "/a~1~1b~0~1c~1~0d~0~0e", "/4"}, 526 {String(" \r\n\t"), "/ \r\n\t", "/5"}, 527 {Null, "/ \r\n\t", "/5"}, 528 {ObjectEnd, "", ""}, 529 530 {ArrayStart, "", ""}, 531 {ObjectStart, "/0", "/0"}, 532 {String(""), "/0/", "/0/0"}, 533 {ArrayStart, "/0/", "/0/0"}, 534 {ObjectStart, "/0//0", "/0/0/0"}, 535 {String("#"), "/0//0/#", "/0/0/0/0"}, 536 {Null, "/0//0/#", "/0/0/0/0"}, 537 {ObjectEnd, "/0//0", "/0/0/0"}, 538 {ArrayEnd, "/0/", "/0/0"}, 539 {ObjectEnd, "/0", "/0"}, 540 {ArrayEnd, "", ""}, 541 } 542 543 for _, allowDupes := range []bool{false, true} { 544 var name string 545 var want func(i int) string 546 switch allowDupes { 547 case false: 548 name = "RejectDuplicateNames" 549 want = func(i int) string { return tests[i].wantWithRejectDuplicateNames } 550 case true: 551 name = "AllowDuplicateNames" 552 want = func(i int) string { return tests[i].wantWithAllowDuplicateNames } 553 } 554 555 t.Run(name, func(t *testing.T) { 556 bb := new(bytes.Buffer) 557 558 enc := EncodeOptions{AllowDuplicateNames: allowDupes}.NewEncoder(bb) 559 for i, tt := range tests { 560 if err := enc.WriteToken(tt.token); err != nil { 561 t.Fatalf("%d: Encoder.WriteToken error: %v", i, err) 562 } 563 if got := enc.StackPointer(); got != want(i) { 564 t.Fatalf("%d: Encoder.StackPointer = %v, want %v", i, got, want(i)) 565 } 566 } 567 568 dec := DecodeOptions{AllowDuplicateNames: allowDupes}.NewDecoder(bb) 569 for i := range tests { 570 if _, err := dec.ReadToken(); err != nil { 571 t.Fatalf("%d: Decoder.ReadToken error: %v", i, err) 572 } 573 if got := dec.StackPointer(); got != want(i) { 574 t.Fatalf("%d: Decoder.StackPointer = %v, want %v", i, got, want(i)) 575 } 576 } 577 }) 578 } 579 } 580 581 func TestCoderBufferGrowth(t *testing.T) { 582 // The growth rate of the internal buffer should be exponential, 583 // but should not grow unbounded. 584 checkGrowth := func(ns []int) { 585 t.Helper() 586 var sumBytes, sumRates, numGrows float64 587 prev := ns[0] 588 for i := 1; i < len(ns)-1; i++ { 589 n := ns[i] 590 if n != prev { 591 sumRates += float64(n) / float64(prev) 592 numGrows++ 593 prev = n 594 } 595 if n > 1<<20 { 596 t.Fatalf("single Read/Write too large: %d", n) 597 } 598 sumBytes += float64(n) 599 } 600 if mean := sumBytes / float64(len(ns)); mean < 1<<10 { 601 t.Fatalf("average Read/Write too small: %0.1f", mean) 602 } 603 switch mean := sumRates / numGrows; { 604 case mean < 1.25: 605 t.Fatalf("average growth rate too slow: %0.3f", mean) 606 case mean > 2.00: 607 t.Fatalf("average growth rate too fast: %0.3f", mean) 608 } 609 } 610 611 bb := &bytesBuffer{new(bytes.Buffer)} 612 613 var writeSizes []int 614 if err := MarshalFull(WriterFunc(func(b []byte) (int, error) { 615 n, err := bb.Write(b) 616 writeSizes = append(writeSizes, n) 617 return n, err 618 }), make([]struct{}, 1e6)); err != nil { 619 t.Fatalf("MarshalFull error: %v", err) 620 } 621 checkGrowth(writeSizes) 622 623 var readSizes []int 624 if err := UnmarshalFull(ReaderFunc(func(b []byte) (int, error) { 625 n, err := bb.Read(b) 626 readSizes = append(readSizes, n) 627 return n, err 628 }), new([]struct{})); err != nil { 629 t.Fatalf("UnmarshalFull error: %v", err) 630 } 631 checkGrowth(readSizes) 632 } 633 634 type ReaderFunc func([]byte) (int, error) 635 636 func (f ReaderFunc) Read(b []byte) (int, error) { return f(b) } 637 638 type WriterFunc func([]byte) (int, error) 639 640 func (f WriterFunc) Write(b []byte) (int, error) { return f(b) } 641 642 // FaultyBuffer implements io.Reader and io.Writer. 643 // It may process fewer bytes than the provided buffer 644 // and may randomly return an error. 645 type FaultyBuffer struct { 646 B []byte 647 648 // MaxBytes is the maximum number of bytes read/written. 649 // A random number of bytes within [0, MaxBytes] are processed. 650 // A non-positive value is treated as infinity. 651 MaxBytes int 652 653 // MayError specifies whether to randomly provide this error. 654 // Even if an error is returned, no bytes are dropped. 655 MayError error 656 657 // Rand to use for pseudo-random behavior. 658 // If nil, it will be initialized with rand.NewSource(0). 659 Rand rand.Source 660 } 661 662 func (p *FaultyBuffer) Read(b []byte) (int, error) { 663 b = b[:copy(b[:p.mayTruncate(len(b))], p.B)] 664 p.B = p.B[len(b):] 665 if len(p.B) == 0 && (len(b) == 0 || p.randN(2) == 0) { 666 return len(b), io.EOF 667 } 668 return len(b), p.mayError() 669 } 670 671 func (p *FaultyBuffer) Write(b []byte) (int, error) { 672 b2 := b[:p.mayTruncate(len(b))] 673 p.B = append(p.B, b2...) 674 if len(b2) < len(b) { 675 return len(b2), io.ErrShortWrite 676 } 677 return len(b2), p.mayError() 678 } 679 680 // mayTruncate may return a value between [0, n]. 681 func (p *FaultyBuffer) mayTruncate(n int) int { 682 if p.MaxBytes > 0 { 683 if n > p.MaxBytes { 684 n = p.MaxBytes 685 } 686 return p.randN(n + 1) 687 } 688 return n 689 } 690 691 // mayError may return a non-nil error. 692 func (p *FaultyBuffer) mayError() error { 693 if p.MayError != nil && p.randN(2) == 0 { 694 return p.MayError 695 } 696 return nil 697 } 698 699 func (p *FaultyBuffer) randN(n int) int { 700 if p.Rand == nil { 701 p.Rand = rand.NewSource(0) 702 } 703 return int(p.Rand.Int63() % int64(n)) 704 }