hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

content_map_test.go (15933B)

    1 // Copyright 2019 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 hugolib
   15 
   16 import (
   17 	"fmt"
   18 	"path/filepath"
   19 	"strings"
   20 	"testing"
   21 
   22 	"github.com/gohugoio/hugo/common/paths"
   23 
   24 	"github.com/gohugoio/hugo/htesting/hqt"
   25 
   26 	"github.com/gohugoio/hugo/hugofs/files"
   27 
   28 	"github.com/gohugoio/hugo/hugofs"
   29 	"github.com/spf13/afero"
   30 
   31 	qt "github.com/frankban/quicktest"
   32 )
   33 
   34 func BenchmarkContentMap(b *testing.B) {
   35 	writeFile := func(c *qt.C, fs afero.Fs, filename, content string) hugofs.FileMetaInfo {
   36 		c.Helper()
   37 		filename = filepath.FromSlash(filename)
   38 		c.Assert(fs.MkdirAll(filepath.Dir(filename), 0777), qt.IsNil)
   39 		c.Assert(afero.WriteFile(fs, filename, []byte(content), 0777), qt.IsNil)
   40 
   41 		fi, err := fs.Stat(filename)
   42 		c.Assert(err, qt.IsNil)
   43 
   44 		mfi := fi.(hugofs.FileMetaInfo)
   45 		return mfi
   46 	}
   47 
   48 	createFs := func(fs afero.Fs, lang string) afero.Fs {
   49 		return hugofs.NewBaseFileDecorator(fs,
   50 			func(fi hugofs.FileMetaInfo) {
   51 				meta := fi.Meta()
   52 				// We have a more elaborate filesystem setup in the
   53 				// real flow, so simulate this here.
   54 				meta.Lang = lang
   55 				meta.Path = meta.Filename
   56 				meta.Classifier = files.ClassifyContentFile(fi.Name(), meta.OpenFunc)
   57 			})
   58 	}
   59 
   60 	b.Run("CreateMissingNodes", func(b *testing.B) {
   61 		c := qt.New(b)
   62 		b.StopTimer()
   63 		mps := make([]*contentMap, b.N)
   64 		for i := 0; i < b.N; i++ {
   65 			m := newContentMap(contentMapConfig{lang: "en"})
   66 			mps[i] = m
   67 			memfs := afero.NewMemMapFs()
   68 			fs := createFs(memfs, "en")
   69 			for i := 1; i <= 20; i++ {
   70 				c.Assert(m.AddFilesBundle(writeFile(c, fs, fmt.Sprintf("sect%d/a/index.md", i), "page")), qt.IsNil)
   71 				c.Assert(m.AddFilesBundle(writeFile(c, fs, fmt.Sprintf("sect2%d/%sindex.md", i, strings.Repeat("b/", i)), "page")), qt.IsNil)
   72 			}
   73 
   74 		}
   75 
   76 		b.StartTimer()
   77 
   78 		for i := 0; i < b.N; i++ {
   79 			m := mps[i]
   80 			c.Assert(m.CreateMissingNodes(), qt.IsNil)
   81 
   82 			b.StopTimer()
   83 			m.pages.DeletePrefix("/")
   84 			m.sections.DeletePrefix("/")
   85 			b.StartTimer()
   86 		}
   87 	})
   88 }
   89 
   90 func TestContentMap(t *testing.T) {
   91 	c := qt.New(t)
   92 
   93 	writeFile := func(c *qt.C, fs afero.Fs, filename, content string) hugofs.FileMetaInfo {
   94 		c.Helper()
   95 		filename = filepath.FromSlash(filename)
   96 		c.Assert(fs.MkdirAll(filepath.Dir(filename), 0777), qt.IsNil)
   97 		c.Assert(afero.WriteFile(fs, filename, []byte(content), 0777), qt.IsNil)
   98 
   99 		fi, err := fs.Stat(filename)
  100 		c.Assert(err, qt.IsNil)
  101 
  102 		mfi := fi.(hugofs.FileMetaInfo)
  103 		return mfi
  104 	}
  105 
  106 	createFs := func(fs afero.Fs, lang string) afero.Fs {
  107 		return hugofs.NewBaseFileDecorator(fs,
  108 			func(fi hugofs.FileMetaInfo) {
  109 				meta := fi.Meta()
  110 				// We have a more elaborate filesystem setup in the
  111 				// real flow, so simulate this here.
  112 				meta.Lang = lang
  113 				meta.Path = meta.Filename
  114 				meta.TranslationBaseName = paths.Filename(fi.Name())
  115 				meta.Classifier = files.ClassifyContentFile(fi.Name(), meta.OpenFunc)
  116 			})
  117 	}
  118 
  119 	c.Run("AddFiles", func(c *qt.C) {
  120 		memfs := afero.NewMemMapFs()
  121 
  122 		fsl := func(lang string) afero.Fs {
  123 			return createFs(memfs, lang)
  124 		}
  125 
  126 		fs := fsl("en")
  127 
  128 		header := writeFile(c, fs, "blog/a/index.md", "page")
  129 
  130 		c.Assert(header.Meta().Lang, qt.Equals, "en")
  131 
  132 		resources := []hugofs.FileMetaInfo{
  133 			writeFile(c, fs, "blog/a/b/data.json", "data"),
  134 			writeFile(c, fs, "blog/a/logo.png", "image"),
  135 		}
  136 
  137 		m := newContentMap(contentMapConfig{lang: "en"})
  138 
  139 		c.Assert(m.AddFilesBundle(header, resources...), qt.IsNil)
  140 
  141 		c.Assert(m.AddFilesBundle(writeFile(c, fs, "blog/b/c/index.md", "page")), qt.IsNil)
  142 
  143 		c.Assert(m.AddFilesBundle(
  144 			writeFile(c, fs, "blog/_index.md", "section page"),
  145 			writeFile(c, fs, "blog/sectiondata.json", "section resource"),
  146 		), qt.IsNil)
  147 
  148 		got := m.testDump()
  149 
  150 		expect := `
  151           Tree 0:
  152               	/blog/__hb_a__hl_
  153               	/blog/__hb_b/c__hl_
  154               Tree 1:
  155               	/blog/
  156               Tree 2:
  157               	/blog/__hb_a__hl_b/data.json
  158               	/blog/__hb_a__hl_logo.png
  159               	/blog/__hl_sectiondata.json
  160               en/pages/blog/__hb_a__hl_|f:blog/a/index.md
  161               	 - R: blog/a/b/data.json
  162               	 - R: blog/a/logo.png
  163               en/pages/blog/__hb_b/c__hl_|f:blog/b/c/index.md
  164               en/sections/blog/|f:blog/_index.md
  165               	 - P: blog/a/index.md
  166               	 - P: blog/b/c/index.md
  167               	 - R: blog/sectiondata.json
  168     
  169 `
  170 
  171 		c.Assert(got, hqt.IsSameString, expect, qt.Commentf(got))
  172 
  173 		// Add a data file to the section bundle
  174 		c.Assert(m.AddFiles(
  175 			writeFile(c, fs, "blog/sectiondata2.json", "section resource"),
  176 		), qt.IsNil)
  177 
  178 		// And then one to the leaf bundles
  179 		c.Assert(m.AddFiles(
  180 			writeFile(c, fs, "blog/a/b/data2.json", "data2"),
  181 		), qt.IsNil)
  182 
  183 		c.Assert(m.AddFiles(
  184 			writeFile(c, fs, "blog/b/c/d/data3.json", "data3"),
  185 		), qt.IsNil)
  186 
  187 		got = m.testDump()
  188 
  189 		expect = `
  190 			 Tree 0:
  191               	/blog/__hb_a__hl_
  192               	/blog/__hb_b/c__hl_
  193               Tree 1:
  194               	/blog/
  195               Tree 2:
  196               	/blog/__hb_a__hl_b/data.json
  197               	/blog/__hb_a__hl_b/data2.json
  198               	/blog/__hb_a__hl_logo.png
  199               	/blog/__hb_b/c__hl_d/data3.json
  200               	/blog/__hl_sectiondata.json
  201               	/blog/__hl_sectiondata2.json
  202               en/pages/blog/__hb_a__hl_|f:blog/a/index.md
  203               	 - R: blog/a/b/data.json
  204               	 - R: blog/a/b/data2.json
  205               	 - R: blog/a/logo.png
  206               en/pages/blog/__hb_b/c__hl_|f:blog/b/c/index.md
  207               	 - R: blog/b/c/d/data3.json
  208               en/sections/blog/|f:blog/_index.md
  209               	 - P: blog/a/index.md
  210               	 - P: blog/b/c/index.md
  211               	 - R: blog/sectiondata.json
  212               	 - R: blog/sectiondata2.json
  213              
  214 `
  215 
  216 		c.Assert(got, hqt.IsSameString, expect, qt.Commentf(got))
  217 
  218 		// Add a regular page (i.e. not a bundle)
  219 		c.Assert(m.AddFilesBundle(writeFile(c, fs, "blog/b.md", "page")), qt.IsNil)
  220 
  221 		c.Assert(m.testDump(), hqt.IsSameString, `
  222 		 Tree 0:
  223               	/blog/__hb_a__hl_
  224               	/blog/__hb_b/c__hl_
  225               	/blog/__hb_b__hl_
  226               Tree 1:
  227               	/blog/
  228               Tree 2:
  229               	/blog/__hb_a__hl_b/data.json
  230               	/blog/__hb_a__hl_b/data2.json
  231               	/blog/__hb_a__hl_logo.png
  232               	/blog/__hb_b/c__hl_d/data3.json
  233               	/blog/__hl_sectiondata.json
  234               	/blog/__hl_sectiondata2.json
  235               en/pages/blog/__hb_a__hl_|f:blog/a/index.md
  236               	 - R: blog/a/b/data.json
  237               	 - R: blog/a/b/data2.json
  238               	 - R: blog/a/logo.png
  239               en/pages/blog/__hb_b/c__hl_|f:blog/b/c/index.md
  240               	 - R: blog/b/c/d/data3.json
  241               en/pages/blog/__hb_b__hl_|f:blog/b.md
  242               en/sections/blog/|f:blog/_index.md
  243               	 - P: blog/a/index.md
  244               	 - P: blog/b/c/index.md
  245               	 - P: blog/b.md
  246               	 - R: blog/sectiondata.json
  247               	 - R: blog/sectiondata2.json
  248              
  249        
  250 				`, qt.Commentf(m.testDump()))
  251 	})
  252 
  253 	c.Run("CreateMissingNodes", func(c *qt.C) {
  254 		memfs := afero.NewMemMapFs()
  255 
  256 		fsl := func(lang string) afero.Fs {
  257 			return createFs(memfs, lang)
  258 		}
  259 
  260 		fs := fsl("en")
  261 
  262 		m := newContentMap(contentMapConfig{lang: "en"})
  263 
  264 		c.Assert(m.AddFilesBundle(writeFile(c, fs, "blog/page.md", "page")), qt.IsNil)
  265 		c.Assert(m.AddFilesBundle(writeFile(c, fs, "blog/a/index.md", "page")), qt.IsNil)
  266 		c.Assert(m.AddFilesBundle(writeFile(c, fs, "bundle/index.md", "page")), qt.IsNil)
  267 
  268 		c.Assert(m.CreateMissingNodes(), qt.IsNil)
  269 
  270 		got := m.testDump()
  271 
  272 		c.Assert(got, hqt.IsSameString, `
  273 			
  274 			 Tree 0:
  275               	/__hb_bundle__hl_
  276               	/blog/__hb_a__hl_
  277               	/blog/__hb_page__hl_
  278               Tree 1:
  279               	/
  280               	/blog/
  281               Tree 2:
  282               en/pages/__hb_bundle__hl_|f:bundle/index.md
  283               en/pages/blog/__hb_a__hl_|f:blog/a/index.md
  284               en/pages/blog/__hb_page__hl_|f:blog/page.md
  285               en/sections/
  286               	 - P: bundle/index.md
  287               en/sections/blog/
  288               	 - P: blog/a/index.md
  289               	 - P: blog/page.md
  290             
  291 			`, qt.Commentf(got))
  292 	})
  293 
  294 	c.Run("cleanKey", func(c *qt.C) {
  295 		for _, test := range []struct {
  296 			in       string
  297 			expected string
  298 		}{
  299 			{"/a/b/", "/a/b"},
  300 			{filepath.FromSlash("/a/b/"), "/a/b"},
  301 			{"/a//b/", "/a/b"},
  302 		} {
  303 			c.Assert(cleanTreeKey(test.in), qt.Equals, test.expected)
  304 		}
  305 	})
  306 }
  307 
  308 func TestContentMapSite(t *testing.T) {
  309 	b := newTestSitesBuilder(t)
  310 
  311 	pageTempl := `
  312 ---
  313 title: "Page %d"
  314 date: "2019-06-0%d"	
  315 lastMod: "2019-06-0%d"
  316 categories: ["funny"]
  317 ---
  318 
  319 Page content.
  320 `
  321 	createPage := func(i int) string {
  322 		return fmt.Sprintf(pageTempl, i, i, i+1)
  323 	}
  324 
  325 	draftTemplate := `---
  326 title: "Draft"
  327 draft: true
  328 ---
  329 
  330 `
  331 
  332 	b.WithContent("_index.md", `
  333 ---
  334 title: "Hugo Home"
  335 cascade:
  336     description: "Common Description"
  337     
  338 ---
  339 
  340 Home Content.
  341 `)
  342 
  343 	b.WithContent("blog/page1.md", createPage(1))
  344 	b.WithContent("blog/page2.md", createPage(2))
  345 	b.WithContent("blog/page3.md", createPage(3))
  346 	b.WithContent("blog/bundle/index.md", createPage(12))
  347 	b.WithContent("blog/bundle/data.json", "data")
  348 	b.WithContent("blog/bundle/page.md", createPage(99))
  349 	b.WithContent("blog/subsection/_index.md", createPage(3))
  350 	b.WithContent("blog/subsection/subdata.json", "data")
  351 	b.WithContent("blog/subsection/page4.md", createPage(8))
  352 	b.WithContent("blog/subsection/page5.md", createPage(10))
  353 	b.WithContent("blog/subsection/draft/index.md", draftTemplate)
  354 	b.WithContent("blog/subsection/draft/data.json", "data")
  355 	b.WithContent("blog/draftsection/_index.md", draftTemplate)
  356 	b.WithContent("blog/draftsection/page/index.md", createPage(12))
  357 	b.WithContent("blog/draftsection/page/folder/data.json", "data")
  358 	b.WithContent("blog/draftsection/sub/_index.md", createPage(12))
  359 	b.WithContent("blog/draftsection/sub/page.md", createPage(13))
  360 	b.WithContent("docs/page6.md", createPage(11))
  361 	b.WithContent("tags/_index.md", createPage(32))
  362 	b.WithContent("overlap/_index.md", createPage(33))
  363 	b.WithContent("overlap2/_index.md", createPage(34))
  364 
  365 	b.WithTemplatesAdded("layouts/index.html", `
  366 Num Regular: {{ len .Site.RegularPages }}
  367 Main Sections: {{ .Site.Params.mainSections }}
  368 Pag Num Pages: {{ len .Paginator.Pages }}
  369 {{ $home := .Site.Home }}
  370 {{ $blog := .Site.GetPage "blog" }}
  371 {{ $categories := .Site.GetPage "categories" }}
  372 {{ $funny := .Site.GetPage "categories/funny" }}
  373 {{ $blogSub := .Site.GetPage "blog/subsection" }}
  374 {{ $page := .Site.GetPage "blog/page1" }}
  375 {{ $page2 := .Site.GetPage "blog/page2" }}
  376 {{ $page4 := .Site.GetPage "blog/subsection/page4" }}
  377 {{ $bundle := .Site.GetPage "blog/bundle" }}
  378 {{ $overlap1 := .Site.GetPage "overlap" }}
  379 {{ $overlap2 := .Site.GetPage "overlap2" }}
  380 
  381 Home: {{ template "print-page" $home }}
  382 Blog Section: {{ template "print-page" $blog }}
  383 Blog Sub Section: {{ template "print-page" $blogSub }}
  384 Page: {{ template "print-page" $page }}
  385 Bundle: {{ template "print-page" $bundle }}
  386 IsDescendant: true: {{ $page.IsDescendant $blog }} true: {{ $blogSub.IsDescendant $blog }} true: {{ $bundle.IsDescendant $blog }} true: {{ $page4.IsDescendant $blog }} true: {{ $blog.IsDescendant $home }} false: {{ $blog.IsDescendant $blog }} false: {{ $home.IsDescendant $blog }}
  387 IsAncestor: true: {{ $blog.IsAncestor $page }} true: {{ $home.IsAncestor $blog }} true: {{ $blog.IsAncestor $blogSub }} true: {{ $blog.IsAncestor $bundle }} true: {{ $blog.IsAncestor $page4 }} true: {{ $home.IsAncestor $page }} false: {{ $blog.IsAncestor $blog }} false: {{ $page.IsAncestor $blog }} false: {{ $blog.IsAncestor $home }}  false: {{ $blogSub.IsAncestor $blog }}
  388 IsDescendant overlap1: false: {{ $overlap1.IsDescendant $overlap2 }}
  389 IsDescendant overlap2: false: {{ $overlap2.IsDescendant $overlap1 }}
  390 IsAncestor overlap1: false: {{ $overlap1.IsAncestor $overlap2 }}
  391 IsAncestor overlap2: false: {{ $overlap2.IsAncestor $overlap1 }}
  392 FirstSection: {{ $blogSub.FirstSection.RelPermalink }} {{ $blog.FirstSection.RelPermalink }} {{ $home.FirstSection.RelPermalink }} {{ $page.FirstSection.RelPermalink }}
  393 InSection: true: {{ $page.InSection $blog }} false: {{ $page.InSection $blogSub }} 
  394 Next: {{ $page2.Next.RelPermalink }}
  395 NextInSection: {{ $page2.NextInSection.RelPermalink }}
  396 Pages: {{ range $blog.Pages }}{{ .RelPermalink }}|{{ end }}
  397 Sections: {{ range $home.Sections }}{{ .RelPermalink }}|{{ end }}
  398 Categories: {{ range .Site.Taxonomies.categories }}{{ .Page.RelPermalink }}; {{ .Page.Title }}; {{ .Count }}|{{ end }}
  399 Category Terms:  {{ $categories.Kind}}: {{ range $categories.Data.Terms.Alphabetical }}{{ .Page.RelPermalink }}; {{ .Page.Title }}; {{ .Count }}|{{ end }}
  400 Category Funny:  {{ $funny.Kind}}; {{ $funny.Data.Term }}: {{ range $funny.Pages }}{{ .RelPermalink }};|{{ end }}
  401 Pag Num Pages: {{ len .Paginator.Pages }}
  402 Pag Blog Num Pages: {{ len $blog.Paginator.Pages }}
  403 Blog Num RegularPages: {{ len $blog.RegularPages }}
  404 Blog Num Pages: {{ len $blog.Pages }}
  405 
  406 Draft1: {{ if (.Site.GetPage "blog/subsection/draft") }}FOUND{{ end }}|
  407 Draft2: {{ if (.Site.GetPage "blog/draftsection") }}FOUND{{ end }}|
  408 Draft3: {{ if (.Site.GetPage "blog/draftsection/page") }}FOUND{{ end }}|
  409 Draft4: {{ if (.Site.GetPage "blog/draftsection/sub") }}FOUND{{ end }}|
  410 Draft5: {{ if (.Site.GetPage "blog/draftsection/sub/page") }}FOUND{{ end }}|
  411 
  412 {{ define "print-page" }}{{ .Title }}|{{ .RelPermalink }}|{{ .Date.Format "2006-01-02" }}|Current Section: {{ .CurrentSection.SectionsPath }}|Resources: {{ range .Resources }}{{ .ResourceType }}: {{ .RelPermalink }}|{{ end }}{{ end }}
  413 `)
  414 
  415 	b.Build(BuildCfg{})
  416 
  417 	b.AssertFileContent("public/index.html",
  418 
  419 		`
  420 	 Num Regular: 7
  421         Main Sections: [blog]
  422         Pag Num Pages: 7
  423         
  424       Home: Hugo Home|/|2019-06-08|Current Section: |Resources: 
  425         Blog Section: Blogs|/blog/|2019-06-08|Current Section: blog|Resources: 
  426         Blog Sub Section: Page 3|/blog/subsection/|2019-06-03|Current Section: blog/subsection|Resources: application: /blog/subsection/subdata.json|
  427         Page: Page 1|/blog/page1/|2019-06-01|Current Section: blog|Resources: 
  428         Bundle: Page 12|/blog/bundle/|0001-01-01|Current Section: blog|Resources: application: /blog/bundle/data.json|page: |
  429         IsDescendant: true: true true: true true: true true: true true: true false: false false: false
  430         IsAncestor: true: true true: true true: true true: true true: true true: true false: false false: false false: false  false: false
  431         IsDescendant overlap1: false: false
  432         IsDescendant overlap2: false: false
  433         IsAncestor overlap1: false: false
  434         IsAncestor overlap2: false: false
  435         FirstSection: /blog/ /blog/ / /blog/
  436         InSection: true: true false: false 
  437         Next: /blog/page3/
  438         NextInSection: /blog/page3/
  439         Pages: /blog/page3/|/blog/subsection/|/blog/page2/|/blog/page1/|/blog/bundle/|
  440         Sections: /blog/|/docs/|
  441         Categories: /categories/funny/; funny; 11|
  442         Category Terms:  taxonomy: /categories/funny/; funny; 11|
  443  		Category Funny:  term; funny: /blog/subsection/page4/;|/blog/page3/;|/blog/subsection/;|/blog/page2/;|/blog/page1/;|/blog/subsection/page5/;|/docs/page6/;|/blog/bundle/;|;|
  444  		Pag Num Pages: 7
  445         Pag Blog Num Pages: 4
  446         Blog Num RegularPages: 4
  447         Blog Num Pages: 5
  448         
  449         Draft1: |
  450         Draft2: |
  451         Draft3: |
  452         Draft4: |
  453         Draft5: |
  454            
  455 `)
  456 }