mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-30 00:53:28 -05:00
0eaaa8fee3
Example: ``` {{ with resources.Get "https://example.com/rss.xml" | transform.Unmarshal }} {{ range .channel.item }} <strong>{{ .title | plainify | htmlUnescape }}</strong><br /> <p>{{ .description | plainify | htmlUnescape }}</p> {{ $link := .link | plainify | htmlUnescape }} <a href="{{ $link }}">{{ $link }}</a><br /> <hr> {{ end }} {{ end }} ``` Closes #4470
299 lines
8 KiB
Go
299 lines
8 KiB
Go
// Copyright 2018 The Hugo Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package metadecoders
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
qt "github.com/frankban/quicktest"
|
|
)
|
|
|
|
func TestUnmarshalXML(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
xmlDoc := `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
|
<rss version="2.0"
|
|
xmlns:atom="http://www.w3.org/2005/Atom">
|
|
<channel>
|
|
<title>Example feed</title>
|
|
<link>https://example.com/</link>
|
|
<description>Example feed</description>
|
|
<generator>Hugo -- gohugo.io</generator>
|
|
<language>en-us</language>
|
|
<copyright>Example</copyright>
|
|
<lastBuildDate>Fri, 08 Jan 2021 14:44:10 +0000</lastBuildDate>
|
|
<atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>
|
|
<item>
|
|
<title>Example title</title>
|
|
<link>https://example.com/2021/11/30/example-title/</link>
|
|
<pubDate>Tue, 30 Nov 2021 15:00:00 +0000</pubDate>
|
|
<guid>https://example.com/2021/11/30/example-title/</guid>
|
|
<description>Example description</description>
|
|
</item>
|
|
</channel>
|
|
</rss>`
|
|
|
|
expect := map[string]interface{}{
|
|
"-atom": "http://www.w3.org/2005/Atom", "-version": "2.0",
|
|
"channel": map[string]interface{}{
|
|
"copyright": "Example",
|
|
"description": "Example feed",
|
|
"generator": "Hugo -- gohugo.io",
|
|
"item": map[string]interface{}{
|
|
"description": "Example description",
|
|
"guid": "https://example.com/2021/11/30/example-title/",
|
|
"link": "https://example.com/2021/11/30/example-title/",
|
|
"pubDate": "Tue, 30 Nov 2021 15:00:00 +0000",
|
|
"title": "Example title"},
|
|
"language": "en-us",
|
|
"lastBuildDate": "Fri, 08 Jan 2021 14:44:10 +0000",
|
|
"link": []interface{}{"https://example.com/", map[string]interface{}{
|
|
"-href": "https://example.com/feed.xml",
|
|
"-rel": "self",
|
|
"-type": "application/rss+xml"}},
|
|
"title": "Example feed",
|
|
}}
|
|
|
|
d := Default
|
|
|
|
m, err := d.Unmarshal([]byte(xmlDoc), XML)
|
|
c.Assert(err, qt.IsNil)
|
|
c.Assert(m, qt.DeepEquals, expect)
|
|
|
|
}
|
|
func TestUnmarshalToMap(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
expect := map[string]interface{}{"a": "b"}
|
|
|
|
d := Default
|
|
|
|
for i, test := range []struct {
|
|
data string
|
|
format Format
|
|
expect interface{}
|
|
}{
|
|
{`a = "b"`, TOML, expect},
|
|
{`a: "b"`, YAML, expect},
|
|
// Make sure we get all string keys, even for YAML
|
|
{"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]interface{}{"a": "Easy!", "b": map[string]interface{}{"c": 2, "d": []interface{}{3, 4}}}},
|
|
{"a:\n true: 1\n false: 2", YAML, map[string]interface{}{"a": map[string]interface{}{"true": 1, "false": 2}}},
|
|
{`{ "a": "b" }`, JSON, expect},
|
|
{`<root><a>b</a></root>`, XML, expect},
|
|
{`#+a: b`, ORG, expect},
|
|
// errors
|
|
{`a = b`, TOML, false},
|
|
{`a,b,c`, CSV, false}, // Use Unmarshal for CSV
|
|
} {
|
|
msg := qt.Commentf("%d: %s", i, test.format)
|
|
m, err := d.UnmarshalToMap([]byte(test.data), test.format)
|
|
if b, ok := test.expect.(bool); ok && !b {
|
|
c.Assert(err, qt.Not(qt.IsNil), msg)
|
|
} else {
|
|
c.Assert(err, qt.IsNil, msg)
|
|
c.Assert(m, qt.DeepEquals, test.expect, msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalToInterface(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
expect := map[string]interface{}{"a": "b"}
|
|
|
|
d := Default
|
|
|
|
for i, test := range []struct {
|
|
data string
|
|
format Format
|
|
expect interface{}
|
|
}{
|
|
{`[ "Brecker", "Blake", "Redman" ]`, JSON, []interface{}{"Brecker", "Blake", "Redman"}},
|
|
{`{ "a": "b" }`, JSON, expect},
|
|
{`#+a: b`, ORG, expect},
|
|
{`#+DATE: <2020-06-26 Fri>`, ORG, map[string]interface{}{"date": "2020-06-26"}},
|
|
{`a = "b"`, TOML, expect},
|
|
{`a: "b"`, YAML, expect},
|
|
{`<root><a>b</a></root>`, XML, expect},
|
|
{`a,b,c`, CSV, [][]string{{"a", "b", "c"}}},
|
|
{"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]interface{}{"a": "Easy!", "b": map[string]interface{}{"c": 2, "d": []interface{}{3, 4}}}},
|
|
// errors
|
|
{`a = "`, TOML, false},
|
|
} {
|
|
msg := qt.Commentf("%d: %s", i, test.format)
|
|
m, err := d.Unmarshal([]byte(test.data), test.format)
|
|
if b, ok := test.expect.(bool); ok && !b {
|
|
c.Assert(err, qt.Not(qt.IsNil), msg)
|
|
} else {
|
|
c.Assert(err, qt.IsNil, msg)
|
|
c.Assert(m, qt.DeepEquals, test.expect, msg)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalStringTo(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
d := Default
|
|
|
|
expectMap := map[string]interface{}{"a": "b"}
|
|
|
|
for i, test := range []struct {
|
|
data string
|
|
to interface{}
|
|
expect interface{}
|
|
}{
|
|
{"a string", "string", "a string"},
|
|
{`{ "a": "b" }`, make(map[string]interface{}), expectMap},
|
|
{"32", int64(1234), int64(32)},
|
|
{"32", int(1234), int(32)},
|
|
{"3.14159", float64(1), float64(3.14159)},
|
|
{"[3,7,9]", []interface{}{}, []interface{}{3, 7, 9}},
|
|
{"[3.1,7.2,9.3]", []interface{}{}, []interface{}{3.1, 7.2, 9.3}},
|
|
} {
|
|
msg := qt.Commentf("%d: %T", i, test.to)
|
|
m, err := d.UnmarshalStringTo(test.data, test.to)
|
|
if b, ok := test.expect.(bool); ok && !b {
|
|
c.Assert(err, qt.Not(qt.IsNil), msg)
|
|
} else {
|
|
c.Assert(err, qt.IsNil, msg)
|
|
c.Assert(m, qt.DeepEquals, test.expect, msg)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestStringifyYAMLMapKeys(t *testing.T) {
|
|
cases := []struct {
|
|
input interface{}
|
|
want interface{}
|
|
replaced bool
|
|
}{
|
|
{
|
|
map[interface{}]interface{}{"a": 1, "b": 2},
|
|
map[string]interface{}{"a": 1, "b": 2},
|
|
true,
|
|
},
|
|
{
|
|
map[interface{}]interface{}{"a": []interface{}{1, map[interface{}]interface{}{"b": 2}}},
|
|
map[string]interface{}{"a": []interface{}{1, map[string]interface{}{"b": 2}}},
|
|
true,
|
|
},
|
|
{
|
|
map[interface{}]interface{}{true: 1, "b": false},
|
|
map[string]interface{}{"true": 1, "b": false},
|
|
true,
|
|
},
|
|
{
|
|
map[interface{}]interface{}{1: "a", 2: "b"},
|
|
map[string]interface{}{"1": "a", "2": "b"},
|
|
true,
|
|
},
|
|
{
|
|
map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": 1}},
|
|
map[string]interface{}{"a": map[string]interface{}{"b": 1}},
|
|
true,
|
|
},
|
|
{
|
|
map[string]interface{}{"a": map[string]interface{}{"b": 1}},
|
|
map[string]interface{}{"a": map[string]interface{}{"b": 1}},
|
|
false,
|
|
},
|
|
{
|
|
[]interface{}{map[interface{}]interface{}{1: "a", 2: "b"}},
|
|
[]interface{}{map[string]interface{}{"1": "a", "2": "b"}},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for i, c := range cases {
|
|
res, replaced := stringifyMapKeys(c.input)
|
|
|
|
if c.replaced != replaced {
|
|
t.Fatalf("[%d] Replaced mismatch: %t", i, replaced)
|
|
}
|
|
if !c.replaced {
|
|
res = c.input
|
|
}
|
|
if !reflect.DeepEqual(res, c.want) {
|
|
t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkStringifyMapKeysStringsOnlyInterfaceMaps(b *testing.B) {
|
|
maps := make([]map[interface{}]interface{}, b.N)
|
|
for i := 0; i < b.N; i++ {
|
|
maps[i] = map[interface{}]interface{}{
|
|
"a": map[interface{}]interface{}{
|
|
"b": 32,
|
|
"c": 43,
|
|
"d": map[interface{}]interface{}{
|
|
"b": 32,
|
|
"c": 43,
|
|
},
|
|
},
|
|
"b": []interface{}{"a", "b"},
|
|
"c": "d",
|
|
}
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
stringifyMapKeys(maps[i])
|
|
}
|
|
}
|
|
|
|
func BenchmarkStringifyMapKeysStringsOnlyStringMaps(b *testing.B) {
|
|
m := map[string]interface{}{
|
|
"a": map[string]interface{}{
|
|
"b": 32,
|
|
"c": 43,
|
|
"d": map[string]interface{}{
|
|
"b": 32,
|
|
"c": 43,
|
|
},
|
|
},
|
|
"b": []interface{}{"a", "b"},
|
|
"c": "d",
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
stringifyMapKeys(m)
|
|
}
|
|
}
|
|
|
|
func BenchmarkStringifyMapKeysIntegers(b *testing.B) {
|
|
maps := make([]map[interface{}]interface{}, b.N)
|
|
for i := 0; i < b.N; i++ {
|
|
maps[i] = map[interface{}]interface{}{
|
|
1: map[interface{}]interface{}{
|
|
4: 32,
|
|
5: 43,
|
|
6: map[interface{}]interface{}{
|
|
7: 32,
|
|
8: 43,
|
|
},
|
|
},
|
|
2: []interface{}{"a", "b"},
|
|
3: "d",
|
|
}
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
stringifyMapKeys(maps[i])
|
|
}
|
|
}
|