summaryrefslogtreecommitdiffhomepage
path: root/parser/parse_frontmatter_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'parser/parse_frontmatter_test.go')
-rw-r--r--parser/parse_frontmatter_test.go316
1 files changed, 316 insertions, 0 deletions
diff --git a/parser/parse_frontmatter_test.go b/parser/parse_frontmatter_test.go
new file mode 100644
index 000000000..08a1cc42c
--- /dev/null
+++ b/parser/parse_frontmatter_test.go
@@ -0,0 +1,316 @@
+// Copyright 2015 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 parser
+
+// TODO Support Mac Encoding (\r)
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+const (
+ contentNoFrontmatter = "a page with no front matter"
+ contentWithFrontmatter = "---\ntitle: front matter\n---\nContent with front matter"
+ contentHTMLNoDoctype = "<html>\n\t<body>\n\t</body>\n</html>"
+ contentHTMLWithDoctype = "<!doctype html><html><body></body></html>"
+ contentHTMLWithFrontmatter = "---\ntitle: front matter\n---\n<!doctype><html><body></body></html>"
+ contentHTML = " <html><body></body></html>"
+ contentLinefeedAndHTML = "\n<html><body></body></html>"
+ contentIncompleteEndFrontmatterDelim = "---\ntitle: incomplete end fm delim\n--\nincomplete frontmatter delim"
+ contentMissingEndFrontmatterDelim = "---\ntitle: incomplete end fm delim\nincomplete frontmatter delim"
+ contentSlugWorking = "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\nslug doc 2 content"
+ contentSlugWorkingVariation = "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\nslug doc 3 content"
+ contentSlugBug = "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\nslug doc 2 content"
+ contentSlugWithJSONFrontMatter = "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories"
+ contentWithJSONLooseFrontmatter = "{\n \"categories\": \"d\"\n \"tags\": [\n \"a\" \n \"b\" \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories"
+ contentSlugWithJSONFrontMatterFirstLineOnly = "{\"categories\":\"d\",\"tags\":[\"a\",\"b\",\"c\"]}\nJSON Front Matter with tags and categories"
+ contentSlugWithJSONFrontMatterFirstLine = "{\"categories\":\"d\",\n \"tags\":[\"a\",\"b\",\"c\"]}\nJSON Front Matter with tags and categories"
+)
+
+var lineEndings = []string{"\n", "\r\n"}
+var delimiters = []string{"---", "+++"}
+
+func pageMust(p Page, err error) *page {
+ if err != nil {
+ panic(err)
+ }
+ return p.(*page)
+}
+
+func TestDegenerateCreatePageFrom(t *testing.T) {
+ tests := []struct {
+ content string
+ }{
+ {contentMissingEndFrontmatterDelim},
+ {contentIncompleteEndFrontmatterDelim},
+ }
+
+ for _, test := range tests {
+ for _, ending := range lineEndings {
+ test.content = strings.Replace(test.content, "\n", ending, -1)
+ _, err := ReadFrom(strings.NewReader(test.content))
+ if err == nil {
+ t.Errorf("Content should return an err:\n%q\n", test.content)
+ }
+ }
+ }
+}
+
+func checkPageRender(t *testing.T, p *page, expected bool) {
+ if p.render != expected {
+ t.Errorf("page.render should be %t, got: %t", expected, p.render)
+ }
+}
+
+func checkPageFrontMatterIsNil(t *testing.T, p *page, content string, expected bool) {
+ if bool(p.frontmatter == nil) != expected {
+ t.Logf("\n%q\n", content)
+ t.Errorf("page.frontmatter == nil? %t, got %t", expected, p.frontmatter == nil)
+ }
+}
+
+func checkPageFrontMatterContent(t *testing.T, p *page, frontMatter string) {
+ if p.frontmatter == nil {
+ return
+ }
+ if !bytes.Equal(p.frontmatter, []byte(frontMatter)) {
+ t.Errorf("frontmatter mismatch\nexp: %q\ngot: %q", frontMatter, p.frontmatter)
+ }
+}
+
+func checkPageContent(t *testing.T, p *page, expected string) {
+ if !bytes.Equal(p.content, []byte(expected)) {
+ t.Errorf("content mismatch\nexp: %q\ngot: %q", expected, p.content)
+ }
+}
+
+func TestStandaloneCreatePageFrom(t *testing.T) {
+ tests := []struct {
+ content string
+ expectedMustRender bool
+ frontMatterIsNil bool
+ frontMatter string
+ bodycontent string
+ }{
+
+ {contentNoFrontmatter, true, true, "", "a page with no front matter"},
+ {contentWithFrontmatter, true, false, "---\ntitle: front matter\n---\n", "Content with front matter"},
+ {contentHTMLNoDoctype, false, true, "", "<html>\n\t<body>\n\t</body>\n</html>"},
+ {contentHTMLWithDoctype, false, true, "", "<!doctype html><html><body></body></html>"},
+ {contentHTMLWithFrontmatter, true, false, "---\ntitle: front matter\n---\n", "<!doctype><html><body></body></html>"},
+ {contentHTML, false, true, "", "<html><body></body></html>"},
+ {contentLinefeedAndHTML, false, true, "", "<html><body></body></html>"},
+ {contentSlugWithJSONFrontMatter, true, false, "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}", "JSON Front Matter with tags and categories"},
+ {contentWithJSONLooseFrontmatter, true, false, "{\n \"categories\": \"d\"\n \"tags\": [\n \"a\" \n \"b\" \n \"c\"\n ]\n}", "JSON Front Matter with tags and categories"},
+ {contentSlugWithJSONFrontMatterFirstLineOnly, true, false, "{\"categories\":\"d\",\"tags\":[\"a\",\"b\",\"c\"]}", "JSON Front Matter with tags and categories"},
+ {contentSlugWithJSONFrontMatterFirstLine, true, false, "{\"categories\":\"d\",\n \"tags\":[\"a\",\"b\",\"c\"]}", "JSON Front Matter with tags and categories"},
+ {contentSlugWorking, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\n", "slug doc 2 content"},
+ {contentSlugWorkingVariation, true, false, "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\n", "slug doc 3 content"},
+ {contentSlugBug, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n", "slug doc 2 content"},
+ }
+
+ for _, test := range tests {
+ for _, ending := range lineEndings {
+ test.content = strings.Replace(test.content, "\n", ending, -1)
+ test.frontMatter = strings.Replace(test.frontMatter, "\n", ending, -1)
+ test.bodycontent = strings.Replace(test.bodycontent, "\n", ending, -1)
+
+ p := pageMust(ReadFrom(strings.NewReader(test.content)))
+
+ checkPageRender(t, p, test.expectedMustRender)
+ checkPageFrontMatterIsNil(t, p, test.content, test.frontMatterIsNil)
+ checkPageFrontMatterContent(t, p, test.frontMatter)
+ checkPageContent(t, p, test.bodycontent)
+ }
+ }
+}
+
+func BenchmarkLongFormRender(b *testing.B) {
+
+ tests := []struct {
+ filename string
+ buf []byte
+ }{
+ {filename: "long_text_test.md"},
+ }
+ for i, test := range tests {
+ path := filepath.FromSlash(test.filename)
+ f, err := os.Open(path)
+ if err != nil {
+ b.Fatalf("Unable to open %s: %s", path, err)
+ }
+ defer f.Close()
+ membuf := new(bytes.Buffer)
+ if _, err := io.Copy(membuf, f); err != nil {
+ b.Fatalf("Unable to read %s: %s", path, err)
+ }
+ tests[i].buf = membuf.Bytes()
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i <= b.N; i++ {
+ for _, test := range tests {
+ ReadFrom(bytes.NewReader(test.buf))
+ }
+ }
+}
+
+func TestPageShouldRender(t *testing.T) {
+ tests := []struct {
+ content []byte
+ expected bool
+ }{
+ {[]byte{}, false},
+ {[]byte{'<'}, false},
+ {[]byte{'-'}, true},
+ {[]byte("--"), true},
+ {[]byte("---"), true},
+ {[]byte("---\n"), true},
+ {[]byte{'a'}, true},
+ }
+
+ for _, test := range tests {
+ for _, ending := range lineEndings {
+ test.content = bytes.Replace(test.content, []byte("\n"), []byte(ending), -1)
+ if render := shouldRender(test.content); render != test.expected {
+
+ t.Errorf("Expected %s to shouldRender = %t, got: %t", test.content, test.expected, render)
+ }
+ }
+ }
+}
+
+func TestPageHasFrontMatter(t *testing.T) {
+ tests := []struct {
+ content []byte
+ expected bool
+ }{
+ {[]byte{'-'}, false},
+ {[]byte("--"), false},
+ {[]byte("---"), false},
+ {[]byte("---\n"), true},
+ {[]byte("---\n"), true},
+ {[]byte("--- \n"), true},
+ {[]byte("--- \n"), true},
+ {[]byte{'a'}, false},
+ {[]byte{'{'}, true},
+ {[]byte("{\n "), true},
+ {[]byte{'}'}, false},
+ }
+ for _, test := range tests {
+ for _, ending := range lineEndings {
+ test.content = bytes.Replace(test.content, []byte("\n"), []byte(ending), -1)
+ if isFrontMatterDelim := isFrontMatterDelim(test.content); isFrontMatterDelim != test.expected {
+ t.Errorf("Expected %q isFrontMatterDelim = %t, got: %t", test.content, test.expected, isFrontMatterDelim)
+ }
+ }
+ }
+}
+
+func TestExtractFrontMatter(t *testing.T) {
+
+ tests := []struct {
+ frontmatter string
+ extracted []byte
+ errIsNil bool
+ }{
+ {"", nil, false},
+ {"-", nil, false},
+ {"---\n", nil, false},
+ {"---\nfoobar", nil, false},
+ {"---\nfoobar\nbarfoo\nfizbaz\n", nil, false},
+ {"---\nblar\n-\n", nil, false},
+ {"---\nralb\n---\n", []byte("---\nralb\n---\n"), true},
+ {"---\neof\n---", []byte("---\neof\n---"), true},
+ {"--- \neof\n---", []byte("---\neof\n---"), true},
+ {"---\nminc\n---\ncontent", []byte("---\nminc\n---\n"), true},
+ {"---\nminc\n--- \ncontent", []byte("---\nminc\n---\n"), true},
+ {"--- \nminc\n--- \ncontent", []byte("---\nminc\n---\n"), true},
+ {"---\ncnim\n---\ncontent\n", []byte("---\ncnim\n---\n"), true},
+ {"---\ntitle: slug doc 2\nslug: slug-doc-2\n---\ncontent\n", []byte("---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n"), true},
+ {"---\npermalink: '/blog/title---subtitle.html'\n---\ncontent\n", []byte("---\npermalink: '/blog/title---subtitle.html'\n---\n"), true},
+ }
+
+ for _, test := range tests {
+ for _, ending := range lineEndings {
+ test.frontmatter = strings.Replace(test.frontmatter, "\n", ending, -1)
+ test.extracted = bytes.Replace(test.extracted, []byte("\n"), []byte(ending), -1)
+ for _, delim := range delimiters {
+ test.frontmatter = strings.Replace(test.frontmatter, "---", delim, -1)
+ test.extracted = bytes.Replace(test.extracted, []byte("---"), []byte(delim), -1)
+ line, err := peekLine(bufio.NewReader(strings.NewReader(test.frontmatter)))
+ if err != nil {
+ continue
+ }
+ l, r := determineDelims(line)
+ fm, err := extractFrontMatterDelims(bufio.NewReader(strings.NewReader(test.frontmatter)), l, r)
+ if (err == nil) != test.errIsNil {
+ t.Logf("\n%q\n", string(test.frontmatter))
+ t.Errorf("Expected err == nil => %t, got: %t. err: %s", test.errIsNil, err == nil, err)
+ continue
+ }
+ if !bytes.Equal(fm, test.extracted) {
+ t.Errorf("Frontmatter did not match:\nexp: %q\ngot: %q", string(test.extracted), fm)
+ }
+ }
+ }
+ }
+}
+
+func TestExtractFrontMatterDelim(t *testing.T) {
+ var (
+ noErrExpected = true
+ errExpected = false
+ )
+ tests := []struct {
+ frontmatter string
+ extracted string
+ errIsNil bool
+ }{
+ {"", "", errExpected},
+ {"{", "", errExpected},
+ {"{}", "{}", noErrExpected},
+ {"{} ", "{}", noErrExpected},
+ {"{ } ", "{ }", noErrExpected},
+ {"{ { }", "", errExpected},
+ {"{ { } }", "{ { } }", noErrExpected},
+ {"{ { } { } }", "{ { } { } }", noErrExpected},
+ {"{\n{\n}\n}\n", "{\n{\n}\n}", noErrExpected},
+ {"{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories", "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}", noErrExpected},
+ {"{\n \"categories\": \"d\"\n \"tags\": [\n \"a\" \n \"b\" \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories", "{\n \"categories\": \"d\"\n \"tags\": [\n \"a\" \n \"b\" \n \"c\"\n ]\n}", noErrExpected},
+ // Issue #3511
+ {`{ "title": "{" }`, `{ "title": "{" }`, noErrExpected},
+ {`{ "title": "{}" }`, `{ "title": "{}" }`, noErrExpected},
+ }
+
+ for i, test := range tests {
+ fm, err := extractFrontMatterDelims(bufio.NewReader(strings.NewReader(test.frontmatter)), []byte("{"), []byte("}"))
+ if (err == nil) != test.errIsNil {
+ t.Logf("\n%q\n", string(test.frontmatter))
+ t.Errorf("[%d] Expected err == nil => %t, got: %t. err: %s", i, test.errIsNil, err == nil, err)
+ continue
+ }
+ if !bytes.Equal(fm, []byte(test.extracted)) {
+ t.Logf("\n%q\n", string(test.frontmatter))
+ t.Errorf("[%d] Frontmatter did not match:\nexp: %q\ngot: %q", i, string(test.extracted), fm)
+ }
+ }
+}