2023-03-01 02:19:06 +00:00
//go:build !yq_noxml
2024-03-12 04:45:08 +00:00
package xml
2021-12-21 04:02:07 +00:00
import (
"bufio"
"fmt"
"testing"
2024-03-12 04:45:08 +00:00
"github.com/mikefarah/yq/v4/pkg/yqlib"
2021-12-21 04:02:07 +00:00
"github.com/mikefarah/yq/v4/test"
)
2023-03-01 23:57:54 +00:00
const yamlInputWithProcInstAndHeadComment = ` # cats
+ p_xml : version = "1.0"
this : is some xml `
const expectedXmlProcInstAndHeadComment = ` < ? xml version = "1.0" ? >
< ! -- cats -- >
< this > is some xml < / this >
`
const xmlProcInstAndHeadCommentBlock = ` < ? xml version = "1.0" ? >
< ! --
cats
-- >
< this > is some xml < / this >
`
const expectedYamlProcInstAndHeadCommentBlock = ` #
# cats
#
+ p_xml : version = "1.0"
this : is some xml
`
2022-06-25 02:22:03 +00:00
const 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-06-25 02:22:03 +00:00
const 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-06-25 02:22:03 +00:00
const 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 :
2022-11-10 11:22:55 +00:00
+ @ sweet : cool
2022-01-15 00:57:59 +00:00
# in d after
# in y after
# in_cat_after
# after cat
`
2022-06-25 02:22:03 +00:00
const 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-06-25 02:22:03 +00:00
const 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 :
2022-11-10 11:22:55 +00:00
+ @ sweet : cool
2022-01-15 00:57:59 +00:00
# in d after
- # in d2 before
z :
2022-11-10 11:22:55 +00:00
+ @ sweet : cool2
2022-01-15 00:57:59 +00:00
# in d2 after
# in y after
# in_cat_after
# after cat
`
2022-06-25 02:22:03 +00:00
const 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
`
2023-03-01 23:57:54 +00:00
const 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 -- >
`
2023-03-01 23:57:54 +00:00
const yamlWithComments = ` #
# header comment
2022-10-28 03:16:46 +00:00
# above_cat
2023-03-01 23:57:54 +00:00
#
2022-01-15 00:57:59 +00:00
cat : # inline_cat
# above_array
array : # inline_array
- val1 # inline_val1
# above_val2
- val2 # inline_val2
# below_cat
`
2022-10-28 03:16:46 +00:00
const expectedXMLWithComments = ` < ! --
2023-03-01 23:57:54 +00:00
header comment
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-11-13 00:13:05 +00:00
const inputXMLWithNamespacedAttr = ` < ? xml version = "1.0" ? >
< map xmlns = "some-namespace" xmlns : xsi = "some-instance" xsi : schemaLocation = "some-url" > < / map >
2022-06-14 23:40:31 +00:00
`
2022-10-23 23:09:42 +00:00
const expectedYAMLWithNamespacedAttr = ` + p_xml : version = "1.0"
map :
2022-11-10 11:22:55 +00:00
+ @ xmlns : some - namespace
+ @ xmlns : xsi : some - instance
2022-11-13 00:13:05 +00:00
+ @ xsi : schemaLocation : some - url
2022-06-14 23:40:31 +00:00
`
2022-10-23 23:09:42 +00:00
const expectedYAMLWithRawNamespacedAttr = ` + p_xml : version = "1.0"
map :
2022-11-10 11:22:55 +00:00
+ @ xmlns : some - namespace
+ @ xmlns : xsi : some - instance
+ @ xsi : schemaLocation : some - url
2022-06-14 23:40:31 +00:00
`
2022-11-13 00:13:05 +00:00
const expectedYAMLWithoutRawNamespacedAttr = ` + p_xml : version = "1.0"
map :
+ @ xmlns : some - namespace
+ @ xmlns : xsi : some - instance
+ @ some - instance : schemaLocation : some - url
`
2022-06-25 02:22:03 +00:00
const xmlWithCustomDtd = `
2022-03-28 03:05:10 +00:00
< ? xml version = "1.0" ? >
< ! DOCTYPE root [
< ! ENTITY writer "Blah." >
< ! ENTITY copyright "Blah" >
] >
< root >
< item > & writer ; & copyright ; < / item >
< / root > `
2022-10-23 23:09:42 +00:00
const expectedDtd = ` < ? xml version = "1.0" ? >
< ! DOCTYPE root [
< ! ENTITY writer "Blah." >
< ! ENTITY copyright "Blah" >
] >
< root >
< item > & amp ; writer ; & amp ; copyright ; < / item >
< / root >
`
const expectedSkippedDtd = ` < ? xml version = "1.0" ? >
< root >
< item > & amp ; writer ; & amp ; copyright ; < / item >
< / root >
`
const xmlWithProcInstAndDirectives = ` < ? xml version = "1.0" ? >
< ! DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
< apple >
< ? coolioo version = "1.0" ? >
< ! CATYPE meow purr puss >
< b > things < / b >
< / apple >
`
const yamlWithProcInstAndDirectives = ` + p_xml : version = "1.0"
+ directive : ' DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" '
apple :
+ p_coolioo : version = "1.0"
+ directive : ' CATYPE meow purr puss '
b : things
`
const expectedXmlWithProcInstAndDirectives = ` < ? xml version = "1.0" ? >
< ! DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
< apple > < ? coolioo version = "1.0" ? > < ! CATYPE meow purr puss >
< b > things < / b >
< / apple >
2022-03-28 03:05:10 +00:00
`
2024-03-12 04:45:08 +00:00
var xmlScenarios = [ ] yqlib . FormatScenario {
2023-12-05 02:21:12 +00:00
{
skipDoc : true ,
description : "bad xml" ,
input : ` <?xml version="1.0" encoding="UTF-8"?></Child></Root> ` ,
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\n" ,
} ,
2022-11-25 02:21:19 +00:00
{
skipDoc : true ,
2022-11-27 06:46:32 +00:00
input : " <root>value<!-- comment--> </root>" ,
2022-11-25 02:21:19 +00:00
expected : "root: value # comment\n" ,
} ,
2022-11-27 06:46:32 +00:00
{
skipDoc : true ,
input : "value<root>value</root>" ,
expectedError : "bad file 'sample.yml': invalid XML: Encountered chardata [value] outside of XML node" ,
scenarioType : "decode-error" ,
} ,
2022-11-27 06:29:27 +00:00
{
skipDoc : true ,
2022-11-27 08:14:41 +00:00
input : "<root><!-- comment-->value</root>" ,
expected : "# comment\nroot: value\n" ,
2022-11-27 06:29:27 +00:00
} ,
2022-11-27 06:58:37 +00:00
{
skipDoc : true ,
input : "<root> <!-- comment--></root>" ,
expected : "root: # comment\n" ,
} ,
2022-11-27 06:29:27 +00:00
{
skipDoc : true ,
input : "<root>value<!-- comment-->anotherValue </root>" ,
expected : "root:\n # comment\n - value\n - anotherValue\n" ,
} ,
2022-12-16 23:27:07 +00:00
{
skipDoc : true ,
input : "<root><cats><cat>quick</cat><cat>soft</cat><!-- kitty_comment--><cat>squishy</cat></cats></root>" ,
expected : "root:\n cats:\n cat:\n - quick\n - soft\n # kitty_comment\n\n - squishy\n" ,
} ,
2023-03-01 23:57:54 +00:00
{
description : "ProcInst with head comment" ,
skipDoc : true ,
input : yamlInputWithProcInstAndHeadComment ,
expected : expectedXmlProcInstAndHeadComment ,
scenarioType : "encode" ,
} ,
2023-03-27 02:54:24 +00:00
{
description : "Scalar roundtrip" ,
skipDoc : true ,
input : "<mike>cat</mike>" ,
expression : ".mike" ,
expected : "cat" ,
scenarioType : "roundtrip" ,
} ,
2023-03-01 23:57:54 +00:00
{
description : "ProcInst with head comment round trip" ,
skipDoc : true ,
input : expectedXmlProcInstAndHeadComment ,
expected : expectedXmlProcInstAndHeadComment ,
scenarioType : "roundtrip" ,
} ,
{
description : "ProcInst with block head comment to yaml" ,
skipDoc : true ,
input : xmlProcInstAndHeadCommentBlock ,
expected : expectedYamlProcInstAndHeadCommentBlock ,
scenarioType : "decode" ,
} ,
{
description : "ProcInst with block head comment from yaml" ,
skipDoc : true ,
input : expectedYamlProcInstAndHeadCommentBlock ,
expected : xmlProcInstAndHeadCommentBlock ,
scenarioType : "encode" ,
} ,
{
description : "ProcInst with head comment round trip block" ,
skipDoc : true ,
input : xmlProcInstAndHeadCommentBlock ,
expected : xmlProcInstAndHeadCommentBlock ,
scenarioType : "roundtrip" ,
} ,
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-10-23 23:09:42 +00:00
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\ncat:\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-10-23 23:09:42 +00:00
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\ncat:\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-10-23 23:09:42 +00:00
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\nanimal:\n - cat\n - goat\n" ,
2021-12-21 04:02:07 +00:00
} ,
2023-09-01 01:52:58 +00:00
{
description : "Parse xml: force as an array" ,
subdescription : "In XML, if your array has a single item, then yq doesn't know its an array. This is how you can consistently force it to be an array. This handles the 3 scenarios of having nothing in the array, having a single item and having multiple." ,
input : "<zoo><animal>cat</animal></zoo>" ,
expression : ".zoo.animal |= ([] + .)" ,
expected : "zoo:\n animal:\n - cat\n" ,
} ,
2023-09-26 04:43:08 +00:00
{
2023-10-18 01:11:53 +00:00
description : "Parse xml: force all as an array" ,
input : "<zoo><thing><frog>boing</frog></thing></zoo>" ,
expression : ".. |= [] + ." ,
expected : "- zoo:\n - thing:\n - frog:\n - boing\n" ,
2023-09-26 04:43:08 +00:00
} ,
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-11-10 11:22:55 +00:00
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\ncat:\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-11-10 11:22:55 +00:00
expected : "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\ncat:\n +content: meow\n +@legs: \"4\"\n" ,
2021-12-21 04:02:07 +00:00
} ,
2022-11-27 06:29:27 +00:00
{
description : "Parse xml: content split between comments/children" ,
subdescription : "Multiple content texts are collected into a sequence." ,
input : "<root> value <!-- comment-->anotherValue <a>frog</a> cool!</root>" ,
expected : "root:\n +content: # comment\n - value\n - anotherValue\n - cool!\n a: frog\n" ,
} ,
2022-03-28 03:05:10 +00:00
{
description : "Parse xml: custom dtd" ,
2022-10-23 23:09:42 +00:00
subdescription : "DTD entities are processed as directives." ,
2022-03-28 03:05:10 +00:00
input : xmlWithCustomDtd ,
expected : expectedDtd ,
2022-10-23 23:09:42 +00:00
scenarioType : "roundtrip" ,
} ,
2022-11-13 00:13:05 +00:00
{
description : "Roundtrip with name spaced attributes" ,
skipDoc : true ,
input : inputXMLWithNamespacedAttr ,
expected : inputXMLWithNamespacedAttr ,
scenarioType : "roundtrip" ,
} ,
2022-10-23 23:09:42 +00:00
{
description : "Parse xml: skip custom dtd" ,
subdescription : "DTDs are directives, skip over directives to skip DTDs." ,
input : xmlWithCustomDtd ,
expected : expectedSkippedDtd ,
scenarioType : "roundtrip-skip-directives" ,
2022-03-28 03:05:10 +00:00
} ,
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
{
2022-11-13 00:13:05 +00:00
description : "Parse xml: keep attribute namespace" ,
subdescription : fmt . Sprintf ( ` Defaults to %v ` , ConfiguredXMLPreferences . KeepNamespace ) ,
skipDoc : false ,
input : inputXMLWithNamespacedAttr ,
expected : expectedYAMLWithNamespacedAttr ,
scenarioType : "decode-keep-ns" ,
2022-06-14 23:40:31 +00:00
} ,
{
description : "Parse xml: keep raw attribute namespace" ,
2022-11-13 00:13:05 +00:00
skipDoc : true ,
2022-06-14 23:40:31 +00:00
input : inputXMLWithNamespacedAttr ,
expected : expectedYAMLWithRawNamespacedAttr ,
scenarioType : "decode-raw-token" ,
} ,
2022-11-13 00:13:05 +00:00
{
description : "Parse xml: keep raw attribute namespace" ,
subdescription : fmt . Sprintf ( ` Defaults to %v ` , ConfiguredXMLPreferences . UseRawToken ) ,
skipDoc : false ,
input : inputXMLWithNamespacedAttr ,
expected : expectedYAMLWithoutRawNamespacedAttr ,
scenarioType : "decode-raw-token-off" ,
} ,
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-07-29 00:26:50 +00:00
{
description : "includes map tags" ,
skipDoc : true ,
input : "<cat>purrs</cat>\n" ,
expression : ` tag ` ,
expected : "!!map\n" ,
scenarioType : "decode" ,
} ,
{
description : "includes array tags" ,
skipDoc : true ,
input : "<cat>purrs</cat><cat>purrs</cat>\n" ,
expression : ` .cat | tag ` ,
expected : "!!seq\n" ,
scenarioType : "decode" ,
} ,
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." ,
2022-11-10 11:22:55 +00:00
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-10-23 23:09:42 +00:00
description : "double prefix" ,
2022-01-15 00:57:59 +00:00
skipDoc : true ,
2022-11-10 11:22:55 +00:00
input : "cat:\n +@+@name: tiger\n meows: true\n" ,
2022-10-23 23:09:42 +00:00
expected : "<cat +@name=\"tiger\">\n <meows>true</meows>\n</cat>\n" ,
2022-01-15 00:57:59 +00:00
scenarioType : "encode" ,
2021-12-21 05:08:37 +00:00
} ,
2022-11-02 10:41:39 +00:00
{
description : "arrays cannot be encoded" ,
skipDoc : true ,
input : "[cat, dog, fish]" ,
expectedError : "cannot encode !!seq to XML - only maps can be encoded" ,
scenarioType : "encode-error" ,
} ,
{
description : "arrays cannot be encoded - 2" ,
skipDoc : true ,
input : "[cat, dog]" ,
expectedError : "cannot encode !!seq to XML - only maps can be encoded" ,
scenarioType : "encode-error" ,
} ,
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." ,
2022-11-10 11:22:55 +00:00
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" ,
} ,
2023-03-01 23:57:54 +00:00
{
description : "round trip multiline 1" ,
skipDoc : true ,
input : "<x><!-- cats --></x>\n" ,
expected : "<x><!-- cats --></x>\n" ,
scenarioType : "roundtrip" ,
} ,
{
description : "round trip multiline 2" ,
skipDoc : true ,
input : "<x><!--\n cats\n --></x>\n" ,
expected : "<x><!--\ncats\n--></x>\n" ,
scenarioType : "roundtrip" ,
} ,
{
description : "round trip multiline 3" ,
skipDoc : true ,
input : "<x><!--\n\tcats\n --></x>\n" ,
expected : "<x><!--\n\tcats\n--></x>\n" ,
scenarioType : "roundtrip" ,
} ,
{
description : "round trip multiline 4" ,
skipDoc : true ,
input : "<x><!--\n\tcats\n\tdogs\n--></x>\n" ,
expected : "<x><!--\n\tcats\n\tdogs\n--></x>\n" ,
scenarioType : "roundtrip" ,
} ,
{
description : "round trip multiline 5" ,
skipDoc : true , // pity spaces aren't kept atm.
input : "<x><!--\ncats\ndogs\n--></x>\n" ,
expected : "<x><!--\ncats\ndogs\n--></x>\n" ,
scenarioType : "roundtrip" ,
} ,
2022-01-15 00:57:59 +00:00
{
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" ,
} ,
2022-10-23 23:09:42 +00:00
{
description : "Encode: doctype and xml declaration" ,
subdescription : "Use the special xml names to add/modify proc instructions and directives." ,
input : yamlWithProcInstAndDirectives ,
expected : expectedXmlWithProcInstAndDirectives ,
scenarioType : "encode" ,
} ,
2022-01-15 00:57:59 +00:00
{
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
} ,
2022-10-23 23:09:42 +00:00
{
description : "Roundtrip: with doctype and declaration" ,
subdescription : "yq parses XML proc instructions and directives into nodes.\nUnfortunately the underlying XML parser loses whitespace information." ,
input : xmlWithProcInstAndDirectives ,
expected : expectedXmlWithProcInstAndDirectives ,
scenarioType : "roundtrip" ,
} ,
2021-12-21 04:02:07 +00:00
}
2024-03-12 04:45:08 +00:00
func testXMLScenario ( t * testing . T , s yqlib . FormatScenario ) {
2022-06-14 23:40:31 +00:00
switch s . scenarioType {
2022-06-25 02:22:03 +00:00
case "" , "decode" :
2024-03-12 04:45:08 +00:00
yamlPrefs := yqlib . ConfiguredYamlPreferences . Copy ( )
2024-02-24 04:36:16 +00:00
yamlPrefs . Indent = 4
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( ConfiguredXMLPreferences ) , NewYamlEncoder ( yamlPrefs ) ) , s . description )
2022-06-14 23:40:31 +00:00
case "encode" :
2024-02-24 04:03:30 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewYamlDecoder ( ConfiguredYamlPreferences ) , NewXMLEncoder ( ConfiguredXMLPreferences ) ) , s . description )
2022-06-14 23:40:31 +00:00
case "roundtrip" :
2024-02-24 04:03:30 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( ConfiguredXMLPreferences ) , NewXMLEncoder ( ConfiguredXMLPreferences ) ) , s . description )
2022-06-14 23:40:31 +00:00
case "decode-keep-ns" :
2022-10-23 23:09:42 +00:00
prefs := NewDefaultXmlPreferences ( )
prefs . KeepNamespace = true
2024-02-24 04:36:16 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewYamlEncoder ( ConfiguredYamlPreferences ) ) , s . description )
2022-06-14 23:40:31 +00:00
case "decode-raw-token" :
2022-10-23 23:09:42 +00:00
prefs := NewDefaultXmlPreferences ( )
prefs . UseRawToken = true
2024-02-24 04:36:16 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewYamlEncoder ( ConfiguredYamlPreferences ) ) , s . description )
2022-11-13 00:13:05 +00:00
case "decode-raw-token-off" :
prefs := NewDefaultXmlPreferences ( )
prefs . UseRawToken = false
2024-02-24 04:36:16 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewYamlEncoder ( ConfiguredYamlPreferences ) ) , s . description )
2022-10-23 23:09:42 +00:00
case "roundtrip-skip-directives" :
prefs := NewDefaultXmlPreferences ( )
prefs . SkipDirectives = true
2024-02-24 04:03:30 +00:00
test . AssertResultWithContext ( t , s . expected , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewXMLEncoder ( prefs ) ) , s . description )
2022-11-27 06:46:32 +00:00
case "decode-error" :
2024-02-24 04:36:16 +00:00
result , err := processFormatScenario ( s , NewXMLDecoder ( NewDefaultXmlPreferences ( ) ) , NewYamlEncoder ( ConfiguredYamlPreferences ) )
2022-11-27 06:46:32 +00:00
if err == nil {
t . Errorf ( "Expected error '%v' but it worked: %v" , s . expectedError , result )
} else {
test . AssertResultComplexWithContext ( t , s . expectedError , err . Error ( ) , s . description )
}
2022-11-02 10:41:39 +00:00
case "encode-error" :
2024-02-24 04:03:30 +00:00
result , err := processFormatScenario ( s , NewYamlDecoder ( ConfiguredYamlPreferences ) , NewXMLEncoder ( NewDefaultXmlPreferences ( ) ) )
2022-11-02 10:41:39 +00:00
if err == nil {
t . Errorf ( "Expected error '%v' but it worked: %v" , s . expectedError , result )
} else {
test . AssertResultComplexWithContext ( t , s . expectedError , err . Error ( ) , s . description )
}
2022-06-14 23:40:31 +00:00
default :
2022-06-25 02:22:03 +00:00
panic ( fmt . Sprintf ( "unhandled scenario type %q" , s . scenarioType ) )
2021-12-21 04:56:08 +00:00
}
2021-12-21 04:02:07 +00:00
}
2024-01-11 02:17:34 +00:00
func documentXMLScenario ( _ * testing . T , w * bufio . Writer , i interface { } ) {
2024-03-12 04:45:08 +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 {
2022-06-25 02:22:03 +00:00
case "" , "decode" :
documentXMLDecodeScenario ( w , s )
2022-06-14 23:40:31 +00:00
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 )
2022-11-13 00:13:05 +00:00
case "decode-raw-token-off" :
2022-06-14 23:40:31 +00:00
documentXMLDecodeKeepNsRawTokenScenario ( w , s )
2022-10-23 23:09:42 +00:00
case "roundtrip-skip-directives" :
2023-09-18 23:52:36 +00:00
documentXMLSkipDirectivesScenario ( w , s )
2022-06-25 02:22:03 +00:00
2022-06-14 23:40:31 +00:00
default :
2022-06-25 02:22:03 +00:00
panic ( fmt . Sprintf ( "unhandled scenario type %q" , s . scenarioType ) )
2021-12-21 04:56:08 +00:00
}
}
2024-03-12 04:45:08 +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 = "."
}
2023-09-01 01:52:58 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```bash\nyq -oy '%v' sample.xml\n```\n" , expression ) )
2021-12-21 04:02:07 +00:00
writeOrPanic ( w , "will output\n" )
2024-02-24 04:36:16 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```yaml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( ConfiguredXMLPreferences ) , NewYamlEncoder ( ConfiguredYamlPreferences ) ) ) )
2022-06-14 23:40:31 +00:00
}
2024-03-12 04:45:08 +00:00
func documentXMLDecodeKeepNsScenario ( w * bufio . Writer , s FormatScenario ) {
2022-06-14 23:40:31 +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" )
2023-09-01 01:52:58 +00:00
writeOrPanic ( w , "```bash\nyq --xml-keep-namespace=false '.' sample.xml\n```\n" )
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , "will output\n" )
2022-10-23 23:09:42 +00:00
prefs := NewDefaultXmlPreferences ( )
2022-11-13 00:13:05 +00:00
prefs . KeepNamespace = false
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewXMLEncoder ( prefs ) ) ) )
2022-06-14 23:40:31 +00:00
2022-10-23 23:09:42 +00:00
prefsWithout := NewDefaultXmlPreferences ( )
2022-11-13 00:13:05 +00:00
prefs . KeepNamespace = true
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , "instead of\n" )
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( prefsWithout ) , NewXMLEncoder ( prefsWithout ) ) ) )
2022-06-14 23:40:31 +00:00
}
2024-03-12 04:45:08 +00:00
func documentXMLDecodeKeepNsRawTokenScenario ( w * bufio . Writer , s FormatScenario ) {
2022-06-14 23:40:31 +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" )
2023-09-01 01:52:58 +00:00
writeOrPanic ( w , "```bash\nyq --xml-raw-token=false '.' sample.xml\n```\n" )
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , "will output\n" )
2022-10-23 23:09:42 +00:00
prefs := NewDefaultXmlPreferences ( )
2022-11-13 00:13:05 +00:00
prefs . UseRawToken = false
2022-10-23 23:09:42 +00:00
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewXMLEncoder ( prefs ) ) ) )
2022-10-23 23:09:42 +00:00
prefsWithout := NewDefaultXmlPreferences ( )
2022-11-13 00:13:05 +00:00
prefsWithout . UseRawToken = true
2022-06-14 23:40:31 +00:00
writeOrPanic ( w , "instead of\n" )
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( prefsWithout ) , NewXMLEncoder ( prefsWithout ) ) ) )
2021-12-21 04:56:08 +00:00
}
2024-03-12 04:45:08 +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" )
2024-02-09 02:54:27 +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
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewYamlDecoder ( ConfiguredYamlPreferences ) , NewXMLEncoder ( ConfiguredXMLPreferences ) ) ) )
2022-01-15 00:57:59 +00:00
}
2024-03-12 04:45:08 +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" )
2023-09-01 01:52:58 +00:00
writeOrPanic ( w , "```bash\nyq '.' sample.xml\n```\n" )
2022-01-15 00:57:59 +00:00
writeOrPanic ( w , "will output\n" )
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( ConfiguredXMLPreferences ) , NewXMLEncoder ( ConfiguredXMLPreferences ) ) ) )
2022-10-23 23:09:42 +00:00
}
2024-03-12 04:45:08 +00:00
func documentXMLSkipDirectivesScenario ( w * bufio . Writer , s FormatScenario ) {
2022-10-23 23:09:42 +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" )
2023-09-01 01:52:58 +00:00
writeOrPanic ( w , "```bash\nyq --xml-skip-directives '.' sample.xml\n```\n" )
2022-10-23 23:09:42 +00:00
writeOrPanic ( w , "will output\n" )
prefs := NewDefaultXmlPreferences ( )
prefs . SkipDirectives = true
2024-02-24 04:03:30 +00:00
writeOrPanic ( w , fmt . Sprintf ( "```xml\n%v```\n\n" , mustProcessFormatScenario ( s , NewXMLDecoder ( prefs ) , NewXMLEncoder ( prefs ) ) ) )
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
}