github.com/rezahousseini/hugo@v0.32.3/parser/frontmatter_test.go (about)

     1  // Copyright 2015 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 parser
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"reflect"
    20  	"strings"
    21  	"testing"
    22  )
    23  
    24  func TestInterfaceToConfig(t *testing.T) {
    25  	cases := []struct {
    26  		input interface{}
    27  		mark  byte
    28  		want  []byte
    29  		isErr bool
    30  	}{
    31  		// TOML
    32  		{map[string]interface{}{}, TOMLLead[0], nil, false},
    33  		{
    34  			map[string]interface{}{"title": "test 1"},
    35  			TOMLLead[0],
    36  			[]byte("title = \"test 1\"\n"),
    37  			false,
    38  		},
    39  
    40  		// YAML
    41  		{map[string]interface{}{}, YAMLLead[0], []byte("{}\n"), false},
    42  		{
    43  			map[string]interface{}{"title": "test 1"},
    44  			YAMLLead[0],
    45  			[]byte("title: test 1\n"),
    46  			false,
    47  		},
    48  
    49  		// JSON
    50  		{map[string]interface{}{}, JSONLead[0], []byte("{}\n"), false},
    51  		{
    52  			map[string]interface{}{"title": "test 1"},
    53  			JSONLead[0],
    54  			[]byte("{\n   \"title\": \"test 1\"\n}\n"),
    55  			false,
    56  		},
    57  
    58  		// Errors
    59  		{nil, TOMLLead[0], nil, true},
    60  		{map[string]interface{}{}, '$', nil, true},
    61  	}
    62  
    63  	for i, c := range cases {
    64  		var buf bytes.Buffer
    65  
    66  		err := InterfaceToConfig(c.input, rune(c.mark), &buf)
    67  		if err != nil {
    68  			if c.isErr {
    69  				continue
    70  			}
    71  			t.Fatalf("[%d] unexpected error value: %v", i, err)
    72  		}
    73  
    74  		if !reflect.DeepEqual(buf.Bytes(), c.want) {
    75  			t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes())
    76  		}
    77  	}
    78  }
    79  
    80  func TestInterfaceToFrontMatter(t *testing.T) {
    81  	cases := []struct {
    82  		input interface{}
    83  		mark  rune
    84  		want  []byte
    85  		isErr bool
    86  	}{
    87  		// TOML
    88  		{map[string]interface{}{}, '+', []byte("+++\n\n+++\n"), false},
    89  		{
    90  			map[string]interface{}{"title": "test 1"},
    91  			'+',
    92  			[]byte("+++\ntitle = \"test 1\"\n\n+++\n"),
    93  			false,
    94  		},
    95  
    96  		// YAML
    97  		{map[string]interface{}{}, '-', []byte("---\n{}\n---\n"), false}, //
    98  		{
    99  			map[string]interface{}{"title": "test 1"},
   100  			'-',
   101  			[]byte("---\ntitle: test 1\n---\n"),
   102  			false,
   103  		},
   104  
   105  		// JSON
   106  		{map[string]interface{}{}, '{', []byte("{}\n"), false},
   107  		{
   108  			map[string]interface{}{"title": "test 1"},
   109  			'{',
   110  			[]byte("{\n   \"title\": \"test 1\"\n}\n"),
   111  			false,
   112  		},
   113  
   114  		// Errors
   115  		{nil, '+', nil, true},
   116  		{map[string]interface{}{}, '$', nil, true},
   117  	}
   118  
   119  	for i, c := range cases {
   120  		var buf bytes.Buffer
   121  		err := InterfaceToFrontMatter(c.input, c.mark, &buf)
   122  		if err != nil {
   123  			if c.isErr {
   124  				continue
   125  			}
   126  			t.Fatalf("[%d] unexpected error value: %v", i, err)
   127  		}
   128  
   129  		if !reflect.DeepEqual(buf.Bytes(), c.want) {
   130  			t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes())
   131  		}
   132  	}
   133  }
   134  
   135  func TestHandleTOMLMetaData(t *testing.T) {
   136  	cases := []struct {
   137  		input []byte
   138  		want  interface{}
   139  		isErr bool
   140  	}{
   141  		{nil, map[string]interface{}{}, false},
   142  		{[]byte("title = \"test 1\""), map[string]interface{}{"title": "test 1"}, false},
   143  		{[]byte("a = [1, 2, 3]"), map[string]interface{}{"a": []interface{}{int64(1), int64(2), int64(3)}}, false},
   144  		{[]byte("b = [\n[1, 2],\n[3, 4]\n]"), map[string]interface{}{"b": []interface{}{[]interface{}{int64(1), int64(2)}, []interface{}{int64(3), int64(4)}}}, false},
   145  		// errors
   146  		{[]byte("z = [\n[1, 2]\n[3, 4]\n]"), nil, true},
   147  	}
   148  
   149  	for i, c := range cases {
   150  		res, err := HandleTOMLMetaData(c.input)
   151  		if err != nil {
   152  			if c.isErr {
   153  				continue
   154  			}
   155  			t.Fatalf("[%d] unexpected error value: %v", i, err)
   156  		}
   157  
   158  		if !reflect.DeepEqual(res, c.want) {
   159  			t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res)
   160  		}
   161  	}
   162  }
   163  
   164  func TestHandleYAMLMetaData(t *testing.T) {
   165  	cases := []struct {
   166  		input []byte
   167  		want  interface{}
   168  		isErr bool
   169  	}{
   170  		{nil, map[string]interface{}{}, false},
   171  		{[]byte("title: test 1"), map[string]interface{}{"title": "test 1"}, false},
   172  		{[]byte("a: Easy!\nb:\n  c: 2\n  d: [3, 4]"), map[string]interface{}{"a": "Easy!", "b": map[interface{}]interface{}{"c": 2, "d": []interface{}{3, 4}}}, false},
   173  		// errors
   174  		{[]byte("z = not toml"), nil, true},
   175  	}
   176  
   177  	for i, c := range cases {
   178  		res, err := HandleYAMLMetaData(c.input)
   179  		if err != nil {
   180  			if c.isErr {
   181  				continue
   182  			}
   183  			t.Fatalf("[%d] unexpected error value: %v", i, err)
   184  		}
   185  
   186  		if !reflect.DeepEqual(res, c.want) {
   187  			t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res)
   188  		}
   189  	}
   190  }
   191  
   192  func TestHandleJSONMetaData(t *testing.T) {
   193  	cases := []struct {
   194  		input []byte
   195  		want  interface{}
   196  		isErr bool
   197  	}{
   198  		{nil, map[string]interface{}{}, false},
   199  		{[]byte("{\"title\": \"test 1\"}"), map[string]interface{}{"title": "test 1"}, false},
   200  		// errors
   201  		{[]byte("{noquotes}"), nil, true},
   202  	}
   203  
   204  	for i, c := range cases {
   205  		res, err := HandleJSONMetaData(c.input)
   206  		if err != nil {
   207  			if c.isErr {
   208  				continue
   209  			}
   210  			t.Fatalf("[%d] unexpected error value: %v", i, err)
   211  		}
   212  
   213  		if !reflect.DeepEqual(res, c.want) {
   214  			t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res)
   215  		}
   216  	}
   217  }
   218  
   219  func TestHandleOrgMetaData(t *testing.T) {
   220  	cases := []struct {
   221  		input []byte
   222  		want  interface{}
   223  		isErr bool
   224  	}{
   225  		{nil, map[string]interface{}{}, false},
   226  		{[]byte("#+title: test 1\n"), map[string]interface{}{"title": "test 1"}, false},
   227  	}
   228  
   229  	for i, c := range cases {
   230  		res, err := HandleOrgMetaData(c.input)
   231  		if err != nil {
   232  			if c.isErr {
   233  				continue
   234  			}
   235  			t.Fatalf("[%d] unexpected error value: %v", i, err)
   236  		}
   237  
   238  		if !reflect.DeepEqual(res, c.want) {
   239  			t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res)
   240  		}
   241  	}
   242  }
   243  
   244  func TestFormatToLeadRune(t *testing.T) {
   245  	for i, this := range []struct {
   246  		kind   string
   247  		expect rune
   248  	}{
   249  		{"yaml", '-'},
   250  		{"yml", '-'},
   251  		{"toml", '+'},
   252  		{"tml", '+'},
   253  		{"json", '{'},
   254  		{"js", '{'},
   255  		{"org", '#'},
   256  		{"unknown", '+'},
   257  	} {
   258  		result := FormatToLeadRune(this.kind)
   259  
   260  		if result != this.expect {
   261  			t.Errorf("[%d] got %q but expected %q", i, result, this.expect)
   262  		}
   263  	}
   264  }
   265  
   266  func TestDetectFrontMatter(t *testing.T) {
   267  	cases := []struct {
   268  		mark rune
   269  		want *FrontmatterType
   270  	}{
   271  		// funcs are uncomparable, so we ignore FrontmatterType.Parse in these tests
   272  		{'-', &FrontmatterType{nil, []byte(YAMLDelim), []byte(YAMLDelim), false}},
   273  		{'+', &FrontmatterType{nil, []byte(TOMLDelim), []byte(TOMLDelim), false}},
   274  		{'{', &FrontmatterType{nil, []byte("{"), []byte("}"), true}},
   275  		{'#', &FrontmatterType{nil, []byte("#+"), []byte("\n"), false}},
   276  		{'$', nil},
   277  	}
   278  
   279  	for _, c := range cases {
   280  		res := DetectFrontMatter(c.mark)
   281  		if res == nil {
   282  			if c.want == nil {
   283  				continue
   284  			}
   285  
   286  			t.Fatalf("want %v, got %v", *c.want, res)
   287  		}
   288  
   289  		if !reflect.DeepEqual(res.markstart, c.want.markstart) {
   290  			t.Errorf("markstart mismatch: want %v, got %v", c.want.markstart, res.markstart)
   291  		}
   292  		if !reflect.DeepEqual(res.markend, c.want.markend) {
   293  			t.Errorf("markend mismatch: want %v, got %v", c.want.markend, res.markend)
   294  		}
   295  		if !reflect.DeepEqual(res.includeMark, c.want.includeMark) {
   296  			t.Errorf("includeMark mismatch: want %v, got %v", c.want.includeMark, res.includeMark)
   297  		}
   298  	}
   299  }
   300  
   301  func TestRemoveTOMLIdentifier(t *testing.T) {
   302  	cases := []struct {
   303  		input string
   304  		want  string
   305  	}{
   306  		{"a = 1", "a = 1"},
   307  		{"a = 1\r\n", "a = 1\r\n"},
   308  		{"+++\r\na = 1\r\n+++\r\n", "a = 1\r\n"},
   309  		{"+++\na = 1\n+++\n", "a = 1\n"},
   310  		{"+++\nb = \"+++ oops +++\"\n+++\n", "b = \"+++ oops +++\"\n"},
   311  		{"+++\nc = \"\"\"+++\noops\n+++\n\"\"\"\"\n+++\n", "c = \"\"\"+++\noops\n+++\n\"\"\"\"\n"},
   312  		{"+++\nd = 1\n+++", "d = 1\n"},
   313  	}
   314  
   315  	for i, c := range cases {
   316  		res := removeTOMLIdentifier([]byte(c.input))
   317  		if string(res) != c.want {
   318  			t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res)
   319  		}
   320  	}
   321  }
   322  
   323  func BenchmarkFrontmatterTags(b *testing.B) {
   324  
   325  	for _, frontmatter := range []string{"JSON", "YAML", "YAML2", "TOML"} {
   326  		for i := 1; i < 60; i += 20 {
   327  			doBenchmarkFrontmatter(b, frontmatter, i)
   328  		}
   329  	}
   330  }
   331  
   332  func doBenchmarkFrontmatter(b *testing.B, fileformat string, numTags int) {
   333  	yamlTemplate := `---
   334  name: "Tags"
   335  tags:
   336  %s
   337  ---
   338  `
   339  
   340  	yaml2Template := `---
   341  name: "Tags"
   342  tags: %s
   343  ---
   344  `
   345  	tomlTemplate := `+++
   346  name = "Tags"
   347  tags = %s
   348  +++
   349  `
   350  
   351  	jsonTemplate := `{
   352  	"name": "Tags",
   353  	"tags": [
   354  		%s
   355  	]
   356  }`
   357  	name := fmt.Sprintf("%s:%d", fileformat, numTags)
   358  	b.Run(name, func(b *testing.B) {
   359  		tags := make([]string, numTags)
   360  		var (
   361  			tagsStr             string
   362  			frontmatterTemplate string
   363  		)
   364  		for i := 0; i < numTags; i++ {
   365  			tags[i] = fmt.Sprintf("Hugo %d", i+1)
   366  		}
   367  		if fileformat == "TOML" {
   368  			frontmatterTemplate = tomlTemplate
   369  			tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1)
   370  		} else if fileformat == "JSON" {
   371  			frontmatterTemplate = jsonTemplate
   372  			tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1)
   373  		} else if fileformat == "YAML2" {
   374  			frontmatterTemplate = yaml2Template
   375  			tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1)
   376  		} else {
   377  			frontmatterTemplate = yamlTemplate
   378  			for _, tag := range tags {
   379  				tagsStr += "\n- " + tag
   380  			}
   381  		}
   382  
   383  		frontmatter := fmt.Sprintf(frontmatterTemplate, tagsStr)
   384  
   385  		p := page{frontmatter: []byte(frontmatter)}
   386  
   387  		b.ResetTimer()
   388  		for i := 0; i < b.N; i++ {
   389  			meta, err := p.Metadata()
   390  			if err != nil {
   391  				b.Fatal(err)
   392  			}
   393  			if meta == nil {
   394  				b.Fatal("Meta is nil")
   395  			}
   396  		}
   397  	})
   398  }