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