From c8d4c3a69568aa23306d2168ebf4ff4e027f8f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 3 Apr 2024 20:37:58 +0900 Subject: [PATCH] internal/encoding/yaml: copy decode_test.go from internal/third_party/yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These test files and test cases were originally inherited from go-yaml when Marcel started the fork in internal/third_party/yaml in late 2018. Many of these tests, particularly unmarshalTests, have been modified and extended over time to cover fixes made to the decoder in the fork. As we transition away from maintaining a fork of go-yaml, we don't want to lose any of these test cases, especially to ensure that we don't re-introduce any bugs fixed in the last five years. Copy the files to internal/encoding/yaml, where we will house the new YAML decoder for the time being. Note that the test files still test against internal/third_party/yaml; we will move them over to internal/encoding/yaml once the new decoder lands. There are some bits of decode_test.go that we omit from the copy, as they are not useful to us: * Commented out code from upstream, such as TestUnmarshalFullTimestamp and BenchmarkUnmarshal. * TestFuzzCrashers, unmodified from upstream, and which is really fuzzing the token scanning code in go-yaml, and not our code. * Unused test case types. Signed-off-by: Daniel Martí Change-Id: Icb5805d0dd8d3de6afc20e37934046bf67bb7f0b Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1191228 Reviewed-by: Roger Peppe TryBot-Result: CUEcueckoo Unity-Result: CUE porcuepine --- internal/encoding/yaml/decode_test.go | 930 +++++++++++++++++++++ internal/encoding/yaml/testdata/merge.out | 80 ++ internal/encoding/yaml/testdata/merge.test | 54 ++ 3 files changed, 1064 insertions(+) create mode 100644 internal/encoding/yaml/decode_test.go create mode 100644 internal/encoding/yaml/testdata/merge.out create mode 100644 internal/encoding/yaml/testdata/merge.test diff --git a/internal/encoding/yaml/decode_test.go b/internal/encoding/yaml/decode_test.go new file mode 100644 index 000000000..3347f00bc --- /dev/null +++ b/internal/encoding/yaml/decode_test.go @@ -0,0 +1,930 @@ +// Many test cases in this file were originally ported from +// github.com/go-yaml/yaml, also known as gopkg.in/yaml.v3. +// +// Copyright 2011-2016 Canonical Ltd. +// Copyright 2020 CUE Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "testing" + + "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/format" + "cuelang.org/go/internal/cuetest" + "cuelang.org/go/internal/third_party/yaml" +) + +var unmarshalTests = []struct { + data string + want string +}{ + { + "", + "", + }, + { + "{}", + "", + }, { + "v: hi", + `v: "hi"`, + }, { + "v: hi", + `v: "hi"`, + }, { + "v: true", + "v: true", + }, { + "v: 10", + "v: 10", + }, { + "v: 0b10", + "v: 0b10", + }, { + "v: 0xA", + "v: 0xA", + }, { + "v: 4294967296", + "v: 4294967296", + }, { + "v: 0.1", + "v: 0.1", + }, { + "v: .1", + "v: 0.1", + }, { + "v: .Inf", + "v: +Inf", + }, { + "v: -.Inf", + "v: -Inf", + }, { + "v: -10", + "v: -10", + }, { + "v: -.1", + "v: -0.1", + }, + + // Simple values. + { + "123", + "123", + }, + + // Floats from spec + { + "canonical: 6.8523e+5", + "canonical: 6.8523e+5", + }, { + "expo: 685.230_15e+03", + "expo: 685.230_15e+03", + }, { + "fixed: 685_230.15", + "fixed: 685_230.15", + }, { + "neginf: -.inf", + "neginf: -Inf", + }, { + "fixed: 685_230.15", + "fixed: 685_230.15", + }, + //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported + //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. + + // Bools from spec + { + "canonical: y", + `canonical: "y"`, + }, { + "answer: n", + `answer: "n"`, + }, { + "answer: NO", + `answer: "NO"`, + }, { + "logical: True", + "logical: true", + }, { + "option: on", + `option: "on"`, + }, { + "answer: off", + `answer: "off"`, + }, + // Ints from spec + { + "canonical: 685230", + "canonical: 685230", + }, { + "decimal: +685_230", + "decimal: +685_230", + }, { + "octal: 02472256", + "octal: 0o2472256", + }, { + "hexa: 0x_0A_74_AE", + "hexa: 0x_0A_74_AE", + }, { + "bin: 0b1010_0111_0100_1010_1110", + "bin: 0b1010_0111_0100_1010_1110", + }, { + "bin: -0b101010", + "bin: -0b101010", + }, { + "bin: -0b1000000000000000000000000000000000000000000000000000000000000000", + "bin: -0b1000000000000000000000000000000000000000000000000000000000000000", + }, { + "decimal: +685_230", + "decimal: +685_230", + }, + + //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported + + // Nulls from spec + { + "empty:", + "empty: null", + }, { + "canonical: ~", + "canonical: null", + }, { + "english: null", + "english: null", + }, { + "_foo: 1", + `"_foo": 1`, + }, { + `"#foo": 1`, + `"#foo": 1`, + }, { + "_#foo: 1", + `"_#foo": 1`, + }, { + "~: null key", + `"null": "null key"`, + }, { + `empty: +apple: "newline"`, + `empty: null +apple: "newline"`, + }, + + // Flow sequence + { + "seq: [A,B]", + `seq: ["A", "B"]`, + }, { + "seq: [A,B,C,]", + `seq: ["A", "B", "C"]`, + }, { + "seq: [A,1,C]", + `seq: ["A", 1, "C"]`, + }, + // Block sequence + { + "seq:\n - A\n - B", + `seq: [ + "A", + "B", +]`, + }, { + "seq:\n - A\n - B\n - C", + `seq: [ + "A", + "B", + "C", +]`, + }, { + "seq:\n - A\n - 1\n - C", + `seq: [ + "A", + 1, + "C", +]`, + }, + + // Literal block scalar + { + "scalar: | # Comment\n\n literal\n\n \ttext\n\n", + `scalar: """ + + literal + + \ttext + + """`, + }, + + // Folded block scalar + { + "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", + `scalar: """ + + folded line + next line + * one + * two + + last line + + """`, + }, + + // Structs + { + "a: {b: c}", + `a: {b: "c"}`, + }, + { + "hello: world", + `hello: "world"`, + }, { + "a:", + "a: null", + }, { + "a: 1", + "a: 1", + }, { + "a: 1.0", + "a: 1.0", + }, { + "a: [1, 2]", + "a: [1, 2]", + }, { + "a: y", + `a: "y"`, + }, { + "{ a: 1, b: {c: 1} }", + `a: 1, b: {c: 1}`, + }, { + ` +True: 1 +Null: 1 +.Inf: 2 +`, + `"true": 1 +"null": 1 +"+Inf": 2`, + }, + + // Some cross type conversions + { + "v: 42", + "v: 42", + }, { + "v: -42", + "v: -42", + }, { + "v: 4294967296", + "v: 4294967296", + }, { + "v: -4294967296", + "v: -4294967296", + }, + + // int + { + "int_max: 2147483647", + "int_max: 2147483647", + }, + { + "int_min: -2147483648", + "int_min: -2147483648", + }, + { + "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 + "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 + }, + + // int64 + { + "int64_max: 9223372036854775807", + "int64_max: 9223372036854775807", + }, + { + "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", + "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", + }, + { + "int64_min: -9223372036854775808", + "int64_min: -9223372036854775808", + }, + { + "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", + "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", + }, + { + "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 + "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 + }, + + // uint + { + "uint_max: 4294967295", + "uint_max: 4294967295", + }, + + // uint64 + { + "uint64_max: 18446744073709551615", + "uint64_max: 18446744073709551615", + }, + { + "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", + "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", + }, + { + "uint64_maxint64: 9223372036854775807", + "uint64_maxint64: 9223372036854775807", + }, + + // float32 + { + "float32_max: 3.40282346638528859811704183484516925440e+38", + "float32_max: 3.40282346638528859811704183484516925440e+38", + }, + { + "float32_nonzero: 1.401298464324817070923729583289916131280e-45", + "float32_nonzero: 1.401298464324817070923729583289916131280e-45", + }, + { + "float32_maxuint64: 18446744073709551615", + "float32_maxuint64: 18446744073709551615", + }, + { + "float32_maxuint64+1: 18446744073709551616", + `"float32_maxuint64+1": 18446744073709551616`, + }, + + // float64 + { + "float64_max: 1.797693134862315708145274237317043567981e+308", + "float64_max: 1.797693134862315708145274237317043567981e+308", + }, + { + "float64_nonzero: 4.940656458412465441765687928682213723651e-324", + "float64_nonzero: 4.940656458412465441765687928682213723651e-324", + }, + { + "float64_maxuint64: 18446744073709551615", + "float64_maxuint64: 18446744073709551615", + }, + { + "float64_maxuint64+1: 18446744073709551616", + `"float64_maxuint64+1": 18446744073709551616`, + }, + + // Overflow cases. + { + "v: 4294967297", + "v: 4294967297", + }, { + "v: 128", + "v: 128", + }, + + // Quoted values. + { + "'1': '\"2\"'", + `"1": "\"2\""`, + }, { + "v:\n- A\n- 'B\n\n C'\n", + `v: [ + "A", + """ + B + C + """, +]`, + }, { + `"\0"`, + `"\u0000"`, + }, + + // Explicit tags. + { + "v: !!float '1.1'", + "v: 1.1", + }, { + "v: !!float 0", + "v: float & 0", // Should this be 0.0? + }, { + "v: !!float -1", + "v: float & -1", // Should this be -1.0? + }, { + "v: !!null ''", + "v: null", + }, { + "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", + "v: 1", + }, + + // Non-specific tag (Issue #75) + { + `v: ! test`, + // TODO this should work and produce a string. + "", + }, + + // Anchors and aliases. + { + "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", + `a: 1 +b: 2 +c: 1 +d: 2`, + }, { + "a: &a {c: 1}\nb: *a", + `a: {c: 1} +b: { + c: 1 +}`, + }, { + "a: &a [1, 2]\nb: *a", + "a: [1, 2]\nb: [1, 2]", // TODO: a: [1, 2], b: a + }, + + { + "foo: ''", + `foo: ""`, + }, { + "foo: null", + "foo: null", + }, + + // Support for ~ + { + "foo: ~", + "foo: null", + }, + + // Bug #1191981 + { + "" + + "%YAML 1.1\n" + + "--- !!str\n" + + `"Generic line break (no glyph)\n\` + "\n" + + ` Generic line break (glyphed)\n\` + "\n" + + ` Line separator\u2028\` + "\n" + + ` Paragraph separator\u2029"` + "\n", + `""" + Generic line break (no glyph) + Generic line break (glyphed) + Line separator\u2028Paragraph separator\u2029 + """`, + }, + + // bug 1243827 + { + "a: -b_c", + `a: "-b_c"`, + }, + { + "a: +b_c", + `a: "+b_c"`, + }, + { + "a: 50cent_of_dollar", + `a: "50cent_of_dollar"`, + }, + + // issue #295 (allow scalars with colons in flow mappings and sequences) + { + "a: {b: https://github.com/go-yaml/yaml}", + `a: {b: "https://github.com/go-yaml/yaml"}`, + }, + { + "a: [https://github.com/go-yaml/yaml]", + `a: ["https://github.com/go-yaml/yaml"]`, + }, + + // Duration + { + "a: 3s", + `a: "3s"`, // for now + }, + + // Issue #24. + { + "a: ", + `a: ""`, + }, + + // Base 60 floats are obsolete and unsupported. + { + "a: 1:1\n", + `a: "1:1"`, + }, + + // Binary data. + { + "a: !!binary gIGC\n", + `a: '\x80\x81\x82'`, + }, { + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + "a: '" + strings.Repeat(`\x90`, 54) + "'", + }, { + "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", + "a: '" + strings.Repeat(`\x00`, 52) + "'", + }, + + // Ordered maps. + { + "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", + `b: 2, a: 1, d: 4, c: 3, sub: {e: 5}`, + }, + + // Spacing + { + ` +a: {} +c: 1 +d: [ +] +e: [] +`, + `a: {} +c: 1 +d: [ +] +e: []`, + }, + + { + ` +a: + - { "a": 1, "b": 2 } + - { "c": 1, "d": 2 } +`, + `a: [{ + a: 1, b: 2 +}, { + c: 1, d: 2 +}]`, + }, + + { + "a:\n b:\n c: d\n e: f\n", + `a: { + b: { + c: "d" + e: "f" + } +}`, + }, + + // Issue #39. + { + "a:\n b:\n c: d\n", + `a: { + b: { + c: "d" + } +}`, + }, + + // Timestamps + { + // Date only. + "a: 2015-01-01\n", + `a: "2015-01-01"`, + }, + { + // RFC3339 + "a: 2015-02-24T18:19:39.12Z\n", + `a: "2015-02-24T18:19:39.12Z"`, + }, + { + // RFC3339 with short dates. + "a: 2015-2-3T3:4:5Z", + `a: "2015-2-3T3:4:5Z"`, + }, + { + // ISO8601 lower case t + "a: 2015-02-24t18:19:39Z\n", + `a: "2015-02-24t18:19:39Z"`, + }, + { + // space separate, no time zone + "a: 2015-02-24 18:19:39\n", + `a: "2015-02-24 18:19:39"`, + }, + // Some cases not currently handled. Uncomment these when + // the code is fixed. + // { + // // space separated with time zone + // "a: 2001-12-14 21:59:43.10 -5", + // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, + // }, + // { + // // arbitrary whitespace between fields + // "a: 2001-12-14 \t\t \t21:59:43.10 \t Z", + // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, + // }, + { + // explicit string tag + "a: !!str 2015-01-01", + `a: "2015-01-01"`, + }, + { + // explicit timestamp tag on quoted string + "a: !!timestamp \"2015-01-01\"", + `a: "2015-01-01"`, + }, + { + // explicit timestamp tag on unquoted string + "a: !!timestamp 2015-01-01", + `a: "2015-01-01"`, + }, + { + // quoted string that's a valid timestamp + "a: \"2015-01-01\"", + "a: \"2015-01-01\"", + }, + + // Empty list + { + "a: []", + "a: []", + }, + + // UTF-16-LE + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", + `ñoño: "very yes"`, + }, + // UTF-16-LE with surrogate. + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", + `ñoño: "very yes 🟔"`, + }, + + // UTF-16-BE + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", + `ñoño: "very yes"`, + }, + // UTF-16-BE with surrogate. + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", + `ñoño: "very yes 🟔"`, + }, + + // This *is* in fact a float number, per the spec. #171 was a mistake. + { + "a: 123456e1\n", + `a: 123456e1`, + }, { + "a: 123456E1\n", + `a: 123456e1`, + }, + // Other float formats: + { + "x: .1", + "x: 0.1", + }, + { + "x: .1e-3", + "x: 0.1e-3", + }, + { + "x: 1.2E4", + "x: 1.2e4", + }, + { + "x: 1.2E+4", + "x: 1.2e+4", + }, + // yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes + { + "First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor\n", + `"First occurrence": "Foo" +"Second occurrence": "Foo" +"Override anchor": "Bar" +"Reuse anchor": "Bar"`, + }, + // Single document with garbage following it. + { + "---\nhello\n...\n}not yaml", + `"hello"`, + }, +} + +type M map[interface{}]interface{} + +func cueStr(node ast.Node) string { + if s, ok := node.(*ast.StructLit); ok { + node = &ast.File{ + Decls: s.Elts, + } + } + b, _ := format.Node(node) + return strings.TrimSpace(string(b)) +} + +func newDecoder(t *testing.T, data string) *yaml.Decoder { + dec, err := yaml.NewDecoder("test.yaml", strings.NewReader(data)) + if err != nil { + t.Fatal(err) + } + return dec +} + +func callUnmarshal(t *testing.T, data string) (ast.Expr, error) { + return yaml.Unmarshal("test.yaml", []byte(data)) +} + +func TestUnmarshal(t *testing.T) { + for i, item := range unmarshalTests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + t.Logf("test %d: %q", i, item.data) + expr, err := callUnmarshal(t, item.data) + if _, ok := err.(*yaml.TypeError); !ok && err != nil { + t.Fatal("expected error to be nil") + } + if got := cueStr(expr); got != item.want { + t.Errorf("\n got:\n%v\nwant:\n%v", got, item.want) + } + }) + } +} + +// For debug purposes: do not delete. +func TestX(t *testing.T) { + y := ` +` + y = strings.TrimSpace(y) + if len(y) == 0 { + t.Skip() + } + + expr, err := callUnmarshal(t, y) + if _, ok := err.(*yaml.TypeError); !ok && err != nil { + t.Fatal(err) + } + t.Error(cueStr(expr)) +} + +func TestDecoderSingleDocument(t *testing.T) { + // Test that Decoder.Decode works as expected on + // all the unmarshal tests. + for i, item := range unmarshalTests { + t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { + if item.data == "" { + // Behaviour differs when there's no YAML. + return + } + expr, err := newDecoder(t, item.data).Decode() + if _, ok := err.(*yaml.TypeError); !ok && err != nil { + t.Errorf("err should be nil, was %v", err) + } + if got := cueStr(expr); got != item.want { + t.Errorf("\n got: %v;\nwant: %v", got, item.want) + } + }) + } +} + +var decoderTests = []struct { + data string + want string +}{{ + "", + "", +}, { + "a: b", + `a: "b"`, +}, { + "---\na: b\n...\n", + `a: "b"`, +}, { + "---\n'hello'\n...\n---\ngoodbye\n...\n", + `"hello"` + "\n" + `"goodbye"`, +}} + +func TestDecoder(t *testing.T) { + for i, item := range decoderTests { + t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { + var values []string + dec := newDecoder(t, item.data) + for { + expr, err := dec.Decode() + if err == io.EOF { + break + } + if err != nil { + t.Errorf("err should be nil, was %v", err) + } + values = append(values, cueStr(expr)) + } + got := strings.Join(values, "\n") + if got != item.want { + t.Errorf("\n got: %v;\nwant: %v", got, item.want) + } + }) + } +} + +func TestUnmarshalNaN(t *testing.T) { + expr, err := callUnmarshal(t, "notanum: .NaN") + if err != nil { + t.Fatal("unexpected error", err) + } + got := cueStr(expr) + want := "notanum: NaN" + if got != want { + t.Errorf("got %v; want %v", got, want) + } +} + +var unmarshalErrorTests = []struct { + data, error string +}{ + {"\nv: !!float 'error'", "test.yaml:2: cannot decode !!str `error` as a !!float"}, + {"v: [A,", "test.yaml:1: did not find expected node content"}, + {"v:\n- [A,", "test.yaml:2: did not find expected node content"}, + {"a:\n- b: *,", "test.yaml:2: did not find expected alphabetic or numeric character"}, + {"a: *b\n", "test.yaml:1: unknown anchor 'b' referenced"}, + {"a: &a\n b: *a\n", "test.yaml:2: anchor 'a' value contains itself"}, + {"value: -", "test.yaml:1: block sequence entries are not allowed in this context"}, + {"a: !!binary ==", "test.yaml:1: !!binary value contains invalid base64 data"}, + {"{[.]}", `test.yaml:1: invalid map key: sequence`}, + {"{{.}}", `test.yaml:1: invalid map key: map`}, + {"b: *a\na: &a {c: 1}", `test.yaml:1: unknown anchor 'a' referenced`}, + {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "test.yaml:1: did not find expected whitespace"}, +} + +func TestUnmarshalErrors(t *testing.T) { + for i, item := range unmarshalErrorTests { + t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { + expr, err := callUnmarshal(t, item.data) + val := "" + if expr != nil { + val = cueStr(expr) + } + if err == nil || err.Error() != item.error { + t.Errorf("got %v; want %v; (value %v)", err, item.error, val) + } + }) + } +} + +func TestDecoderErrors(t *testing.T) { + for i, item := range unmarshalErrorTests { + t.Run(fmt.Sprintf("test %d: %q", i, item.data), func(t *testing.T) { + _, err := newDecoder(t, item.data).Decode() + if err == nil || err.Error() != item.error { + t.Errorf("got %v; want %v", err, item.error) + } + }) + } +} + +func TestFiles(t *testing.T) { + files := []string{"merge"} + for _, test := range files { + t.Run(test, func(t *testing.T) { + testname := fmt.Sprintf("testdata/%s.test", test) + filename := fmt.Sprintf("testdata/%s.out", test) + mergeTests, err := os.ReadFile(testname) + if err != nil { + t.Fatal(err) + } + expr, err := yaml.Unmarshal("test.yaml", mergeTests) + if err != nil { + t.Fatal(err) + } + got := cueStr(expr) + if cuetest.UpdateGoldenFiles { + os.WriteFile(filename, []byte(got), 0644) + return + } + b, err := os.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + if want := string(b); got != want { + t.Errorf("\n got: %v;\nwant: %v", got, want) + } + }) + } +} diff --git a/internal/encoding/yaml/testdata/merge.out b/internal/encoding/yaml/testdata/merge.out new file mode 100644 index 000000000..ee6e6bbe0 --- /dev/null +++ b/internal/encoding/yaml/testdata/merge.out @@ -0,0 +1,80 @@ +// From http://yaml.org/type/merge.html +// Test +anchors: { + list: [{ + x: 1, y: 2 + }, { + x: 0, y: 2 + }, { + r: 10 + }, { + r: 1 + }] +} + +// All the following maps are equal: + +plain: { + // Explicit keys + x: 1 + y: 2 + r: 10 + label: "center/big" +} + +mergeOne: { + x: 1 + y: 2 + // Merge one map + + r: 10 + label: "center/big" +} + +mergeMultiple: { + r: 10 + x: 1 + y: 2 + // Merge multiple maps + + label: "center/big" +} + +override: { + r: 10 + x: 1 + y: 2 + label: "center/big" +} + +shortTag: { + r: 10 + x: 1 + y: 2 + // Explicit short merge tag + + label: "center/big" +} + +longTag: { + r: 10 + x: 1 + y: 2 + // Explicit merge long tag + + label: "center/big" +} + +inlineMap: { + // Inlined map + x: 1, y: 2, r: 10 + label: "center/big" +} + +inlineSequenceMap: { + // Inlined map in sequence + r: 10 + x: 1 + y: 2 + label: "center/big" +} \ No newline at end of file diff --git a/internal/encoding/yaml/testdata/merge.test b/internal/encoding/yaml/testdata/merge.test new file mode 100644 index 000000000..733ba2bb3 --- /dev/null +++ b/internal/encoding/yaml/testdata/merge.test @@ -0,0 +1,54 @@ +# From http://yaml.org/type/merge.html +# Test +anchors: + list: + - &CENTER { "x": 1, "y": 2 } + - &LEFT { "x": 0, "y": 2 } + - &BIG { "r": 10 } + - &SMALL { "r": 1 } + +# All the following maps are equal: + +plain: + # Explicit keys + "x": 1 + "y": 2 + "r": 10 + label: center/big + +mergeOne: + # Merge one map + << : *CENTER + "r": 10 + label: center/big + +mergeMultiple: + # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +override: + # Override + << : [ *BIG, *LEFT, *SMALL ] + "x": 1 + label: center/big + +shortTag: + # Explicit short merge tag + !!merge "<<" : [ *CENTER, *BIG ] + label: center/big + +longTag: + # Explicit merge long tag + ! "<<" : [ *CENTER, *BIG ] + label: center/big + +inlineMap: + # Inlined map + << : {"x": 1, "y": 2, "r": 10} + label: center/big + +inlineSequenceMap: + # Inlined map in sequence + << : [ *CENTER, {"r": 10} ] + label: center/big \ No newline at end of file -- 2.51.2