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  }