diff --git a/decode_test.go b/decode_test.go index 2373804..c884326 100644 --- a/decode_test.go +++ b/decode_test.go @@ -2890,3 +2890,117 @@ func TestSameNameInineStruct(t *testing.T) { t.Fatalf("failed to decode") } } + +// Test newline in key as encoded by alternative YAML tools: +func TestUnmarshalNewlineInKey(t *testing.T) { + tests := []struct { + name string + spec string + }{ + { + name: "multiline-key-with-newline-01", + /* As rendered by this library from this JSON: + { + "a": "a", + "c\nc": "cc", + "d": "d" + } + */ + spec: `a: a +|- + c + c: cc +d: d +`, + }, + { + name: "quoted-key-with-newline", + spec: `a: a +"c\nc": "cc" +d: d +`, + }, + { + name: "multiline-key-with-newline-02", + // As encoded by alternative online YAML tools (e.g. https://www.json2yaml.com/) + spec: `a: a +? |- + c + c +: cc +d: d +`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + decoder := yaml.NewDecoder(strings.NewReader(test.spec)) + var out map[string]interface{} + for i := 0; ; i++ { + err := decoder.Decode(&out) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Error on iteration %d: %v", i, err) + } + } + }) + } +} + +func TestUnmarshalBackslashInKey(t *testing.T) { + spec := `outer: + "b\\b": b + d : d +` + + decoder := yaml.NewDecoder(strings.NewReader(spec)) + var out map[string]interface{} + for i := 0; ; i++ { + err := decoder.Decode(&out) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Error on iteration %d: %v", i, err) + } + } +} + +func TestUnmarshalQuotesInKey(t *testing.T) { + spec := `outer: + "a\"b\"c": a + "d\"e\"f": d +` + decoder := yaml.NewDecoder(strings.NewReader(spec)) + var out map[string]interface{} + for i := 0; ; i++ { + err := decoder.Decode(&out) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Error on iteration %d: %v", i, err) + } + } +} + +func TestUnmarshalCollectedEscapesInKey(t *testing.T) { + spec := `outer: + "a\"b\"c\\d\ne": a + "d\"e\"f": d + ` + decoder := yaml.NewDecoder(strings.NewReader(spec)) + var out map[string]interface{} + for i := 0; ; i++ { + err := decoder.Decode(&out) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Error on iteration %d: %v", i, err) + } + } +} diff --git a/encode_test.go b/encode_test.go index 3ff6f1c..93b890c 100644 --- a/encode_test.go +++ b/encode_test.go @@ -7,6 +7,7 @@ import ( "math" "reflect" "strconv" + "strings" "testing" "time" @@ -1630,3 +1631,35 @@ b: t.Fatalf("failed to encode. expected %s but got %s", expected, got) } } + +func TestMarshalNewlineInKey(t *testing.T) { + spec := map[string]string{ + "a": "a", + "c\nc": "cc", + "d": "d", + } + expected := []string{ + `a: a +"c\nc": "cc" +d: d +`, + `--- +a: a +? |- + c + c +: cc +d: d +`} + y, err := yaml.Marshal(spec) + if err != nil { + t.Fatalf("Error marshalling YAML: %v", err) + } + for _, exp := range expected { + if string(y) == exp { + return + } + } + + t.Fatalf("testNewlineEncode FAILED\nExpected: '%s'\nGot: '%s'", strings.Join(expected, "\nor\n"), string(y)) +}