cuelang.org/go@v0.10.1/internal/encoding/yaml/decode_test.go (about) 1 // Many test cases in this file were originally ported from 2 // github.com/go-yaml/yaml, also known as gopkg.in/yaml.v3. 3 // 4 // Copyright 2011-2016 Canonical Ltd. 5 // Copyright 2020 CUE Authors 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 19 package yaml_test 20 21 import ( 22 "fmt" 23 "io" 24 "os" 25 "strconv" 26 "strings" 27 "testing" 28 29 "github.com/go-quicktest/qt" 30 "github.com/google/go-cmp/cmp" 31 32 "cuelang.org/go/cue/ast" 33 "cuelang.org/go/cue/format" 34 "cuelang.org/go/internal" 35 "cuelang.org/go/internal/cueexperiment" 36 "cuelang.org/go/internal/cuetest" 37 "cuelang.org/go/internal/encoding/yaml" 38 ) 39 40 // These tests are only for the new YAML decoder. 41 // The old YAML decoder has its own tests in internal/third_party/yaml. 42 func init() { cueexperiment.Flags.YAMLV3Decoder = true } 43 44 var unmarshalTests = []struct { 45 data string 46 want string 47 }{ 48 { 49 "", 50 "null", 51 }, 52 { 53 "{}", 54 "", 55 }, { 56 "v: hi", 57 `v: "hi"`, 58 }, { 59 "v: hi", 60 `v: "hi"`, 61 }, { 62 "v: true", 63 "v: true", 64 }, { 65 "v: 10", 66 "v: 10", 67 }, { 68 "v: 0b10", 69 "v: 0b10", 70 }, { 71 "v: 0xA", 72 "v: 0xA", 73 }, { 74 "v: 4294967296", 75 "v: 4294967296", 76 }, { 77 "v: 0.1", 78 "v: 0.1", 79 }, { 80 "v: .1", 81 "v: 0.1", 82 }, { 83 "v: .Inf", 84 "v: +Inf", 85 }, { 86 "v: -.Inf", 87 "v: -Inf", 88 }, { 89 "v: -10", 90 "v: -10", 91 }, { 92 "v: --10", 93 `v: "--10"`, 94 }, { 95 "v: -.1", 96 "v: -0.1", 97 }, 98 99 // Simple values. 100 { 101 "123", 102 "123", 103 }, 104 105 // Floats from spec 106 { 107 "canonical: 6.8523e+5", 108 "canonical: 6.8523e+5", 109 }, { 110 "expo: 685.230_15e+03", 111 "expo: 685.230_15e+03", 112 }, { 113 "fixed: 685_230.15", 114 "fixed: 685_230.15", 115 }, { 116 "neginf: -.inf", 117 "neginf: -Inf", 118 }, { 119 "fixed: 685_230.15", 120 "fixed: 685_230.15", 121 }, 122 //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported 123 //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. 124 125 // Bools from spec 126 { 127 "canonical: y", 128 `canonical: "y"`, 129 }, { 130 "answer: n", 131 `answer: "n"`, 132 }, { 133 "answer: NO", 134 `answer: "NO"`, 135 }, { 136 "logical: True", 137 "logical: true", 138 }, { 139 "option: on", 140 `option: "on"`, 141 }, { 142 "answer: off", 143 `answer: "off"`, 144 }, 145 // Ints from spec 146 { 147 "canonical: 685230", 148 "canonical: 685230", 149 }, { 150 "decimal: +685_230", 151 "decimal: +685_230", 152 }, { 153 "octal_yaml11: 02472256", 154 "octal_yaml11: 0o2472256", 155 }, { 156 "octal_yaml12: 0o2472256", 157 "octal_yaml12: 0o2472256", 158 }, { 159 "not_octal_yaml11: 0123456789", 160 `not_octal_yaml11: "0123456789"`, 161 }, { 162 "not_octal_yaml12: 0o123456789", 163 `not_octal_yaml12: "0o123456789"`, 164 }, { 165 // TODO(mvdan): 01234 is not a valid numeric literal in CUE. 166 "float_octal_yaml11: !!float 01234", 167 "float_octal_yaml11: number & 01234", 168 }, { 169 "float_octal_yaml12: !!float 0o1234", 170 "float_octal_yaml12: number & 0o1234", 171 }, { 172 "hexa: 0x_0A_74_AE", 173 "hexa: 0x_0A_74_AE", 174 }, { 175 "bin: 0b1010_0111_0100_1010_1110", 176 "bin: 0b1010_0111_0100_1010_1110", 177 }, { 178 "bin: -0b101010", 179 "bin: -0b101010", 180 }, { 181 "bin: -0b1000000000000000000000000000000000000000000000000000000000000000", 182 "bin: -0b1000000000000000000000000000000000000000000000000000000000000000", 183 }, { 184 "decimal: +685_230", 185 "decimal: +685_230", 186 }, 187 188 //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported 189 190 // Nulls from spec 191 { 192 "empty:", 193 "empty: null", 194 }, { 195 "canonical: ~", 196 "canonical: null", 197 }, { 198 "english: null", 199 "english: null", 200 }, { 201 "_foo: 1", 202 `"_foo": 1`, 203 }, { 204 `"#foo": 1`, 205 `"#foo": 1`, 206 }, { 207 "_#foo: 1", 208 `"_#foo": 1`, 209 }, { 210 "~: null key", 211 `"null": "null key"`, 212 }, { 213 `empty: 214 apple: "newline"`, 215 `empty: null 216 apple: "newline"`, 217 }, 218 219 // Flow sequence 220 { 221 "seq: [A,B]", 222 `seq: ["A", "B"]`, 223 }, { 224 "seq: [A,B,C,]", 225 `seq: ["A", "B", "C"]`, 226 }, { 227 "seq: [A,1,C]", 228 `seq: ["A", 1, "C"]`, 229 }, 230 // Block sequence 231 { 232 "seq:\n - A\n - B", 233 `seq: [ 234 "A", 235 "B", 236 ]`, 237 }, { 238 "seq:\n - A\n - B\n - C", 239 `seq: [ 240 "A", 241 "B", 242 "C", 243 ]`, 244 }, { 245 "seq:\n - A\n - 1\n - C", 246 `seq: [ 247 "A", 248 1, 249 "C", 250 ]`, 251 }, 252 253 // Literal block scalar 254 { 255 "scalar: | # Comment\n\n literal\n\n \ttext\n\n", 256 `scalar: """ 257 258 literal 259 260 \ttext 261 262 """ // Comment`, 263 }, 264 265 // Folded block scalar 266 { 267 "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", 268 `scalar: """ 269 270 folded line 271 next line 272 * one 273 * two 274 275 last line 276 277 """ // Comment`, 278 }, 279 280 // Structs 281 { 282 "a: {b: c}", 283 `a: {b: "c"}`, 284 }, 285 { 286 "hello: world", 287 `hello: "world"`, 288 }, { 289 "a:", 290 "a: null", 291 }, { 292 "a: 1", 293 "a: 1", 294 }, { 295 "a: 1.0", 296 "a: 1.0", 297 }, { 298 "a: [1, 2]", 299 "a: [1, 2]", 300 }, { 301 "a: y", 302 `a: "y"`, 303 }, { 304 "{ a: 1, b: {c: 1} }", 305 `a: 1, b: {c: 1}`, 306 }, { 307 ` 308 True: 1 309 Null: 1 310 .Inf: 2 311 `, 312 `"true": 1 313 "null": 1 314 "+Inf": 2`, 315 }, 316 317 // Some cross type conversions 318 { 319 "v: 42", 320 "v: 42", 321 }, { 322 "v: -42", 323 "v: -42", 324 }, { 325 "v: 4294967296", 326 "v: 4294967296", 327 }, { 328 "v: -4294967296", 329 "v: -4294967296", 330 }, 331 332 // int 333 { 334 "int_max: 2147483647", 335 "int_max: 2147483647", 336 }, 337 { 338 "int_min: -2147483648", 339 "int_min: -2147483648", 340 }, 341 { 342 "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 343 "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 344 }, 345 346 // int64 347 { 348 "int64_max: 9223372036854775807", 349 "int64_max: 9223372036854775807", 350 }, 351 { 352 "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", 353 "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", 354 }, 355 { 356 "int64_min: -9223372036854775808", 357 "int64_min: -9223372036854775808", 358 }, 359 { 360 "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", 361 "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", 362 }, 363 { 364 "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 365 "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 366 }, 367 368 // uint 369 { 370 "uint_max: 4294967295", 371 "uint_max: 4294967295", 372 }, 373 374 // uint64 375 { 376 "uint64_max: 18446744073709551615", 377 "uint64_max: 18446744073709551615", 378 }, 379 { 380 "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", 381 "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", 382 }, 383 { 384 "uint64_maxint64: 9223372036854775807", 385 "uint64_maxint64: 9223372036854775807", 386 }, 387 388 // float32 389 { 390 "float32_max: 3.40282346638528859811704183484516925440e+38", 391 "float32_max: 3.40282346638528859811704183484516925440e+38", 392 }, 393 { 394 "float32_nonzero: 1.401298464324817070923729583289916131280e-45", 395 "float32_nonzero: 1.401298464324817070923729583289916131280e-45", 396 }, 397 { 398 "float32_maxuint64: 18446744073709551615", 399 "float32_maxuint64: 18446744073709551615", 400 }, 401 { 402 "float32_maxuint64+1: 18446744073709551616", 403 `"float32_maxuint64+1": number & 18446744073709551616`, 404 }, 405 406 // float64 407 { 408 "float64_max: 1.797693134862315708145274237317043567981e+308", 409 "float64_max: 1.797693134862315708145274237317043567981e+308", 410 }, 411 { 412 "float64_nonzero: 4.940656458412465441765687928682213723651e-324", 413 "float64_nonzero: 4.940656458412465441765687928682213723651e-324", 414 }, 415 { 416 "float64_maxuint64: 18446744073709551615", 417 "float64_maxuint64: 18446744073709551615", 418 }, 419 // TODO(mvdan): yaml.v3 uses strconv APIs like ParseUint to try to detect 420 // whether a scalar should be considered a YAML integer or a float. 421 // Integers in CUE aren't limited to 64 bits, so we should arguably not decode 422 // large integers that don't fit in 64 bits as floats via `number &`. 423 { 424 "float64_maxuint64+1: 18446744073709551616", 425 `"float64_maxuint64+1": number & 18446744073709551616`, 426 }, 427 428 // Overflow cases. 429 { 430 "v: 4294967297", 431 "v: 4294967297", 432 }, { 433 "v: 128", 434 "v: 128", 435 }, 436 437 // Quoted values. 438 { 439 "'1': '\"2\"'", 440 `"1": "\"2\""`, 441 }, { 442 "v:\n- A\n- 'B\n\n C'\n", 443 `v: [ 444 "A", 445 """ 446 B 447 C 448 """, 449 ]`, 450 }, { 451 `"\0"`, 452 `"\u0000"`, 453 }, 454 455 // Explicit tags. 456 { 457 "v: !!float '1.1'", 458 "v: 1.1", 459 }, { 460 "v: !!float 0", 461 "v: number & 0", 462 }, { 463 "v: !!float -1", 464 "v: number & -1", 465 }, { 466 "v: !!null ''", 467 "v: null", 468 }, { 469 "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", 470 "v: 1", 471 }, 472 473 // Non-specific tag (Issue #75) 474 { 475 `v: ! test`, 476 `v: "test"`, 477 }, 478 479 // Anchors and aliases. 480 { 481 "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", 482 `a: 1 483 b: 2 484 c: 1 485 d: 2`, 486 }, { 487 "a: &a {c: 1}\nb: *a", 488 `a: {c: 1} 489 b: { 490 c: 1 491 }`, 492 }, { 493 "a: &a [1, 2]\nb: *a", 494 "a: [1, 2]\nb: [1, 2]", // TODO: a: [1, 2], b: a 495 }, 496 497 { 498 "foo: ''", 499 `foo: ""`, 500 }, { 501 "foo: null", 502 "foo: null", 503 }, 504 505 // Support for ~ 506 { 507 "foo: ~", 508 "foo: null", 509 }, 510 511 // Bug #1191981 512 { 513 "" + 514 "%YAML 1.1\n" + 515 "--- !!str\n" + 516 `"Generic line break (no glyph)\n\` + "\n" + 517 ` Generic line break (glyphed)\n\` + "\n" + 518 ` Line separator\u2028\` + "\n" + 519 ` Paragraph separator\u2029"` + "\n", 520 `""" 521 Generic line break (no glyph) 522 Generic line break (glyphed) 523 Line separator\u2028Paragraph separator\u2029 524 """`, 525 }, 526 527 // bug 1243827 528 { 529 "a: -b_c", 530 `a: "-b_c"`, 531 }, 532 { 533 "a: +b_c", 534 `a: "+b_c"`, 535 }, 536 { 537 "a: 50cent_of_dollar", 538 `a: "50cent_of_dollar"`, 539 }, 540 541 // issue #295 (allow scalars with colons in flow mappings and sequences) 542 { 543 "a: {b: https://github.com/go-yaml/yaml}", 544 `a: {b: "https://github.com/go-yaml/yaml"}`, 545 }, 546 { 547 "a: [https://github.com/go-yaml/yaml]", 548 `a: ["https://github.com/go-yaml/yaml"]`, 549 }, 550 551 // Duration 552 { 553 "a: 3s", 554 `a: "3s"`, // for now 555 }, 556 557 // Issue #24. 558 { 559 "a: <foo>", 560 `a: "<foo>"`, 561 }, 562 563 // Base 60 floats are obsolete and unsupported. 564 { 565 "a: 1:1\n", 566 `a: "1:1"`, 567 }, 568 569 // Binary data. 570 { 571 "a: !!binary gIGC\n", 572 `a: '\x80\x81\x82'`, 573 }, { 574 "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", 575 "a: '" + strings.Repeat(`\x90`, 54) + "'", 576 }, { 577 "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", 578 "a: '" + strings.Repeat(`\x00`, 52) + "'", 579 }, 580 581 // Ordered maps. 582 { 583 "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", 584 `b: 2, a: 1, d: 4, c: 3, sub: {e: 5}`, 585 }, 586 587 // Spacing 588 { 589 ` 590 a: {} 591 b: { 592 } 593 c: 1 594 d: [ 595 ] 596 e: [] 597 `, 598 // TODO(mvdan): keep the separated opening/closing tokens once yaml.v3 exposes end positions. 599 `a: {} 600 b: {} 601 c: 1 602 d: [] 603 e: []`, 604 }, 605 606 { 607 ` 608 a: 609 - { "a": 1, "b": 2 } 610 - { "c": 1, "d": 2 } 611 `, 612 `a: [{ 613 a: 1, b: 2 614 }, { 615 c: 1, d: 2 616 }]`, 617 }, 618 619 { 620 "a:\n b:\n c: d\n e: f\n", 621 `a: { 622 b: { 623 c: "d" 624 e: "f" 625 } 626 }`, 627 }, 628 629 // Issue #39. 630 { 631 "a:\n b:\n c: d\n", 632 `a: { 633 b: { 634 c: "d" 635 } 636 }`, 637 }, 638 639 // Timestamps 640 { 641 // Date only. 642 "a: 2015-01-01\n", 643 `a: "2015-01-01"`, 644 }, 645 { 646 // RFC3339 647 "a: 2015-02-24T18:19:39.12Z\n", 648 `a: "2015-02-24T18:19:39.12Z"`, 649 }, 650 { 651 // RFC3339 with short dates. 652 "a: 2015-2-3T3:4:5Z", 653 `a: "2015-2-3T3:4:5Z"`, 654 }, 655 { 656 // ISO8601 lower case t 657 "a: 2015-02-24t18:19:39Z\n", 658 `a: "2015-02-24t18:19:39Z"`, 659 }, 660 { 661 // space separate, no time zone 662 "a: 2015-02-24 18:19:39\n", 663 `a: "2015-02-24 18:19:39"`, 664 }, 665 // Some cases not currently handled. Uncomment these when 666 // the code is fixed. 667 // { 668 // // space separated with time zone 669 // "a: 2001-12-14 21:59:43.10 -5", 670 // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, 671 // }, 672 // { 673 // // arbitrary whitespace between fields 674 // "a: 2001-12-14 \t\t \t21:59:43.10 \t Z", 675 // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, 676 // }, 677 { 678 // explicit string tag 679 "a: !!str 2015-01-01", 680 `a: "2015-01-01"`, 681 }, 682 { 683 // explicit timestamp tag on quoted string 684 "a: !!timestamp \"2015-01-01\"", 685 `a: "2015-01-01"`, 686 }, 687 { 688 // explicit timestamp tag on unquoted string 689 "a: !!timestamp 2015-01-01", 690 `a: "2015-01-01"`, 691 }, 692 { 693 // quoted string that's a valid timestamp 694 "a: \"2015-01-01\"", 695 "a: \"2015-01-01\"", 696 }, 697 698 // Empty list 699 { 700 "a: []", 701 "a: []", 702 }, 703 704 // Floating comments. 705 // TODO(mvdan): all empty lines separating comments should stay in place. 706 // TODO(mvdan): avoid losing comments in empty lists and objects. 707 { 708 "# Start\n\na: 123\n\n# Middle\n\nb: 456\n\n# End", 709 "// Start\na: 123\n\n// Middle\nb: 456\n// End", 710 }, 711 { 712 "a: [\n\t# Comment\n]", 713 "a: []", 714 }, 715 { 716 "a: {\n\t# Comment\n}", 717 "a: {}", 718 }, 719 720 // Attached comments. 721 { 722 "start: 100\n\n# Before\na: 123 # Inline\n# After\n\nend: 200", 723 "start: 100\n\n// Before\n// After\na: 123 // Inline\nend: 200", 724 }, 725 { 726 "# One\none: null\n\n# Two\ntwo: [2, 2]\n\n# Three\nthree: {val: 3}", 727 "// One\none: null\n\n// Two\ntwo: [2, 2]\n\n// Three\nthree: {val: 3}", 728 }, 729 730 // UTF-16-LE 731 { 732 "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", 733 `ñoño: "very yes"`, 734 }, 735 // UTF-16-LE with surrogate. 736 { 737 "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", 738 `ñoño: "very yes 🟔"`, 739 }, 740 741 // UTF-16-BE 742 { 743 "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", 744 `ñoño: "very yes"`, 745 }, 746 // UTF-16-BE with surrogate. 747 { 748 "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", 749 `ñoño: "very yes 🟔"`, 750 }, 751 752 // This *is* in fact a float number, per the spec. #171 was a mistake. 753 { 754 "a: 123456e1\n", 755 `a: 123456e1`, 756 }, { 757 "a: 123456E1\n", 758 `a: 123456e1`, 759 }, 760 // Other float formats: 761 { 762 "x: .1", 763 "x: 0.1", 764 }, 765 { 766 "x: .1e-3", 767 "x: 0.1e-3", 768 }, 769 { 770 "x: 1.2E4", 771 "x: 1.2e4", 772 }, 773 { 774 "x: 1.2E+4", 775 "x: 1.2e+4", 776 }, 777 // yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes 778 { 779 "First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor\n", 780 `"First occurrence": "Foo" 781 "Second occurrence": "Foo" 782 "Override anchor": "Bar" 783 "Reuse anchor": "Bar"`, 784 }, 785 } 786 787 type M map[interface{}]interface{} 788 789 func cueStr(node ast.Node) string { 790 if node == nil { 791 return "" 792 } 793 b, _ := format.Node(internal.ToFile(node)) 794 return strings.TrimSpace(string(b)) 795 } 796 797 func newDecoder(t *testing.T, data string) yaml.Decoder { 798 t.Helper() 799 t.Logf(" yaml:\n%s", data) 800 return yaml.NewDecoder("test.yaml", []byte(data)) 801 } 802 803 func callUnmarshal(t *testing.T, data string) (ast.Expr, error) { 804 t.Helper() 805 t.Logf(" yaml:\n%s", data) 806 return yaml.Unmarshal("test.yaml", []byte(data)) 807 } 808 809 func TestUnmarshal(t *testing.T) { 810 for i, item := range unmarshalTests { 811 t.Run(strconv.Itoa(i), func(t *testing.T) { 812 t.Logf("test %d: %q", i, item.data) 813 expr, err := callUnmarshal(t, item.data) 814 if err != nil { 815 t.Fatalf("expected error to be nil: %v", err) 816 } 817 if got := cueStr(expr); got != item.want { 818 t.Errorf("\n got:\n%v\n want:\n%v", got, item.want) 819 } 820 }) 821 } 822 } 823 824 // For debug purposes: do not delete. 825 func TestX(t *testing.T) { 826 y := ` 827 ` 828 y = strings.TrimSpace(y) 829 if len(y) == 0 { 830 t.Skip() 831 } 832 833 expr, err := callUnmarshal(t, y) 834 if err != nil { 835 t.Fatal(err) 836 } 837 t.Error(cueStr(expr)) 838 } 839 840 func TestDecoderSingleDocument(t *testing.T) { 841 // Test that Decoder.Decode works as expected on 842 // all the unmarshal tests. 843 for i, item := range unmarshalTests { 844 t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { 845 if item.data == "" { 846 // Behaviour differs when there's no YAML. 847 return 848 } 849 expr, err := newDecoder(t, item.data).Decode() 850 if err != nil { 851 t.Errorf("err should be nil, was %v", err) 852 } 853 if got := cueStr(expr); got != item.want { 854 t.Errorf("\n got:\n%v\n want:\n%v", got, item.want) 855 } 856 }) 857 } 858 } 859 860 var decoderTests = []struct { 861 data string 862 want string 863 }{{ 864 "", 865 "null", 866 }, { 867 "a: b", 868 `a: "b"`, 869 }, { 870 "---\na: b\n...\n", 871 `a: "b"`, 872 }, { 873 "---\na: b\n---\n---\n", 874 "a: \"b\"\nnull\nnull", 875 }, { 876 "---\n---\n---\na: b\n", 877 "null\nnull\na: \"b\"", 878 }, { 879 "---\n'hello'\n...\n---\ngoodbye\n...\n", 880 `"hello"` + "\n" + `"goodbye"`, 881 }} 882 883 func TestDecoder(t *testing.T) { 884 for i, item := range decoderTests { 885 t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { 886 var values []string 887 dec := newDecoder(t, item.data) 888 for { 889 expr, err := dec.Decode() 890 if err == io.EOF { 891 break 892 } 893 if err != nil { 894 t.Errorf("err should be nil, was %v", err) 895 } 896 values = append(values, cueStr(expr)) 897 } 898 got := strings.Join(values, "\n") 899 if got != item.want { 900 t.Errorf("\n got:\n%v\n want:\n%v", got, item.want) 901 } 902 }) 903 } 904 } 905 906 func TestUnmarshalNaN(t *testing.T) { 907 expr, err := callUnmarshal(t, "notanum: .NaN") 908 if err != nil { 909 t.Fatal("unexpected error", err) 910 } 911 got := cueStr(expr) 912 want := "notanum: NaN" 913 if got != want { 914 t.Errorf("got %v; want %v", got, want) 915 } 916 } 917 918 var unmarshalErrorTests = []struct { 919 data, error string 920 }{ 921 {"\nv: !!float 'error'", `test.yaml:2: cannot decode "error" as !!float: illegal number start "error"`}, 922 {"\nv: !!int 'error'", `test.yaml:2: cannot decode "error" as !!int: illegal number start "error"`}, 923 {"\nv: !!int 123.456", `test.yaml:2: cannot decode "123.456" as !!int: not a literal number`}, 924 {"v: [A,", "test.yaml:1: did not find expected node content"}, 925 {"v:\n- [A,", "test.yaml:2: did not find expected node content"}, 926 {"a:\n- b: *,", "test.yaml:2: did not find expected alphabetic or numeric character"}, 927 {"a: *b\n", "test.yaml: unknown anchor 'b' referenced"}, 928 {"a: &a\n b: *a\n", `test.yaml:2: anchor "a" value contains itself`}, 929 {"value: -", "test.yaml: block sequence entries are not allowed in this context"}, 930 {"a: !!binary ==", "test.yaml:1: !!binary value contains invalid base64 data"}, 931 {"{[.]}", `test.yaml:1: invalid map key: !!seq`}, 932 {"{{.}}", `test.yaml:1: invalid map key: !!map`}, 933 {"b: *a\na: &a {c: 1}", `test.yaml: unknown anchor 'a' referenced`}, 934 {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "test.yaml: did not find expected whitespace"}, 935 } 936 937 func TestUnmarshalErrors(t *testing.T) { 938 for i, item := range unmarshalErrorTests { 939 t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { 940 expr, err := callUnmarshal(t, item.data) 941 val := "" 942 if expr != nil { 943 val = cueStr(expr) 944 } 945 if err == nil || err.Error() != item.error { 946 t.Errorf("got %v; want %v; (value %v)", err, item.error, val) 947 } 948 }) 949 } 950 } 951 952 func TestDecoderErrors(t *testing.T) { 953 for i, item := range unmarshalErrorTests { 954 t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { 955 _, err := newDecoder(t, item.data).Decode() 956 if err == nil || err.Error() != item.error { 957 t.Errorf("got %v; want %v", err, item.error) 958 } 959 }) 960 } 961 } 962 963 func TestFiles(t *testing.T) { 964 files := []string{"merge"} 965 for _, test := range files { 966 t.Run(test, func(t *testing.T) { 967 testname := fmt.Sprintf("testdata/%s.test", test) 968 filename := fmt.Sprintf("testdata/%s.out", test) 969 mergeTests, err := os.ReadFile(testname) 970 if err != nil { 971 t.Fatal(err) 972 } 973 expr, err := yaml.Unmarshal("test.yaml", mergeTests) 974 if err != nil { 975 t.Fatal(err) 976 } 977 got := cueStr(expr) 978 if cuetest.UpdateGoldenFiles { 979 os.WriteFile(filename, []byte(got), 0644) 980 return 981 } 982 b, err := os.ReadFile(filename) 983 if err != nil { 984 t.Fatal(err) 985 } 986 if want := string(b); got != want { 987 t.Error(cmp.Diff(want, got)) 988 } 989 }) 990 } 991 } 992 993 func TestTrailingInput(t *testing.T) { 994 for _, input := range []string{ 995 "---\nfirst\n...\n}invalid yaml", 996 "---\nfirst\n---\nsecond\n", 997 } { 998 t.Run("", func(t *testing.T) { 999 // Unmarshal should fail as it expects one value. 1000 wantErr := ".*expected a single YAML document.*" 1001 _, err := callUnmarshal(t, input) 1002 qt.Assert(t, qt.ErrorMatches(err, wantErr)) 1003 1004 // A single Decode call should succeed, no matter whether there is any valid or invalid trailing input. 1005 wantFirst := `"first"` 1006 dec := newDecoder(t, input) 1007 expr, err := dec.Decode() 1008 qt.Assert(t, qt.IsNil(err)) 1009 gotFirst := cueStr(expr) 1010 qt.Assert(t, qt.Equals(gotFirst, wantFirst)) 1011 }) 1012 } 1013 1014 }