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}