Repository: tableauio/tableau
Pull request #148 (2024-09-25 06:55:10)

xml: add unittest cases for patch
1. fix a bug where message name is not correct on list of struct in xml
2. add unittest cases for patch

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/148?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 64.77%. Comparing base [(`cc3971a`)](https://app.codecov.io/gh/tableauio/tableau/commit/cc3971a1530cd833c4e031ee74b6874bf8495468?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`2555c42`)](https://app.codecov.io/gh/tableauio/tableau/commit/2555c4256572249cfd45cfd7ef93a38a2d19ad14?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #148      +/-   ##
==========================================
+ Coverage   64.74%   64.77%   +0.02%     
==========================================
  Files          69       69              
  Lines        7597     7597              
==========================================
+ Hits         4919     4921       +2     
+ Misses       2102     2101       -1     
+ Partials      576      575       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tableauio/tableau/pull/148?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).


Patch:
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index 59613b5..2cca61d 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -428,7 +428,7 @@ func parseXMLAttribute(bnode *book.Node, attrName, attrValue string, isFirstAttr
 			if isFirstAttr {
 				bnode.Children = append(bnode.Children, &book.Node{
 					Name:  book.KeywordType,
-					Value: fmt.Sprintf("[%s]", bnode.Name),
+					Value: fmt.Sprintf("[%s]", desc.ElemType),
 				}, &book.Node{
 					Name:  book.KeywordVariable,
 					Value: fmt.Sprintf("%sList", bnode.Name),


Test patch:
diff --git a/test/functest/conf/base/XmlPatchConf.json b/test/functest/conf/base/XmlPatchConf.json
new file mode 100644
index 0000000..c48d573
--- /dev/null
+++ b/test/functest/conf/base/XmlPatchConf.json
@@ -0,0 +1,46 @@
+{
+    "env": "base",
+    "strictStruct": {
+        "id": 1,
+        "name": "apple",
+        "start": "2024-10-01T02:10:10Z",
+        "expiry": "3600s"
+    },
+    "scalarListList": [
+        1,
+        2,
+        3
+    ],
+    "structListList": [
+        {
+            "id": 1,
+            "name": "dog"
+        },
+        {
+            "id": 2,
+            "name": "bird"
+        }
+    ],
+    "scalarMapMap": {
+        "1": "dog",
+        "2": "bird",
+        "3": "cat"
+    },
+    "structMapMap": {
+        "1": {
+            "key": 1,
+            "name": "apple",
+            "num": 10
+        },
+        "2": {
+            "key": 2,
+            "name": "orange",
+            "num": 20
+        },
+        "3": {
+            "key": 3,
+            "name": "banana",
+            "num": 30
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/dev/XmlPatchConf.json b/test/functest/conf/dev/XmlPatchConf.json
new file mode 100644
index 0000000..8e052d6
--- /dev/null
+++ b/test/functest/conf/dev/XmlPatchConf.json
@@ -0,0 +1,7 @@
+{
+    "env": "base",
+    "strictStruct": {
+        "start": "2026-09-30T22:06:06Z",
+        "expiry": "7200s"
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/prod/XmlPatchConf.json b/test/functest/conf/prod/XmlPatchConf.json
new file mode 100644
index 0000000..260b92b
--- /dev/null
+++ b/test/functest/conf/prod/XmlPatchConf.json
@@ -0,0 +1,23 @@
+{
+    "env": "prod",
+    "scalarMapMap": {
+        "10": "snake",
+        "20": "python"
+    },
+    "structMapMap": {
+        "1": {
+            "key": 1,
+            "num": 100
+        },
+        "20": {
+            "key": 20,
+            "name": "pineapple",
+            "num": 200
+        },
+        "30": {
+            "key": 30,
+            "name": "durian",
+            "num": 300
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/test/XmlPatchConf.json b/test/functest/conf/test/XmlPatchConf.json
new file mode 100644
index 0000000..62422d3
--- /dev/null
+++ b/test/functest/conf/test/XmlPatchConf.json
@@ -0,0 +1,18 @@
+{
+    "env": "test",
+    "scalarListList": [
+        10,
+        20,
+        30
+    ],
+    "structListList": [
+        {
+            "id": 10,
+            "name": "snake"
+        },
+        {
+            "id": 20,
+            "name": "python"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/test/functest/proto/xml__patch__base__env.proto b/test/functest/proto/xml__patch__base__env.proto
new file mode 100644
index 0000000..aa4cfa7
--- /dev/null
+++ b/test/functest/proto/xml__patch__base__env.proto
@@ -0,0 +1,39 @@
+// Code generated by tableau (protogen v0.6.1). DO NOT EDIT.
+// clang-format off
+
+syntax = "proto3";
+
+package protoconf;
+
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"xml/patch/base/Env.xml"};
+
+message XmlPatchConf {
+  option (tableau.worksheet) = {name:"XmlPatchConf" scatter:"../overlays/*/Env.xml" optional:true patch:PATCH_MERGE with_parent_dir:true scatter_without_book_name:true};
+
+  string env = 1 [(tableau.field) = {name:"Env"}];
+  Fruit strict_struct = 2 [(tableau.field) = {name:"StrictStruct"}];
+  message Fruit {
+    uint32 id = 1 [(tableau.field) = {name:"ID"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    google.protobuf.Timestamp start = 3 [(tableau.field) = {name:"Start"}];
+    google.protobuf.Duration expiry = 4 [(tableau.field) = {name:"Expiry"}];
+  }
+  repeated int32 scalar_list_list = 3 [(tableau.field) = {name:"ScalarList" prop:{patch:PATCH_REPLACE}}];
+  repeated Animal struct_list_list = 4 [(tableau.field) = {name:"StructList"}];
+  message Animal {
+    uint32 id = 1 [(tableau.field) = {name:"ID"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+  }
+  map<uint32, string> scalar_map_map = 5 [(tableau.field) = {name:"ScalarMap" layout:LAYOUT_INCELL prop:{patch:PATCH_REPLACE}}];
+  map<uint32, Item> struct_map_map = 6 [(tableau.field) = {name:"StructMap" key:"key"}];
+  message Item {
+    uint32 key = 1 [(tableau.field) = {name:"key"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    int32 num = 3 [(tableau.field) = {name:"Num"}];
+  }
+}
diff --git a/test/functest/testdata/xml/patch/base/Env.xml b/test/functest/testdata/xml/patch/base/Env.xml
new file mode 100644
index 0000000..5517b50
--- /dev/null
+++ b/test/functest/testdata/xml/patch/base/Env.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+<@TABLEAU>
+  <Item Sheet="XmlPatchConf" Patch="PATCH_MERGE" Optional="true" Scatter="../overlays/*/Env.xml" ScatterWithoutBookName="true" WithParentDir="true" />
+</@TABLEAU>
+<XmlPatchConf>
+  <Env>string</Env>
+  <StrictStruct ID="{Fruit}uint32" Name="string" Start="datetime" Expiry="duration" />
+  <ScalarList>[int32]|{patch:PATCH_REPLACE}</ScalarList>
+  <StructList ID="[Animal]uint32" Name="string" />
+  <ScalarMap>map<uint32, string>|{patch:PATCH_REPLACE}</ScalarMap>
+  <StructMap key="map<uint32, Item>" Name="string" Num="int32" />
+</XmlPatchConf>
+-->
+
+<XmlPatchConf>
+  <Env>base</Env>
+  <StrictStruct ID="1" Name="apple" Start="2024-10-01 10:10:10" Expiry="1h" />
+  <ScalarList>1</ScalarList>
+  <ScalarList>2</ScalarList>
+  <ScalarList>3</ScalarList>
+  <StructList ID="1" Name="dog" />
+  <StructList ID="2" Name="bird" />
+  <ScalarMap>1:dog,2:bird,3:cat</ScalarMap>
+  <StructMap key="1" Name="apple" Num="10" />
+  <StructMap key="2" Name="orange" Num="20" />
+  <StructMap key="3" Name="banana" Num="30" />
+</XmlPatchConf>
diff --git a/test/functest/testdata/xml/patch/overlays/dev/Env.xml b/test/functest/testdata/xml/patch/overlays/dev/Env.xml
new file mode 100644
index 0000000..6b68dca
--- /dev/null
+++ b/test/functest/testdata/xml/patch/overlays/dev/Env.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XmlPatchConf>
+  <Env>base</Env>
+  <StrictStruct ID="0" Name="" Start="2026-10-01 06:06:06" Expiry="2h" />
+</XmlPatchConf>
diff --git a/test/functest/testdata/xml/patch/overlays/prod/Env.xml b/test/functest/testdata/xml/patch/overlays/prod/Env.xml
new file mode 100644
index 0000000..f3eed7a
--- /dev/null
+++ b/test/functest/testdata/xml/patch/overlays/prod/Env.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XmlPatchConf>
+  <Env>prod</Env>
+  <ScalarMap>10:snake,20:python</ScalarMap>
+  <StructMap key="1" Num="100" />
+  <StructMap key="20" Name="pineapple" Num="200" />
+  <StructMap key="30" Name="durian" Num="300" />
+</XmlPatchConf>
diff --git a/test/functest/testdata/xml/patch/overlays/test/Env.xml b/test/functest/testdata/xml/patch/overlays/test/Env.xml
new file mode 100644
index 0000000..aa939c3
--- /dev/null
+++ b/test/functest/testdata/xml/patch/overlays/test/Env.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XmlPatchConf>
+  <Env>test</Env>
+  <ScalarList>10</ScalarList>
+  <ScalarList>20</ScalarList>
+  <ScalarList>30</ScalarList>
+  <StructList ID="10" Name="snake" />
+  <StructList ID="20" Name="python" />
+</XmlPatchConf>