github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/parser/metadecoders/decoder_test.go (about) 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 []byte 120 format Format 121 expect any 122 }{ 123 {[]byte(`[ "Brecker", "Blake", "Redman" ]`), JSON, []any{"Brecker", "Blake", "Redman"}}, 124 {[]byte(`{ "a": "b" }`), JSON, expect}, 125 {[]byte(``), JSON, map[string]any{}}, 126 {[]byte(nil), JSON, map[string]any{}}, 127 {[]byte(`#+a: b`), ORG, expect}, 128 {[]byte(`#+DATE: <2020-06-26 Fri>`), ORG, map[string]any{"date": "2020-06-26"}}, 129 {[]byte(`a = "b"`), TOML, expect}, 130 {[]byte(`a: "b"`), YAML, expect}, 131 {[]byte(`<root><a>b</a></root>`), XML, expect}, 132 {[]byte(`a,b,c`), CSV, [][]string{{"a", "b", "c"}}}, 133 {[]byte("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}}}}, 134 // errors 135 {[]byte(`a = "`), TOML, false}, 136 } { 137 msg := qt.Commentf("%d: %s", i, test.format) 138 m, err := d.Unmarshal(test.data, test.format) 139 if b, ok := test.expect.(bool); ok && !b { 140 c.Assert(err, qt.Not(qt.IsNil), msg) 141 } else { 142 c.Assert(err, qt.IsNil, msg) 143 c.Assert(m, qt.DeepEquals, test.expect, msg) 144 } 145 146 } 147 } 148 149 func TestUnmarshalStringTo(t *testing.T) { 150 c := qt.New(t) 151 152 d := Default 153 154 expectMap := map[string]any{"a": "b"} 155 156 for i, test := range []struct { 157 data string 158 to any 159 expect any 160 }{ 161 {"a string", "string", "a string"}, 162 {`{ "a": "b" }`, make(map[string]any), expectMap}, 163 {"32", int64(1234), int64(32)}, 164 {"32", int(1234), int(32)}, 165 {"3.14159", float64(1), float64(3.14159)}, 166 {"[3,7,9]", []any{}, []any{3, 7, 9}}, 167 {"[3.1,7.2,9.3]", []any{}, []any{3.1, 7.2, 9.3}}, 168 } { 169 msg := qt.Commentf("%d: %T", i, test.to) 170 m, err := d.UnmarshalStringTo(test.data, test.to) 171 if b, ok := test.expect.(bool); ok && !b { 172 c.Assert(err, qt.Not(qt.IsNil), msg) 173 } else { 174 c.Assert(err, qt.IsNil, msg) 175 c.Assert(m, qt.DeepEquals, test.expect, msg) 176 } 177 178 } 179 } 180 181 func TestStringifyYAMLMapKeys(t *testing.T) { 182 cases := []struct { 183 input any 184 want any 185 replaced bool 186 }{ 187 { 188 map[any]any{"a": 1, "b": 2}, 189 map[string]any{"a": 1, "b": 2}, 190 true, 191 }, 192 { 193 map[any]any{"a": []any{1, map[any]any{"b": 2}}}, 194 map[string]any{"a": []any{1, map[string]any{"b": 2}}}, 195 true, 196 }, 197 { 198 map[any]any{true: 1, "b": false}, 199 map[string]any{"true": 1, "b": false}, 200 true, 201 }, 202 { 203 map[any]any{1: "a", 2: "b"}, 204 map[string]any{"1": "a", "2": "b"}, 205 true, 206 }, 207 { 208 map[any]any{"a": map[any]any{"b": 1}}, 209 map[string]any{"a": map[string]any{"b": 1}}, 210 true, 211 }, 212 { 213 map[string]any{"a": map[string]any{"b": 1}}, 214 map[string]any{"a": map[string]any{"b": 1}}, 215 false, 216 }, 217 { 218 []any{map[any]any{1: "a", 2: "b"}}, 219 []any{map[string]any{"1": "a", "2": "b"}}, 220 false, 221 }, 222 } 223 224 for i, c := range cases { 225 res, replaced := stringifyMapKeys(c.input) 226 227 if c.replaced != replaced { 228 t.Fatalf("[%d] Replaced mismatch: %t", i, replaced) 229 } 230 if !c.replaced { 231 res = c.input 232 } 233 if !reflect.DeepEqual(res, c.want) { 234 t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res) 235 } 236 } 237 } 238 239 func BenchmarkStringifyMapKeysStringsOnlyInterfaceMaps(b *testing.B) { 240 maps := make([]map[any]any, b.N) 241 for i := 0; i < b.N; i++ { 242 maps[i] = map[any]any{ 243 "a": map[any]any{ 244 "b": 32, 245 "c": 43, 246 "d": map[any]any{ 247 "b": 32, 248 "c": 43, 249 }, 250 }, 251 "b": []any{"a", "b"}, 252 "c": "d", 253 } 254 } 255 b.ResetTimer() 256 for i := 0; i < b.N; i++ { 257 stringifyMapKeys(maps[i]) 258 } 259 } 260 261 func BenchmarkStringifyMapKeysStringsOnlyStringMaps(b *testing.B) { 262 m := map[string]any{ 263 "a": map[string]any{ 264 "b": 32, 265 "c": 43, 266 "d": map[string]any{ 267 "b": 32, 268 "c": 43, 269 }, 270 }, 271 "b": []any{"a", "b"}, 272 "c": "d", 273 } 274 275 b.ResetTimer() 276 for i := 0; i < b.N; i++ { 277 stringifyMapKeys(m) 278 } 279 } 280 281 func BenchmarkStringifyMapKeysIntegers(b *testing.B) { 282 maps := make([]map[any]any, b.N) 283 for i := 0; i < b.N; i++ { 284 maps[i] = map[any]any{ 285 1: map[any]any{ 286 4: 32, 287 5: 43, 288 6: map[any]any{ 289 7: 32, 290 8: 43, 291 }, 292 }, 293 2: []any{"a", "b"}, 294 3: "d", 295 } 296 } 297 b.ResetTimer() 298 for i := 0; i < b.N; i++ { 299 stringifyMapKeys(maps[i]) 300 } 301 }