decoder_test.go (7539B)
1 // Copyright 2018 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package metadecoders
15
16 import (
17 "reflect"
18 "testing"
19
20 qt "github.com/frankban/quicktest"
21 )
22
23 func TestUnmarshalXML(t *testing.T) {
24 c := qt.New(t)
25
26 xmlDoc := `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
27 <rss version="2.0"
28 xmlns:atom="http://www.w3.org/2005/Atom">
29 <channel>
30 <title>Example feed</title>
31 <link>https://example.com/</link>
32 <description>Example feed</description>
33 <generator>Hugo -- gohugo.io</generator>
34 <language>en-us</language>
35 <copyright>Example</copyright>
36 <lastBuildDate>Fri, 08 Jan 2021 14:44:10 +0000</lastBuildDate>
37 <atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>
38 <item>
39 <title>Example title</title>
40 <link>https://example.com/2021/11/30/example-title/</link>
41 <pubDate>Tue, 30 Nov 2021 15:00:00 +0000</pubDate>
42 <guid>https://example.com/2021/11/30/example-title/</guid>
43 <description>Example description</description>
44 </item>
45 </channel>
46 </rss>`
47
48 expect := map[string]any{
49 "-atom": "http://www.w3.org/2005/Atom", "-version": "2.0",
50 "channel": map[string]any{
51 "copyright": "Example",
52 "description": "Example feed",
53 "generator": "Hugo -- gohugo.io",
54 "item": map[string]any{
55 "description": "Example description",
56 "guid": "https://example.com/2021/11/30/example-title/",
57 "link": "https://example.com/2021/11/30/example-title/",
58 "pubDate": "Tue, 30 Nov 2021 15:00:00 +0000",
59 "title": "Example title"},
60 "language": "en-us",
61 "lastBuildDate": "Fri, 08 Jan 2021 14:44:10 +0000",
62 "link": []any{"https://example.com/", map[string]any{
63 "-href": "https://example.com/feed.xml",
64 "-rel": "self",
65 "-type": "application/rss+xml"}},
66 "title": "Example feed",
67 }}
68
69 d := Default
70
71 m, err := d.Unmarshal([]byte(xmlDoc), XML)
72 c.Assert(err, qt.IsNil)
73 c.Assert(m, qt.DeepEquals, expect)
74
75 }
76 func TestUnmarshalToMap(t *testing.T) {
77 c := qt.New(t)
78
79 expect := map[string]any{"a": "b"}
80
81 d := Default
82
83 for i, test := range []struct {
84 data string
85 format Format
86 expect any
87 }{
88 {`a = "b"`, TOML, expect},
89 {`a: "b"`, YAML, expect},
90 // Make sure we get all string keys, even for YAML
91 {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}},
92 {"a:\n true: 1\n false: 2", YAML, map[string]any{"a": map[string]any{"true": 1, "false": 2}}},
93 {`{ "a": "b" }`, JSON, expect},
94 {`<root><a>b</a></root>`, XML, expect},
95 {`#+a: b`, ORG, expect},
96 // errors
97 {`a = b`, TOML, false},
98 {`a,b,c`, CSV, false}, // Use Unmarshal for CSV
99 } {
100 msg := qt.Commentf("%d: %s", i, test.format)
101 m, err := d.UnmarshalToMap([]byte(test.data), test.format)
102 if b, ok := test.expect.(bool); ok && !b {
103 c.Assert(err, qt.Not(qt.IsNil), msg)
104 } else {
105 c.Assert(err, qt.IsNil, msg)
106 c.Assert(m, qt.DeepEquals, test.expect, msg)
107 }
108 }
109 }
110
111 func TestUnmarshalToInterface(t *testing.T) {
112 c := qt.New(t)
113
114 expect := map[string]any{"a": "b"}
115
116 d := Default
117
118 for i, test := range []struct {
119 data string
120 format Format
121 expect any
122 }{
123 {`[ "Brecker", "Blake", "Redman" ]`, JSON, []any{"Brecker", "Blake", "Redman"}},
124 {`{ "a": "b" }`, JSON, expect},
125 {`#+a: b`, ORG, expect},
126 {`#+DATE: <2020-06-26 Fri>`, ORG, map[string]any{"date": "2020-06-26"}},
127 {`a = "b"`, TOML, expect},
128 {`a: "b"`, YAML, expect},
129 {`<root><a>b</a></root>`, XML, expect},
130 {`a,b,c`, CSV, [][]string{{"a", "b", "c"}}},
131 {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}},
132 // errors
133 {`a = "`, TOML, false},
134 } {
135 msg := qt.Commentf("%d: %s", i, test.format)
136 m, err := d.Unmarshal([]byte(test.data), test.format)
137 if b, ok := test.expect.(bool); ok && !b {
138 c.Assert(err, qt.Not(qt.IsNil), msg)
139 } else {
140 c.Assert(err, qt.IsNil, msg)
141 c.Assert(m, qt.DeepEquals, test.expect, msg)
142 }
143
144 }
145 }
146
147 func TestUnmarshalStringTo(t *testing.T) {
148 c := qt.New(t)
149
150 d := Default
151
152 expectMap := map[string]any{"a": "b"}
153
154 for i, test := range []struct {
155 data string
156 to any
157 expect any
158 }{
159 {"a string", "string", "a string"},
160 {`{ "a": "b" }`, make(map[string]any), expectMap},
161 {"32", int64(1234), int64(32)},
162 {"32", int(1234), int(32)},
163 {"3.14159", float64(1), float64(3.14159)},
164 {"[3,7,9]", []any{}, []any{3, 7, 9}},
165 {"[3.1,7.2,9.3]", []any{}, []any{3.1, 7.2, 9.3}},
166 } {
167 msg := qt.Commentf("%d: %T", i, test.to)
168 m, err := d.UnmarshalStringTo(test.data, test.to)
169 if b, ok := test.expect.(bool); ok && !b {
170 c.Assert(err, qt.Not(qt.IsNil), msg)
171 } else {
172 c.Assert(err, qt.IsNil, msg)
173 c.Assert(m, qt.DeepEquals, test.expect, msg)
174 }
175
176 }
177 }
178
179 func TestStringifyYAMLMapKeys(t *testing.T) {
180 cases := []struct {
181 input any
182 want any
183 replaced bool
184 }{
185 {
186 map[any]any{"a": 1, "b": 2},
187 map[string]any{"a": 1, "b": 2},
188 true,
189 },
190 {
191 map[any]any{"a": []any{1, map[any]any{"b": 2}}},
192 map[string]any{"a": []any{1, map[string]any{"b": 2}}},
193 true,
194 },
195 {
196 map[any]any{true: 1, "b": false},
197 map[string]any{"true": 1, "b": false},
198 true,
199 },
200 {
201 map[any]any{1: "a", 2: "b"},
202 map[string]any{"1": "a", "2": "b"},
203 true,
204 },
205 {
206 map[any]any{"a": map[any]any{"b": 1}},
207 map[string]any{"a": map[string]any{"b": 1}},
208 true,
209 },
210 {
211 map[string]any{"a": map[string]any{"b": 1}},
212 map[string]any{"a": map[string]any{"b": 1}},
213 false,
214 },
215 {
216 []any{map[any]any{1: "a", 2: "b"}},
217 []any{map[string]any{"1": "a", "2": "b"}},
218 false,
219 },
220 }
221
222 for i, c := range cases {
223 res, replaced := stringifyMapKeys(c.input)
224
225 if c.replaced != replaced {
226 t.Fatalf("[%d] Replaced mismatch: %t", i, replaced)
227 }
228 if !c.replaced {
229 res = c.input
230 }
231 if !reflect.DeepEqual(res, c.want) {
232 t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res)
233 }
234 }
235 }
236
237 func BenchmarkStringifyMapKeysStringsOnlyInterfaceMaps(b *testing.B) {
238 maps := make([]map[any]any, b.N)
239 for i := 0; i < b.N; i++ {
240 maps[i] = map[any]any{
241 "a": map[any]any{
242 "b": 32,
243 "c": 43,
244 "d": map[any]any{
245 "b": 32,
246 "c": 43,
247 },
248 },
249 "b": []any{"a", "b"},
250 "c": "d",
251 }
252 }
253 b.ResetTimer()
254 for i := 0; i < b.N; i++ {
255 stringifyMapKeys(maps[i])
256 }
257 }
258
259 func BenchmarkStringifyMapKeysStringsOnlyStringMaps(b *testing.B) {
260 m := map[string]any{
261 "a": map[string]any{
262 "b": 32,
263 "c": 43,
264 "d": map[string]any{
265 "b": 32,
266 "c": 43,
267 },
268 },
269 "b": []any{"a", "b"},
270 "c": "d",
271 }
272
273 b.ResetTimer()
274 for i := 0; i < b.N; i++ {
275 stringifyMapKeys(m)
276 }
277 }
278
279 func BenchmarkStringifyMapKeysIntegers(b *testing.B) {
280 maps := make([]map[any]any, b.N)
281 for i := 0; i < b.N; i++ {
282 maps[i] = map[any]any{
283 1: map[any]any{
284 4: 32,
285 5: 43,
286 6: map[any]any{
287 7: 32,
288 8: 43,
289 },
290 },
291 2: []any{"a", "b"},
292 3: "d",
293 }
294 }
295 b.ResetTimer()
296 for i := 0; i < b.N; i++ {
297 stringifyMapKeys(maps[i])
298 }
299 }