diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-02-05 10:20:06 +0700 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-02-17 17:15:26 +0100 |
commit | 93ca7c9e958e34469a337e4efcc7c75774ec50fd (patch) | |
tree | 5dfa296cfe74fd5ef8f0d41ea4078704f453aa04 /hugolib | |
parent | e34af6ee30f70f5780a281e2fd8f4ed9b487ee61 (diff) |
all: Refactor to nonglobal Viper, i18n etc.
This is a final rewrite that removes all the global state in Hugo, which also enables
the use if `t.Parallel` in tests.
Updates #2701
Fixes #3016
Diffstat (limited to 'hugolib')
47 files changed, 1667 insertions, 1616 deletions
diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go index 22803d22e..653156495 100644 --- a/hugolib/alias_test.go +++ b/hugolib/alias_test.go @@ -18,7 +18,6 @@ import ( "testing" "github.com/spf13/hugo/deps" - "github.com/spf13/hugo/hugofs" "github.com/stretchr/testify/require" ) @@ -33,38 +32,44 @@ const basicTemplate = "<html><body>{{.Content}}</body></html>" const aliasTemplate = "<html><body>ALIASTEMPLATE</body></html>" func TestAlias(t *testing.T) { - testCommonResetState() + t.Parallel() - fs := hugofs.NewMem() + var ( + cfg, fs = newTestCfg() + th = testHelper{cfg} + ) 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{}) + buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) // the real page - assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") + th.assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") // the alias redirector - assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ") + th.assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ") } func TestAliasTemplate(t *testing.T) { - testCommonResetState() + t.Parallel() - fs := hugofs.NewMem() + var ( + cfg, fs = newTestCfg() + th = testHelper{cfg} + ) writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias) writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate) writeSource(t, fs, filepath.Join("layouts", "alias.html"), aliasTemplate) - sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs}) + sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) require.NoError(t, err) require.NoError(t, sites.Build(BuildCfg{})) // the real page - assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") + th.assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") // the alias redirector - assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE") + th.assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE") } diff --git a/hugolib/case_insensitive_test.go b/hugolib/case_insensitive_test.go index eefde1727..802f6c70b 100644 --- a/hugolib/case_insensitive_test.go +++ b/hugolib/case_insensitive_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "github.com/spf13/viper" - + "github.com/spf13/afero" "github.com/spf13/hugo/deps" "github.com/spf13/hugo/hugofs" + "github.com/stretchr/testify/require" ) var ( @@ -111,26 +111,28 @@ ColorS: ` ) -func caseMixingTestsWriteCommonSources(t *testing.T, fs *hugofs.Fs) { - writeSource(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1) - writeSource(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2) - writeSource(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En) +func caseMixingTestsWriteCommonSources(t *testing.T, fs afero.Fs) { + writeToFs(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1) + writeToFs(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2) + writeToFs(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En) - writeSource(t, fs, "layouts/shortcodes/shortcode.html", ` + writeToFs(t, fs, "layouts/shortcodes/shortcode.html", ` Shortcode Page: {{ .Page.Params.COLOR }}|{{ .Page.Params.Colors.Blue }} Shortcode Site: {{ .Page.Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} `) - writeSource(t, fs, "layouts/partials/partial.html", ` + writeToFs(t, fs, "layouts/partials/partial.html", ` Partial Page: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} Partial Site: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} `) - writeSource(t, fs, "config.toml", caseMixingSiteConfigTOML) + writeToFs(t, fs, "config.toml", caseMixingSiteConfigTOML) } func TestCaseInsensitiveConfigurationVariations(t *testing.T) { + t.Parallel() + // See issues 2615, 1129, 2590 and maybe some others // Also see 2598 // @@ -143,22 +145,22 @@ func TestCaseInsensitiveConfigurationVariations(t *testing.T) { // language: new and overridden values, in regular fields and nested paramsmap // page frontmatter: regular fields, blackfriday config, param with nested map - testCommonResetState() + mm := afero.NewMemMapFs() - depsCfg := newTestDepsConfig() - viper.SetFs(depsCfg.Fs.Source) + caseMixingTestsWriteCommonSources(t, mm) - caseMixingTestsWriteCommonSources(t, depsCfg.Fs) + cfg, err := LoadConfig(mm, "", "config.toml") + require.NoError(t, err) - if err := LoadGlobalConfig("", "config.toml"); err != nil { - t.Fatalf("Failed to load config: %s", err) - } + fs := hugofs.NewFrom(mm, cfg) + + th := testHelper{cfg} - writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "baseof.html"), ` + writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), ` Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} {{ block "main" . }}default{{end}}`) - writeSource(t, depsCfg.Fs, filepath.Join("layouts", "sect2", "single.html"), ` + writeSource(t, fs, filepath.Join("layouts", "sect2", "single.html"), ` {{ define "main"}} Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }} Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }} @@ -167,7 +169,7 @@ Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }} {{ end }} `) - writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "single.html"), ` + writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), ` Page Title: {{ .Title }} Site Title: {{ .Site.Title }} Site Lang Mood: {{ .Site.Language.Params.MOoD }} @@ -177,7 +179,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} {{ partial "partial.html" . }} `) - sites, err := NewHugoSitesFromConfiguration(depsCfg) + sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) if err != nil { t.Fatalf("Failed to create sites: %s", err) @@ -189,7 +191,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, + th.assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, "Page Colors: red|heavenly", "Site Colors: green|yellow", "Site Lang Mood: Happy", @@ -202,7 +204,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} "«Hi»", // angled quotes ) - assertFileContent(t, sites.Fs, filepath.Join("public", "en", "sect1", "page1", "index.html"), true, + th.assertFileContent(t, sites.Fs, filepath.Join("public", "en", "sect1", "page1", "index.html"), true, "Site Colors: Pink|golden", "Page Colors: black|bluesy", "Site Lang Mood: Thoughtful", @@ -211,7 +213,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} "“Hi”", ) - assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true, + th.assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true, "Page Colors: black|sky", "Site Colors: green|yellow", "Shortcode Page: black|sky", @@ -222,6 +224,8 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} } func TestCaseInsensitiveConfigurationForAllTemplateEngines(t *testing.T) { + t.Parallel() + noOp := func(s string) string { return s } @@ -252,16 +256,16 @@ func TestCaseInsensitiveConfigurationForAllTemplateEngines(t *testing.T) { func doTestCaseInsensitiveConfigurationForTemplateEngine(t *testing.T, suffix string, templateFixer func(s string) string) { - testCommonResetState() + mm := afero.NewMemMapFs() - fs := hugofs.NewMem() - viper.SetFs(fs.Source) + caseMixingTestsWriteCommonSources(t, mm) - caseMixingTestsWriteCommonSources(t, fs) + cfg, err := LoadConfig(mm, "", "config.toml") + require.NoError(t, err) - if err := LoadGlobalConfig("", "config.toml"); err != nil { - t.Fatalf("Failed to load config: %s", err) - } + fs := hugofs.NewFrom(mm, cfg) + + th := testHelper{cfg} t.Log("Testing", suffix) @@ -280,7 +284,7 @@ p writeSource(t, fs, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ) - sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs}) + sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) if err != nil { t.Fatalf("Failed to create sites: %s", err) @@ -292,7 +296,7 @@ p t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, + th.assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, "Page Colors: red|heavenly", "Site Colors: green|yellow", "Shortcode Page: red|heavenly", diff --git a/hugolib/config.go b/hugolib/config.go index e6d28051e..552b19dac 100644 --- a/hugolib/config.go +++ b/hugolib/config.go @@ -16,94 +16,100 @@ package hugolib import ( "fmt" + "github.com/spf13/afero" "github.com/spf13/hugo/helpers" "github.com/spf13/viper" ) -// LoadGlobalConfig loads Hugo configuration into the global Viper. -func LoadGlobalConfig(relativeSourcePath, configFilename string) error { +// LoadConfig loads Hugo configuration into a new Viper and then adds +// a set of defaults. +func LoadConfig(fs afero.Fs, relativeSourcePath, configFilename string) (*viper.Viper, error) { + v := viper.New() + v.SetFs(fs) if relativeSourcePath == "" { relativeSourcePath = "." } - viper.AutomaticEnv() - viper.SetEnvPrefix("hugo") - viper.SetConfigFile(configFilename) + v.AutomaticEnv() + v.SetEnvPrefix("hugo") + v.SetConfigFile(configFilename) // See https://github.com/spf13/viper/issues/73#issuecomment-126970794 if relativeSourcePath == "" { - viper.AddConfigPath(".") + v.AddConfigPath(".") } else { - viper.AddConfigPath(relativeSourcePath) + v.AddConfigPath(relativeSourcePath) } - err := viper.ReadInConfig() + err := v.ReadInConfig() if err != nil { if _, ok := err.(viper.ConfigParseError); ok { - return err + return nil, err } - return fmt.Errorf("Unable to locate Config file. Perhaps you need to create a new site.\n Run `hugo help new` for details. (%s)\n", err) + return nil, fmt.Errorf("Unable to locate Config file. Perhaps you need to create a new site.\n Run `hugo help new` for details. (%s)\n", err) } - viper.RegisterAlias("indexes", "taxonomies") + v.RegisterAlias("indexes", "taxonomies") - loadDefaultSettings() + loadDefaultSettingsFor(v) - return nil + return v, nil } -func loadDefaultSettings() { - viper.SetDefault("cleanDestinationDir", false) - viper.SetDefault("watch", false) - viper.SetDefault("metaDataFormat", "toml") - viper.SetDefault("disable404", false) - viper.SetDefault("disableRSS", false) - viper.SetDefault("disableSitemap", false) - viper.SetDefault("disableRobotsTXT", false) - viper.SetDefault("contentDir", "content") - viper.SetDefault("layoutDir", "layouts") - viper.SetDefault("staticDir", "static") - viper.SetDefault("archetypeDir", "archetypes") - viper.SetDefault("publishDir", "public") - viper.SetDefault("dataDir", "data") - viper.SetDefault("i18nDir", "i18n") - viper.SetDefault("themesDir", "themes") - viper.SetDefault("defaultLayout", "post") - viper.SetDefault("buildDrafts", false) - viper.SetDefault("buildFuture", false) - viper.SetDefault("buildExpired", false) - viper.SetDefault("uglyURLs", false) - viper.SetDefault("verbose", false) - viper.SetDefault("ignoreCache", false) - viper.SetDefault("canonifyURLs", false) - viper.SetDefault("relativeURLs", false) - viper.SetDefault("removePathAccents", false) - viper.SetDefault("taxonomies", map[string]string{"tag": "tags", "category": "categories"}) - viper.SetDefault("permalinks", make(PermalinkOverrides, 0)) - viper.SetDefault("sitemap", Sitemap{Priority: -1, Filename: "sitemap.xml"}) - viper.SetDefault("defaultExtension", "html") - viper.SetDefault("pygmentsStyle", "monokai") - viper.SetDefault("pygmentsUseClasses", false) - viper.SetDefault("pygmentsCodeFences", false) - viper.SetDefault("pygmentsOptions", "") - viper.SetDefault("disableLiveReload", false) - viper.SetDefault("pluralizeListTitles", true) - viper.SetDefault("preserveTaxonomyNames", false) - viper.SetDefault("forceSyncStatic", false) - viper.SetDefault("footnoteAnchorPrefix", "") - viper.SetDefault("footnoteReturnLinkContents", "") - viper.SetDefault("newContentEditor", "") - viper.SetDefault("paginate", 10) - viper.SetDefault("paginatePath", "page") - viper.SetDefault("blackfriday", helpers.NewBlackfriday(viper.GetViper())) - viper.SetDefault("rSSUri", "index.xml") - viper.SetDefault("sectionPagesMenu", "") - viper.SetDefault("disablePathToLower", false) - viper.SetDefault("hasCJKLanguage", false) - viper.SetDefault("enableEmoji", false) - viper.SetDefault("pygmentsCodeFencesGuessSyntax", false) - viper.SetDefault("useModTimeAsFallback", false) - viper.SetDefault("currentContentLanguage", helpers.NewDefaultLanguage()) - viper.SetDefault("defaultContentLanguage", "en") - viper.SetDefault("defaultContentLanguageInSubdir", false) - viper.SetDefault("enableMissingTranslationPlaceholders", false) - viper.SetDefault("enableGitInfo", false) +func loadDefaultSettingsFor(v *viper.Viper) { + + c := helpers.NewContentSpec(v) + + v.SetDefault("cleanDestinationDir", false) + v.SetDefault("watch", false) + v.SetDefault("metaDataFormat", "toml") + v.SetDefault("disable404", false) + v.SetDefault("disableRSS", false) + v.SetDefault("disableSitemap", false) + v.SetDefault("disableRobotsTXT", false) + v.SetDefault("contentDir", "content") + v.SetDefault("layoutDir", "layouts") + v.SetDefault("staticDir", "static") + v.SetDefault("archetypeDir", "archetypes") + v.SetDefault("publishDir", "public") + v.SetDefault("dataDir", "data") + v.SetDefault("i18nDir", "i18n") + v.SetDefault("themesDir", "themes") + v.SetDefault("defaultLayout", "post") + v.SetDefault("buildDrafts", false) + v.SetDefault("buildFuture", false) + v.SetDefault("buildExpired", false) + v.SetDefault("uglyURLs", false) + v.SetDefault("verbose", false) + v.SetDefault("ignoreCache", false) + v.SetDefault("canonifyURLs", false) + v.SetDefault("relativeURLs", false) + v.SetDefault("removePathAccents", false) + v.SetDefault("taxonomies", map[string]string{"tag": "tags", "category": "categories"}) + v.SetDefault("permalinks", make(PermalinkOverrides, 0)) + v.SetDefault("sitemap", Sitemap{Priority: -1, Filename: "sitemap.xml"}) + v.SetDefault("defaultExtension", "html") + v.SetDefault("pygmentsStyle", "monokai") + v.SetDefault("pygmentsUseClasses", false) + v.SetDefault("pygmentsCodeFences", false) + v.SetDefault("pygmentsOptions", "") + v.SetDefault("disableLiveReload", false) + v.SetDefault("pluralizeListTitles", true) + v.SetDefault("preserveTaxonomyNames", false) + v.SetDefault("forceSyncStatic", false) + v.SetDefault("footnoteAnchorPrefix", "") + v.SetDefault("footnoteReturnLinkContents", "") + v.SetDefault("newContentEditor", "") + v.SetDefault("paginate", 10) + v.SetDefault("paginatePath", "page") + v.SetDefault("blackfriday", c.NewBlackfriday()) + v.SetDefault("rSSUri", "index.xml") + v.SetDefault("sectionPagesMenu", "") + v.SetDefault("disablePathToLower", false) + v.SetDefault("hasCJKLanguage", false) + v.SetDefault("enableEmoji", false) + v.SetDefault("pygmentsCodeFencesGuessSyntax", false) + v.SetDefault("useModTimeAsFallback", false) + v.SetDefault("defaultContentLanguage", "en") + v.SetDefault("defaultContentLanguageInSubdir", false) + v.SetDefault("enableMissingTranslationPlaceholders", false) + v.SetDefault("enableGitInfo", false) } diff --git a/hugolib/config_test.go b/hugolib/config_test.go index cbfc71a22..780e5c33d 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -16,28 +16,28 @@ package hugolib import ( "testing" - "github.com/spf13/hugo/helpers" - - "github.com/spf13/hugo/hugofs" - "github.com/spf13/viper" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestLoadGlobalConfig(t *testing.T) { +func TestLoadConfig(t *testing.T) { + t.Parallel() + // Add a random config variable for testing. // side = page in Norwegian. configContent := ` PaginatePath = "side" ` - fs := hugofs.NewMem() - viper.SetFs(fs.Source) + mm := afero.NewMemMapFs() + + writeToFs(t, mm, "hugo.toml", configContent) - writeSource(t, fs, "hugo.toml", configContent) + cfg, err := LoadConfig(mm, "", "hugo.toml") + require.NoError(t, err) - require.NoError(t, LoadGlobalConfig("", "hugo.toml")) - assert.Equal(t, "side", helpers.Config().GetString("paginatePath")) + assert.Equal(t, "side", cfg.GetString("paginatePath")) // default - assert.Equal(t, "layouts", viper.GetString("layoutDir")) + assert.Equal(t, "layouts", cfg.GetString("layoutDir")) } diff --git a/hugolib/datafiles_test.go b/hugolib/datafiles_test.go index 0f848594a..3aac3201f 100644 --- a/hugolib/datafiles_test.go +++ b/hugolib/datafiles_test.go @@ -19,15 +19,24 @@ import ( "strings" "testing" + "io/ioutil" + "log" + "os" + + "github.com/spf13/hugo/deps" + jww "github.com/spf13/jwalterweatherman" + "github.com/spf13/hugo/parser" "github.com/spf13/hugo/source" "github.com/stretchr/testify/require" ) func TestDataDirJSON(t *testing.T) { + t.Parallel() + sources := []source.ByteSource{ - {Name: filepath.FromSlash("test/foo.json"), Content: []byte(`{ "bar": "foofoo" }`)}, - {Name: filepath.FromSlash("test.json"), Content: []byte(`{ "hello": [ { "world": "foo" } ] }`)}, + {Name: filepath.FromSlash("data/test/foo.json"), Content: []byte(`{ "bar": "foofoo" }`)}, + {Name: filepath.FromSlash("data/test.json"), Content: |