2021-12-21 04:02:07 +00:00
package yqlib
import (
"bufio"
"fmt"
"testing"
"github.com/mikefarah/yq/v4/test"
)
2022-02-07 00:55:55 +00:00
var inputXMLWithComments = `
2022-01-15 00:57:59 +00:00
< ! -- before cat -- >
< cat >
< ! -- in cat before -- >
< x > 3 < ! -- multi
line comment
for x -- > < / x >
< ! -- before y -- >
< y >
< ! -- in y before -- >
< d > < ! -- in d before -- > z < ! -- in d after -- > < / d >
< ! -- in y after -- >
< / y >
< ! -- in_cat_after -- >
< / cat >
< ! -- after cat -- >
`
2022-02-07 00:55:55 +00:00
var inputXMLWithCommentsWithSubChild = `
2022-01-15 00:57:59 +00:00
< ! -- before cat -- >
< cat >
< ! -- in cat before -- >
< x > 3 < ! -- multi
line comment
for x -- > < / x >
< ! -- before y -- >
< y >
< ! -- in y before -- >
< d > < ! -- in d before -- > < z sweet = "cool" / > < ! -- in d after -- > < / d >
< ! -- in y after -- >
< / y >
< ! -- in_cat_after -- >
< / cat >
< ! -- after cat -- >
`
2022-02-10 01:02:53 +00:00
var expectedDecodeYamlWithSubChild = ` # before cat
2022-01-15 00:57:59 +00:00
cat :
# in cat before
x : "3" # multi
# line comment
# for x
# before y
y :
# in y before
d :
# in d before
z :
+ sweet : cool
# in d after
# in y after
# in_cat_after
# after cat
`
2022-02-07 00:55:55 +00:00
var inputXMLWithCommentsWithArray = `
2022-01-15 00:57:59 +00:00
< ! -- before cat -- >
< cat >
< ! -- in cat before -- >
< x > 3 < ! -- multi
line comment
for x -- > < / x >
< ! -- before y -- >
< y >
< ! -- in y before -- >
< d > < ! -- in d before -- > < z sweet = "cool" / > < ! -- in d after -- > < / d >
< d > < ! -- in d2 before -- > < z sweet = "cool2" / > < ! -- in d2 after -- > < / d >
< ! -- in y after -- >
< / y >
< ! -- in_cat_after -- >
< / cat >
< ! -- after cat -- >
`
2022-02-10 01:02:53 +00:00
var expectedDecodeYamlWithArray = ` # before cat
2022-01-15 00:57:59 +00:00
cat :
# in cat before
x : "3" # multi
# line comment
# for x
# before y
y :
# in y before
d :
- # in d before
z :
+ sweet : cool
# in d after
- # in d2 before
z :
+ sweet : cool2
# in d2 after
# in y after
# in_cat_after
# after cat
`
2022-02-10 01:02:53 +00:00
var expectedDecodeYamlWithComments = ` # before cat
2022-01-15 00:57:59 +00:00
cat :
# in cat before
x : "3" # multi
# line comment
# for x
# before y
y :
# in y before
# in d before
d : z # in d after
# in y after
# in_cat_after
# after cat
`
2022-02-07 00:55:55 +00:00
var expectedRoundtripXMLWithComments = ` < ! -- before cat -- > < cat > < ! -- in cat before -- >
2022-01-15 00:57:59 +00:00
< x > 3 < ! -- multi
line comment
for x -- > < / x > < ! -- before y -- >
< y > < ! -- in y before
in d before -- >
< d > z < ! -- in d after -- > < / d > < ! -- in y after -- >
< / y > < ! -- in_cat_after -- >
< / cat > < ! -- after cat -- >
`
var yamlWithComments = ` # above_cat
cat : # inline_cat
# above_array
array : # inline_array
- val1 # inline_val1
# above_val2
- val2 # inline_val2
# below_cat
`
2022-02-07 00:55:55 +00:00
var expectedXMLWithComments = ` < ! -- above_cat inline_cat -- > < cat > < ! -- above_array inline_array -- >
2022-01-15 00:57:59 +00:00
< array > val1 < ! -- inline_val1 -- > < / array >
< array > < ! -- above_val2 -- > val2 < ! -- inline_val2 -- > < / array >
< / cat > < ! -- below_cat -- >
`
2022-06-14 23:40:31 +00:00
var inputXMLWithNamespacedAttr = `
< ? xml version = "1.0" ? >
< map xmlns = "some-namespace" xmlns : xsi = "some-instance" xsi : schemaLocation = "some-url" >
< / map >
`
var expectedYAMLWithNamespacedAttr = ` map :
+ xmlns : some - namespace
+ xmlns : xsi : some - instance
+ some - instance : schemaLocation : some - url
`
var expectedYAMLWithRawNamespacedAttr = ` map :
+ xmlns : some - namespace
+ xmlns : xsi : some - instance
+ xsi : schemaLocation : some - url
`
2022-03-28 03:05:10 +00:00
var xmlWithCustomDtd = `
< ? xml version = "1.0" ? >
< ! DOCTYPE root [
< ! ENTITY writer "Blah." >
< ! ENTITY copyright "Blah" >
] >
< root >
< item > & writer ; & copyright ; < / item >
< / root > `
var expectedDtd = ` root :
item : ' & writer ; & copyright ; '
`
2022-01-15 07:18:52 +00:00
var xmlScenarios = [ ] formatScenario {
2021-12-21 04:02:07 +00:00
{
2022-01-22 01:35:33 +00:00
description : "Parse xml: simple" ,
subdescription : "Notice how all the values are strings, see the next example on how you can fix that." ,
input : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cat>\n <says>meow</says>\n <legs>4</legs>\n <cute>true</cute>\n</cat>" ,
2022-02-10 01:02:53 +00:00
expected : "cat:\n says: meow\n legs: \"4\"\n cute: \"true\"\n" ,
2022-01-22 01:35:33 +00:00
} ,
{
description : "Parse xml: number" ,
subdescription : "All values are assumed to be strings when parsing XML, but you can use the `from_yaml` operator on all the strings values to autoparse into the correct type." ,
input : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cat>\n <says>meow</says>\n <legs>4</legs>\n <cute>true</cute>\n</cat>" ,
expression : " (.. | select(tag == \"!!str\")) |= from_yaml" ,
2022-02-10 01:02:53 +00:00
expected : "cat:\n says: meow\n legs: 4\n cute: true\n" ,
2021-12-21 04:02:07 +00:00
} ,
{
description : "Parse xml: array" ,
subdescription : "Consecutive nodes with identical xml names are assumed to be arrays." ,
2022-01-22 01:35:33 +00:00
input : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<animal>cat</animal>\n<animal>goat</animal>" ,
2022-02-10 01:02:53 +00:00
expected : "animal:\n - cat\n - goat\n" ,
2021-12-21 04:02:07 +00:00
} ,
{
description : "Parse xml: attributes" ,
2022-01-15 00:57:59 +00:00
subdescription : "Attributes are converted to fields, with the default attribute prefix '+'. Use '--xml-attribute-prefix` to set your own." ,
2021-12-21 04:56:08 +00:00
input : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cat legs=\"4\">\n <legs>7</legs>\n</cat>" ,
2022-02-10 01:02:53 +00:00
expected : "cat:\n +legs: \"4\"\n legs: \"7\"\n" ,
2021-12-21 04:02:07 +00:00
} ,
{
description : "Parse xml: attributes with content" ,
2022-01-22 01:35:33 +00:00
subdescription : "Content is added as a field, using the default content name of `+content`. Use `--xml-content-name` to set your own." ,
2021-12-21 04:56:08 +00:00
input : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cat legs=\"4\">meow</cat>" ,
2022-02-10 01:02:53 +00:00
expected : "cat:\n +content: meow\n +legs: \"4\"\n" ,
2021-12-21 04:02:07 +00:00
} ,
2022-03-28 03:05:10 +00:00
{
description : "Parse xml: custom dtd" ,
subdescription : "DTD entities are ignored." ,
input : xmlWithCustomDtd ,
expected : expectedDtd ,
} ,
2021-12-21 04:56:08 +00:00
{
2022-01-15 00:57:59 +00:00
description : "Parse xml: with comments" ,
subdescription : "A best attempt is made to preserve comments." ,
2022-02-07 00:55:55 +00:00
input : inputXMLWithComments ,
2022-01-15 00:57:59 +00:00
expected : expectedDecodeYamlWithComments ,
scenarioType : "decode" ,
} ,
2022-02-07 00:26:48 +00:00
{
description : "Empty doc" ,
skipDoc : true ,
input : "" ,
2022-05-27 01:18:38 +00:00
expected : "\n" ,
2022-02-07 00:26:48 +00:00
scenarioType : "decode" ,
} ,
{
description : "Empty single node" ,
skipDoc : true ,
input : "<a/>" ,
2022-02-10 01:02:53 +00:00
expected : "a:\n" ,
2022-02-07 00:26:48 +00:00
scenarioType : "decode" ,
} ,
{
description : "Empty close node" ,
skipDoc : true ,
input : "<a></a>" ,
2022-02-10 01:02:53 +00:00
expected : "a:\n" ,
2022-02-07 00:26:48 +00:00
scenarioType : "decode" ,
} ,
{
description : "Nested empty" ,
skipDoc : true ,
input : "<a><b/></a>" ,
2022-02-10 01:02:53 +00:00
expected : "a:\n b:\n" ,
2022-02-07 00:26:48 +00:00
scenarioType : "decode" ,
} ,
2022-01-15 00:57:59 +00:00
{
description : "Parse xml: with comments subchild" ,
skipDoc : true ,
2022-02-07 00:55:55 +00:00
input : inputXMLWithCommentsWithSubChild ,
2022-01-15 00:57:59 +00:00
expected : expectedDecodeYamlWithSubChild ,
scenarioType : "decode" ,
} ,
{
description : "Parse xml: with comments array" ,
skipDoc : true ,
2022-02-07 00:55:55 +00:00
input : inputXMLWithCommentsWithArray ,
2022-01-15 00:57:59 +00:00
expected : expectedDecodeYamlWithArray ,
scenarioType : "decode" ,
} ,
2022-06-14 23:40:31 +00:00
{
description : "Parse xml: keep attribute namespace" ,
skipDoc : false ,
input : inputXMLWithNamespacedAttr ,
expected : expectedYAMLWithNamespacedAttr ,
scenarioType : "decode-keep-ns" ,
} ,
{
description : "Parse xml: keep raw attribute namespace" ,
skipDoc : false ,
input : inputXMLWithNamespacedAttr ,
expected : expectedYAMLWithRawNamespacedAttr ,
scenarioType : "decode-raw-token" ,
} ,
2022-01-15 00:57:59 +00:00
{
description : "Encode xml: simple" ,
input : "cat: purrs" ,
expected : "<cat>purrs</cat>\n" ,
scenarioType : "encode" ,
2021-12-21 04:56:08 +00:00
} ,
{
2022-01-15 00:57:59 +00:00
description : "Encode xml: array" ,
input : "pets:\n cat:\n - purrs\n - meows" ,
expected : "<pets>\n <cat>purrs</cat>\n <cat>meows</cat>\n</pets>\n" ,
scenarioType : "encode" ,
2021-12-21 04:56:08 +00:00
} ,
2021-12-21 05:08:37 +00:00
{
description : "Encode xml: attributes" ,
subdescription : "Fields with the matching xml-attribute-prefix are assumed to be attributes." ,
input : "cat:\n +name: tiger\n meows: true\n" ,
2022-01-15 00:57:59 +00:00
expected : "<cat name=\"tiger\">\n <meows>true</meows>\n</cat>\n" ,
scenarioType : "encode" ,
2021-12-21 05:08:37 +00:00
} ,
{
2022-01-15 00:57:59 +00:00
skipDoc : true ,
input : "cat:\n ++name: tiger\n meows: true\n" ,
expected : "<cat +name=\"tiger\">\n <meows>true</meows>\n</cat>\n" ,
scenarioType : "encode" ,
2021-12-21 05:08:37 +00:00
} ,
2021-12-21 05:19:27 +00:00
{
description : "Encode xml: attributes with content" ,
subdescription : "Fields with the matching xml-content-name is assumed to be content." ,
input : "cat:\n +name: tiger\n +content: cool\n" ,
2022-01-15 00:57:59 +00:00
expected : "<cat name=\"tiger\">cool</cat>\n" ,
scenarioType : "encode" ,
} ,
{
description : "Encode xml: comments" ,
subdescription : "A best attempt is made to copy comments to xml." ,
input : yamlWithComments ,
2022-02-07 00:55:55 +00:00
expected : expectedXMLWithComments ,
2022-01-15 00:57:59 +00:00
scenarioType : "encode" ,
} ,
{
description : "Round trip: with comments" ,
subdescription : "A best effort is made, but comment positions and white space are not preserved perfectly." ,
2022-02-07 00:55:55 +00:00
input : inputXMLWithComments ,
expected : expectedRoundtripXMLWithComments ,
2022-01-15 00:57:59 +00:00
scenarioType : "roundtrip" ,
2021-12-21 05:19:27 +00:00
} ,
2021-12-21 04:02:07 +00:00
}
2022-02-07 00:55:55 +00:00
func testXMLScenario ( t * testing . T , s formatScenario ) {
2022-06-14 23:40:31 +00:00
switch s . scenarioType {
case "encode" :
2022-02-10 01:02:53 +00:00
test . AssertResultWithContext ( t , s . expected , processFormatScenario ( s , NewYamlDecoder ( ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) , s . description )
2022-06-14 23:40:31 +00:00
case "roundtrip" :
test . AssertResultWithContext ( t , s . expected , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) , s . description )
case "decode-keep-ns" :
test . AssertResultWithContext ( t , s . expected , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , true , false ) , NewYamlEncoder ( 2 , false , true , true ) ) , s . description )
case "decode-raw-token" :
test . AssertResultWithContext ( t , s . expected , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , true , true ) , NewYamlEncoder ( 2 , false , true , true ) ) , s . description )
default :
test . AssertResultWithContext ( t , s . expected , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewYamlEncoder ( 4 , false , true , true ) ) , s . description )
2021-12-21 04:56:08 +00:00
}
2021-12-21 04:02:07 +00:00
}
2022-02-07 00:55:55 +00:00
func documentXMLScenario ( t * testing . T , w * bufio . Writer , i interface { } ) {
2022-01-15 07:18:52 +00:00
s := i . ( formatScenario )
2021-12-21 04:02:07 +00:00
if s . skipDoc {
return
}
2022-06-14 23:40:31 +00:00
switch s . scenarioType {
case "encode" :
2022-02-07 00:55:55 +00:00
documentXMLEncodeScenario ( w , s )
2022-06-14 23:40:31 +00:00
case "roundtrip" :
2022-02-07 00:55:55 +00:00
documentXMLRoundTripScenario ( w , s )
2022-06-14 23:40:31 +00:00
case "decode-keep-ns" :
documentXMLDecodeKeepNsScenario ( w , s )
case "decode-raw-token" :
documentXMLDecodeKeepNsRawTokenScenario ( w , s )
default :
2022-02-10 01:02:53 +00:00
documentXMLDecodeScenario ( w , s )
2021-12-21 04:56:08 +00:00
}
}
2022-02-10 01:02:53 +00:00
func documentXMLDecodeScenario ( w * bufio . Writer , s formatScenario ) {
2021-12-21 04:02:07 +00:00
writeOrPanic ( w , fmt . Sprintf ( "## %v\n" , s . description ) )
if s . subdescription != "" {
writeOrPanic ( w , s . subdescription )
writeOrPanic ( w , "\n\n" )
}
writeOrPanic ( w , "Given a sample.xml file of:\n" )
2021-12-21 04:56:08 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v\n```\n" , s . input ) )
2021-12-21 04:02:07 +00:00
writeOrPanic ( w , "then\n" )
2022-01-22 01:35:33 +00:00
expression := s . expression
if expression == "" {
expression = "."
}
2022-01-27 06:21:10 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```bash\nyq -p=xml '%v' sample.xml\n```\n" , expression ) )
2021-12-21 04:02:07 +00:00
writeOrPanic ( w , "will output\n" )
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```yaml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewYamlEncoder ( 2 , false , true , true ) ) ) )
}
func documentXMLDecodeKeepNsScenario ( w * bufio . Writer , s formatScenario ) {
writeOrPanic ( w , fmt . Sprintf ( "## %v\n" , s . description ) )
if s . subdescription != "" {
writeOrPanic ( w , s . subdescription )
writeOrPanic ( w , "\n\n" )
}
writeOrPanic ( w , "Given a sample.xml file of:\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v\n```\n" , s . input ) )
writeOrPanic ( w , "then\n" )
writeOrPanic ( w , "```bash\nyq -p=xml -o=xml --xml-keep-namespace '.' sample.xml\n```\n" )
writeOrPanic ( w , "will output\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , true , false ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
writeOrPanic ( w , "instead of\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
}
func documentXMLDecodeKeepNsRawTokenScenario ( w * bufio . Writer , s formatScenario ) {
writeOrPanic ( w , fmt . Sprintf ( "## %v\n" , s . description ) )
if s . subdescription != "" {
writeOrPanic ( w , s . subdescription )
writeOrPanic ( w , "\n\n" )
}
writeOrPanic ( w , "Given a sample.xml file of:\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v\n```\n" , s . input ) )
writeOrPanic ( w , "then\n" )
writeOrPanic ( w , "```bash\nyq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml\n```\n" )
writeOrPanic ( w , "will output\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , true , true ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
writeOrPanic ( w , "instead of\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
2021-12-21 04:56:08 +00:00
}
2022-02-07 00:55:55 +00:00
func documentXMLEncodeScenario ( w * bufio . Writer , s formatScenario ) {
2021-12-21 04:56:08 +00:00
writeOrPanic ( w , fmt . Sprintf ( "## %v\n" , s . description ) )
if s . subdescription != "" {
writeOrPanic ( w , s . subdescription )
writeOrPanic ( w , "\n\n" )
}
writeOrPanic ( w , "Given a sample.yml file of:\n" )
writeOrPanic ( w , fmt . Sprintf ( "```yaml\n%v\n```\n" , s . input ) )
writeOrPanic ( w , "then\n" )
2022-01-27 06:21:10 +00:00
writeOrPanic ( w , "```bash\nyq -o=xml '.' sample.yml\n```\n" )
2021-12-21 04:56:08 +00:00
writeOrPanic ( w , "will output\n" )
2021-12-21 04:02:07 +00:00
2022-02-10 01:02:53 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewYamlDecoder ( ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
2022-01-15 00:57:59 +00:00
}
2022-02-07 00:55:55 +00:00
func documentXMLRoundTripScenario ( w * bufio . Writer , s formatScenario ) {
2022-01-15 00:57:59 +00:00
writeOrPanic ( w , fmt . Sprintf ( "## %v\n" , s . description ) )
if s . subdescription != "" {
writeOrPanic ( w , s . subdescription )
writeOrPanic ( w , "\n\n" )
}
writeOrPanic ( w , "Given a sample.xml file of:\n" )
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v\n```\n" , s . input ) )
writeOrPanic ( w , "then\n" )
2022-01-27 06:21:10 +00:00
writeOrPanic ( w , "```bash\nyq -p=xml -o=xml '.' sample.xml\n```\n" )
2022-01-15 00:57:59 +00:00
writeOrPanic ( w , "will output\n" )
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , processFormatScenario ( s , NewXMLDecoder ( "+" , "+content" , false , false , false ) , NewXMLEncoder ( 2 , "+" , "+content" ) ) ) )
2021-12-21 04:02:07 +00:00
}
2022-02-07 00:55:55 +00:00
func TestXMLScenarios ( t * testing . T ) {
2021-12-21 04:02:07 +00:00
for _ , tt := range xmlScenarios {
2022-02-07 00:55:55 +00:00
testXMLScenario ( t , tt )
2021-12-21 04:02:07 +00:00
}
genericScenarios := make ( [ ] interface { } , len ( xmlScenarios ) )
for i , s := range xmlScenarios {
genericScenarios [ i ] = s
}
2022-02-07 00:55:55 +00:00
documentScenarios ( t , "usage" , "xml" , genericScenarios , documentXMLScenario )
2021-12-21 04:02:07 +00:00
}