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 }