2019-10-18 13:05:36 +09:00
package yaml_test
import (
2020-03-02 23:11:18 +09:00
"bytes"
2024-10-28 21:24:15 +09:00
"errors"
2023-09-14 21:33:31 -05:00
"strings"
2019-10-18 13:05:36 +09:00
"testing"
2023-03-01 16:59:07 +09:00
"github.com/google/go-cmp/cmp"
2019-10-18 13:05:36 +09:00
"github.com/goccy/go-yaml"
2020-03-02 23:11:18 +09:00
"github.com/goccy/go-yaml/ast"
2019-10-18 13:05:36 +09:00
)
2019-10-19 18:28:36 +09:00
func TestMarshal ( t * testing . T ) {
var v struct {
A int
B string
}
v . A = 1
v . B = "hello"
bytes , err := yaml . Marshal ( v )
if err != nil {
t . Fatalf ( "%+v" , err )
}
if string ( bytes ) != "a: 1\nb: hello\n" {
t . Fatal ( "failed to marshal" )
}
}
2019-10-18 13:05:36 +09:00
func TestUnmarshal ( t * testing . T ) {
yml := `
% YAML 1.2
-- -
a : 1
b : c
`
var v struct {
A int
B string
}
if err := yaml . Unmarshal ( [ ] byte ( yml ) , & v ) ; err != nil {
t . Fatalf ( "%+v" , err )
}
}
2019-10-24 17:16:29 +09:00
type marshalTest struct { }
2019-10-25 02:38:32 +09:00
func ( t * marshalTest ) MarshalYAML ( ) ( [ ] byte , error ) {
return yaml . Marshal ( yaml . MapSlice {
2019-10-24 17:16:29 +09:00
{
"a" , 1 ,
} ,
{
"b" , "hello" ,
} ,
{
"c" , true ,
} ,
2019-12-12 17:43:58 +09:00
{
"d" , map [ string ] string { "x" : "y" } ,
} ,
2019-10-25 02:38:32 +09:00
} )
}
type marshalTest2 struct { }
func ( t * marshalTest2 ) MarshalYAML ( ) ( interface { } , error ) {
return yaml . MapSlice {
{
"a" , 2 ,
} ,
{
"b" , "world" ,
} ,
{
"c" , true ,
} ,
2019-10-24 17:16:29 +09:00
} , nil
}
func TestMarshalYAML ( t * testing . T ) {
var v struct {
A * marshalTest
2019-10-25 02:38:32 +09:00
B * marshalTest2
2019-10-24 17:16:29 +09:00
}
v . A = & marshalTest { }
2019-10-25 02:38:32 +09:00
v . B = & marshalTest2 { }
2019-10-24 17:16:29 +09:00
bytes , err := yaml . Marshal ( v )
if err != nil {
t . Fatalf ( "failed to Marshal: %+v" , err )
}
expect := `
a :
a : 1
b : hello
c : true
2019-12-12 17:43:58 +09:00
d :
2023-03-14 15:24:34 +09:00
x : "y"
2019-10-25 02:38:32 +09:00
b :
a : 2
b : world
c : true
2019-10-24 17:16:29 +09:00
`
actual := "\n" + string ( bytes )
if expect != actual {
t . Fatalf ( "failed to MarshalYAML expect:[%s], actual:[%s]" , expect , actual )
}
}
2019-10-24 17:18:03 +09:00
type unmarshalTest struct {
a int
b string
c bool
}
2019-10-24 23:54:17 +09:00
func ( t * unmarshalTest ) UnmarshalYAML ( b [ ] byte ) error {
if t . a != 0 {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to a" )
2019-10-24 23:54:17 +09:00
}
if t . b != "" {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to b" )
2019-10-24 23:54:17 +09:00
}
if t . c {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to c" )
2019-10-24 23:54:17 +09:00
}
var v struct {
A int
B string
C bool
}
if err := yaml . Unmarshal ( b , & v ) ; err != nil {
return err
}
t . a = v . A
t . b = v . B
t . c = v . C
return nil
}
type unmarshalTest2 struct {
a int
b string
c bool
}
func ( t * unmarshalTest2 ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
2019-10-24 17:18:03 +09:00
var v struct {
A int
B string
C bool
}
if t . a != 0 {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to a" )
2019-10-24 17:18:03 +09:00
}
if t . b != "" {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to b" )
2019-10-24 17:18:03 +09:00
}
if t . c {
2024-10-28 21:24:15 +09:00
return errors . New ( "unexpected field value to c" )
2019-10-24 17:18:03 +09:00
}
if err := unmarshal ( & v ) ; err != nil {
return err
}
t . a = v . A
t . b = v . B
t . c = v . C
return nil
}
func TestUnmarshalYAML ( t * testing . T ) {
yml := `
a :
a : 1
b : hello
c : true
2019-10-24 23:54:17 +09:00
b :
a : 2
b : world
c : true
2019-10-24 17:18:03 +09:00
`
var v struct {
A * unmarshalTest
2019-10-24 23:54:17 +09:00
B * unmarshalTest2
2019-10-24 17:18:03 +09:00
}
if err := yaml . Unmarshal ( [ ] byte ( yml ) , & v ) ; err != nil {
t . Fatalf ( "failed to Unmarshal: %+v" , err )
}
if v . A == nil {
t . Fatal ( "failed to UnmarshalYAML" )
}
if v . A . a != 1 {
t . Fatal ( "failed to UnmarshalYAML" )
}
if v . A . b != "hello" {
t . Fatal ( "failed to UnmarshalYAML" )
}
if ! v . A . c {
t . Fatal ( "failed to UnmarshalYAML" )
}
2019-10-24 23:54:17 +09:00
if v . B == nil {
t . Fatal ( "failed to UnmarshalYAML" )
}
if v . B . a != 2 {
t . Fatal ( "failed to UnmarshalYAML" )
}
if v . B . b != "world" {
t . Fatal ( "failed to UnmarshalYAML" )
}
if ! v . B . c {
t . Fatal ( "failed to UnmarshalYAML" )
}
2019-10-24 17:18:03 +09:00
}
2020-03-02 23:11:18 +09:00
type ObjectMap map [ string ] * Object
type ObjectDecl struct {
Name string ` yaml:"-" `
* Object ` yaml:",inline,anchor" `
}
func ( m ObjectMap ) MarshalYAML ( ) ( interface { } , error ) {
newMap := map [ string ] * ObjectDecl { }
for k , v := range m {
newMap [ k ] = & ObjectDecl { Name : k , Object : v }
}
return newMap , nil
}
type rootObject struct {
Single ObjectMap ` yaml:"single" `
Collection map [ string ] [ ] * Object ` yaml:"collection" `
}
type Object struct {
* Object ` yaml:",omitempty,inline,alias" `
MapValue map [ string ] interface { } ` yaml:",omitempty,inline" `
}
func TestInlineAnchorAndAlias ( t * testing . T ) {
yml := ` -- -
single :
default : & default
id : 1
name : john
user_1 : & user_1
id : 1
name : ken
user_2 : & user_2
<< : * default
id : 2
collection :
defaults :
- * default
- << : * default
- << : * default
id : 2
users :
- << : * user_1
- << : * user_2
- << : * user_1
id : 3
- << : * user_1
id : 4
- << : * user_1
id : 5
`
var v rootObject
if err := yaml . Unmarshal ( [ ] byte ( yml ) , & v ) ; err != nil {
2020-06-22 23:44:26 +09:00
t . Fatal ( err )
2020-03-02 23:11:18 +09:00
}
opt := yaml . MarshalAnchor ( func ( anchor * ast . AnchorNode , value interface { } ) error {
if o , ok := value . ( * ObjectDecl ) ; ok {
2020-06-22 23:44:26 +09:00
return anchor . SetName ( o . Name )
2020-03-02 23:11:18 +09:00
}
return nil
} )
var buf bytes . Buffer
if err := yaml . NewEncoder ( & buf , opt ) . Encode ( v ) ; err != nil {
t . Fatalf ( "%+v" , err )
}
actual := "---\n" + buf . String ( )
if yml != actual {
t . Fatalf ( "failed to marshal: expected:[%s] actual:[%s]" , yml , actual )
}
}
2020-06-21 16:20:46 +09:00
func TestMapSlice_Map ( t * testing . T ) {
yml := `
a : b
c : d
`
var v yaml . MapSlice
if err := yaml . Unmarshal ( [ ] byte ( yml ) , & v ) ; err != nil {
t . Fatal ( err )
}
m := v . ToMap ( )
if len ( m ) != 2 {
t . Fatal ( "failed to convert MapSlice to map" )
}
if m [ "a" ] != "b" {
t . Fatal ( "failed to convert MapSlice to map" )
}
if m [ "c" ] != "d" {
t . Fatal ( "failed to convert MapSlice to map" )
}
}
2020-06-22 23:30:51 +09:00
func TestMarshalWithModifiedAnchorAlias ( t * testing . T ) {
yml := `
a : & a 1
b : * a
`
var v struct {
A * int ` yaml:"a,anchor" `
2024-12-23 10:25:20 +09:00
B * int ` yaml:"b" `
2020-06-22 23:30:51 +09:00
}
if err := yaml . Unmarshal ( [ ] byte ( yml ) , & v ) ; err != nil {
t . Fatal ( err )
}
node , err := yaml . ValueToNode ( v )
if err != nil {
t . Fatal ( err )
}
anchors := ast . Filter ( ast . AnchorType , node )
if len ( anchors ) != 1 {
t . Fatal ( "failed to filter node" )
}
2024-10-28 11:31:15 +09:00
anchor , _ := anchors [ 0 ] . ( * ast . AnchorNode )
2020-06-22 23:30:51 +09:00
if err := anchor . SetName ( "b" ) ; err != nil {
t . Fatal ( err )
}
aliases := ast . Filter ( ast . AliasType , node )
if len ( anchors ) != 1 {
t . Fatal ( "failed to filter node" )
}
2024-10-28 11:31:15 +09:00
alias , _ := aliases [ 0 ] . ( * ast . AliasNode )
2020-06-22 23:30:51 +09:00
if err := alias . SetName ( "b" ) ; err != nil {
t . Fatal ( err )
}
expected := `
a : & b 1
b : * b `
actual := "\n" + node . String ( )
if expected != actual {
t . Fatalf ( "failed to marshal: expected:[%q] but got [%q]" , expected , actual )
}
}
2020-10-26 19:00:29 +09:00
func Test_YAMLToJSON ( t * testing . T ) {
yml := `
foo :
bar :
- a
- b
- c
a : 1
`
actual , err := yaml . YAMLToJSON ( [ ] byte ( yml ) )
if err != nil {
t . Fatal ( err )
}
expected := ` { "foo": { "bar": ["a", "b", "c"]}, "a": 1} `
if expected + "\n" != string ( actual ) {
t . Fatalf ( "failed to convert yaml to json: expected [%q] but got [%q]" , expected , actual )
}
}
func Test_JSONToYAML ( t * testing . T ) {
json := ` { "foo": { "bar": ["a", "b", "c"]}, "a": 1} `
expected := `
foo :
bar :
- a
- b
- c
a : 1
`
actual , err := yaml . JSONToYAML ( [ ] byte ( json ) )
if err != nil {
t . Fatal ( err )
}
if expected != "\n" + string ( actual ) {
t . Fatalf ( "failed to convert json to yaml: expected [%q] but got [%q]" , expected , actual )
}
}
2021-07-20 14:26:02 +09:00
2021-07-20 14:44:18 +09:00
func Test_WithCommentOption ( t * testing . T ) {
2021-07-20 14:26:02 +09:00
t . Run ( "line comment" , func ( t * testing . T ) {
2023-03-01 16:59:07 +09:00
v := struct {
Foo string ` yaml:"foo" `
Bar map [ string ] interface { } ` yaml:"bar" `
Baz struct {
X int ` yaml:"x" `
} ` yaml:"baz" `
} {
Foo : "aaa" ,
Bar : map [ string ] interface { } { "bbb" : "ccc" } ,
Baz : struct {
X int ` yaml:"x" `
} { X : 10 } ,
}
2021-07-20 14:26:02 +09:00
b , err := yaml . MarshalWithOptions ( v , yaml . WithComment (
yaml . CommentMap {
2023-03-01 16:59:07 +09:00
"$.foo" : [ ] * yaml . Comment { yaml . LineComment ( "foo comment" ) } ,
"$.bar" : [ ] * yaml . Comment { yaml . LineComment ( "bar comment" ) } ,
"$.bar.bbb" : [ ] * yaml . Comment { yaml . LineComment ( "bbb comment" ) } ,
"$.baz.x" : [ ] * yaml . Comment { yaml . LineComment ( "x comment" ) } ,
2021-07-20 14:26:02 +09:00
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
foo : aaa # foo comment
bar : # bar comment
bbb : ccc # bbb comment
baz :
x : 10 # x comment
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
2023-03-01 16:59:07 +09:00
t . Run ( "line comment2" , func ( t * testing . T ) {
v := struct {
Foo map [ string ] interface { } ` yaml:"foo" `
} {
Foo : map [ string ] interface { } {
"bar" : map [ string ] interface { } {
"baz" : true ,
} ,
} ,
}
b , err := yaml . MarshalWithOptions ( v , yaml . WithComment (
yaml . CommentMap {
"$.foo.bar" : [ ] * yaml . Comment { yaml . HeadComment ( " bar head comment" ) , yaml . LineComment ( " bar line comment" ) } ,
"$.foo.bar.baz" : [ ] * yaml . Comment { yaml . LineComment ( " baz line comment" ) } ,
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
foo :
# bar head comment
bar : # bar line comment
baz : true # baz line comment
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
t . Run ( "single head comment" , func ( t * testing . T ) {
v := struct {
Foo string ` yaml:"foo" `
Bar map [ string ] interface { } ` yaml:"bar" `
Baz struct {
X int ` yaml:"x" `
} ` yaml:"baz" `
} {
Foo : "aaa" ,
Bar : map [ string ] interface { } { "bbb" : "ccc" } ,
Baz : struct {
X int ` yaml:"x" `
} { X : 10 } ,
}
b , err := yaml . MarshalWithOptions ( v , yaml . WithComment (
yaml . CommentMap {
"$.foo" : [ ] * yaml . Comment { yaml . HeadComment ( "foo comment" ) } ,
"$.bar" : [ ] * yaml . Comment { yaml . HeadComment ( "bar comment" ) } ,
"$.bar.bbb" : [ ] * yaml . Comment { yaml . HeadComment ( "bbb comment" ) } ,
"$.baz.x" : [ ] * yaml . Comment { yaml . HeadComment ( "x comment" ) } ,
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
# foo comment
foo : aaa
# bar comment
bar :
# bbb comment
bbb : ccc
baz :
# x comment
x : 10
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
t . Run ( "multiple head comment" , func ( t * testing . T ) {
v := struct {
Foo string ` yaml:"foo" `
Bar map [ string ] interface { } ` yaml:"bar" `
Baz struct {
X int ` yaml:"x" `
} ` yaml:"baz" `
} {
Foo : "aaa" ,
Bar : map [ string ] interface { } { "bbb" : "ccc" } ,
Baz : struct {
X int ` yaml:"x" `
} { X : 10 } ,
}
2021-07-20 14:26:02 +09:00
b , err := yaml . MarshalWithOptions ( v , yaml . WithComment (
yaml . CommentMap {
2023-03-01 16:59:07 +09:00
"$.foo" : [ ] * yaml . Comment {
yaml . HeadComment (
"foo comment" ,
"foo comment2" ,
) ,
} ,
"$.bar" : [ ] * yaml . Comment {
yaml . HeadComment (
"bar comment" ,
"bar comment2" ,
) ,
} ,
"$.bar.bbb" : [ ] * yaml . Comment {
yaml . HeadComment (
"bbb comment" ,
"bbb comment2" ,
) ,
} ,
"$.baz.x" : [ ] * yaml . Comment {
yaml . HeadComment (
"x comment" ,
"x comment2" ,
) ,
} ,
2021-07-20 14:26:02 +09:00
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
# foo comment
# foo comment2
foo : aaa
# bar comment
# bar comment2
bar :
# bbb comment
# bbb comment2
bbb : ccc
baz :
# x comment
# x comment2
x : 10
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
2023-03-01 16:59:07 +09:00
t . Run ( "foot comment" , func ( t * testing . T ) {
v := struct {
Bar map [ string ] interface { } ` yaml:"bar" `
Baz [ ] int ` yaml:"baz" `
} {
Bar : map [ string ] interface { } { "bbb" : "ccc" } ,
Baz : [ ] int { 1 , 2 } ,
}
b , err := yaml . MarshalWithOptions ( v , yaml . IndentSequence ( true ) , yaml . WithComment (
yaml . CommentMap {
"$.bar.bbb" : [ ] * yaml . Comment { yaml . FootComment ( "ccc: ddd" ) } ,
"$.baz[1]" : [ ] * yaml . Comment { yaml . FootComment ( "- 3" ) } ,
"$.baz" : [ ] * yaml . Comment { yaml . FootComment ( " foot comment" , "foot comment2" ) } ,
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
bar :
bbb : ccc
# ccc : ddd
baz :
- 1
- 2
# - 3
# foot comment
# foot comment2
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
t . Run ( "combination" , func ( t * testing . T ) {
v := struct {
Foo map [ string ] interface { } ` yaml:"foo" `
O map [ string ] interface { } ` yaml:"o" `
T map [ string ] bool ` yaml:"t" `
Bar map [ string ] interface { } ` yaml:"bar" `
Baz [ ] int ` yaml:"baz" `
Hoge map [ string ] interface { } ` yaml:"hoge" `
} {
Foo : map [ string ] interface { } {
"a" : map [ string ] interface { } {
"b" : map [ string ] interface { } {
"c" : "d" ,
} ,
} ,
} ,
O : map [ string ] interface { } {
"p" : map [ string ] interface { } {
"q" : map [ string ] interface { } {
"r" : "s" ,
} ,
} ,
} ,
T : map [ string ] bool {
"u" : true ,
} ,
Bar : map [ string ] interface { } { "bbb" : "ccc" } ,
Baz : [ ] int { 1 , 2 } ,
Hoge : map [ string ] interface { } {
"moga" : true ,
} ,
}
b , err := yaml . MarshalWithOptions ( v , yaml . IndentSequence ( true ) , yaml . WithComment (
yaml . CommentMap {
"$.foo" : [ ] * yaml . Comment {
yaml . HeadComment ( " foo head comment" , " foo head comment2" ) ,
yaml . LineComment ( " foo line comment" ) ,
} ,
"$.foo.a" : [ ] * yaml . Comment {
yaml . HeadComment ( " a head comment" ) ,
yaml . LineComment ( " a line comment" ) ,
} ,
"$.foo.a.b" : [ ] * yaml . Comment {
yaml . HeadComment ( " b head comment" ) ,
yaml . LineComment ( " b line comment" ) ,
} ,
"$.foo.a.b.c" : [ ] * yaml . Comment {
yaml . LineComment ( " c line comment" ) ,
} ,
"$.o" : [ ] * yaml . Comment {
yaml . LineComment ( " o line comment" ) ,
} ,
"$.o.p" : [ ] * yaml . Comment {
yaml . HeadComment ( " p head comment" , " p head comment2" ) ,
yaml . LineComment ( " p line comment" ) ,
} ,
"$.o.p.q" : [ ] * yaml . Comment {
yaml . HeadComment ( " q head comment" , " q head comment2" ) ,
yaml . LineComment ( " q line comment" ) ,
} ,
"$.o.p.q.r" : [ ] * yaml . Comment {
yaml . LineComment ( " r line comment" ) ,
} ,
"$.t.u" : [ ] * yaml . Comment {
yaml . LineComment ( " u line comment" ) ,
} ,
"$.bar" : [ ] * yaml . Comment {
yaml . HeadComment ( " bar head comment" ) ,
yaml . LineComment ( " bar line comment" ) ,
} ,
"$.bar.bbb" : [ ] * yaml . Comment {
yaml . HeadComment ( " bbb head comment" ) ,
yaml . LineComment ( " bbb line comment" ) ,
yaml . FootComment ( " bbb foot comment" ) ,
} ,
"$.baz[0]" : [ ] * yaml . Comment {
yaml . HeadComment ( " sequence head comment" ) ,
yaml . LineComment ( " sequence line comment" ) ,
} ,
"$.baz[1]" : [ ] * yaml . Comment {
yaml . HeadComment ( " sequence head comment2" ) ,
yaml . LineComment ( " sequence line comment2" ) ,
yaml . FootComment ( " sequence foot comment" ) ,
} ,
"$.baz" : [ ] * yaml . Comment {
yaml . HeadComment ( " baz head comment" , " baz head comment2" ) ,
yaml . LineComment ( " baz line comment" ) ,
yaml . FootComment ( " baz foot comment" ) ,
} ,
"$.hoge.moga" : [ ] * yaml . Comment {
yaml . LineComment ( " moga line comment" ) ,
yaml . FootComment ( " moga foot comment" ) ,
} ,
} ,
) )
if err != nil {
t . Fatal ( err )
}
expected := `
# foo head comment
# foo head comment2
foo : # foo line comment
# a head comment
a : # a line comment
# b head comment
b : # b line comment
c : d # c line comment
o : # o line comment
# p head comment
# p head comment2
p : # p line comment
# q head comment
# q head comment2
q : # q line comment
r : s # r line comment
t :
u : true # u line comment
# bar head comment
bar : # bar line comment
# bbb head comment
bbb : ccc # bbb line comment
# bbb foot comment
# baz head comment
# baz head comment2
baz : # baz line comment
# sequence head comment
- 1 # sequence line comment
# sequence head comment2
- 2 # sequence line comment2
# sequence foot comment
# baz foot comment
hoge :
moga : true # moga line comment
# moga foot comment
`
actual := "\n" + string ( b )
if expected != actual {
t . Fatalf ( "expected:%s but got %s" , expected , actual )
}
} )
2021-07-20 14:26:02 +09:00
}
2021-09-07 19:49:15 +09:00
func Test_CommentToMapOption ( t * testing . T ) {
2024-11-06 18:21:28 +09:00
type testCase struct {
name string
yml string
options [ ] yaml . DecodeOption
expected [ ] struct {
path string
comments [ ] * yaml . Comment
}
}
tests := [ ] testCase {
{
name : "line comment" ,
yml : `
2021-09-07 19:49:15 +09:00
foo : aaa # foo comment
bar : # bar comment
bbb : ccc # bbb comment
baz :
x : 10 # x comment
2024-11-06 18:21:28 +09:00
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.foo" , [ ] * yaml . Comment { yaml . LineComment ( "foo comment" ) } } ,
{ "$.bar" , [ ] * yaml . Comment { yaml . LineComment ( "bar comment" ) } } ,
{ "$.bar.bbb" , [ ] * yaml . Comment { yaml . LineComment ( "bbb comment" ) } } ,
{ "$.baz.x" , [ ] * yaml . Comment { yaml . LineComment ( "x comment" ) } } ,
2021-09-07 19:49:15 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
{
name : "line comment2" ,
yml : `
2023-03-01 16:59:07 +09:00
foo :
2024-11-06 18:21:28 +09:00
bar : baz # comment ` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.foo.bar" , [ ] * yaml . Comment { yaml . LineComment ( " comment" ) } } ,
2023-03-01 16:59:07 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
{
name : "single head comment" ,
yml : `
2023-03-01 16:59:07 +09:00
# foo comment
foo : aaa
# bar comment
bar :
# bbb comment
bbb : ccc
baz :
# x comment
x : 10
2024-11-06 18:21:28 +09:00
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.foo" , [ ] * yaml . Comment { yaml . HeadComment ( "foo comment" ) } } ,
{ "$.bar" , [ ] * yaml . Comment { yaml . HeadComment ( "bar comment" ) } } ,
{ "$.bar.bbb" , [ ] * yaml . Comment { yaml . HeadComment ( "bbb comment" ) } } ,
{ "$.baz.x" , [ ] * yaml . Comment { yaml . HeadComment ( "x comment" ) } } ,
2023-03-01 16:59:07 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
{
name : "single head comment ordered map" ,
yml : `
# first comment
first : value
# second comment
second :
# third comment
third : value
# forth comment
forth : value
# fifth comment
fifth :
# sixth comment
sixth : value
# seventh comment
seventh : value
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.first" , [ ] * yaml . Comment { yaml . HeadComment ( "first comment" ) } } ,
{ "$.second" , [ ] * yaml . Comment { yaml . HeadComment ( "second comment" ) } } ,
{ "$.second.third" , [ ] * yaml . Comment { yaml . HeadComment ( "third comment" ) } } ,
{ "$.second.forth" , [ ] * yaml . Comment { yaml . HeadComment ( "forth comment" ) } } ,
{ "$.fifth" , [ ] * yaml . Comment { yaml . HeadComment ( "fifth comment" ) } } ,
{ "$.fifth.sixth" , [ ] * yaml . Comment { yaml . HeadComment ( "sixth comment" ) } } ,
{ "$.fifth.seventh" , [ ] * yaml . Comment { yaml . HeadComment ( "seventh comment" ) } } ,
2023-03-01 16:59:07 +09:00
} ,
2024-11-06 18:21:28 +09:00
options : [ ] yaml . DecodeOption { yaml . UseOrderedMap ( ) } ,
} ,
{
name : "multiple head comments" ,
yml : `
2021-09-07 19:49:15 +09:00
# foo comment
# foo comment2
foo : aaa
# bar comment
# bar comment2
bar :
# bbb comment
# bbb comment2
bbb : ccc
baz :
# x comment
# x comment2
x : 10
2024-11-06 18:21:28 +09:00
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.foo" , [ ] * yaml . Comment { yaml . HeadComment ( "foo comment" , "foo comment2" ) } } ,
{ "$.bar" , [ ] * yaml . Comment { yaml . HeadComment ( "bar comment" , "bar comment2" ) } } ,
{ "$.bar.bbb" , [ ] * yaml . Comment { yaml . HeadComment ( "bbb comment" , "bbb comment2" ) } } ,
{ "$.baz.x" , [ ] * yaml . Comment { yaml . HeadComment ( "x comment" , "x comment2" ) } } ,
2023-03-01 16:59:07 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
{
name : "foot comment" ,
yml : `
2023-03-01 16:59:07 +09:00
bar :
bbb : ccc
# ccc : ddd
baz :
- 1
- 2
# - 3
# foot comment
# foot comment2
2024-11-06 18:21:28 +09:00
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.bar.bbb" , [ ] * yaml . Comment { yaml . FootComment ( "ccc: ddd" ) } } ,
{ "$.baz[1]" , [ ] * yaml . Comment { yaml . FootComment ( "- 3" ) } } ,
{ "$.baz" , [ ] * yaml . Comment { yaml . FootComment ( " foot comment" , "foot comment2" ) } } ,
2023-03-01 16:59:07 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
{
name : "combination" ,
yml : `
2023-03-01 16:59:07 +09:00
# foo head comment
# foo head comment2
foo : # foo line comment
# a head comment
a : # a line comment
# b head comment
b : # b line comment
c : d # c line comment
o : # o line comment
# p head comment
# p head comment2
p : # p line comment
# q head comment
# q head comment2
q : # q line comment
r : s # r line comment
t :
u : true # u line comment
# bar head comment
bar : # bar line comment
# bbb head comment
bbb : ccc # bbb line comment
# bbb foot comment
# baz head comment
# baz head comment2
baz : # baz line comment
# sequence head comment
- 1 # sequence line comment
# sequence head comment2
- 2 # sequence line comment2
# sequence foot comment
hoge :
moga : true # moga line comment
# moga foot comment
# hoge foot comment
2024-11-06 18:21:28 +09:00
` ,
expected : [ ] struct {
path string
comments [ ] * yaml . Comment
} {
{ "$.foo" , [ ] * yaml . Comment { yaml . HeadComment ( " foo head comment" , " foo head comment2" ) , yaml . LineComment ( " foo line comment" ) } } ,
{ "$.foo.a" , [ ] * yaml . Comment { yaml . HeadComment ( " a head comment" ) , yaml . LineComment ( " a line comment" ) } } ,
{ "$.foo.a.b" , [ ] * yaml . Comment { yaml . HeadComment ( " b head comment" ) , yaml . LineComment ( " b line comment" ) } } ,
{ "$.foo.a.b.c" , [ ] * yaml . Comment { yaml . LineComment ( " c line comment" ) } } ,
{ "$.o" , [ ] * yaml . Comment { yaml . LineComment ( " o line comment" ) } } ,
{ "$.o.p" , [ ] * yaml . Comment { yaml . HeadComment ( " p head comment" , " p head comment2" ) , yaml . LineComment ( " p line comment" ) } } ,
{ "$.o.p.q" , [ ] * yaml . Comment { yaml . HeadComment ( " q head comment" , " q head comment2" ) , yaml . LineComment ( " q line comment" ) } } ,
{ "$.o.p.q.r" , [ ] * yaml . Comment { yaml . LineComment ( " r line comment" ) } } ,
{ "$.t.u" , [ ] * yaml . Comment { yaml . LineComment ( " u line comment" ) } } ,
{ "$.bar" , [ ] * yaml . Comment { yaml . HeadComment ( " bar head comment" ) , yaml . LineComment ( " bar line comment" ) } } ,
{ "$.bar.bbb" , [ ] * yaml . Comment { yaml . HeadComment ( " bbb head comment" ) , yaml . LineComment ( " bbb line comment" ) , yaml . FootComment ( " bbb foot comment" ) } } ,
{ "$.baz[0]" , [ ] * yaml . Comment { yaml . HeadComment ( " sequence head comment" ) , yaml . LineComment ( " sequence line comment" ) } } ,
{ "$.baz[1]" , [ ] * yaml . Comment { yaml . HeadComment ( " sequence head comment2" ) , yaml . LineComment ( " sequence line comment2" ) , yaml . FootComment ( " sequence foot comment" ) } } ,
{ "$.baz" , [ ] * yaml . Comment { yaml . HeadComment ( " baz head comment" , " baz head comment2" ) , yaml . LineComment ( " baz line comment" ) } } ,
{ "$.hoge" , [ ] * yaml . Comment { yaml . FootComment ( " hoge foot comment" ) } } ,
{ "$.hoge.moga" , [ ] * yaml . Comment { yaml . LineComment ( " moga line comment" ) , yaml . FootComment ( " moga foot comment" ) } } ,
2021-09-07 19:49:15 +09:00
} ,
2024-11-06 18:21:28 +09:00
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
cm := yaml . CommentMap { }
opts := [ ] yaml . DecodeOption { yaml . CommentToMap ( cm ) }
opts = append ( opts , tc . options ... )
var v interface { }
if err := yaml . UnmarshalWithOptions ( [ ] byte ( tc . yml ) , & v , opts ... ) ; err != nil {
t . Fatal ( err )
2021-09-07 19:49:15 +09:00
}
2024-11-06 18:21:28 +09:00
if len ( cm ) != len ( tc . expected ) {
t . Fatalf ( "comment size does not match: got: %d, expected: %d" , len ( cm ) , len ( tc . expected ) )
2021-09-07 19:49:15 +09:00
}
2024-11-06 18:21:28 +09:00
for _ , exp := range tc . expected {
comments := cm [ exp . path ]
if comments == nil {
t . Fatalf ( "failed to get path %s" , exp . path )
}
if diff := cmp . Diff ( exp . comments , comments ) ; diff != "" {
t . Errorf ( "(-got, +want)\n%s" , diff )
}
}
} )
}
2021-09-07 19:49:15 +09:00
}
2023-04-02 10:50:03 +09:00
2023-09-14 21:33:31 -05:00
func TestCommentMapRoundTrip ( t * testing . T ) {
// test that an unmarshal and marshal round trip retains comments.
// if expect is empty, the test will use the input as the expected result.
tests := [ ] struct {
name string
source string
expect string
encodeOptions [ ] yaml . EncodeOption
} {
{
name : "simple map" ,
source : `
# head
a : 1 # line
# foot
` ,
} ,
{
name : "nesting" ,
source : `
- 1 # one
- foo :
a : b
# c comment
c : d # d comment
"e#f" : g # g comment
h . i : j # j comment
"k.#l" : m # m comment
` ,
} ,
{
name : "single quotes" ,
source : ` 'a#b': c # c comment ` ,
encodeOptions : [ ] yaml . EncodeOption { yaml . UseSingleQuote ( true ) } ,
} ,
{
name : "single quotes added in encode" ,
source : ` a#b: c # c comment ` ,
encodeOptions : [ ] yaml . EncodeOption { yaml . UseSingleQuote ( true ) } ,
expect : ` 'a#b': c # c comment ` ,
} ,
{
name : "double quotes quotes transformed to single quotes" ,
source : ` "a#b": c # c comment ` ,
encodeOptions : [ ] yaml . EncodeOption { yaml . UseSingleQuote ( true ) } ,
expect : ` 'a#b': c # c comment ` ,
} ,
{
name : "single quotes quotes transformed to double quotes" ,
source : ` 'a#b': c # c comment ` ,
expect : ` "a#b": c # c comment ` ,
} ,
{
name : "single quotes removed" ,
source : ` 'a': b # b comment ` ,
expect : ` a: b # b comment ` ,
} ,
{
name : "double quotes removed" ,
source : ` "a": b # b comment ` ,
expect : ` a: b # b comment ` ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
var val any
cm := yaml . CommentMap { }
source := strings . TrimSpace ( test . source )
if err := yaml . UnmarshalWithOptions ( [ ] byte ( source ) , & val , yaml . CommentToMap ( cm ) ) ; err != nil {
t . Fatalf ( "%+v" , err )
}
marshaled , err := yaml . MarshalWithOptions ( val , append ( test . encodeOptions , yaml . WithComment ( cm ) ) ... )
if err != nil {
t . Fatalf ( "%+v" , err )
}
got := strings . TrimSpace ( string ( marshaled ) )
expect := strings . TrimSpace ( test . expect )
if expect == "" {
expect = source
}
if got != expect {
t . Fatalf ( "expected:\n%s\ngot:\n%s\n" , expect , got )
}
} )
}
}
2023-04-02 10:50:03 +09:00
func TestRegisterCustomMarshaler ( t * testing . T ) {
type T struct {
Foo [ ] byte ` yaml:"foo" `
}
yaml . RegisterCustomMarshaler [ T ] ( func ( _ T ) ( [ ] byte , error ) {
return [ ] byte ( ` "override" ` ) , nil
} )
b , err := yaml . Marshal ( & T { Foo : [ ] byte ( "bar" ) } )
if err != nil {
t . Fatal ( err )
}
if ! bytes . Equal ( b , [ ] byte ( "\"override\"\n" ) ) {
t . Fatalf ( "failed to register custom marshaler. got: %q" , b )
}
}
func TestRegisterCustomUnmarshaler ( t * testing . T ) {
type T struct {
Foo [ ] byte ` yaml:"foo" `
}
yaml . RegisterCustomUnmarshaler [ T ] ( func ( v * T , _ [ ] byte ) error {
v . Foo = [ ] byte ( "override" )
return nil
} )
var v T
2024-11-17 23:58:35 +09:00
if err := yaml . Unmarshal ( [ ] byte ( ` "foo": "bar" ` ) , & v ) ; err != nil {
2023-04-02 10:50:03 +09:00
t . Fatal ( err )
}
if ! bytes . Equal ( v . Foo , [ ] byte ( "override" ) ) {
t . Fatalf ( "failed to decode. got %q" , v . Foo )
}
}