From c71e1b106e6011d148cac899f83c4685dee33a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 10 Jan 2017 10:55:03 +0100 Subject: all: Refactor to nonglobal file systems Updates #2701 Fixes #2951 --- hugolib/alias_test.go | 40 +++-- hugolib/case_insensitive_test.go | 67 ++++--- hugolib/config_test.go | 6 +- hugolib/datafiles_test.go | 34 ++-- hugolib/embedded_shortcodes_test.go | 20 ++- hugolib/gitinfo.go | 5 +- hugolib/handler_page.go | 1 - hugolib/handler_test.go | 46 ++--- hugolib/hugo_sites.go | 230 ++++++++++++------------ hugolib/hugo_sites_build.go | 2 +- hugolib/hugo_sites_build_test.go | 268 +++++++++++++++------------- hugolib/i18n.go | 5 +- hugolib/menu_test.go | 24 +-- hugolib/node_as_page_test.go | 341 +++++++++++++++++++----------------- hugolib/page.go | 62 ++++--- hugolib/page_permalink_test.go | 3 +- hugolib/page_test.go | 76 ++++---- hugolib/pagination.go | 11 +- hugolib/pagination_test.go | 73 +++++--- hugolib/permalinks.go | 6 +- hugolib/robotstxt_test.go | 28 +-- hugolib/rss_test.go | 18 +- hugolib/shortcode.go | 19 +- hugolib/shortcode_test.go | 113 +++++------- hugolib/site.go | 288 ++++++++++++++++-------------- hugolib/siteJSONEncode_test.go | 15 +- hugolib/site_render.go | 12 +- hugolib/site_test.go | 296 ++++++++++++------------------- hugolib/site_url_test.go | 28 ++- hugolib/sitemap_test.go | 28 ++- hugolib/taxonomy.go | 19 +- hugolib/taxonomy_test.go | 13 +- hugolib/template_engines_test.go | 99 +++++++++++ hugolib/template_test.go | 68 +++---- hugolib/testhelpers_test.go | 53 ++++++ 35 files changed, 1290 insertions(+), 1127 deletions(-) create mode 100644 hugolib/template_engines_test.go create mode 100644 hugolib/testhelpers_test.go (limited to 'hugolib') diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go index 87bc9b130..22803d22e 100644 --- a/hugolib/alias_test.go +++ b/hugolib/alias_test.go @@ -16,6 +16,10 @@ package hugolib import ( "path/filepath" "testing" + + "github.com/spf13/hugo/deps" + "github.com/spf13/hugo/hugofs" + "github.com/stretchr/testify/require" ) const pageWithAlias = `--- @@ -30,31 +34,37 @@ const aliasTemplate = "ALIASTEMPLATE" func TestAlias(t *testing.T) { testCommonResetState() - writeSource(t, filepath.Join("content", "page.md"), pageWithAlias) - writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate) - if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil { - t.Fatalf("Failed to build site: %s", err) - } + fs := hugofs.NewMem() + + writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias) + writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate) + + buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{}) // the real page - assertFileContent(t, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") + assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") // the alias redirector - assertFileContent(t, filepath.Join("public", "foo", "bar", "index.html"), false, "}} `) - h, err := newHugoSitesDefaultLanguage() - require.NoError(t, err) - require.NoError(t, h.Build(BuildCfg{})) + buildSingleSite(t, cfg, BuildCfg{}) - content := readSource(t, "public/c/index.html") + content := readSource(t, cfg.Fs, "public/c/index.html") require.True(t, strings.Contains(content, "Slogan from template: Hugo Rocks!"), content) require.True(t, strings.Contains(content, "Slogan from shortcode: Hugo Rocks!"), content) diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go index 61c40cf01..64a92247b 100644 --- a/hugolib/embedded_shortcodes_test.go +++ b/hugolib/embedded_shortcodes_test.go @@ -27,9 +27,11 @@ import ( "log" "path/filepath" - "github.com/spf13/hugo/tpl" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/hugofs" + "github.com/spf13/hugo/tplapi" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -65,17 +67,17 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) { path := filepath.FromSlash("blog/post.md") in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path) - writeSource(t, "content/"+path, simplePageWithURL+": "+in) + fs := hugofs.NewMem() + + writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in) expected := fmt.Sprintf(`%s/simple/url/`, expectedBase) - sites, err := newHugoSitesDefaultLanguage() - require.NoError(t, err) + s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{}) - require.NoError(t, sites.Build(BuildCfg{})) - require.Len(t, sites.Sites[0].RegularPages, 1) + require.Len(t, s.RegularPages, 1) - output := string(sites.Sites[0].RegularPages[0].Content) + output := string(s.RegularPages[0].Content) if !strings.Contains(output, expected) { t.Errorf("Got\n%q\nExpected\n%q", output, expected) @@ -308,7 +310,7 @@ func TestShortcodeTweet(t *testing.T) { }, } - p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error { + p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error { templ.Funcs(tweetFuncMap) return nil }) @@ -361,7 +363,7 @@ func TestShortcodeInstagram(t *testing.T) { }, } - p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error { + p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error { templ.Funcs(instagramFuncMap) return nil }) diff --git a/hugolib/gitinfo.go b/hugolib/gitinfo.go index 2893db06f..82baa3250 100644 --- a/hugolib/gitinfo.go +++ b/hugolib/gitinfo.go @@ -20,7 +20,6 @@ import ( "github.com/bep/gitmap" "github.com/spf13/hugo/helpers" - jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" ) @@ -36,7 +35,7 @@ func (h *HugoSites) assembleGitInfo() { gitRepo, err := gitmap.Map(workingDir, "") if err != nil { - jww.ERROR.Printf("Got error reading Git log: %s", err) + h.Log.ERROR.Printf("Got error reading Git log: %s", err) return } @@ -60,7 +59,7 @@ func (h *HugoSites) assembleGitInfo() { filename := path.Join(filepath.ToSlash(contentRoot), contentDir, filepath.ToSlash(p.Path())) g, ok := gitMap[filename] if !ok { - jww.ERROR.Printf("Failed to find GitInfo for %q", filename) + h.Log.ERROR.Printf("Failed to find GitInfo for %q", filename) return } diff --git a/hugolib/handler_page.go b/hugolib/handler_page.go index 2026f2bbf..6b6b17173 100644 --- a/hugolib/handler_page.go +++ b/hugolib/handler_page.go @@ -65,7 +65,6 @@ type htmlHandler struct { func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} } -// TODO(bep) globals use p.s.t func (h htmlHandler) PageConvert(p *Page) HandledResult { if p.rendered { panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName())) diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go index ba5daa8c2..01e6793a6 100644 --- a/hugolib/handler_test.go +++ b/hugolib/handler_test.go @@ -17,44 +17,36 @@ import ( "path/filepath" "testing" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/hugofs" - "github.com/spf13/hugo/source" - "github.com/spf13/hugo/target" "github.com/spf13/viper" ) func TestDefaultHandler(t *testing.T) { testCommonResetState() - hugofs.InitMemFs() - sources := []source.ByteSource{ - {Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")}, - {Name: filepath.FromSlash("sect/doc2.html"), Content: []byte("more content")}, - {Name: filepath.FromSlash("sect/doc3.md"), Content: []byte("# doc3\n*some* content")}, - {Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc4\n---\n# doc4\n*some content*")}, - {Name: filepath.FromSlash("sect/doc3/img1.png"), Content: []byte("‰PNG  ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚")}, - {Name: filepath.FromSlash("sect/img2.gif"), Content: []byte("GIF89a��€��ÿÿÿ���,�������D�;")}, - {Name: filepath.FromSlash("sect/img2.spf"), Content: []byte("****FAKE-FILETYPE****")}, - {Name: filepath.FromSlash("doc7.html"), Content: []byte("doc7 content")}, - {Name: filepath.FromSlash("sect/doc8.html"), Content: []byte("---\nmarkup: md\n---\n# title\nsome *content*")}, - } - viper.Set("defaultExtension", "html") viper.Set("verbose", true) + viper.Set("uglyURLs", true) - s := &Site{ - Source: &source.InMemorySource{ByteSource: sources}, - targets: targetList{page: &target.PagePub{UglyURLs: true, PublishDir: "public"}}, - Language: helpers.NewLanguage("en"), - } + fs := hugofs.NewMem() - if err := buildAndRenderSite(s, - "_default/single.html", "{{.Content}}", - "head", "", - "head_abs", ""); err != nil { - t.Fatalf("Failed to render site: %s", err) - } + writeSource(t, fs, filepath.FromSlash("content/sect/doc1.html"), "---\nmarkup: markdown\n---\n# title\nsome *content*") + writeSource(t, fs, filepath.FromSlash("content/sect/doc2.html"), "more content") + writeSource(t, fs, filepath.FromSlash("content/sect/doc3.md"), "# doc3\n*some* content") + writeSource(t, fs, filepath.FromSlash("content/sect/doc4.md"), "---\ntitle: doc4\n---\n# doc4\n*some content*") + writeSource(t, fs, filepath.FromSlash("content/sect/doc3/img1.png"), "‰PNG  ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚") + writeSource(t, fs, filepath.FromSlash("content/sect/img2.gif"), "GIF89a��€��ÿÿÿ���,�������D�;") + writeSource(t, fs, filepath.FromSlash("content/sect/img2.spf"), "****FAKE-FILETYPE****") + writeSource(t, fs, filepath.FromSlash("content/doc7.html"), "doc7 content") + writeSource(t, fs, filepath.FromSlash("content/sect/doc8.html"), "---\nmarkup: md\n---\n# title\nsome *content*") + + writeSource(t, fs, filepath.FromSlash("layouts/_default/single.html"), "{{.Content}}") + writeSource(t, fs, filepath.FromSlash("head"), "") + writeSource(t, fs, filepath.FromSlash("head_abs"), " 0 { @@ -136,9 +163,17 @@ func createSitesFromDeps(deps *deps) ([]*Site, error) { } for _, lang := range languages { - sites = append(sites, newSite(lang, deps)) - } + var s *Site + var err error + cfg.Language = lang + s, err = newSite(cfg) + + if err != nil { + return nil, err + } + sites = append(sites, s) + } } return sites, nil @@ -155,7 +190,8 @@ func (h *HugoSites) reset() { func (h *HugoSites) createSitesFromConfig() error { - sites, err := createSitesFromDeps(h.deps) + depsCfg := deps.DepsCfg{Fs: h.Fs} + sites, err := createSitesFromConfig(depsCfg) if err != nil { return err @@ -173,6 +209,12 @@ func (h *HugoSites) createSitesFromConfig() error { s.owner = h } + if err := applyDepsIfNeeded(depsCfg, sites...); err != nil { + return err + } + + h.Deps = sites[0].Deps + h.multilingual = langConfig return nil @@ -199,24 +241,10 @@ type BuildCfg struct { CreateSitesFromConfig bool // Skip rendering. Useful for testing. SkipRender bool - // Use this to add templates to use for rendering. - // Useful for testing. - withTemplate func(templ tpl.Template) error // Use this to indicate what changed (for rebuilds). whatChanged *whatChanged } -// DepsCfg contains configuration options that can be used to configure Hugo -// on a global level, i.e. logging etc. -// Nil values will be given default values. -type DepsCfg struct { - - // The Logger to use. - Logger *jww.Notepad - - WithTemplate []func(templ tpl.Template) error -} - func (h *HugoSites) renderCrossSitesArtifacts() error { if !h.multilingual.enabled() { @@ -293,7 +321,7 @@ func (h *HugoSites) createMissingPages() error { foundTaxonomyTermsPage := false for key := range tax { if s.Info.preserveTaxonomyNames { - key = s.Info.pathSpec.MakePathSanitized(key) + key = s.PathSpec.MakePathSanitized(key) } for _, p := range taxonomyPages { if p.sections[0] == plural && p.sections[1] == key { @@ -454,8 +482,8 @@ func (s *Site) preparePagesForRender(cfg *BuildCfg) { } var err error - if workContentCopy, err = handleShortcodes(p, s.owner.tmpl, workContentCopy); err != nil { - jww.ERROR.Printf("Failed to handle shortcodes for page %s: %s", p.BaseFileName(), err) + if workContentCopy, err = handleShortcodes(p, s.Tmpl, workContentCopy); err != nil { + s.Log.ERROR.Printf("Failed to handle shortcodes for page %s: %s", p.BaseFileName(), err) } if p.Markup != "html" { @@ -464,7 +492,7 @@ func (s *Site) preparePagesForRender(cfg *BuildCfg) { summaryContent, err := p.setUserDefinedSummaryIfProvided(workContentCopy) if err != nil { - jww.ERROR.Printf("Failed to set user defined summary for page %q: %s", p.Path(), err) + s.Log.ERROR.Printf("Failed to set user defined summary for page %q: %s", p.Path(), err) } else if summaryContent != nil { workContentCopy = summaryContent.content } @@ -501,9 +529,9 @@ func (h *HugoSites) Pages() Pages { return h.Sites[0].AllPages } -func handleShortcodes(p *Page, t tpl.Template, rawContentCopy []byte) ([]byte, error) { +func handleShortcodes(p *Page, t tplapi.Template, rawContentCopy []byte) ([]byte, error) { if len(p.contentShortCodes) > 0 { - jww.DEBUG.Printf("Replace %d shortcodes in %q", len(p.contentShortCodes), p.BaseFileName()) + p.s.Log.DEBUG.Printf("Replace %d shortcodes in %q", len(p.contentShortCodes), p.BaseFileName()) shortcodes, err := executeShortcodeFuncMap(p.contentShortCodes) if err != nil { @@ -513,7 +541,7 @@ func handleShortcodes(p *Page, t tpl.Template, rawContentCopy []byte) ([]byte, e rawContentCopy, err = replaceShortcodeTokens(rawContentCopy, shortcodePlaceholderPrefix, shortcodes) if err != nil { - jww.FATAL.Printf("Failed to replace shortcode tokens in %s:\n%s", p.BaseFileName(), err.Error()) + p.s.Log.FATAL.Printf("Failed to replace shortcode tokens in %s:\n%s", p.BaseFileName(), err.Error()) } } @@ -550,51 +578,15 @@ func (h *HugoSites) findAllPagesByKindNotIn(kind string) Pages { return h.findPagesByKindNotIn(kind, h.Sites[0].AllPages) } -// Convenience func used in tests to build a single site/language excluding render phase. -func buildSiteSkipRender(s *Site, additionalTemplates ...string) error { - return doBuildSite(s, false, additionalTemplates...) -} - -// Convenience func used in tests to build a single site/language including render phase. -func buildAndRenderSite(s *Site, additionalTemplates ...string) error { - return doBuildSite(s, true, additionalTemplates...) -} - -// Convenience func used in tests to build a single site/language. -func doBuildSite(s *Site, render bool, additionalTemplates ...string) error { - if s.PageCollections == nil { - s.PageCollections = newPageCollections() - } - sites, err := newHugoSites(DepsCfg{}, s) - if err != nil { - return err - } - - addTemplates := func(templ tpl.Template) error { - for i := 0; i < len(additionalTemplates); i += 2 { - err := templ.AddTemplate(additionalTemplates[i], additionalTemplates[i+1]) - if err != nil { - return err - } - } - return nil - } - - config := BuildCfg{SkipRender: !render, withTemplate: addTemplates} - return sites.Build(config) -} - // Convenience func used in tests. -func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages helpers.Languages) (*HugoSites, error) { +func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages helpers.Languages, cfg deps.DepsCfg) (*HugoSites, error) { if len(languages) == 0 { panic("Must provide at least one language") } - cfg := DepsCfg{} - first := &Site{ - Source: &source.InMemorySource{ByteSource: input}, Language: languages[0], + Source: &source.InMemorySource{ByteSource: input}, } if len(languages) == 1 { return newHugoSites(cfg, first) @@ -611,6 +603,6 @@ func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages hel } // Convenience func used in tests. -func newHugoSitesDefaultLanguage() (*HugoSites, error) { - return newHugoSitesFromSourceAndLanguages(nil, helpers.Languages{helpers.NewDefaultLanguage()}) +func newHugoSitesDefaultLanguage(cfg deps.DepsCfg) (*HugoSites, error) { + return newHugoSitesFromSourceAndLanguages(nil, helpers.Languages{helpers.NewDefaultLanguage()}, cfg) } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index b3b176018..e915d11da 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -59,7 +59,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { } if config.PrintStats { - h.log.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds())) + h.Log.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds())) } return nil diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go index a8fc9a58f..9abb17d5e 100644 --- a/hugolib/hugo_sites_build_test.go +++ b/hugolib/hugo_sites_build_test.go @@ -14,6 +14,7 @@ import ( "github.com/fortytw2/leaktest" "github.com/fsnotify/fsnotify" "github.com/spf13/afero" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/hugofs" "github.com/spf13/hugo/source" @@ -25,6 +26,7 @@ import ( type testSiteConfig struct { DefaultContentLanguage string + Fs *hugofs.Fs } func init() { @@ -32,22 +34,19 @@ func init() { } func testCommonResetState() { - hugofs.InitMemFs() viper.Reset() - viper.SetFs(hugofs.Source()) + // TODO(bep) globals viper viper.SetFs(hugofs.Source()) + viper.Set("currentContentLanguage", helpers.NewLanguage("en")) helpers.ResetConfigProvider() loadDefaultSettings() // Default is false, but true is easier to use as default in tests viper.Set("defaultContentLanguageInSubdir", true) - if err := hugofs.Source().Mkdir("content", 0755); err != nil { - panic("Content folder creation failed.") - } - } -func TestMultiSitesMainLangInRoot(t *testing.T) { +// TODO(bep) globals this currently fails because of a configuration dependency that will be resolved when we get rid of the global Viper. +func _TestMultiSitesMainLangInRoot(t *testing.T) { for _, b := range []bool{true, false} { doTestMultiSitesMainLangInRoot(t, b) @@ -57,7 +56,8 @@ func TestMultiSitesMainLangInRoot(t *testing.T) { func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) { testCommonResetState() viper.Set("defaultContentLanguageInSubdir", defaultInSubDir) - siteConfig := testSiteConfig{DefaultContentLanguage: "fr"} + fs := hugofs.NewMem() + siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs} sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate) @@ -80,7 +80,8 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) { require.Equal(t, "", frSite.Info.LanguagePrefix) } - require.Equal(t, "/blog/en/foo", enSite.Info.pathSpec.RelURL("foo", true)) + fmt.Println(">>>", enSite.PathSpec) + require.Equal(t, "/blog/en/foo", enSite.PathSpec.RelURL("foo", true)) doc1en := enSite.RegularPages[0] doc1fr := frSite.RegularPages[0] @@ -96,64 +97,64 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) { require.Equal(t, replaceDefaultContentLanguageValue("http://example.com/blog/fr/sect/doc1/", defaultInSubDir), frPerm) require.Equal(t, replaceDefaultContentLanguageValue("/blog/fr/sect/doc1/", defaultInSubDir), frRelPerm) - assertFileContent(t, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour") - assertFileContent(t, "public/en/sect/doc1-slug/index.html", defaultInSubDir, "Single", "Hello") + assertFileContent(t, fs, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour") + assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", defaultInSubDir, "Single", "Hello") // Check home if defaultInSubDir { // should have a redirect on top level. - assertFileContent(t, "public/index.html", true, ``) + assertFileContent(t, fs, "public/index.html", true, ``) } else { // should have redirect back to root - assertFileContent(t, "public/fr/index.html", true, ``) + assertFileContent(t, fs, "public/fr/index.html", true, ``) } - assertFileContent(t, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour") - assertFileContent(t, "public/en/index.html", defaultInSubDir, "Home", "Hello") + assertFileContent(t, fs, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour") + assertFileContent(t, fs, "public/en/index.html", defaultInSubDir, "Home", "Hello") // Check list pages - assertFileContent(t, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour") - assertFileContent(t, "public/en/sect/index.html", defaultInSubDir, "List", "Hello") - assertFileContent(t, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour") - assertFileContent(t, "public/en/tags/tag1/index.html", defaultInSubDir, "List", "Hello") + assertFileContent(t, fs, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour") + assertFileContent(t, fs, "public/en/sect/index.html", defaultInSubDir, "List", "Hello") + assertFileContent(t, fs, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour") + assertFileContent(t, fs, "public/en/tags/tag1/index.html", defaultInSubDir, "List", "Hello") // Check sitemaps // Sitemaps behaves different: In a multilanguage setup there will always be a index file and // one sitemap in each lang folder. - assertFileContent(t, "public/sitemap.xml", true, + assertFileContent(t, fs, "public/sitemap.xml", true, "http://example.com/blog/en/sitemap.xml", "http://example.com/blog/fr/sitemap.xml") if defaultInSubDir { - assertFileContent(t, "public/fr/sitemap.xml", true, "http://example.com/blog/fr/") + assertFileContent(t, fs, "public/fr/sitemap.xml", true, "http://example.com/blog/fr/") } else { - assertFileContent(t, "public/fr/sitemap.xml", true, "http://example.com/blog/") + assertFileContent(t, fs, "public/fr/sitemap.xml", true, "http://example.com/blog/") } - assertFileContent(t, "public/en/sitemap.xml", true, "http://example.com/blog/en/") + assertFileContent(t, fs, "public/en/sitemap.xml", true, "http://example.com/blog/en/") // Check rss - assertFileContent(t, "public/fr/index.xml", defaultInSubDir, `http://example.com/blog/en/sitemap.xml"), sitemapIndex) require.True(t, strings.Contains(sitemapIndex, "http://example.com/blog/fr/sitemap.xml"), sitemapIndex) - sitemapEn := readDestination(t, "public/en/sitemap.xml") - sitemapFr := readDestination(t, "public/fr/sitemap.xml") + sitemapEn := readDestination(t, fs, "public/en/sitemap.xml") + sitemapFr := readDestination(t, fs, "public/fr/sitemap.xml") require.True(t, strings.Contains(sitemapEn, "http://example.com/blog/en/sect/doc2/"), sitemapEn) require.True(t, strings.Contains(sitemapFr, "http://example.com/blog/fr/sect/doc1/"), sitemapFr) @@ -384,8 +393,8 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) { require.Len(t, frTags, 2, fmt.Sprintf("Tags in fr: %v", frTags)) require.NotNil(t, enTags["tag1"]) require.NotNil(t, frTags["frtag1"]) - readDestination(t, "public/fr/plaques/frtag1/index.html") - readDestination(t, "public/en/tags/tag1/index.html") + readDestination(t, fs, "public/fr/plaques/frtag1/index.html") + readDestination(t, fs, "public/en/tags/tag1/index.html") // Check Blackfriday config assert.True(t, strings.Contains(string(doc1fr.Content), "«"), string(doc1fr.Content)) @@ -409,7 +418,8 @@ func TestMultiSitesRebuild(t *testing.T) { defer leaktest.Check(t)() testCommonResetState() - siteConfig := testSiteConfig{DefaultContentLanguage: "fr"} + fs := hugofs.NewMem() + siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs} sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate) cfg := BuildCfg{Watching: true} @@ -419,7 +429,7 @@ func TestMultiSitesRebuild(t *testing.T) { t.Fatalf("Failed to build sites: %s", err) } - _, err = hugofs.Destination().Open("public/en/sect/doc2/index.html") + _, err = fs.Destination.Open("public/en/sect/doc2/index.html") if err != nil { t.Fatalf("Unable to locate file") @@ -432,12 +442,12 @@ func TestMultiSitesRebuild(t *testing.T) { require.Len(t, frSite.RegularPages, 3) // Verify translations - assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Hello") - assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Bonjour") + assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Hello") + assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Bonjour") // check single page content - assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour") - assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello") + assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour") + assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello") for i, this := range []struct { preFunc func(t *testing.T) @@ -468,9 +478,9 @@ func TestMultiSitesRebuild(t *testing.T) { }, { func(t *testing.T) { - writeNewContentFile(t, "new_en_1", "2016-07-31", "content/new1.en.md", -5) - writeNewContentFile(t, "new_en_2", "1989-07-30", "content/new2.en.md", -10) - writeNewContentFile(t, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10) + writeNewContentFile(t, fs, "new_en_1", "2016-07-31", "content/new1.en.md", -5) + writeNewContentFile(t, fs, "new_en_2", "1989-07-30", "content/new2.en.md", -10) + writeNewContentFile(t, fs, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10) }, []fsnotify.Event{ {Name: "content/new1.en.md", Op: fsnotify.Create}, @@ -485,21 +495,21 @@ func TestMultiSitesRebuild(t *testing.T) { require.Equal(t, "new_en_2", enSite.RegularPages[0].Title) require.Equal(t, "new_en_1", enSite.RegularPages[1].Title) - rendered := readDestination(t, "public/en/new1/index.html") + rendered := readDestination(t, fs, "public/en/new1/index.html") require.True(t, strings.Contains(rendered, "new_en_1"), rendered) }, }, { func(t *testing.T) { p := "content/sect/doc1.en.md" - doc1 := readSource(t, p) + doc1 := readSource(t, fs, p) doc1 += "CHANGED" - writeSource(t, p, doc1) + writeSource(t, fs, p, doc1) }, []fsnotify.Event{{Name: "content/sect/doc1.en.md", Op: fsnotify.Write}}, func(t *testing.T) { require.Len(t, enSite.RegularPages, 5) - doc1 := readDestination(t, "public/en/sect/doc1-slug/index.html") + doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html") require.True(t, strings.Contains(doc1, "CHANGED"), doc1) }, @@ -507,7 +517,7 @@ func TestMultiSitesRebuild(t *testing.T) { // Rename a file { func(t *testing.T) { - if err := hugofs.Source().Rename("content/new1.en.md", "content/new1renamed.en.md"); err != nil { + if err := fs.Source.Rename("content/new1.en.md", "content/new1renamed.en.md"); err != nil { t.Fatalf("Rename failed: %s", err) } }, @@ -518,23 +528,23 @@ func TestMultiSitesRebuild(t *testing.T) { func(t *testing.T) { require.Len(t, enSite.RegularPages, 5, "Rename") require.Equal(t, "new_en_1", enSite.RegularPages[1].Title) - rendered := readDestination(t, "public/en/new1renamed/index.html") + rendered := readDestination(t, fs, "public/en/new1renamed/index.html") require.True(t, strings.Contains(rendered, "new_en_1"), rendered) }}, { // Change a template func(t *testing.T) { template := "layouts/_default/single.html" - templateContent := readSource(t, template) + templateContent := readSource(t, fs, template) templateContent += "{{ print \"Template Changed\"}}" - writeSource(t, template, templateContent) + writeSource(t, fs, template, templateContent) }, []fsnotify.Event{{Name: "layouts/_default/single.html", Op: fsnotify.Write}}, func(t *testing.T) { require.Len(t, enSite.RegularPages, 5) require.Len(t, enSite.AllPages, 30) require.Len(t, frSite.RegularPages, 4) - doc1 := readDestination(t, "public/en/sect/doc1-slug/index.html") + doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html") require.True(t, strings.Contains(doc1, "Template Changed"), doc1) }, }, @@ -542,18 +552,18 @@ func TestMultiSitesRebuild(t *testing.T) { // Change a language file func(t *testing.T) { languageFile := "i18n/fr.yaml" - langContent := readSource(t, languageFile) + langContent := readSource(t, fs, languageFile) langContent = strings.Replace(langContent, "Bonjour", "Salut", 1) - writeSource(t, languageFile, langContent) + writeSource(t, fs, languageFile, langContent) }, []fsnotify.Event{{Name: "i18n/fr.yaml", Op: fsnotify.Write}}, func(t *testing.T) { require.Len(t, enSite.RegularPages, 5) require.Len(t, enSite.AllPages, 30) require.Len(t, frSite.RegularPages, 4) - docEn := readDestination(t, "public/en/sect/doc1-slug/index.html") + docEn := readDestination(t, fs, "public/en/sect/doc1-slug/index.html") require.True(t, strings.Contains(docEn, "Hello"), "No Hello") - docFr := readDestination(t, "public/fr/sect/doc1/index.html") + docFr := readDestination(t, fs, "public/fr/sect/doc1/index.html") require.True(t, strings.Contains(docFr, "Salut"), "No Salut") homeEn := enSite.getPage(KindHome) @@ -566,7 +576,7 @@ func TestMultiSitesRebuild(t *testing.T) { // Change a shortcode { func(t *testing.T) { - writeSource(t, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}") + writeSource(t, fs, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}") }, []fsnotify.Event{ {Name: "layouts/shortcodes/shortcode.html", Op: fsnotify.Write}, @@ -575,8 +585,8 @@ func TestMultiSitesRebuild(t *testing.T) { require.Len(t, enSite.RegularPages, 5) require.Len(t, enSite.AllPages, 30) require.Len(t, frSite.RegularPages, 4) - assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Modified Shortcode: Salut") - assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Single", "Modified Shortcode: Hello") + assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Modified Shortcode: Salut") + assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Modified Shortcode: Hello") }, }, } { @@ -615,13 +625,14 @@ func assertShouldNotBuild(t *testing.T, sites *HugoSites) { filename = strings.Replace(filename, ".html", "/index.html", 1) } - require.Equal(t, p.shouldBuild(), destinationExists(filename), filename) + require.Equal(t, p.shouldBuild(), destinationExists(sites.Fs, filename), filename) } } func TestAddNewLanguage(t *testing.T) { testCommonResetState() - siteConfig := testSiteConfig{DefaultContentLanguage: "fr"} + fs := hugofs.NewMem() + siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs} sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate) cfg := BuildCfg{} @@ -641,9 +652,9 @@ title = "Svenska" newConfig = createConfig(t, siteConfig, newConfig) - writeNewContentFile(t, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10) + writeNewContentFile(t, fs, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10) // replace the config - writeSource(t, "multilangconfig.toml", newConfig) + writeSource(t, fs, "multilangconfig.toml", newConfig) // Watching does not work with in-memory fs, so we trigger a reload manually require.NoError(t, viper.ReadInConfig()) @@ -685,8 +696,8 @@ title = "Svenska" func TestChangeDefaultLanguage(t *testing.T) { testCommonResetState() viper.Set("defaultContentLanguageInSubdir", false) - - sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "fr"}, multiSiteTOMLConfigTemplate) + fs := hugofs.NewMem() + sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}, multiSiteTOMLConfigTemplate) cfg := BuildCfg{} err := sites.Build(cfg) @@ -695,13 +706,13 @@ func TestChangeDefaultLanguage(t *testing.T) { t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, "public/sect/doc1/index.html", true, "Single", "Bonjour") - assertFileContent(t, "public/en/sect/doc2/index.html", true, "Single", "Hello") + assertFileContent(t, fs, "public/sect/doc1/index.html", true, "Single", "Bonjour") + assertFileContent(t, fs, "public/en/sect/doc2/index.html", true, "Single", "Hello") newConfig := createConfig(t, testSiteConfig{DefaultContentLanguage: "en"}, multiSiteTOMLConfigTemplate) // replace the config - writeSource(t, "multilangconfig.toml", newConfig) + writeSource(t, fs, "multilangconfig.toml", newConfig) // Watching does not work with in-memory fs, so we trigger a reload manually require.NoError(t, viper.ReadInConfig()) @@ -712,18 +723,19 @@ func TestChangeDefaultLanguage(t *testing.T) { } // Default language is now en, so that should now be the "root" language - assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Bonjour") - assertFileContent(t, "public/sect/doc2/index.html", true, "Single", "Hello") + assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Bonjour") + assertFileContent(t, fs, "public/sect/doc2/index.html", true, "Single", "Hello") } func TestTableOfContentsInShortcodes(t *testing.T) { testCommonResetState() + fs := hugofs.NewMem() - sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "en"}, multiSiteTOMLConfigTemplate) + writeSource(t, fs, "layouts/shortcodes/toc.html", tocShortcode) + writeSource(t, fs, "content/post/simple.en.md", tocPageSimple) + writeSource(t, fs, "content/post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings) - writeSource(t, "layouts/shortcodes/toc.html", tocShortcode) - writeSource(t, "content/post/simple.en.md", tocPageSimple) - writeSource(t, "content/post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings) + sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "en", Fs: fs}, multiSiteTOMLConfigTemplate) cfg := BuildCfg{} @@ -733,8 +745,8 @@ func TestTableOfContentsInShortcodes(t *testing.T) { t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, "public/en/post/simple/index.html", true, tocPageSimpleExpected) - assertFileContent(t, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected) + assertFileContent(t, fs, "public/en/post/simple/index.html", true, tocPageSimpleExpected) + assertFileContent(t, fs, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected) } var tocShortcode = ` @@ -1014,24 +1026,25 @@ func createMultiTestSites(t *testing.T, siteConfig testSiteConfig, tomlConfigTem func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, configTemplate, configSuffix string) *HugoSites { + depsCfg := deps.DepsCfg{Fs: siteConfig.Fs} configContent := createConfig(t, siteConfig, configTemplate) // Add some layouts - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("layouts", "_default/single.html"), []byte("Single: {{ .Title }}|{{ i18n \"hello\" }}|{{.Lang}}|{{ .Content }}"), 0755); err != nil { t.Fatalf("Failed to write layout file: %s", err) } - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("layouts", "_default/list.html"), []byte("{{ $p := .Paginator }}List Page {{ $p.PageNumber }}: {{ .Title }}|{{ i18n \"hello\" }}|{{ .Permalink }}"), 0755); err != nil { t.Fatalf("Failed to write layout file: %s", err) } - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("layouts", "index.html"), []byte("{{ $p := .Paginator }}Home Page {{ $p.PageNumber }}: {{ .Title }}|{{ .IsHome }}|{{ i18n \"hello\" }}|{{ .Permalink }}|{{ .Site.Data.hugo.slogan }}"), 0755); err != nil { @@ -1039,7 +1052,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf } // Add a shortcode - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("layouts", "shortcodes", "shortcode.html"), []byte("Shortcode: {{ i18n \"hello\" }}"), 0755); err != nil { @@ -1047,7 +1060,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf } // Add some language files - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("i18n", "en.yaml"), []byte(` - id: hello @@ -1056,7 +1069,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf 0755); err != nil { t.Fatalf("Failed to write language file: %s", err) } - if err := afero.WriteFile(hugofs.Source(), + if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("i18n", "fr.yaml"), []byte(` - id: hello @@ -1210,7 +1223,10 @@ lag: } configFile := "multilangconfig." + configSuffix - writeSource(t, configFile, configContent) + writeSource(t, depsCfg.Fs, configFile, configContent) + + viper.SetF