Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--commands/benchmark.go2
-rw-r--r--commands/commandeer.go46
-rw-r--r--commands/convert.go19
-rw-r--r--commands/hugo.go326
-rw-r--r--commands/list.go19
-rw-r--r--commands/list_config.go10
-rw-r--r--commands/new.go19
-rw-r--r--commands/new_test.go24
-rw-r--r--commands/server.go38
-rw-r--r--commands/server_test.go8
-rw-r--r--config/configProvider.go (renamed from helpers/configProvider.go)37
-rw-r--r--create/content.go34
-rw-r--r--create/content_test.go43
-rw-r--r--deps/deps.go67
-rw-r--r--helpers/content.go74
-rw-r--r--helpers/content_renderer.go57
-rw-r--r--helpers/content_renderer_test.go34
-rw-r--r--helpers/content_test.go49
-rw-r--r--helpers/general.go5
-rw-r--r--helpers/language.go46
-rw-r--r--helpers/language_test.go7
-rw-r--r--helpers/path.go49
-rw-r--r--helpers/path_test.go44
-rw-r--r--helpers/pathspec.go54
-rw-r--r--helpers/pathspec_test.go38
-rw-r--r--helpers/pygments.go26
-rw-r--r--helpers/pygments_test.go19
-rw-r--r--helpers/testhelpers_test.go37
-rw-r--r--helpers/url.go7
-rw-r--r--helpers/url_test.go42
-rw-r--r--hugofs/fs.go25
-rw-r--r--hugofs/fs_test.go19
-rw-r--r--hugolib/alias_test.go27
-rw-r--r--hugolib/case_insensitive_test.go68
-rw-r--r--hugolib/config.go144
-rw-r--r--hugolib/config_test.go22
-rw-r--r--hugolib/datafiles_test.go103
-rw-r--r--hugolib/embedded_shortcodes_test.go282
-rw-r--r--hugolib/gitinfo.go7
-rw-r--r--hugolib/handler_page.go10
-rw-r--r--hugolib/handler_test.go16
-rw-r--r--hugolib/hugo_sites.go91
-rw-r--r--hugolib/hugo_sites_build.go4
-rw-r--r--hugolib/hugo_sites_build_test.go371
-rw-r--r--hugolib/i18n.go42
-rw-r--r--hugolib/menu_test.go96
-rw-r--r--hugolib/multilingual.go17
-rw-r--r--hugolib/node_as_page_test.go264
-rw-r--r--hugolib/page.go51
-rw-r--r--hugolib/pageCache_test.go8
-rw-r--r--hugolib/pageGroup_test.go44
-rw-r--r--hugolib/pageSort.go8
-rw-r--r--hugolib/pageSort_test.go56
-rw-r--r--hugolib/page_permalink_test.go68
-rw-r--r--hugolib/page_taxonomy_test.go4
-rw-r--r--hugolib/page_test.go200
-rw-r--r--hugolib/page_time_integration_test.go8
-rw-r--r--hugolib/pagesPrevNext_test.go17
-rw-r--r--hugolib/pagination.go10
-rw-r--r--hugolib/pagination_test.go377
-rw-r--r--hugolib/path_separators_test.go8
-rw-r--r--hugolib/path_separators_windows_test.go4
-rw-r--r--hugolib/permalinks_test.go10
-rw-r--r--hugolib/robotstxt_test.go19
-rw-r--r--hugolib/rss_test.go23
-rw-r--r--hugolib/scratch_test.go9
-rw-r--r--hugolib/shortcode.go35
-rw-r--r--hugolib/shortcode_test.go121
-rw-r--r--hugolib/shortcodeparser_test.go1
-rw-r--r--hugolib/site.go204
-rw-r--r--hugolib/siteJSONEncode_test.go7
-rw-r--r--hugolib/site_render.go23
-rw-r--r--hugolib/site_test.go227
-rw-r--r--hugolib/site_url_test.go31
-rw-r--r--hugolib/sitemap_test.go15
-rw-r--r--hugolib/taxonomy_test.go12
-rw-r--r--hugolib/template_engines_test.go14
-rw-r--r--hugolib/template_test.go39
-rw-r--r--hugolib/testhelpers_test.go66
-rw-r--r--i18n/i18n.go (renamed from tpl/template_i18n.go)68
-rw-r--r--i18n/i18n_test.go (renamed from tpl/template_i18n_test.go)40
-rw-r--r--i18n/translationProvider.go73
-rw-r--r--source/content_directory_test.go13
-rw-r--r--source/file.go27
-rw-r--r--source/file_test.go26
-rw-r--r--source/filesystem.go31
-rw-r--r--source/filesystem_test.go15
-rw-r--r--source/inmemory.go14
-rw-r--r--target/page_test.go3
-rw-r--r--tpl/template.go15
-rw-r--r--tpl/template_ast_transformers_test.go3
-rw-r--r--tpl/template_func_truncate_test.go1
-rw-r--r--tpl/template_funcs.go25
-rw-r--r--tpl/template_funcs_test.go167
-rw-r--r--tpl/template_resources.go40
-rw-r--r--tpl/template_resources_test.go54
-rw-r--r--tpl/template_test.go37
-rw-r--r--transform/livereloadinject.go31
-rw-r--r--transform/livereloadinject_test.go5
99 files changed, 2840 insertions, 2455 deletions
diff --git a/commands/benchmark.go b/commands/benchmark.go
index 42966c67a..4f9ab828b 100644
--- a/commands/benchmark.go
+++ b/commands/benchmark.go
@@ -54,7 +54,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
return err
}
- c := commandeer{cfg}
+ c := newCommandeer(cfg)
var memProf *os.File
if memProfileFile != "" {
diff --git a/commands/commandeer.go b/commands/commandeer.go
new file mode 100644
index 000000000..e7fd70651
--- /dev/null
+++ b/commands/commandeer.go
@@ -0,0 +1,46 @@
+// Copyright 2017 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 commands
+
+import (
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/helpers"
+)
+
+type commandeer struct {
+ *deps.DepsCfg
+ pathSpec *helpers.PathSpec
+ configured bool
+}
+
+func (c *commandeer) Set(key string, value interface{}) {
+ if c.configured {
+ panic("commandeer cannot be changed")
+ }
+ c.Cfg.Set(key, value)
+}
+
+// PathSpec lazily creates a new PathSpec, as all the paths must
+// be configured before it is created.
+func (c *commandeer) PathSpec() *helpers.PathSpec {
+ c.configured = true
+ if c.pathSpec == nil {
+ c.pathSpec = helpers.NewPathSpec(c.Fs, c.Cfg)
+ }
+ return c.pathSpec
+}
+
+func newCommandeer(cfg *deps.DepsCfg) *commandeer {
+ return &commandeer{DepsCfg: cfg}
+}
diff --git a/commands/convert.go b/commands/convert.go
index 79c3c75e8..c24282550 100644
--- a/commands/convert.go
+++ b/commands/convert.go
@@ -21,11 +21,8 @@ import (
"github.com/spf13/cast"
"github.com/spf13/cobra"
- "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/parser"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
var outputDir string
@@ -86,7 +83,7 @@ func convertContents(mark rune) error {
return err
}
- h, err := hugolib.NewHugoSitesFromConfiguration(cfg)
+ h, err := hugolib.NewHugoSites(*cfg)
if err != nil {
return err
}
@@ -104,10 +101,10 @@ func convertContents(mark rune) error {
return errors.New("No source files found")
}
- contentDir := helpers.AbsPathify(viper.GetString("contentDir"))
- jww.FEEDBACK.Println("processing", len(site.Source.Files()), "content files")
+ contentDir := site.PathSpec.AbsPathify(site.Cfg.GetString("contentDir"))
+ site.Log.FEEDBACK.Println("processing", len(site.Source.Files()), "content files")
for _, file := range site.Source.Files() {
- jww.INFO.Println("Attempting to convert", file.LogicalName())
+ site.Log.INFO.Println("Attempting to convert", file.LogicalName())
page, err := site.NewPage(file.LogicalName())
if err != nil {
return err
@@ -115,12 +112,12 @@ func convertContents(mark rune) error {
psr, err := parser.ReadFrom(file.Contents)
if err != nil {
- jww.ERROR.Println("Error processing file:", file.Path())
+ site.Log.ERROR.Println("Error processing file:", file.Path())
return err
}
metadata, err := psr.Metadata()
if err != nil {
- jww.ERROR.Println("Error processing file:", file.Path())
+ site.Log.ERROR.Println("Error processing file:", file.Path())
return err
}
@@ -139,7 +136,7 @@ func convertContents(mark rune) error {
page.SetDir(filepath.Join(contentDir, file.Dir()))
page.SetSourceContent(psr.Content())
if err = page.SetSourceMetaData(metadata, mark); err != nil {
- jww.ERROR.Printf("Failed to set source metadata for file %q: %s. For more info see For more info see https://github.com/spf13/hugo/issues/2458", page.FullFilePath(), err)
+ site.Log.ERROR.Printf("Failed to set source metadata for file %q: %s. For more info see For more info see https://github.com/spf13/hugo/issues/2458", page.FullFilePath(), err)
continue
}
@@ -153,7 +150,7 @@ func convertContents(mark rune) error {
return fmt.Errorf("Failed to save file %q: %s", page.FullFilePath(), err)
}
} else {
- jww.FEEDBACK.Println("Unsafe operation not allowed, use --unsafe or set a different output path")
+ site.Log.FEEDBACK.Println("Unsafe operation not allowed, use --unsafe or set a different output path")
}
}
}
diff --git a/commands/hugo.go b/commands/hugo.go
index 566e68603..3e8351658 100644
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -27,8 +27,7 @@ import (
"sync"
"time"
- "github.com/spf13/hugo/tpl"
-
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/parser"
@@ -51,10 +50,6 @@ import (
"github.com/spf13/viper"
)
-type commandeer struct {
- deps.DepsCfg
-}
-
// Hugo represents the Hugo sites to build. This variable is exported as it
// is used by at least one external library (the Hugo caddy plugin). We should
// provide a cleaner external API, but until then, this is it.
@@ -64,7 +59,6 @@ var Hugo *hugolib.HugoSites
// for benchmark testing etc. via the CLI commands.
func Reset() error {
Hugo = nil
- viper.Reset()
return nil
}
@@ -124,10 +118,10 @@ Complete documentation is available at http://gohugo.io/.`,
return err
}
- c := commandeer{cfg}
+ c := newCommandeer(cfg)
if buildWatch {
- viper.Set("disableLiveReload", true)
+ cfg.Cfg.Set("disableLiveReload", true)
c.watchConfig()
}
@@ -148,16 +142,17 @@ var (
)
var (
- baseURL string
- cacheDir string
- contentDir string
- layoutDir string
- cfgFile string
- destination string
- logFile string
- theme string
- themesDir string
- source string
+ baseURL string
+ cacheDir string
+ contentDir string
+ layoutDir string
+ cfgFile string
+ destination string
+ logFile string
+ theme string
+ themesDir string
+ source string
+ logI18nWarnings bool
)
// Execute adds all child commands to the root command HugoCmd and sets flags appropriately.
@@ -242,7 +237,7 @@ func initHugoBuildCommonFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("forceSyncStatic", "", false, "Copy all files when static is changed.")
cmd.Flags().BoolP("noTimes", "", false, "Don't sync modification time of files")
cmd.Flags().BoolP("noChmod", "", false, "Don't sync permission mode of files")
- cmd.Flags().BoolVarP(&tpl.Logi18nWarnings, "i18n-warnings", "", false, "Print missing translations")
+ cmd.Flags().BoolVarP(&logI18nWarnings, "i18n-warnings", "", false, "Print missing translations")
// Set bash-completion.
// Each flag must first be defined before using the SetAnnotation() call.
@@ -275,39 +270,56 @@ func init() {
}
// InitializeConfig initializes a config file with sensible default configuration flags.
-func InitializeConfig(subCmdVs ...*cobra.Command) (deps.DepsCfg, error) {
+func InitializeConfig(subCmdVs ...*cobra.Command) (*deps.DepsCfg, error) {
- var cfg deps.DepsCfg
+ var cfg *deps.DepsCfg = &deps.DepsCfg{}
- if err := hugolib.LoadGlobalConfig(source, cfgFile); err != nil {
+ // Init file systems. This may be changed at a later point.
+ osFs := hugofs.Os
+
+ config, err := hugolib.LoadConfig(osFs, source, cfgFile)
+ if err != nil {
return cfg, err
}
+ cfg.Cfg = config
+
+ c := newCommandeer(cfg)
+
for _, cmdV := range append([]*cobra.Command{hugoCmdV}, subCmdVs...) {
- initializeFlags(cmdV)
+ c.initializeFlags(cmdV)
+ }
+
+ logger, err := createLogger(cfg.Cfg)
+ if err != nil {
+ return cfg, err
}
+ cfg.Logger = logger
+
+ config.Set("logI18nWarnings", logI18nWarnings)
+
if baseURL != "" {
if !strings.HasSuffix(baseURL, "/") {
baseURL = baseURL + "/"
}
- viper.Set("baseURL", baseURL)
+ config.Set("baseURL", baseURL)
}
- if !viper.GetBool("relativeURLs") && viper.GetString("baseURL") == "" {
- jww.ERROR.Println("No 'baseURL' set in configuration or as a flag. Features like page menus will not work without one.")
+ if !config.GetBool("relativeURLs") && config.GetString("baseURL") == "" {
+ cfg.Logger.ERROR.Println("No 'baseURL' set in configuration or as a flag. Features like page menus will not work without one.")
}
if theme != "" {
- viper.Set("theme", theme)
+ config.Set("theme", theme)
}
if themesDir != "" {
- viper.Set("themesDir", themesDir)
+ config.Set("themesDir", themesDir)
}
if destination != "" {
- viper.Set("publishDir", destination)
+ config.Set("publishDir", destination)
}
var dir string
@@ -316,24 +328,32 @@ func InitializeConfig(subCmdVs ...*cobra.Command) (deps.DepsCfg, error) {
} else {
dir, _ = os.Getwd()
}
- viper.Set("workingDir", dir)
+ config.Set("workingDir", dir)
+
+ cfg.Fs = hugofs.NewFrom(osFs, config)
+
+ // Hugo writes the output to memory instead of the disk.
+ // This is only used for benchmark testing. Cause the content is only visible
+ // in memory.
+ if renderToMemory {
+ c.Fs.Destination = new(afero.MemMapFs)
+ // Rendering to memoryFS, publish to Root regardless of publishDir.
+ c.Set("publishDir", "/")
+ }
if contentDir != "" {
- viper.Set("contentDir", contentDir)
+ config.Set("contentDir", contentDir)
}
if layoutDir != "" {
- viper.Set("layoutDir", layoutDir)
+ config.Set("layoutDir", layoutDir)
}
if cacheDir != "" {
- viper.Set("cacheDir", cacheDir)
+ config.Set("cacheDir", cacheDir)
}
- // Init file systems. This may be changed at a later point.
- cfg.Fs = hugofs.NewDefault()
-
- cacheDir = viper.GetString("cacheDir")
+ cacheDir = config.GetString("cacheDir")
if cacheDir != "" {
if helpers.FilePathSeparator != cacheDir[len(cacheDir)-1:] {
cacheDir = cacheDir + helpers.FilePathSeparator
@@ -343,39 +363,32 @@ func InitializeConfig(subCmdVs ...*cobra.Command) (deps.DepsCfg, error) {
if !isDir {
mkdir(cacheDir)
}
- viper.Set("cacheDir", cacheDir)
+ config.Set("cacheDir", cacheDir)
} else {
- viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", cfg.Fs.Source))
+ config.Set("cacheDir", helpers.GetTempDir("hugo_cache", cfg.Fs.Source))
}
- jww.INFO.Println("Using config file:", viper.ConfigFileUsed())
+ cfg.Logger.INFO.Println("Using config file:", viper.ConfigFileUsed())
- themeDir := helpers.GetThemeDir()
+ themeDir := c.PathSpec().GetThemeDir()
if themeDir != "" {
if _, err := cfg.Fs.Source.Stat(themeDir); os.IsNotExist(err) {
return cfg, newSystemError("Unable to find theme Directory:", themeDir)
}
}
- themeVersionMismatch, minVersion := isThemeVsHugoVersionMismatch(cfg.Fs.Source)
+ themeVersionMismatch, minVersion := c.isThemeVsHugoVersionMismatch()
if themeVersionMismatch {
- jww.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
+ cfg.Logger.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
helpers.HugoReleaseVersion(), minVersion)
}
- logger, err := createLogger()
- if err != nil {
- return cfg, err
- }
-
- cfg.Logger = logger
-
return cfg, nil
}
-func createLogger() (*jww.Notepad, error) {
+func createLogger(cfg config.Provider) (*jww.Notepad, error) {
var (
logHandle = ioutil.Discard
outHandle = os.Stdout
@@ -383,11 +396,11 @@ func createLogger() (*jww.Notepad, error) {
logThreshold = jww.LevelWarn
)
- if verboseLog || logging || (viper.IsSet("logFile") && viper.GetString("logFile") != "") {
+ if verboseLog || logging || (cfg.GetString("logFile") != "") {
var err error
- if viper.IsSet("logFile") && viper.GetString("logFile") != "" {
- path := viper.GetString("logFile")
+ if cfg.GetString("logFile") != "" {
+ path := cfg.GetString("logFile")
logHandle, err = os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
return nil, newSystemError("Failed to open log file:", path, err)
@@ -398,7 +411,7 @@ func createLogger() (*jww.Notepad, error) {
return nil, newSystemError(err)
}
}
- } else if !quiet && viper.GetBool("verbose") {
+ } else if !quiet && cfg.GetBool("verbose") {
stdoutThreshold = jww.LevelInfo
}
@@ -409,7 +422,7 @@ func createLogger() (*jww.Notepad, error) {
return jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime), nil
}
-func initializeFlags(cmd *cobra.Command) {
+func (c *commandeer) initializeFlags(cmd *cobra.Command) {
persFlagKeys := []string{"verbose", "logFile"}
flagKeys := []string{
"cleanDestinationDir",
@@ -432,21 +445,21 @@ func initializeFlags(cmd *cobra.Command) {
}
for _, key := range persFlagKeys {
- setValueFromFlag(cmd.PersistentFlags(), key)
+ c.setValueFromFlag(cmd.PersistentFlags(), key)
}
for _, key := range flagKeys {
- setValueFromFlag(cmd.Flags(), key)
+ c.setValueFromFlag(cmd.Flags(), key)
}
}
-func setValueFromFlag(flags *flag.FlagSet, key string) {
- if flagChanged(flags, key) {
+func (c *commandeer) setValueFromFlag(flags *flag.FlagSet, key string) {
+ if c.flagChanged(flags, key) {
f := flags.Lookup(key)
- viper.Set(key, f.Value.String())
+ c.Set(key, f.Value.String())
}
}
-func flagChanged(flags *flag.FlagSet, key string) bool {
+func (c *commandeer) flagChanged(flags *flag.FlagSet, key string) bool {
flag := flags.Lookup(key)
if flag == nil {
return false
@@ -454,31 +467,23 @@ func flagChanged(flags *flag.FlagSet, key string) bool {
return flag.Changed
}
-func (c commandeer) watchConfig() {
- viper.WatchConfig()
- viper.OnConfigChange(func(e fsnotify.Event) {
- jww.FEEDBACK.Println("Config file changed:", e.Name)
+func (c *commandeer) watchConfig() {
+ v := c.Cfg.(*viper.Viper)
+ v.WatchConfig()
+ v.OnConfigChange(func(e fsnotify.Event) {
+ c.Logger.FEEDBACK.Println("Config file changed:", e.Name)
// Force a full rebuild
utils.CheckErr(c.recreateAndBuildSites(true))
- if !viper.GetBool("disableLiveReload") {
+ if !c.Cfg.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
livereload.ForceRefresh()
}
})
}
-func (c commandeer) build(watches ...bool) error {
- // Hugo writes the output to memory instead of the disk.
- // This is only used for benchmark testing. Cause the content is only visible
- // in memory.
- if renderToMemory {
- c.Fs.Destination = new(afero.MemMapFs)
- // Rendering to memoryFS, publish to Root regardless of publishDir.
- viper.Set("publishDir", "/")
- }
-
+func (c *commandeer) build(watches ...bool) error {
if err := c.copyStatic(); err != nil {
- return fmt.Errorf("Error copying static files to %s: %s", helpers.AbsPathify(viper.GetString("publishDir")), err)
+ return fmt.Errorf("Error copying static files to %s: %s", c.PathSpec().AbsPathify(c.Cfg.GetString("publishDir")), err)
}
watch := false
if len(watches) > 0 && watches[0] {
@@ -489,37 +494,35 @@ func (c commandeer) build(watches ...bool) error {
}
if buildWatch {
- jww.FEEDBACK.Println("Watching for changes in", helpers.AbsPathify(viper.GetString("contentDir")))
- jww.FEEDBACK.Println("Press Ctrl+C to stop")
+ c.Logger.FEEDBACK.Println("Watching for changes in", c.PathSpec().AbsPathify(c.Cfg.GetString("contentDir")))
+ c.Logger.FEEDBACK.Println("Press Ctrl+C to stop")
utils.CheckErr(c.newWatcher(0))
}
return nil
}
-func (c commandeer) getStaticSourceFs() afero.Fs {
+func (c *commandeer) getStaticSourceFs() afero.Fs {
source := c.Fs.Source
- pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
- themeDir, err := pathSpec.GetThemeStaticDirPath()
- staticDir := helpers.GetStaticDirPath() + helpers.FilePathSeparator
-
+ themeDir, err := c.PathSpec().GetThemeStaticDirPath()
+ staticDir := c.PathSpec().GetStaticDirPath() + helpers.FilePathSeparator
useTheme := true
useStatic := true
if err != nil {
if err != helpers.ErrThemeUndefined {
- jww.WARN.Println(err)
+ c.Logger.WARN.Println(err)
}
useTheme = false
} else {
if _, err := source.Stat(themeDir); os.IsNotExist(err) {
- jww.WARN.Println("Unable to find Theme Static Directory:", themeDir)
+ c.Logger.WARN.Println("Unable to find Theme Static Directory:", themeDir)
useTheme = false
}
}
if _, err := source.Stat(staticDir); os.IsNotExist(err) {
- jww.WARN.Println("Unable to find Static Directory:", staticDir)
+ c.Logger.WARN.Println("Unable to find Static Directory:", staticDir)
useStatic = false
}
@@ -528,25 +531,25 @@ func (c commandeer) getStaticSourceFs() afero.Fs {
}
if !useStatic {
- jww.INFO.Println(themeDir, "is the only static directory available to sync from")
+ c.Logger.INFO.Println(themeDir, "is the only static directory available to sync from")
return afero.NewReadOnlyFs(afero.NewBasePathFs(source, themeDir))
}
if !useTheme {
- jww.INFO.Println(staticDir, "is the only static directory available to sync from")
+ c.Logger.INFO.Println(staticDir, "is the only static directory available to sync from")
return afero.NewReadOnlyFs(afero.NewBasePathFs(source, staticDir))
}
- jww.INFO.Println("using a UnionFS for static directory comprised of:")
- jww.INFO.Println("Base:", themeDir)
- jww.INFO.Println("Overlay:", staticDir)
+ c.Logger.INFO.Println("using a UnionFS for static directory comprised of:")
+ c.Logger.INFO.Println("Base:", themeDir)
+ c.Logger.INFO.Println("Overlay:", staticDir)
base := afero.NewReadOnlyFs(afero.NewBasePathFs(source, themeDir))
overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(source, staticDir))
return afero.NewCopyOnWriteFs(base, overlay)
}
-func (c commandeer) copyStatic() error {
- publishDir := helpers.AbsPathify(viper.GetString("publishDir")) + helpers.FilePathSeparator
+func (c *commandeer) copyStatic() error {
+ publishDir := c.PathSpec().AbsPathify(c.Cfg.GetString("publishDir")) + helpers.FilePathSeparator
// If root, remove the second '/'
if publishDir == "//" {
@@ -557,22 +560,22 @@ func (c commandeer) copyStatic() error {
staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
- jww.WARN.Println("No static directories found to sync")
+ c.Logger.WARN.Println("No static directories found to sync")
return nil
}
syncer := fsync.NewSyncer()
- syncer.NoTimes = viper.GetBool("noTimes")
- syncer.NoChmod = viper.GetBool("noChmod")
+ syncer.NoTimes = c.Cfg.GetBool("noTimes")
+ syncer.NoChmod = c.Cfg.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
syncer.DestFs = c.Fs.Destination
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
- syncer.Delete = viper.GetBool("cleanDestinationDir")
+ syncer.Delete = c.Cfg.GetBool("cleanDestinationDir")
if syncer.Delete {
- jww.INFO.Println("removing all files from destination that don't exist in static dirs")
+ c.Logger.INFO.Println("removing all files from destination that don't exist in static dirs")
}
- jww.INFO.Println("syncing static files to", publishDir)
+ c.Logger.INFO.Println("syncing static files to", publishDir)
// because we are using a baseFs (to get the union right).
// set sync src to root
@@ -580,37 +583,37 @@ func (c commandeer) copyStatic() error {
}
// getDirList provides NewWatcher() with a list of directories to watch for changes.
-func (c commandeer) getDirList() []string {
+func (c *commandeer) getDirList() []string {
var a []string
- dataDir := helpers.AbsPathify(viper.GetString("dataDir"))
- i18nDir := helpers.AbsPathify(viper.GetString("i18nDir"))
- layoutDir := helpers.AbsPathify(viper.GetString("layoutDir"))
- staticDir := helpers.AbsPathify(viper.GetString("staticDir"))
+ dataDir := c.PathSpec().AbsPathify(c.Cfg.GetString("dataDir"))
+ i18nDir := c.PathSpec().AbsPathify(c.Cfg.GetString("i18nDir"))
+ layoutDir := c.PathSpec().AbsPathify(c.Cfg.GetString("layoutDir"))
+ staticDir := c.PathSpec().AbsPathify(c.Cfg.GetString("staticDir"))
var themesDir string
- if helpers.ThemeSet() {
- themesDir = helpers.AbsPathify(viper.GetString("themesDir") + "/" + viper.GetString("theme"))
+ if c.PathSpec().ThemeSet() {
+ themesDir = c.PathSpec().AbsPathify(c.Cfg.GetString("themesDir") + "/" + c.Cfg.GetString("theme"))
}
walker := func(path string, fi os.FileInfo, err error) error {
if err != nil {
if path == dataDir && os.IsNotExist(err) {
- jww.WARN.Println("Skip dataDir:", err)
+ c.Logger.WARN.Println("Skip dataDir:", err)
return nil
}
if path == i18nDir && os.IsNotExist(err) {
- jww.WARN.Println("Skip i18nDir:", err)
+ c.Logger.WARN.Println("Skip i18nDir:", err)
return nil
}
if path == layoutDir && os.IsNotExist(err) {
- jww.WARN.Println("Skip layoutDir:", err)
+ c.Logger.WARN.Println("Skip layoutDir:", err)
return nil
}
if path == staticDir && os.IsNotExist(err) {
- jww.WARN.Println("Skip staticDir:", err)
+ c.Logger.WARN.Println("Skip staticDir:", err)
return nil
}
@@ -619,23 +622,23 @@ func (c commandeer) getDirList() []string {
return nil
}
- jww.ERROR.Println("Walker: ", err)
+ c.Logger.ERROR.Println("Walker: ", err)
return nil
}
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
link, err := filepath.EvalSymlinks(path)
if err != nil {
- jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
+ c.Logger.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
return nil
}
linkfi, err := c.Fs.Source.Stat(link)
if err != nil {
- jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
+ c.Logger.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return nil
}
if !linkfi.Mode().IsRegular() {
- jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", path)
+ c.Logger.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", path)
}
return nil
}
@@ -651,12 +654,12 @@ func (c commandeer) getDirList() []string {
}
helpers.SymbolicWalk(c.Fs.Source, dataDir, walker)
- helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("contentDir")), walker)
+ helpers.SymbolicWalk(c.Fs.Source, c.PathSpec().AbsPathify(c.Cfg.GetString("contentDir")), walker)
helpers.SymbolicWalk(c.Fs.Source, i18nDir, walker)
- helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("layoutDir")), walker)
+ helpers.SymbolicWalk(c.Fs.Source, c.PathSpec().AbsPathify(c.Cfg.GetString("layoutDir")), walker)
helpers.SymbolicWalk(c.Fs.Source, staticDir, walker)
- if helpers.ThemeSet() {
+ if c.PathSpec().ThemeSet() {
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "layouts"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "static"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "i18n"), walker)
@@ -667,32 +670,31 @@ func (c commandeer) getDirList() []string {
return a
}
-func (c commandeer) recreateAndBuildSites(watching bool) (err error) {
+func (c *commandeer) recreateAndBuildSites(watching bool) (err error) {
if err := c.initSites(); err != nil {
return err
}
if !quiet {
- jww.FEEDBACK.Println("Started building sites ...")
+ c.Logger.FEEDBACK.Println("Started building sites ...")
}
return Hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true, Watching: watching, PrintStats: !quiet})
}
-func (c commandeer) resetAndBuildSites(watching bool) (err error) {
+func (c *commandeer) resetAndBuildSites(watching bool) (err error) {
if err = c.initSites(); err != nil {
return
}
if !quiet {
- jww.FEEDBACK.Println("Started building sites ...")
+ c.Logger.FEEDBACK.Println("Started building sites ...")
}
return Hugo.Build(hugolib.BuildCfg{ResetState: true, Watching: watching, PrintStats: !quiet})
}
-func (c commandeer) initSites() error {
+func (c *commandeer) initSites() error {
if Hugo != nil {
return nil
}
-
- h, err := hugolib.NewHugoSitesFromConfiguration(c.DepsCfg)
+ h, err := hugolib.NewHugoSites(*c.DepsCfg)
if err != nil {
return err
@@ -702,17 +704,17 @@ func (c commandeer) initSites() error {
return nil
}
-func (c commandeer) buildSites(watching bool) (err error) {
+func (c *commandeer) buildSites(watching bool) (err error) {
if err := c.initSites(); err != nil {
return err
}
if !quiet {
- jww.FEEDBACK.Println("Started building sites ...")
+ c.Logger.FEEDBACK.Println("Started building sites ...")
}
return Hugo.Build(hugolib.BuildCfg{Watching: watching, PrintStats: !quiet})
}
-func (c commandeer) rebuildSites(events []fsnotify.Event) error {
+func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
if err := c.initSites(); err != nil {
return err
}
@@ -720,13 +722,11 @@ func (c commandeer) rebuildSites(events []fsnotify.Event) error {
}
// newWatcher creates a new watcher to watch filesystem events.
-func (c commandeer) newWatcher(port int) error {
+func (c *commandeer) newWatcher(port int) error {
if runtime.GOOS == "darwin" {
tweakLimit()
}
- pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
-
watcher, err := watcher.New(1 * time.Second)
var wg sync.WaitGroup
@@ -748,7 +748,7 @@ func (c commandeer) newWatcher(port int) error {
for {
select {
case evs := <-watcher.Events:
- jww.INFO.Println("Received System Events:", evs)
+ c.Logger.INFO.Println("Received System Events:", evs)
staticEvents := []fsnotify.Event{}
dynamicEvents := []fsnotify.Event{}
@@ -794,7 +794,7 @@ func (c commandeer) newWatcher(port int) error {
walkAdder := func(path string, f os.FileInfo, err error) error {
if f.IsDir() {
- jww.FEEDBACK.Println("adding created directory to watchlist", path)
+ c.Logger.FEEDBACK.Println("adding created directory to watchlist", path)
watcher.Add(path)
}
return nil
@@ -808,7 +808,7 @@ func (c commandeer) newWatcher(port int) error {
}
}
- isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(pathSpec.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, pathSpec.GetThemesDirPath()))
+ isstatic := strings.HasPrefix(ev.Name, c.PathSpec().GetStaticDirPath()) || (len(c.PathSpec().GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, c.PathSpec().GetThemesDirPath()))
if isstatic {
staticEvents = append(staticEvents, ev)
@@ -818,19 +818,19 @@ func (c commandeer) newWatcher(port int) error {
}
if len(staticEvents) > 0 {
- publishDir := helpers.AbsPathify(viper.GetString("publishDir")) + helpers.FilePathSeparator
+ publishDir := c.PathSpec().AbsPathify(c.Cfg.GetString("publishDir")) + helpers.FilePathSeparator
// If root, remove the second '/'
if publishDir == "//" {
publishDir = helpers.FilePathSeparator
}
- jww.FEEDBACK.Println("\nStatic file changes detected")
+ c.Logger.FEEDBACK.Println("\nStatic file changes detected")
const layout = "2006-01-02 15:04 -0700"
- jww.FEEDBACK.Println(time.Now().Format(layout))
+ c.Logger.FEEDBACK.Println(time.Now().Format(layout))
- if viper.GetBool("forceSyncStatic") {
- jww.FEEDBACK.Printf("Syncing all static files\n")
+ if c.Cfg.GetBool("forceSyncStatic") {
+ c.Logger.FEEDBACK.Printf("Syncing all static files\n")
err := c.copyStatic()
if err != nil {
utils.StopOnErr(err, fmt.Sprintf("Error copying static files to %s", publishDir))
@@ -839,13 +839,13 @@ func (c commandeer) newWatcher(port int) error {
staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
- jww.WARN.Println("No static directories found to sync")
+ c.Logger.WARN.Println("No static directories found to sync")
return
}
syncer := fsync.NewSyncer()
- syncer.NoTimes = viper.GetBool("noTimes")
- syncer.NoChmod = viper.GetBool("noChmod")
+ syncer.NoTimes = c.Cfg.GetBool("noTimes")
+ syncer.NoChmod = c.Cfg.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
syncer.DestFs = c.Fs.Destination
@@ -872,9 +872,9 @@ func (c commandeer) newWatcher(port int) error {
fromPath := ev.Name
// If we are here we already know the event took place in a static dir
- relPath, err := pathSpec.MakeStaticPathRelative(fromPath)
+ relPath, err := c.PathSpec().MakeStaticPathRelative(fromPath)
if err != nil {
- jww.ERROR.Println(err)
+ c.Logger.ERROR.Println(err)
continue
}
@@ -897,10 +897,10 @@ func (c commandeer) newWatcher(port int) error {
// If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir)
if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil {
- jww.ERROR.Println(err)
+ c.Logger.ERROR.Println(err)
}
} else {
- jww.ERROR.Println(err)
+ c.Logger.ERROR.Println(err)
}
continue
@@ -909,18 +909,18 @@ func (c commandeer) newWatcher(port int) error {
// For all other event operations Hugo will sync static.
logger.Println("Syncing", relPath, "to", publishDir)
if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil {
- jww.ERROR.Println(err)
+ c.Logger.ERROR.Println(err)
}
}
}
- if !buildWatch && !viper.GetBool("disableLiveReload") {
+ if !buildWatch && !c.Cfg.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
// force refresh when more than one file
if len(staticEvents) > 0 {
for _, ev := range staticEvents {
- path, _ := pathSpec.MakeStaticPathRelative(ev.Name)
+ path, _ := c.PathSpec().MakeStaticPathRelative(ev.Name)
livereload.RefreshPath(path)
}
@@ -931,27 +931,27 @@ func (c commandeer) newWatcher(port int) error {
}
if len(dynamicEvents) > 0 {
- jww.FEEDBACK.Println("\nChange detected, rebuilding site")
+ c.Logger.FEEDBACK.Println("\nChange detected, rebuilding site")
const layout = "2006-01-02 15:04 -0700"
- jww.FEEDBACK.Println(time.Now().Format(layout))
+ c.Logger.FEEDBACK.Println(time.Now().Format(layout))
c.rebuildSites(dynamicEvents)
- if !buildWatch && !viper.GetBool("disableLiveReload") {
+ if !buildWatch && !c.Cfg.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
livereload.ForceRefresh()
}
}
case err := <-watcher.Errors:
if err != nil {
- jww.ERROR.Println(err)
+ c.Logger.ERROR.Println(err)
}
}
}
}()
if port > 0 {
- if !viper.GetBool("disableLiveReload") {
+ if !c.Cfg.GetBool("disableLiveReload") {
livereload.Initialize()
http.HandleFunc("/livereload.js", livereload.ServeJS)
http.HandleFunc("/livereload", livereload.Handler)
@@ -966,30 +966,30 @@ func (c commandeer) newWatcher(port int) error {
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
// less than the theme's min_version.
-func isThemeVsHugoVersionMismatch(fs afero.Fs) (mismatch bool, requiredMinVersion string) {
- if !helpers.ThemeSet() {
+func (c *commandeer) isThemeVsHugoVersionMismatch() (mismatch bool, requiredMinVersion string) {
+ if !c.PathSpec().ThemeSet() {
return
}
- themeDir := helpers.GetThemeDir()
+ themeDir := c.PathSpec().GetThemeDir()
path := filepath.Join(themeDir, "theme.toml")
- exists, err := helpers.Exists(path, fs)
+ exists, err := helpers.Exists(path, c.Fs.Source)
if err != nil || !exists {
return
}
- b, err := afero.ReadFile(fs, path)
+ b, err := afero.ReadFile(c.Fs.Source, path)
- c, err := parser.HandleTOMLMetaData(b)
+ tomlMeta, err := parser.HandleTOMLMetaData(b)
if err != nil {
return
}
- config := c.(map[string]interface{})
+ config := tomlMeta.(map[string]interface{})
if minVersion, ok := config["min_version"]; ok {
switch minVersion.(type) {
diff --git a/commands/list.go b/commands/list.go
index 870370f71..3f3286f38 100644
--- a/commands/list.go
+++ b/commands/list.go
@@ -19,7 +19,6 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/hugo/hugolib"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
func init() {
@@ -50,9 +49,11 @@ var listDraftsCmd = &cobra.Command{
return err
}
- viper.Set("buildDrafts", true)
+ c := newCommandeer(cfg)
- sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
+ c.Set("buildDrafts", true)
+
+ sites, err := hugolib.NewHugoSites(*cfg)
if err != nil {
return newSystemError("Error creating sites", err)
@@ -86,9 +87,11 @@ posted in the future.`,
return err
}
- viper.Set("buildFuture", true)
+ c := newCommandeer(cfg)
+
+ c.Set("buildFuture", true)
- sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
+ sites, err := hugolib.NewHugoSites(*cfg)
if err != nil {
return newSystemError("Error creating sites", err)
@@ -122,9 +125,11 @@ expired.`,
return err
}
- viper.Set("buildExpired", true)
+ c := newCommandeer(cfg)
+
+ c.Set("buildExpired", true)
- sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
+ sites, err := hugolib.NewHugoSites(*cfg)
if err != nil {
return newSystemError("Error creating sites", err)
diff --git a/commands/list_config.go b/commands/list_config.go
index c04cb66be..f47f6e144 100644
--- a/commands/list_config.go
+++ b/commands/list_config.go
@@ -29,15 +29,17 @@ var configCmd = &cobra.Command{
}
func init() {
- configCmd.RunE = config
+ configCmd.RunE = printConfig
}
-func config(cmd *cobra.Command, args []string) error {
- if _, err := InitializeConfig(configCmd); err != nil {
+func printConfig(cmd *cobra.Command, args []string) error {
+ cfg, err := InitializeConfig(configCmd)
+
+ if err != nil {
return err
}
- allSettings := viper.AllSettings()
+ allSettings := cfg.Cfg.(*viper.Viper).AllSettings()
var separator string
if allSettings["metadataformat"] == "toml" {
diff --git a/commands/new.go b/commands/new.go
index b4a2740f3..a54ad5d2c 100644
--- a/commands/new.go
+++ b/commands/new.go
@@ -93,12 +93,14 @@ func NewContent(cmd *cobra.Command, args []string) error {
return err
}
- if flagChanged(cmd.Flags(), "format") {
- viper.Set("metaDataFormat", configFormat)
+ c := newCommandeer(cfg)
+
+ if c.flagChanged(cmd.Flags(), "format") {
+ c.Set("metaDataFormat", configFormat)
}
- if flagChanged(cmd.Flags(), "editor") {
- viper.Set("newContentEditor", contentEditor)
+ if c.flagChanged(cmd.Flags(), "editor") {
+ c.Set("newContentEditor", contentEditor)
}
if len(args) < 1 {
@@ -115,7 +117,7 @@ func NewContent(cmd *cobra.Command, args []string) error {
kind = contentType
}
- s, err := hugolib.NewSite(cfg)
+ s, err := hugolib.NewSite(*cfg)
if err != nil {
return newSystemError(err)
@@ -203,7 +205,7 @@ func NewSite(cmd *cobra.Command, args []string) error {
forceNew, _ := cmd.Flags().GetBool("force")
- return doNewSite(hugofs.NewDefault(), createpath, forceNew)
+ return doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
}
// NewTheme creates a new Hugo theme.
@@ -215,11 +217,12 @@ func NewTheme(cmd *cobra.Command, args []string) error {
}
if len(args) < 1 {
-
return newUserError("theme name needs to be provided")
}
- createpath := helpers.AbsPathify(filepath.Join(viper.GetString("themesDir"), args[0]))
+ c := newCommandeer(cfg)
+
+ createpath := c.PathSpec().AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
jww.INFO.Println("creating theme at", createpath)
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
diff --git a/commands/new_test.go b/commands/new_test.go
index acb3d7598..dafe0400c 100644
--- a/commands/new_test.go
+++ b/commands/new_test.go
@@ -18,6 +18,7 @@ import (
"testing"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -48,7 +49,7 @@ func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
func TestDoNewSite(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
require.NoError(t, doNewSite(fs, basepath, false))
@@ -57,7 +58,7 @@ func TestDoNewSite(t *testing.T) {
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
@@ -66,7 +67,7 @@ func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
func TestDoNewSite_error_base_exists(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
_, err := fs.Source.Create(filepath.Join(basepath, "foo"))
@@ -78,7 +79,7 @@ func TestDoNewSite_error_base_exists(t *testing.T) {
func TestDoNewSite_force_empty_dir(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
@@ -89,7 +90,7 @@ func TestDoNewSite_force_empty_dir(t *testing.T) {
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
contentPath := filepath.Join(basepath, "content")
@@ -99,7 +100,7 @@ func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
basepath := filepath.Join("base", "blog")
- fs := hugofs.NewMem()
+ _, fs := newTestCfg()
configPath := filepath.Join(basepath, "config.toml")
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
@@ -108,3 +109,14 @@ func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
require.Error(t, doNewSite(fs, basepath, true))
}
+
+func newTestCfg() (*viper.Viper, *hugofs.Fs) {
+
+ v := viper.New()
+ fs := hugofs.NewMem(v)
+
+ v.SetFs(fs.Source)
+
+ return v, fs
+
+}
diff --git a/commands/server.go b/commands/server.go
index 6b1776f4a..8e35d30e5 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -28,9 +28,9 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cobra"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
var (
@@ -42,8 +42,6 @@ var (
serverWatch bool
)
-//var serverCmdV *cobra.Command
-
var serverCmd = &cobra.Command{
Use: "server",
Aliases: []string{"serve"},
@@ -108,17 +106,17 @@ func server(cmd *cobra.Command, args []string) error {
return err
}
- c := commandeer{cfg}
+ c := newCommandeer(cfg)
- if flagChanged(cmd.Flags(), "disableLiveReload") {
- viper.Set("disableLiveReload", disableLiveReload)
+ if c.flagChanged(cmd.Flags(), "disableLiveReload") {
+ c.Set("disableLiveReload", disableLiveReload)
}
if serverWatch {
- viper.Set("watch", true)
+ c.Set("watch", true)
}
- if viper.GetBool("watch") {
+ if c.Cfg.GetBool("watch") {
serverWatch = true
c.watchConfig()
}
@@ -127,7 +125,7 @@ func server(cmd *cobra.Command, args []string) error {
if err == nil {
l.Close()
} else {
- if flagChanged(serverCmd.Flags(), "port") {
+ if c.flagChanged(serverCmd.Flags(), "port") {
// port set explicitly by user -- he/she probably meant it!
return newSystemErrorF("Server startup failed: %s", err)
}
@@ -139,13 +137,13 @@ func server(cmd *cobra.Command, args []string) error {
serverPort = sp.Port
}
- viper.Set("port", serverPort)
+ c.Set("port", serverPort)
- baseURL, err = fixURL(baseURL)
+ baseURL, err = fixURL(c.Cfg, baseURL)
if err != nil {
return err
}
- viper.Set("baseURL", baseURL)
+ c.Set("baseURL", baseURL)
if err := memStats(); err != nil {
jww.ERROR.Println("memstats error:", err)
@@ -160,7 +158,7 @@ func server(cmd *cobra.Command, args []string) error {
if !renderToDisk {
cfg.Fs.Destination = new(afero.MemMapFs)
// Rendering to memoryFS, publish to Root regardless of publishDir.
- viper.Set("publishDir", "/")
+ c.Set("publishDir", "/")
}
if err := c.build(serverWatch); err != nil {
@@ -170,7 +168,7 @@ func server(cmd *cobra.Command, args []string) error {
// Watch runs its own server as part of the routine
if serverWatch {
watchDirs := c.getDirList()
- baseWatchDir := viper.GetString("workingDir")
+ baseWatchDir := c.Cfg.GetString("workingDir")
for i, dir := range watchDirs {
watchDirs[i], _ = helpers.GetRelativePath(dir, baseWatchDir)
}
@@ -190,19 +188,19 @@ func server(cmd *cobra.Command, args []string) error {
return nil
}
-func (c commandeer) serve(port int) {
+func (c *commandeer) serve(port int) {
if renderToDisk {
- jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("publishDir")))
+ jww.FEEDBACK.Println("Serving pages from " + c.PathSpec().AbsPathify(c.Cfg.GetString("publishDir")))
} else {
jww.FEEDBACK.Println("Serving pages from memory")
}
httpFs := afero.NewHttpFs(c.Fs.Destination)
- fs := filesOnlyFs{httpFs.Dir(helpers.AbsPathify(viper.GetString("publishDir")))}
+ fs := filesOnlyFs{httpFs.Dir(c.PathSpec().AbsPathify(c.Cfg.GetString("publishDir")))}
fileserver := http.FileServer(fs)
// We're only interested in the path
- u, err := url.Parse(viper.GetString("baseURL"))
+ u, err := url.Parse(c.Cfg.GetString("baseURL"))
if err != nil {
jww.ERROR.Fatalf("Invalid baseURL: %s", err)
}
@@ -225,10 +223,10 @@ func (c commandeer) serve(port int) {
// fixURL massages the baseURL into a form needed for serving
// all pages correctly.
-func fixURL(s string) (string, error) {
+func fixURL(cfg config.Provider, s string) (string, error) {
useLocalhost := false
if s == "" {
- s = viper.GetString("baseURL")
+ s = cfg.GetString("baseURL")
useLocalhost = true
}
diff --git a/commands/server_test.go b/commands/server_test.go
index dea79ed7e..3f1518aaa 100644
--- a/commands/server_test.go
+++ b/commands/server_test.go
@@ -20,8 +20,6 @@ import (
)
func TestFixURL(t *testing.T) {
- defer viper.Reset()
-
type data struct {
TestName string
CLIBaseURL string
@@ -44,12 +42,12 @@ func TestFixURL(t *testing.T) {
}
for i, test := range tests {
- viper.Reset()
+ v := viper.New()
baseURL = test.CLIBaseURL
- viper.Set("baseURL", test.CfgBaseURL)
+ v.Set("baseURL", test.CfgBaseURL)
serverAppend = test.AppendPort
serverPort = test.Port
- result, err := fixURL(baseURL)
+ result, err := fixURL(v, baseURL)
if err != nil {
t.Errorf("Test #%d %s: unexpected error %s", i, test.TestName, err)
}
diff --git a/helpers/configProvider.go b/config/configProvider.go
index b96018257..a2c8ebb6d 100644
--- a/helpers/configProvider.go
+++ b/config/configProvider.go
@@ -1,4 +1,4 @@
-// Copyright 2016-present The Hugo Authors. All rights reserved.
+// Copyright 2017-present 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.
@@ -15,11 +15,7 @@
// and on content. The helper functions defined here lay down the
// foundation of how Hugo works with files and filepaths, and perform
// string operations on content.
-package helpers
-
-import (
- "github.com/spf13/viper"
-)
+package config
// A cached version of the current ConfigProvider (language) and relatives. These globals
// are unfortunate, but we still have some places that needs this that does
@@ -28,36 +24,17 @@ import (
//
// TODO(bep) Get rid of these.
var (
- currentConfigProvider ConfigProvider
+ currentConfigProvider Provider
)
-// ConfigProvider provides the configuration settings for Hugo.
-type ConfigProvider interface {
+// Provider provides the configuration settings for Hugo.
+type Provider interface {
GetString(key string) string
GetInt(key string) int
GetBool(key string) bool
GetStringMap(key string) map[string]interface{}
GetStringMapString(key string) map[string]string
Get(key string) interface{}
-}
-
-// Config returns the currently active Hugo config. This will be set
-// per site (language) rendered.
-func Config() ConfigProvider {
- if currentConfigProvider != nil {
- return currentConfigProvider
- }
- // Some tests rely on this. We will fix that, eventually.
- return viper.Get("currentContentLanguage").(ConfigProvider)
-}
-
-// InitConfigProviderForCurrentContentLanguage does what it says.
-func InitConfigProviderForCurrentContentLanguage() {
- currentConfigProvider = viper.Get("CurrentContentLanguage").(ConfigProvider)
-}
-
-// ResetConfigProvider is used in tests.
-func ResetConfigProvider() {
- currentConfigProvider = nil
-
+ Set(key string, value interface{})
+ IsSet(key string) bool
}
diff --git a/create/content.go b/create/content.go
index 6a03c8c9b..d38d3c7fb 100644
--- a/create/content.go
+++ b/create/content.go
@@ -29,7 +29,6 @@ import (
"github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/parser"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
// NewContent creates a new content file in the content directory based upon the
@@ -37,7 +36,7 @@ import (
func NewContent(s *hugolib.Site, kind, name string) (err error) {
jww.INFO.Println("attempting to create ", name, "of", kind)
- location := FindArchetype(s.Fs.Source, kind)
+ location := FindArchetype(s, kind)
var by []byte
@@ -67,23 +66,23 @@ func NewContent(s *hugolib.Site, kind, name string) (err error) {
return err
}
- if err = page.SetSourceMetaData(metadata, parser.FormatToLeadRune(viper.GetString("metaDataFormat"))); err != nil {
+ if err = page.SetSourceMetaData(metadata, parser.FormatToLeadRune(s.Cfg.GetString("metaDataFormat"))); err != nil {
return
}
page.SetSourceContent(psr.Content())
- if err = page.SafeSaveSourceAs(filepath.Join(viper.GetString("contentDir"), name)); err != nil {
+ if err = page.SafeSaveSourceAs(filepath.Join(s.Cfg.GetString("contentDir"), name)); err != nil {
return
}
- jww.FEEDBACK.Println(helpers.AbsPathify(filepath.Join(viper.GetString("contentDir"), name)), "created")
+ jww.FEEDBACK.Println(s.PathSpec.AbsPathify(filepath.Join(s.Cfg.GetString("contentDir"), name)), "created")
- editor := viper.GetString("newContentEditor")
+ editor := s.Cfg.GetString("newContentEditor")
if editor != "" {
jww.FEEDBACK.Printf("Editing %s with %q ...\n", name, editor)
- cmd := exec.Command(editor, helpers.AbsPathify(path.Join(viper.GetString("contentDir"), name)))
+ cmd := exec.Command(editor, s.PathSpec.AbsPathify(path.Join(s.Cfg.GetString("contentDir"), name)))
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -138,12 +137,7 @@ func createMetadata(archetype parser.Page, name string) (map[string]interface{},
metadata["title"] = helpers.MakeTitle(helpers.Filename(name))
}
- // TOD(bep) what is this?
- if x := parser.FormatSanitize(viper.GetString("metaDataFormat")); x == "json" || x == "yaml" || x == "toml" {
- metadata["date"] = date.Format(time.RFC3339)
- } else {
- metadata["date"] = date
- }
+ metadata["date"] = date.Format(time.RFC3339)
return metadata, nil
}
@@ -151,13 +145,13 @@ func createMetadata(archetype parser.Page, name string) (map[string]interface{},
// FindArchetype takes a given kind/archetype of content and returns an output
// path for that archetype. If no archetype is found, an empty string is
// returned.
-func FindArchetype(fs afero.Fs, kind string) (outpath string) {
- search := []string{helpers.AbsPathify(viper.GetString("archetypeDir"))}
+func FindArchetype(s *hugolib.Site, kind string) (outpath string) {
+ search := []string{s.PathSpec.AbsPathify(s.Cfg.GetString("archetypeDir"))}
- if viper.GetString("theme") != "" {
- themeDir := filepath.Join(helpers.AbsPathify(viper.GetString("themesDir")+"/"+viper.GetString("theme")), "/archetypes/")
- if _, err := fs.Stat(themeDir); os.IsNotExist(err) {
- jww.ERROR.Printf("Unable to find archetypes directory for theme %q at %q", viper.GetString("theme"), themeDir)
+ if s.Cfg.GetString("theme") != "" {
+ themeDir := filepath.Join(s.PathSpec.AbsPathify(s.Cfg.GetString("themesDir")+"/"+s.Cfg.GetString("theme")), "/archetypes/")
+ if _, err := s.Fs.Source.Stat(themeDir); os.IsNotExist(err) {
+ jww.ERROR.Printf("Unable to find archetypes directory for theme %q at %q", s.Cfg.GetString("theme"), themeDir)
} else {
search = append(search, themeDir)
}
@@ -177,7 +171,7 @@ func FindArchetype(fs afero.Fs, kind string) (outpath string) {
for _, p := range pathsToCheck {
curpath := filepath.Join(x, p)
jww.DEBUG.Println("checking", curpath, "for archetypes")
- if exists, _ := helpers.Exists(curpath, fs); exists {
+ if exists, _ := helpers.Exists(curpath, s.Fs.Source); exists {
jww.INFO.Println("curpath: " + curpath)
return curpath
}
diff --git a/create/content_test.go b/create/content_test.go
index fcfecff10..4ef230202 100644
--- a/create/content_test.go
+++ b/create/content_test.go
@@ -19,6 +19,8 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/deps"
+
"github.com/spf13/hugo/hugolib"
"fmt"
@@ -33,7 +35,8 @@ import (
)
func TestNewContent(t *testing.T) {
- initViper()
+ v := viper.New()
+ initViper(v)
cases := []struct {
kind string
@@ -48,14 +51,17 @@ func TestNewContent(t *testing.T) {
}
for _, c := range cases {
- s, err := hugolib.NewEnglishSiteMem()
+ cfg, fs := newTestCfg()
+ h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
require.NoError(t, err)
- require.NoError(t, initFs(s.Fs))
+ require.NoError(t, initFs(fs))
+
+ s := h.Sites[0]
require.NoError(t, create.NewContent(s, c.kind, c.path))
fname := filepath.Join("content", filepath.FromSlash(c.path))
- content := readFileFromFs(t, s.Fs.Source, fname)
+ content := readFileFromFs(t, fs.Source, fname)
for i, v := range c.expected {
found := strings.Contains(content, v)
if !found {
@@ -65,14 +71,14 @@ func TestNewContent(t *testing.T) {
}
}
-func initViper() {
- viper.Reset()
- viper.Set("metaDataFormat", "toml")
- viper.Set("archetypeDir", "archetypes")
- viper.Set("contentDir", "content")
- viper.Set("themesDir", "themes")
- viper.Set("layoutDir", "layouts")
- viper.Set("theme", "sample")
+func initViper(v *viper.Viper) {
+ v.Set("metaDataFormat", "toml")
+ v.Set("archetypeDir", "archetypes")
+ v.Set("contentDir", "content")
+ v.Set("themesDir", "themes")
+ v.Set("layoutDir", "layouts")
+ v.Set("i18nDir", "i18n")
+ v.Set("theme", "sample")
}
func initFs(fs *hugofs.Fs) error {
@@ -143,3 +149,16 @@ func readFileFromFs(t *testing.T, fs afero.Fs, filename string) string {
}
return string(b)
}
+
+func newTestCfg() (*viper.Viper, *hugofs.Fs) {
+
+ v := viper.New()
+ fs := hugofs.NewMem(v)
+
+ v.SetFs(fs.Source)
+
+ initViper(v)
+
+ return v, fs
+
+}
diff --git a/deps/deps.go b/deps/deps.go
index ee3ba94cf..39a3d31a4 100644
--- a/deps/deps.go
+++ b/deps/deps.go
@@ -5,6 +5,7 @@ import (
"log"
"os"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/tplapi"
@@ -27,24 +28,40 @@ type Deps struct {
// The PathSpec to use
*helpers.PathSpec `json:"-"`
- templateProvider TemplateProvider
- WithTemplate func(templ tplapi.Template) error
+ // The ContentSpec to use
+ *helpers.ContentSpec `json:"-"`
+
+ // The configuration to use
+ Cfg config.Provider `json:"-"`
+
+ // The translation func to use
+ Translate func(translationID string, args ...interface{}) string `json:"-"`
+
+ Language *helpers.Language
- // TODO(bep) globals next in line: Viper
+ templateProvider ResourceProvider
+ WithTemplate func(templ tplapi.Template) error `json:"-"`
+ translationProvider ResourceProvider
}
-// Used to create and refresh, and clone the template.
-type TemplateProvider interface {
+// Used to create and refresh, and clone resources needed.
+type ResourceProvider interface {
Update(deps *Deps) error
Clone(deps *Deps) error
}
-func (d *Deps) LoadTemplates() error {
+func (d *Deps) LoadResources() error {
+ // Note that the translations need to be loaded before the templates.
+ if err := d.translationProvider.Update(d); err != nil {
+ return err
+ }
+
if err := d.templateProvider.Update(d); err != nil {
return err
}
d.Tmpl.PrintErrors()
+
return nil
}
@@ -58,6 +75,10 @@ func New(cfg DepsCfg) *Deps {
panic("Must have a TemplateProvider")
}
+ if cfg.TranslationProvider == nil {
+ panic("Must have a TranslationProvider")
+ }
+
if cfg.Language == nil {
panic("Must have a Language")
}
@@ -67,16 +88,20 @@ func New(cfg DepsCfg) *Deps {
}
if fs == nil {
- // Default to the production file systems.
- fs = hugofs.NewDefault()
+ // Default to the production file system.
+ fs = hugofs.NewDefault(cfg.Language)
}
d := &Deps{
- Fs: fs,
- Log: logger,
- templateProvider: cfg.TemplateProvider,
- WithTemplate: cfg.WithTemplate,
- PathSpec: helpers.NewPathSpec(fs, cfg.Language),
+ Fs: fs,
+ Log: logger,
+ templateProvider: cfg.TemplateProvider,
+ translationProvider: cfg.TranslationProvider,
+ WithTemplate: cfg.WithTemplate,
+ PathSpec: helpers.NewPathSpec(fs, cfg.Language),
+ ContentSpec: helpers.NewContentSpec(cfg.Language),
+ Cfg: cfg.Language,
+ Language: cfg.Language,
}
return d
@@ -87,6 +112,14 @@ func New(cfg DepsCfg) *Deps {
func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) {
d.PathSpec = helpers.NewPathSpec(d.Fs, l)
+ d.ContentSpec = helpers.NewContentSpec(l)
+ d.Cfg = l
+ d.Language = l
+
+ if err := d.translationProvider.Clone(&d); err != nil {
+ return nil, err
+ }
+
if err := d.templateProvider.Clone(&d); err != nil {
return nil, err
}
@@ -109,7 +142,13 @@ type DepsCfg struct {
// The language to use.
Language *helpers.Language
+ // The configuration to use.
+ Cfg config.Provider
+
// Template handling.
- TemplateProvider TemplateProvider
+ TemplateProvider ResourceProvider
WithTemplate func(templ tplapi.Template) error
+
+ // i18n handling.
+ TranslationProvider ResourceProvider
}
diff --git a/helpers/content.go b/helpers/content.go
index d8130e83f..0eb687af6 100644
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -24,12 +24,13 @@ import (
"unicode"
"unicode/utf8"
+ "github.com/spf13/hugo/config"
+
"github.com/miekg/mmark"
"github.com/mitchellh/mapstructure"
"github.com/russross/blackfriday"
bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
"strings"
"sync"
@@ -41,6 +42,14 @@ var SummaryLength = 70
// SummaryDivider denotes where content summarization should end. The default is "<!--more-->".
var SummaryDivider = []byte("<!--more-->")
+type ContentSpec struct {
+ cfg config.Provider
+}
+
+func NewContentSpec(cfg config.Provider) *ContentSpec {
+ return &ContentSpec{cfg}
+}
+
// Blackfriday holds configuration values for Blackfriday rendering.
type Blackfriday struct {
Smartypants bool
@@ -58,7 +67,7 @@ type Blackfriday struct {
}
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
-func NewBlackfriday(c ConfigProvider) *Blackfriday {
+func (c ContentSpec) NewBlackfriday() *Blackfriday {
defaultParam := map[string]interface{}{
"smartypants": true,
@@ -75,7 +84,7 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday {
ToLowerMap(defaultParam)
- siteParam := c.GetStringMap("blackfriday")
+ siteParam := c.cfg.GetStringMap("blackfriday")
siteConfig := make(map[string]interface{})
@@ -187,10 +196,10 @@ func BytesToHTML(b []byte) template.HTML {
}
// getHTMLRenderer creates a new Blackfriday HTML Renderer with the given configuration.
-func getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
+func (c ContentSpec) getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
renderParameters := blackfriday.HtmlRendererParameters{
- FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
- FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
+ FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
+ FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
}
b := len(ctx.DocumentID) != 0
@@ -265,21 +274,21 @@ func getMarkdownExtensions(ctx *RenderingContext) int {
return flags
}
-func markdownRender(ctx *RenderingContext) []byte {
+func (c ContentSpec) markdownRender(ctx *RenderingContext) []byte {
if ctx.RenderTOC {
return blackfriday.Markdown(ctx.Content,
- getHTMLRenderer(blackfriday.HTML_TOC, ctx),
+ c.getHTMLRenderer(blackfriday.HTML_TOC, ctx),
getMarkdownExtensions(ctx))
}
- return blackfriday.Markdown(ctx.Content, getHTMLRenderer(0, ctx),
+ return blackfriday.Markdown(ctx.Content, c.getHTMLRenderer(0, ctx),
getMarkdownExtensions(ctx))
}
// getMmarkHTMLRenderer creates a new mmark HTML Renderer with the given configuration.
-func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
+func (c ContentSpec) getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
renderParameters := mmark.HtmlRendererParameters{
- FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
- FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
+ FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
+ FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
}
b := len(ctx.DocumentID) != 0
@@ -294,6 +303,7 @@ func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Rendere
return &HugoMmarkHTMLRenderer{
mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters),
+ c.cfg,
}
}
@@ -321,8 +331,8 @@ func getMmarkExtensions(ctx *RenderingContext) int {
return flags
}
-func mmarkRender(ctx *RenderingContext) []byte {
- return mmark.Parse(ctx.Content, getMmarkHTMLRenderer(0, ctx),
+func (c ContentSpec) mmarkRender(ctx *RenderingContext) []byte {
+ return mmark.Parse(ctx.Content, c.getMmarkHTMLRenderer(0, ctx),
getMmarkExtensions(ctx)).Bytes()
}
@@ -365,42 +375,44 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
// RenderingContext holds contextual information, like content and configuration,
// for a given content rendering.
type RenderingContext struct {
- Content []byte
- PageFmt string
- DocumentID string
- DocumentName string
- Config *Blackfriday
- RenderTOC bool
- FileResolver FileResolverFunc
- LinkResolver LinkResolverFunc
- ConfigProvider ConfigProvider
- configInit sync.Once
+ Content []byte
+ PageFmt string
+ DocumentID string
+ DocumentName string
+ Config *Blackfriday
+ RenderTOC bool
+ FileResolver FileResolverFunc
+ LinkResolver LinkResolverFunc
+ Cfg config.Provider
+ configInit sync.Once
}
-func newViperProvidedRenderingContext() *RenderingContext {
- return &RenderingContext{ConfigProvider: viper.GetViper()}
+func newRenderingContext(cfg config.Provider) *RenderingContext {
+ return &RenderingContext{Cfg: cfg}
}
func (c *RenderingContext) getConfig() *Blackfriday {
+ // TODO(bep) get rid of this
c.configInit.Do(func() {
if c.Config == nil {
- c.Config = NewBlackfriday(c.ConfigProvider)
+ cs := NewContentSpec(c.Cfg)
+ c.Config = cs.NewBlackfriday()
}
})
return c.Config
}
// RenderBytes renders a []byte.
-func RenderBytes(ctx *RenderingContext) []byte {
+func (c ContentSpec) RenderBytes(ctx *RenderingContext) []byte {
switch ctx.PageFmt {
default:
- return markdownRender(ctx)
+ return c.markdownRender(ctx)
case "markdown":
- return markdownRender(ctx)
+ return c.markdownRender(ctx)
case "asciidoc":
return getAsciidocContent(ctx)
case "mmark":
- return mmarkRender(ctx)
+ return c.mmarkRender(ctx)
case "rst":
return getRstContent(ctx)
}
diff --git a/helpers/content_renderer.go b/helpers/content_renderer.go
index dbc29f431..6082ae945 100644
--- a/helpers/content_renderer.go
+++ b/helpers/content_renderer.go
@@ -19,8 +19,8 @@ import (
"github.com/miekg/mmark"
"github.com/russross/blackfriday"
+ "github.com/spf13/hugo/config"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
type LinkResolverFunc func(ref string) (string, error)
@@ -33,49 +33,49 @@ type HugoHTMLRenderer struct {
blackfriday.Renderer
}
-func (renderer *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
- if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
- opts := viper.GetString("pygmentsOptions")
+func (r *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
+ if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
+ opts := r.Cfg.GetString("pygmentsOptions")
str := html.UnescapeString(string(text))
- out.WriteString(Highlight(str, lang, opts))
+ out.WriteString(Highlight(r.RenderingContext.Cfg, str, lang, opts))
} else {
- renderer.Renderer.BlockCode(out, text, lang)
+ r.Renderer.BlockCode(out, text, lang)
}
}
-func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
- if renderer.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
+func (r *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
+ if r.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
// Use the blackfriday built in Link handler
- renderer.Renderer.Link(out, link, title, content)
+ r.Renderer.Link(out, link, title, content)
} else {
// set by SourceRelativeLinksEval
- newLink, err := renderer.LinkResolver(string(link))
+ newLink, err := r.LinkResolver(string(link))
if err != nil {
newLink = string(link)
jww.ERROR.Printf("LinkResolver: %s", err)
}
- renderer.Renderer.Link(out, []byte(newLink), title, content)
+ r.Renderer.Link(out, []byte(newLink), title, content)
}
}
-func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
- if renderer.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
+func (r *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
+ if r.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
// Use the blackfriday built in Image handler
- renderer.Renderer.Image(out, link, title, alt)
+ r.Renderer.Image(out, link, title, alt)
} else {
// set by SourceRelativeLinksEval
- newLink, err := renderer.FileResolver(string(link))
+ newLink, err := r.FileResolver(string(link))
if err != nil {
newLink = string(link)
jww.ERROR.Printf("FileResolver: %s", err)
}
- renderer.Renderer.Image(out, []byte(newLink), title, alt)
+ r.Renderer.Image(out, []byte(newLink), title, alt)
}
}
// ListItem adds task list support to the Blackfriday renderer.
-func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
- if !renderer.Config.TaskLists {
- renderer.Renderer.ListItem(out, text, flags)
+func (r *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
+ if !r.Config.TaskLists {
+ r.Renderer.ListItem(out, text, flags)
return
}
@@ -87,17 +87,17 @@ func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags
text = append([]byte(`<input type="checkbox" checked disabled class="task-list-item">`), text[3:]...)
}
- renderer.Renderer.ListItem(out, text, flags)
+ r.Renderer.ListItem(out, text, flags)
}
// List adds task list support to the Blackfriday renderer.
-func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
- if !renderer.Config.TaskLists {
- renderer.Renderer.List(out, text, flags)
+func (r *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
+ if !r.Config.TaskLists {
+ r.Renderer.List(out, text, flags)
return
}
marker := out.Len()
- renderer.Renderer.List(out, text, flags)
+ r.Renderer.List(out, text, flags)
if out.Len() > marker {
list := out.Bytes()[marker:]
if bytes.Contains(list, []byte("task-list-item")) {
@@ -114,13 +114,14 @@ func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flag
// Enabling Hugo to customise the rendering experience
type HugoMmarkHTMLRenderer struct {
mmark.Renderer
+ Cfg config.Provider
}
-func (renderer *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
- if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
+func (r *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
+ if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
str := html.UnescapeString(string(text))
- out.WriteString(Highlight(str, lang, ""))
+ out.WriteString(Highlight(r.Cfg, str, lang, ""))
} else {
- renderer.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
+ r.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
}
}
diff --git a/helpers/content_renderer_test.go b/helpers/content_renderer_test.go
index 82168fcfd..ab663966b 100644
--- a/helpers/content_renderer_test.go
+++ b/helpers/content_renderer_test.go
@@ -22,9 +22,9 @@ import (
)
// Renders a codeblock using Blackfriday
-func render(input string) string {
- ctx := newViperProvidedRenderingContext()
- render := getHTMLRenderer(0, ctx)
+func (c ContentSpec) render(input string) string {
+ ctx := newRenderingContext(c.cfg)
+ render := c.getHTMLRenderer(0, ctx)
buf := &bytes.Buffer{}
render.BlockCode(buf, []byte(input), "html")
@@ -32,9 +32,9 @@ func render(input string) string {
}
// Renders a codeblock using Mmark
-func renderWithMmark(input string) string {
- ctx := newViperProvidedRenderingContext()
- render := getMmarkHTMLRenderer(0, ctx)
+func (c ContentSpec) renderWithMmark(input string) string {
+ ctx := newRenderingContext(c.cfg)
+ render := c.getMmarkHTMLRenderer(0, ctx)
buf := &bytes.Buffer{}
render.BlockCode(buf, []byte(input), "html", []byte(""), false, false)
@@ -59,16 +59,16 @@ func TestCodeFence(t *testing.T) {
{false, "<html></html>", `(?s)^<pre><code class="language-html">.*?</code></pre>\n$`},
}
- viper.Reset()
- defer viper.Reset()
+ for i, d := range data {
+ v := viper.New()
- viper.Set("pygmentsStyle", "monokai")
- viper.Set("pygmentsUseClasses", true)
+ v.Set("pygmentsStyle", "monokai")
+ v.Set("pygmentsUseClasses", true)
+ v.Set("pygmentsCodeFences", d.enabled)
- for i, d := range data {
- viper.Set("pygmentsCodeFences", d.enabled)
+ c := NewContentSpec(v)
- result := render(d.input)
+ result := c.render(d.input)
expectedRe, err := regexp.Compile(d.expected)
@@ -81,7 +81,7 @@ func TestCodeFence(t *testing.T) {
t.Errorf("Test %d failed. BlackFriday enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
}
- result = renderWithMmark(d.input)
+ result = c.renderWithMmark(d.input)
matched = expectedRe.MatchString(result)
if !matched {
t.Errorf("Test %d failed. Mmark enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
@@ -90,6 +90,8 @@ func TestCodeFence(t *testing.T) {
}
func TestBlackfridayTaskList(t *testing.T) {
+ c := newTestContentSpec()
+
for i, this := range []struct {
markdown string
taskListEnabled bool
@@ -118,11 +120,11 @@ END
</ul>
`},
} {
- blackFridayConfig := NewBlackfriday(viper.GetViper())
+ blackFridayConfig := c.NewBlackfriday()
blackFridayConfig.TaskLists = this.taskListEnabled
ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig}
- result := string(RenderBytes(ctx))
+ result := string(c.RenderBytes(ctx))
if result != this.expect {
t.Errorf("[%d] got \n%v but expected \n%v", i, result, this.expect)
diff --git a/helpers/content_test.go b/helpers/content_test.go
index 67c50f9d6..52bb85097 100644
--- a/helpers/content_test.go
+++ b/helpers/content_test.go
@@ -21,7 +21,6 @@ import (
"github.com/miekg/mmark"
"github.com/russross/blackfriday"
- "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
@@ -152,8 +151,9 @@ func TestTruncateWordsByRune(t *testing.T) {
}
func TestGetHTMLRendererFlags(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
- renderer := getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
+ renderer := c.getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
flags := renderer.GetFlags()
if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
t.Errorf("Test flag: %d was not found amongs set flags:%d; Result: %d", blackfriday.HTML_USE_XHTML, flags, flags&blackfriday.HTML_USE_XHTML)
@@ -161,6 +161,8 @@ func TestGetHTMLRendererFlags(t *testing.T) {
}
func TestGetHTMLRendererAllFlags(t *testing.T) {
+ c := newTestContentSpec()
+
type data struct {
testFlag int
}
@@ -176,7 +178,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
{blackfriday.HTML_SMARTYPANTS_LATEX_DASHES},
}
defaultFlags := blackfriday.HTML_USE_XHTML
- ctx := newViperProvidedRenderingContext()
+ ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.AngledQuotes = true
ctx.Config.Fractions = true
@@ -186,7 +188,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
ctx.Config.SmartDashes = true
ctx.Config.Smartypants = true
ctx.Config.SourceRelativeLinksEval = true
- renderer := getHTMLRenderer(defaultFlags, ctx)
+ renderer := c.getHTMLRenderer(defaultFlags, ctx)
actualFlags := renderer.GetFlags()
var expectedFlags int
//OR-ing flags together...
@@ -199,12 +201,13 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
}
func TestGetHTMLRendererAnchors(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.DocumentID = "testid"
ctx.Config = ctx.getConfig()
ctx.Config.PlainIDAnchors = false
- actualRenderer := getHTMLRenderer(0, ctx)
+ actualRenderer := c.getHTMLRenderer(0, ctx)
headerBuffer := &bytes.Buffer{}
footnoteBuffer := &bytes.Buffer{}
expectedFootnoteHref := []byte("href=\"#fn:testid:href\"")
@@ -223,11 +226,12 @@ func TestGetHTMLRendererAnchors(t *testing.T) {
}
func TestGetMmarkHTMLRenderer(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.DocumentID = "testid"
ctx.Config = ctx.getConfig()
ctx.Config.PlainIDAnchors = false
- actualRenderer := getMmarkHTMLRenderer(0, ctx)
+ actualRenderer := c.getMmarkHTMLRenderer(0, ctx)
headerBuffer := &bytes.Buffer{}
footnoteBuffer := &bytes.Buffer{}
@@ -247,7 +251,8 @@ func TestGetMmarkHTMLRenderer(t *testing.T) {
}
func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"headerId"}
ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"}
@@ -262,7 +267,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
type data struct {
testFlag int
}
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{""}
ctx.Config.ExtensionsMask = []string{""}
@@ -294,7 +300,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
}
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"definitionLists"}
ctx.Config.ExtensionsMask = []string{""}
@@ -306,10 +313,11 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
}
func TestGetMarkdownRenderer(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
- actualRenderedMarkdown := markdownRender(ctx)
+ actualRenderedMarkdown := c.markdownRender(ctx)
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
@@ -317,10 +325,11 @@ func TestGetMarkdownRenderer(t *testing.T) {
}
func TestGetMarkdownRendererWithTOC(t *testing.T) {
- ctx := &RenderingContext{RenderTOC: true, ConfigProvider: viper.GetViper()}
+ c := newTestContentSpec()
+ ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg}
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
- actualRenderedMarkdown := markdownRender(ctx)
+ actualRenderedMarkdown := c.markdownRender(ctx)
expectedRenderedMarkdown := []byte("<nav>\n</nav>\n\n<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
@@ -332,7 +341,8 @@ func TestGetMmarkExtensions(t *testing.T) {
type data struct {
testFlag int
}
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"tables"}
ctx.Config.ExtensionsMask = []string{""}
@@ -361,10 +371,11 @@ func TestGetMmarkExtensions(t *testing.T) {
}
func TestMmarkRender(t *testing.T) {
- ctx := newViperProvidedRenderingContext()
+ c := newTestContentSpec()
+ ctx := newRenderingContext(c.cfg)
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
- actualRenderedMarkdown := mmarkRender(ctx)
+ actualRenderedMarkdown := c.mmarkRender(ctx)
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
diff --git a/helpers/general.go b/helpers/general.go
index 1088d9dd1..91c25c04c 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -32,7 +32,6 @@ import (
bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/pflag"
- "github.com/spf13/viper"
)
// FilePathSeparator as defined by os.Separator.
@@ -196,8 +195,8 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
}
// ThemeSet checks whether a theme is in use or not.
-func ThemeSet() bool {
- return viper.GetString("theme") != ""
+func (p *PathSpec) ThemeSet() bool {
+ return p.theme != ""
}
type logPrinter interface {
diff --git a/helpers/language.go b/helpers/language.go
index 000c846b1..9b7136854 100644
--- a/helpers/language.go
+++ b/helpers/language.go
@@ -19,8 +19,7 @@ import (
"sync"
"github.com/spf13/cast"
-
- "github.com/spf13/viper"
+ "github.com/spf13/hugo/config"
)
// These are the settings that should only be looked up in the global Viper
@@ -41,26 +40,28 @@ type Language struct {
LanguageName string
Title string
Weight int
- params map[string]interface{}
- paramsInit sync.Once
+
+ Cfg config.Provider
+ params map[string]interface{}
+ paramsInit sync.Once
}
func (l *Language) String() string {
return l.Lang
}
-func NewLanguage(lang string) *Language {
- return &Language{Lang: lang, params: make(map[string]interface{})}
+func NewLanguage(lang string, cfg config.Provider) *Language {
+ return &Language{Lang: lang, Cfg: cfg, params: make(map[string]interface{})}
}
-func NewDefaultLanguage() *Language {
- defaultLang := viper.GetString("defaultContentLanguage")
+func NewDefaultLanguage(cfg config.Provider) *Language {
+ defaultLang := cfg.GetString("defaultContentLanguage")
if defaultLang == "" {
defaultLang = "en"
}
- return NewLanguage(defaultLang)
+ return NewLanguage(defaultLang, cfg)
}
type Languages []*Language
@@ -83,7 +84,7 @@ func (l *Language) Params() map[string]interface{} {
// Merge with global config.
// TODO(bep) consider making this part of a constructor func.
- globalParams := viper.GetStringMap("params")
+ globalParams := l.Cfg.GetStringMap("params")
for k, v := range globalParams {
if _, ok := l.params[k]; !ok {
l.params[k] = v
@@ -132,5 +133,28 @@ func (l *Language) Get(key string) interface{} {
return v
}
}
- return viper.Get(key)
+ return l.Cfg.Get(key)
+}
+
+// Set sets the value for the key in the language's params.
+func (l *Language) Set(key string, value interface{}) {
+ if l == nil {
+ panic("language not set")
+ }
+ key = strings.ToLower(key)
+ l.params[key] = value
+}
+
+// IsSet checks whether the key is set in the language or the related config store.
+func (l *Language) IsSet(key string) bool {
+ key = strings.ToLower(key)
+
+ key = strings.ToLower(key)
+ if !globalOnlySettings[key] {
+ if _, ok := l.params[key]; ok {
+ return true
+ }
+ }
+ return l.Cfg.IsSet(key)
+
}
diff --git a/helpers/language_test.go b/helpers/language_test.go
index f812d26a2..902177e1a 100644
--- a/helpers/language_test.go
+++ b/helpers/language_test.go
@@ -21,11 +21,12 @@ import (
)
func TestGetGlobalOnlySetting(t *testing.T) {
- lang := NewDefaultLanguage()
+ v := viper.New()
+ lang := NewDefaultLanguage(v)
lang.SetParam("defaultContentLanguageInSubdir", false)
lang.SetParam("paginatePath", "side")
- viper.Set("defaultContentLanguageInSubdir", true)
- viper.Set("paginatePath", "page")
+ v.Set("defaultContentLanguageInSubdir", true)
+ v.Set("paginatePath", "page")
require.True(t, lang.GetBool("defaultContentLanguageInSubdir"))
require.Equal(t, "side", lang.GetString("paginatePath"))
diff --git a/helpers/path.go b/helpers/path.go
index 83a91deba..14d98e2c0 100644
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -24,7 +24,6 @@ import (
"unicode"
"github.com/spf13/afero"
- "github.com/spf13/viper"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
@@ -153,41 +152,41 @@ func ReplaceExtension(path string, newExt string) string {
// AbsPathify creates an absolute path if given a relative path. If already
// absolute, the path is just cleaned.
-func AbsPathify(inPath string) string {
+func (p *PathSpec) AbsPathify(inPath string) string {
if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}
// TODO(bep): Consider moving workingDir to argument list
- return filepath.Clean(filepath.Join(viper.GetString("workingDir"), inPath))
+ return filepath.Clean(filepath.Join(p.workingDir, inPath))
}
// GetLayoutDirPath returns the absolute path to the layout file dir
// for the current Hugo project.
-func GetLayoutDirPath() string {
- return AbsPathify(viper.GetString("layoutDir"))
+func (p *PathSpec) GetLayoutDirPath() string {
+ return p.AbsPathify(p.layoutDir)
}
// GetStaticDirPath returns the absolute path to the static file dir
// for the current Hugo project.
-func GetStaticDirPath() string {
- return AbsPathify(viper.GetString("staticDir"))
+func (p *PathSpec) GetStaticDirPath() string {
+ return p.AbsPathify(p.staticDir)
}
// GetThemeDir gets the root directory of the current theme, if there is one.
// If there is no theme, returns the empty string.
-func GetThemeDir() string {
- if ThemeSet() {
- return AbsPathify(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")))
+func (p *PathSpec) GetThemeDir() string {
+ if p.ThemeSet() {
+ return p.AbsPathify(filepath.Join(p.themesDir, p.theme))
}
return ""
}
// GetRelativeThemeDir gets the relative root directory of the current theme, if there is one.
// If there is no theme, returns the empty string.
-func GetRelativeThemeDir() string {
- if ThemeSet() {
- return strings.TrimPrefix(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")), FilePathSeparator)
+func (p *PathSpec) GetRelativeThemeDir() string {
+ if p.ThemeSet() {
+ return strings.TrimPrefix(filepath.Join(p.themesDir, p.theme), FilePathSeparator)
}
return ""
}
@@ -211,13 +210,13 @@ func (p *PathSpec) GetThemeI18nDirPath() (string, error) {
}
func (p *PathSpec) getThemeDirPath(path string) (string, error) {
- if !ThemeSet() {
+ if !p.ThemeSet() {
return "", ErrThemeUndefined
}
- themeDir := filepath.Join(GetThemeDir(), path)
+ themeDir := filepath.Join(p.GetThemeDir(), path)
if _, err := p.fs.Source.Stat(themeDir); os.IsNotExist(err) {
- return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir)
+ return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, p.theme, themeDir)
}
return themeDir, nil
@@ -235,7 +234,7 @@ func (p *PathSpec) GetThemesDirPath() string {
// It does so by taking either the project's static path or the theme's static
// path into consideration.
func (p *PathSpec) MakeStaticPathRelative(inPath string) (string, error) {
- staticDir := GetStaticDirPath()
+ staticDir := p.GetStaticDirPath()
themeStaticDir := p.GetThemesDirPath()
return makePathRelative(inPath, staticDir, themeStaticDir)
@@ -360,20 +359,20 @@ func GetRelativePath(path, base string) (final string, err error) {
}
// PaginateAliasPath creates a path used to access the aliases in the paginator.
-func PaginateAliasPath(base string, page int) string {
- paginatePath := Config().GetString("paginatePath")
- uglify := viper.GetBool("uglyURLs")
- var p string
+func (p *PathSpec) PaginateAliasPath(base string, page int) string {
+ paginatePath := p.paginatePath
+ uglify := p.uglyURLs
+ var pth string
if base != "" {
- p = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
+ pth = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
} else {
- p = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
+ pth = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
}
if uglify {
- p += ".html"
+ pth += ".html"
}
- return p
+ return pth
}
// GuessSection returns the section given a source path.
diff --git a/helpers/path_test.go b/helpers/path_test.go
index 46141befc..90dd95288 100644
--- a/helpers/path_test.go
+++ b/helpers/path_test.go
@@ -34,15 +34,7 @@ import (
"github.com/spf13/viper"
)
-func initCommonTestConfig() {
- viper.Set("currentContentLanguage", NewLanguage("en"))
-}
-
func TestMakePath(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
- initCommonTestConfig()
-
tests := []struct {
input string
expected string
@@ -64,8 +56,10 @@ func TestMakePath(t *testing.T) {
}
for _, test := range tests {
- viper.Set("removePathAccents", test.removeAccents)
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v := viper.New()
+ l := NewDefaultLanguage(v)
+ v.Set("removePathAccents", test.removeAccents)
+ p := NewPathSpec(hugofs.NewMem(v), l)
output := p.MakePath(test.input)
if output != test.expected {
@@ -75,11 +69,9 @@ func TestMakePath(t *testing.T) {
}
func TestMakePathSanitized(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
- initCommonTestConfig()
-
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v := viper.New()
+ l := NewDefaultLanguage(v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -102,12 +94,12 @@ func TestMakePathSanitized(t *testing.T) {
}
func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
+ v := viper.New()
+
+ v.Set("disablePathToLower", true)
- initCommonTestConfig()
- viper.Set("disablePathToLower", true)
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ l := NewDefaultLanguage(v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -553,9 +545,9 @@ func TestAbsPathify(t *testing.T) {
for i, d := range data {
viper.Reset()
// todo see comment in AbsPathify
- viper.Set("workingDir", d.workingDir)
+ ps := newTestDefaultPathSpec("workingDir", d.workingDir)
- expected := AbsPathify(d.inPath)
+ expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}
@@ -563,18 +555,18 @@ func TestAbsPathify(t *testing.T) {
t.Logf("Running platform specific path tests for %s", runtime.GOOS)
if runtime.GOOS == "windows" {
for i, d := range windowsData {
- viper.Set("workingDir", d.workingDir)
+ ps := newTestDefaultPathSpec("workingDir", d.workingDir)
- expected := AbsPathify(d.inPath)
+ expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}
}
} else {
for i, d := range unixData {
- viper.Set("workingDir", d.workingDir)
+ ps := newTestDefaultPathSpec("workingDir", d.workingDir)
- expected := AbsPathify(d.inPath)
+ expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}
diff --git a/helpers/pathspec.go b/helpers/pathspec.go
index 0fc957b3b..ddc183380 100644
--- a/helpers/pathspec.go
+++ b/helpers/pathspec.go
@@ -16,6 +16,7 @@ package helpers
import (
"fmt"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
)
@@ -26,11 +27,20 @@ type PathSpec struct {
uglyURLs bool
canonifyURLs bool
- currentContentLanguage *Language
+ language *Language
// pagination path handling
paginatePath string
+ baseURL string
+ theme string
+
+ // Directories
+ themesDir string
+ layoutDir string
+ workingDir string
+ staticDir string
+
// The PathSpec looks up its config settings in both the current language
// and then in the global Viper config.
// Some settings, the settings listed below, does not make sense to be set
@@ -45,31 +55,35 @@ type PathSpec struct {
}
func (p PathSpec) String() string {
- return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.currentContentLanguage.Lang, p.getLanguagePrefix(), p.multilingual)
+ return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.language.Lang, p.getLanguagePrefix(), p.multilingual)
}
-// NewPathSpec creats a new PathSpec from the given filesystems and ConfigProvider.
-func NewPathSpec(fs *hugofs.Fs, config ConfigProvider) *PathSpec {
-
- currCl, ok := config.Get("currentContentLanguage").(*Language)
+// NewPathSpec creats a new PathSpec from the given filesystems and Language.
+func NewPathSpec(fs *hugofs.Fs, cfg config.Provider) *PathSpec {
- if !ok {
- // TODO(bep) globals
- currCl = NewLanguage("en")
+ ps := &PathSpec{
+ fs: fs,
+ disablePathToLower: cfg.GetBool("disablePathToLower"),
+ removePathAccents: cfg.GetBool("removePathAccents"),
+ uglyURLs: cfg.GetBool("uglyURLs"),
+ canonifyURLs: cfg.GetBool("canonifyURLs"),
+ multilingual: cfg.GetBool("multilingual"),
+ defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
+ defaultContentLanguage: cfg.GetString("defaultContentLanguage"),
+ paginatePath: cfg.GetString("paginatePath"),
+ baseURL: cfg.GetString("baseURL"),
+ themesDir: cfg.GetString("themesDir"),
+ layoutDir: cfg.GetString("layoutDir"),
+ workingDir: cfg.GetString("workingDir"),
+ staticDir: cfg.GetString("staticDir"),
+ theme: cfg.GetString("theme"),
}
- return &PathSpec{
- fs: fs,
- disablePathToLower: config.GetBool("disablePathToLower"),
- removePathAccents: config.GetBool("removePathAccents"),
- uglyURLs: config.GetBool("uglyURLs"),
- canonifyURLs: config.GetBool("canonifyURLs"),
- multilingual: config.GetBool("multilingual"),
- defaultContentLanguageInSubdir: config.GetBool("defaultContentLanguageInSubdir"),
- defaultContentLanguage: config.GetString("defaultContentLanguage"),
- currentContentLanguage: currCl,
- paginatePath: config.GetString("paginatePath"),
+ if language, ok := cfg.(*Language); ok {
+ ps.language = language
}
+
+ return ps
}
// PaginatePath returns the configured root path used for paginator pages.
diff --git a/helpers/pathspec_test.go b/helpers/pathspec_test.go
index 42d828519..07948bb65 100644
--- a/helpers/pathspec_test.go
+++ b/helpers/pathspec_test.go
@@ -23,17 +23,24 @@ import (
)
func TestNewPathSpecFromConfig(t *testing.T) {
- viper.Set("disablePathToLower", true)
- viper.Set("removePathAccents", true)
- viper.Set("uglyURLs", true)
- viper.Set("multilingual", true)
- viper.Set("defaultContentLanguageInSubdir", true)
- viper.Set("defaultContentLanguage", "no")
- viper.Set("currentContentLanguage", NewLanguage("no"))
- viper.Set("canonifyURLs", true)
- viper.Set("paginatePath", "side")
-
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v := viper.New()
+ l := NewLanguage("no", v)
+ v.Set("disablePathToLower", true)
+ v.Set("removePathAccents", true)
+ v.Set("uglyURLs", true)
+ v.Set("multilingual", true)
+ v.Set("defaultContentLanguageInSubdir", true)
+ v.Set("defaultContentLanguage", "no")
+ v.Set("canonifyURLs", true)
+ v.Set("paginatePath", "side")
+ v.Set("baseURL", "http://base.com")
+ v.Set("themesDir", "thethemes")
+ v.Set("layoutDir", "thelayouts")
+ v.Set("workingDir", "thework")
+ v.Set("staticDir", "thestatic")
+ v.Set("theme", "thetheme")
+
+ p := NewPathSpec(hugofs.NewMem(v), l)
require.True(t, p.canonifyURLs)
require.True(t, p.defaultContentLanguageInSubdir)
@@ -42,6 +49,13 @@ func TestNewPathSpecFromConfig(t *testing.T) {
require.True(t, p.removePathAccents)
require.True(t, p.uglyURLs)
require.Equal(t, "no", p.defaultContentLanguage)
- require.Equal(t, "no", p.currentContentLanguage.Lang)
+ require.Equal(t, "no", p.language.Lang)
require.Equal(t, "side", p.paginatePath)
+
+ require.Equal(t, "http://base.com", p.baseURL)
+ require.Equal(t, "thethemes", p.themesDir)
+ require.Equal(t, "thelayouts", p.layoutDir)
+ require.Equal(t, "thework", p.workingDir)
+ require.Equal(t, "thestatic", p.staticDir)
+ require.Equal(t, "thetheme", p.theme)
}
diff --git a/helpers/pygments.go b/helpers/pygments.go
index 8e6d1a998..051612608 100644
--- a/helpers/pygments.go
+++ b/helpers/pygments.go
@@ -24,9 +24,9 @@ import (
"sort"
"strings"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
const pygmentsBin = "pygmentize"
@@ -41,13 +41,13 @@ func HasPygments() bool {
}
// Highlight takes some code and returns highlighted code.
-func Highlight(code, lang, optsStr string) string {
+func Highlight(cfg config.Provider, code, lang, optsStr string) string {
if !HasPygments() {
jww.WARN.Println("Highlighting requires Pygments to be installed and in the path")
return code
}
- options, err := parsePygmentsOpts(optsStr)
+ options, err := parsePygmentsOpts(cfg, optsStr)
if err != nil {
jww.ERROR.Print(err.Error())
@@ -62,8 +62,8 @@ func Highlight(code, lang, optsStr string) string {
fs := hugofs.Os
- ignoreCache := viper.GetBool("ignoreCache")
- cacheDir := viper.GetString("cacheDir")
+ ignoreCache := cfg.GetBool("ignoreCache")
+ cacheDir := cfg.GetString("cacheDir")
var cachefile string
if !ignoreCache && cacheDir != "" {
@@ -195,19 +195,19 @@ func createOptionsString(options map[string]string) string {
return optionsStr
}
-func parseDefaultPygmentsOpts() (map[string]string, error) {
+func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) {
options := make(map[string]string)
- err := parseOptions(options, viper.GetString("pygmentsOptions"))
+ err := parseOptions(options, cfg.GetString("pygmentsOptions"))
if err != nil {
return nil, err
}
- if viper.IsSet("pygmentsStyle") {
- options["style"] = viper.GetString("pygmentsStyle")
+ if cfg.IsSet("pygmentsStyle") {
+ options["style"] = cfg.GetString("pygmentsStyle")
}
- if viper.IsSet("pygmentsUseClasses") {
- if viper.GetBool("pygmentsUseClasses") {
+ if cfg.IsSet("pygmentsUseClasses") {
+ if cfg.GetBool("pygmentsUseClasses") {
options["noclasses"] = "false"
} else {
options["noclasses"] = "true"
@@ -222,8 +222,8 @@ func parseDefaultPygmentsOpts() (map[string]string, error) {
return options, nil
}
-func parsePygmentsOpts(in string) (string, error) {
- options, err := parseDefaultPygmentsOpts()
+func parsePygmentsOpts(cfg config.Provider, in string) (string, error) {
+ options, err := parseDefaultPygmentsOpts(cfg)
if err != nil {
return "", err
}
diff --git a/helpers/pygments_test.go b/helpers/pygments_test.go
index 1cca68eac..1fce17859 100644
--- a/helpers/pygments_test.go
+++ b/helpers/pygments_test.go
@@ -34,11 +34,12 @@ func TestParsePygmentsArgs(t *testing.T) {
{"boo=invalid", "foo", false, false},
{"style", "foo", false, false},
} {
- viper.Reset()
- viper.Set("pygmentsStyle", this.pygmentsStyle)
- viper.Set("pygmentsUseClasses", this.pygmentsUseClasses)
- result1, err := parsePygmentsOpts(this.in)
+ v := viper.New()
+ v.Set("pygmentsStyle", this.pygmentsStyle)
+ v.Set("pygmentsUseClasses", this.pygmentsUseClasses)
+
+ result1, err := parsePygmentsOpts(v, this.in)
if b, ok := this.expect1.(bool); ok && !b {
if err == nil {
t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i)
@@ -70,19 +71,19 @@ func TestParseDefaultPygmentsArgs(t *testing.T) {
{"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"},
{"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"},
} {
- viper.Reset()
+ v := viper.New()
- viper.Set("pygmentsOptions", this.pygmentsOptions)
+ v.Set("pygmentsOptions", this.pygmentsOptions)
if s, ok := this.pygmentsStyle.(string); ok {
- viper.Set("pygmentsStyle", s)
+ v.Set("pygmentsStyle", s)
}
if b, ok := this.pygmentsUseClasses.(bool); ok {
- viper.Set("pygmentsUseClasses", b)
+ v.Set("pygmentsUseClasses", b)
}
- result, err := parsePygmentsOpts(this.in)
+ result, err := parsePygmentsOpts(v, this.in)
if err != nil {
t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
continue
diff --git a/helpers/testhelpers_test.go b/helpers/testhelpers_test.go
new file mode 100644
index 000000000..303f9feb6
--- /dev/null
+++ b/helpers/testhelpers_test.go
@@ -0,0 +1,37 @@
+package helpers
+
+import (
+ "github.com/spf13/viper"
+
+ "github.com/spf13/hugo/hugofs"
+)
+
+func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *PathSpec {
+ l := NewDefaultLanguage(v)
+ return NewPathSpec(fs, l)
+}
+
+func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec {
+ v := viper.New()
+ fs := hugofs.NewMem(v)
+ cfg := newTestCfg(fs)
+
+ for i := 0; i < len(configKeyValues); i += 2 {
+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
+ return newTestPathSpec(fs, cfg)
+}
+
+func newTestCfg(fs *hugofs.Fs) *viper.Viper {
+ v := viper.New()
+
+ v.SetFs(fs.Source)
+
+ return v
+
+}
+
+func newTestContentSpec() *ContentSpec {
+ v := viper.New()
+ return NewContentSpec(v)
+}
diff --git a/helpers/url.go b/helpers/url.go
index 2f35b059f..3a060eee9 100644
--- a/helpers/url.go
+++ b/helpers/url.go
@@ -21,7 +21,6 @@ import (
"github.com/PuerkitoBio/purell"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
type pathBridge struct {
@@ -158,7 +157,7 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
return in
}
- baseURL := viper.GetString("baseURL")
+ baseURL := p.baseURL
if strings.HasPrefix(in, "/") {
p, err := url.Parse(baseURL)
if err != nil {
@@ -200,7 +199,7 @@ func (p *PathSpec) getLanguagePrefix() string {
defaultLang := p.defaultContentLanguage
defaultInSubDir := p.defaultContentLanguageInSubdir
- currentLang := p.currentContentLanguage.Lang
+ currentLang := p.language.Lang
if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
return ""
}
@@ -220,7 +219,7 @@ func IsAbsURL(path string) bool {
// RelURL creates a URL relative to the BaseURL root.
// Note: The result URL will not include the context root if canonifyURLs is enabled.
func (p *PathSpec) RelURL(in string, addLanguage bool) string {
- baseURL := viper.GetString("baseURL")
+ baseURL := p.baseURL
canonifyURLs := p.canonifyURLs
if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
return in
diff --git a/helpers/url_test.go b/helpers/url_test.go
index b50a9efd8..b53e2e6cc 100644
--- a/helpers/url_test.go
+++ b/helpers/url_test.go
@@ -25,9 +25,10 @@ import (
)
func TestURLize(t *testing.T) {
- initCommonTestConfig()
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v := viper.New()
+ l := NewDefaultLanguage(v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -62,11 +63,10 @@ func TestAbsURL(t *testing.T) {
}
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
- viper.Reset()
- viper.Set("multilingual", multilingual)
- viper.Set("currentContentLanguage", NewLanguage(lang))
- viper.Set("defaultContentLanguage", "en")
- viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
+ v := viper.New()
+ v.Set("multilingual", multilingual)
+ v.Set("defaultContentLanguage", "en")
+ v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
tests := []struct {
input string
@@ -86,10 +86,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
{"http//foo", "http://base/path", "http://base/path/MULTIhttp/foo"},
}
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
-
for _, test := range tests {
- viper.Set("baseURL", test.baseURL)
+ v.Set("baseURL", test.baseURL)
+ l := NewLanguage(lang, v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
output := p.AbsURL(test.input, addLanguage)
expected := test.expected
@@ -138,11 +138,10 @@ func TestRelURL(t *testing.T) {
}
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
- viper.Reset()
- viper.Set("multilingual", multilingual)
- viper.Set("currentContentLanguage", NewLanguage(lang))
- viper.Set("defaultContentLanguage", "en")
- viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
+ v := viper.New()
+ v.Set("multilingual", multilingual)
+ v.Set("defaultContentLanguage", "en")
+ v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
tests := []struct {
input string
@@ -165,9 +164,10 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
}
for i, test := range tests {
- viper.Set("baseURL", test.baseURL)
- viper.Set("canonifyURLs", test.canonify)
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v.Set("baseURL", test.baseURL)
+ v.Set("canonifyURLs", test.canonify)
+ l := NewLanguage(lang, v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
output := p.RelURL(test.input, addLanguage)
@@ -252,8 +252,10 @@ func TestURLPrep(t *testing.T) {
}
for i, d := range data {
- viper.Set("uglyURLs", d.ugly)
- p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+ v := viper.New()
+ v.Set("uglyURLs", d.ugly)
+ l := NewDefaultLanguage(v)
+ p := NewPathSpec(hugofs.NewMem(v), l)
output := p.URLPrep(d.input)
if d.output != output {
diff --git a/hugofs/fs.go b/hugofs/fs.go
index 3afa17956..1ddb98214 100644
--- a/hugofs/fs.go
+++ b/hugofs/fs.go
@@ -16,7 +16,7 @@ package hugofs
import (
"github.com/spf13/afero"
- "github.com/spf13/viper"
+ "github.com/spf13/hugo/config"
)
// Os points to an Os Afero file system.
@@ -39,30 +39,37 @@ type Fs struct {
// NewDefault creates a new Fs with the OS file system
// as source and destination file systems.
-func NewDefault() *Fs {
+func NewDefault(cfg config.Provider) *Fs {
fs := &afero.OsFs{}
- return newFs(fs)
+ return newFs(fs, cfg)
}
// NewDefault creates a new Fs with the MemMapFs
// as source and destination file systems.
// Useful for testing.
-func NewMem() *Fs {
+func NewMem(cfg config.Provider) *Fs {
fs := &afero.MemMapFs{}
- return newFs(fs)
+ return newFs(fs, cfg)
}
-func newFs(base afero.Fs) *Fs {
+// NewFrom creates a new Fs based on the provided Afero Fs
+// as source and destination file systems.
+// Useful for testing.
+func NewFrom(fs afero.Fs, cfg config.Provider) *Fs {
+ return newFs(fs, cfg)
+}
+
+func newFs(base afero.Fs, cfg config.Provider) *Fs {
return &Fs{
Source: base,
Destination: base,
Os: &afero.OsFs{},
- WorkingDir: getWorkingDirFs(base),
+ WorkingDir: getWorkingDirFs(base, cfg),
}
}
-func getWorkingDirFs(base afero.Fs) *afero.BasePathFs {
- workingDir := viper.GetString("workingDir")
+func getWorkingDirFs(base afero.Fs, cfg config.Provider) *afero.BasePathFs {
+ workingDir := cfg.GetString("workingDir")
if workingDir != "" {
return afero.NewBasePathFs(afero.NewReadOnlyFs(base), workingDir).(*afero.BasePathFs)
diff --git a/hugofs/fs_test.go b/hugofs/fs_test.go
index 5482e6d27..95900e6a2 100644
--- a/hugofs/fs_test.go
+++ b/hugofs/fs_test.go
@@ -22,10 +22,8 @@ import (
)
func TestNewDefault(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
-
- f := NewDefault()
+ v := viper.New()
+ f := NewDefault(v)
assert.NotNil(t, f.Source)
assert.IsType(t, new(afero.OsFs), f.Source)
@@ -39,10 +37,8 @@ func TestNewDefault(t *testing.T) {
}
func TestNewMem(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
-
- f := NewMem()
+ v := viper.New()
+ f := NewMem(v)
assert.NotNil(t, f.Source)
assert.IsType(t, new(afero.MemMapFs), f.Source)
@@ -53,12 +49,11 @@ func TestNewMem(t *testing.T) {
}
func TestWorkingDir(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
+ v := viper.New()
- viper.Set("workingDir", "/a/b/")
+ v.Set("workingDir", "/a/b/")
- f := NewMem()
+ f := NewMem(v)
assert.NotNil(t, f.WorkingDir)
assert.IsType(t, new(afero.BasePathFs), f.WorkingDir)
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 }}
"&laquo;Hi&raquo;", // 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 }}
"&ldquo;Hi&rdquo;",
)
- 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: []byte(`{ "hello": [ { "world": "foo" } ] }`)},
}
expected, err := parser.HandleJSONMetaData([]byte(`{ "test": { "hello": [{ "world": "foo" }] , "foo": { "bar":"foofoo" } } }`))
@@ -36,12 +45,14 @@ func TestDataDirJSON(t *testing.T) {
t.Fatalf("Error %s", err)
}
- doTestDataDir(t, expected, []source.Input{&source.InMemorySource{ByteSource: sources}})
+ doTestDataDir(t, expected, sources)
}
func TestDataDirToml(t *testing.T) {
+ t.Parallel()
+
sources := []source.ByteSource{
- {Name: filepath.FromSlash("test/kung.toml"), Content: []byte("[foo]\nbar = 1")},
+ {Name: filepath.FromSlash("data/test/kung.toml"), Content: []byte("[foo]\nbar = 1")},
}
expected, err := parser.HandleTOMLMetaData([]byte("[test]\n[test.kung]\n[test.kung.foo]\nbar = 1"))
@@ -50,78 +61,102 @@ func TestDataDirToml(t *testing.T) {
t.Fatalf("Error %s", err)
}
- doTestDataDir(t, expected, []source.Input{&source.InMemorySource{ByteSource: sources}})
+ doTestDataDir(t, expected, sources)
}
func TestDataDirYAMLWithOverridenValue(t *testing.T) {
+ t.Parallel()
+
sources := []source.ByteSource{
// filepath.Walk walks the files in lexical order, '/' comes before '.'. Simulate this:
- {Name: filepath.FromSlash("a.yaml"), Content: []byte("a: 1")},
- {Name: filepath.FromSlash("test/v1.yaml"), Content: []byte("v1-2: 2")},
- {Name: filepath.FromSlash("test/v2.yaml"), Content: []byte("v2:\n- 2\n- 3")},
- {Name: filepath.FromSlash("test.yaml"), Content: []byte("v1: 1")},
+ {Name: filepath.FromSlash("data/a.yaml"), Content: []byte("a: 1")},
+ {Name: filepath.FromSlash("data/test/v1.yaml"), Content: []byte("v1-2: 2")},
+ {Name: filepath.FromSlash("data/test/v2.yaml"), Content: []byte("v2:\n- 2\n- 3")},
+ {Name: filepath.FromSlash("data/test.yaml"), Content: []byte("v1: 1")},
}
expected := map[string]interface{}{"a": map[string]interface{}{"a": 1},
"test": map[string]interface{}{"v1": map[string]interface{}{"v1-2": 2}, "v2": map[string]interface{}{"v2": []interface{}{2, 3}}}}
- doTestDataDir(t, expected, []source.Input{&source.InMemorySource{ByteSource: sources}})
+ doTestDataDir(t, expected, sources)
}
// issue 892
func TestDataDirMultipleSources(t *testing.T) {
- s1 := []source.ByteSource{
- {Name: filepath.FromSlash("test/first.toml"), Content: []byte("bar = 1")},
- }
+ t.Parallel()
- s2 := []source.ByteSource{
- {Name: filepath.FromSlash("test/first.toml"), Content: []byte("bar = 2")},
- {Name: filepath.FromSlash("test/second.toml"), Content: []byte("tender = 2")},
+ sources := []source.ByteSource{
+ {Name: filepath.FromSlash("data/test/first.toml"), Content: []byte("bar = 1")},
+ {Name: filepath.FromSlash("themes/mytheme/data/test/first.toml"), Content: []byte("bar = 2")},
+ {Name: filepath.FromSlash("data/test/second.toml"), Content: []byte("tender = 2")},
}
expected, _ := parser.HandleTOMLMetaData([]byte("[test.first]\nbar = 1\n[test.second]\ntender=2"))
- doTestDataDir(t, expected, []source.Input{&source.InMemorySource{ByteSource: s1}, &source.InMemorySource{ByteSource: s2}})
+ doTestDataDir(t, expected, sources,
+ "theme", "mytheme")
}
func TestDataDirUnknownFormat(t *testing.T) {
+ t.Parallel()
+
sources := []source.ByteSource{
- {Name: filepath.FromSlash("test.roml"), Content: []byte("boo")},
+ {Name: filepath.FromSlash("data/test.roml"), Content: []byte("boo")},
}
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
- require.NoError(t, s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}}))
+ doTestDataDir(t, true, sources)
}
-func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) {
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
- require.NoError(t, s.loadData(sources))
+func doTestDataDir(t *testing.T, expected interface{}, sources []source.ByteSource, configKeyValues ...interface{}) {
+ var (
+ cfg, fs = newTestCfg()
+ )
+
+ for i := 0; i < len(configKeyValues); i += 2 {
+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
+
+ var (
+ logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+ depsCfg = deps.DepsCfg{Fs: fs, Cfg: cfg, Logger: logger}
+ )
+
+ writeSource(t, fs, filepath.Join("content", "dummy.md"), "content")
+ writeSourcesToSource(t, "", fs, sources...)
+
+ expectBuildError := false
+
+ if ok, shouldFail := expected.(bool); ok && shouldFail {
+ expectBuildError = true
+ }
+
+ s := buildSingleSiteExpected(t, expectBuildError, depsCfg, BuildCfg{SkipRender: true})
- if !reflect.DeepEqual(expected, s.Data) {
+ if !expectBuildError && !reflect.DeepEqual(expected, s.Data) {
t.Errorf("Expected structure\n%#v got\n%#v", expected, s.Data)
}
}
func TestDataFromShortcode(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
- cfg := newTestDepsConfig()
+ var (
+ cfg, fs = newTestCfg()
+ )
- writeSource(t, cfg.Fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
- writeSource(t, cfg.Fs, "layouts/_default/single.html", `
+ writeSource(t, fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
+ writeSource(t, fs, "layouts/_default/single.html", `
* Slogan from template: {{ .Site.Data.hugo.slogan }}
* {{ .Content }}`)
- writeSource(t, cfg.Fs, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`)
- writeSource(t, cfg.Fs, "content/c.md", `---
+ writeSource(t, fs, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`)
+ writeSource(t, fs, "content/c.md", `---
---
Slogan from shortcode: {{< d >}}
`)
- buildSingleSite(t, cfg, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
- content := readSource(t, cfg.Fs, "public/c/index.html")
+ content := readSource(t, 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 64a92247b..513767eb4 100644
--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -17,41 +17,36 @@ import (
"encoding/json"
"fmt"
"html/template"
- "net/url"
- "os"
- "regexp"
"strings"
"testing"
- "io/ioutil"
- "log"
"path/filepath"
"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"
)
-var logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
-
const (
baseURL = "http://foo/bar"
)
func TestShortcodeCrossrefs(t *testing.T) {
+ t.Parallel()
+
for _, relative := range []bool{true, false} {
doTestShortcodeCrossrefs(t, relative)
}
}
func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
- testCommonResetState()
- viper.Set("baseURL", baseURL)
+ var (
+ cfg, fs = newTestCfg()
+ )
+
+ cfg.Set("baseURL", baseURL)
var refShortcode string
var expectedBase string
@@ -67,13 +62,11 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
path := filepath.FromSlash("blog/post.md")
in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path)
- fs := hugofs.NewMem()
-
writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in)
expected := fmt.Sprintf(`%s/simple/url/`, expectedBase)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
require.Len(t, s.RegularPages, 1)
@@ -85,216 +78,243 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
}
func TestShortcodeHighlight(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
if !helpers.HasPygments() {
t.Skip("Skip test as Pygments is not installed")
}
- viper.Set("pygmentsStyle", "bw")
- viper.Set("pygmentsUseClasses", false)
- for i, this := range []struct {
+ for _, this := range []struct {
in, expected string
}{
- {`
-{{< highlight java >}}
+ {`{{< highlight java >}}
void do();
{{< /highlight >}}`,
- "(?s)^\n<div class=\"highlight\" style=\"background: #ffffff\"><pre style=\"line-height: 125%\">.*?void</span> do().*?</pre></div>\n$",
+ "(?s)<div class=\"highlight\" style=\"background: #ffffff\"><pre style=\"line-height: 125%\">.*?void</span> do().*?</pre></div>\n",
},
- {`
-{{< highlight java "style=friendly" >}}
+ {`{{< highlight java "style=friendly" >}}
void do();
{{< /highlight >}}`,
- "(?s)^\n<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n$",
+ "(?s)<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
- if err != nil {
- t.Fatalf("[%d] Handle shortcode error", i)
- }
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- matched, err := regexp.MatchString(this.expected, output)
+ cfg.Set("pygmentsStyle", "bw")
+ cfg.Set("pygmentsUseClasses", false)
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
}
}
func TestShortcodeFigure(t *testing.T) {
- for i, this := range []struct {
+ t.Parallel()
+
+ for _, this := range []struct {
in, expected string
}{
{
`{{< figure src="/img/hugo-logo.png" >}}`,
- "(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?</figure>\n$",
+ "(?s)\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?</figure>\n",
},
{
// set alt
`{{< figure src="/img/hugo-logo.png" alt="Hugo logo" >}}`,
- "(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" alt=\"Hugo logo\" />.*?</figure>\n$",
+ "(?s)\n<figure >.*?<img src=\"/img/hugo-logo.png\" alt=\"Hugo logo\" />.*?</figure>\n",
},
// set title
{
`{{< figure src="/img/hugo-logo.png" title="Hugo logo" >}}`,
- "(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<h4>Hugo logo</h4>.*?</figcaption>.*?</figure>\n$",
+ "(?s)\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<h4>Hugo logo</h4>.*?</figcaption>.*?</figure>\n",
},
// set attr and attrlink
{
`{{< figure src="/img/hugo-logo.png" attr="Hugo logo" attrlink="/img/hugo-logo.png" >}}`,
- "(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n$",
+ "(?s)\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
- matched, err := regexp.MatchString(this.expected, output)
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
}
}
func TestShortcodeSpeakerdeck(t *testing.T) {
- for i, this := range []struct {
+ t.Parallel()
+
+ for _, this := range []struct {
in, expected string
}{
{
`{{< speakerdeck 4e8126e72d853c0060001f97 >}}`,
- "(?s)^<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>$",
+ "(?s)<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
- matched, err := regexp.MatchString(this.expected, output)
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
}
}
func TestShortcodeYoutube(t *testing.T) {
- for i, this := range []struct {
+ t.Parallel()
+
+ for _, this := range []struct {
in, expected string
}{
{
`{{< youtube w7Ft2ymGmfc >}}`,
- "(?s)^\n<div style=\".*?\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" style=\".*?\" allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>\n$",
+ "(?s)\n<div style=\".*?\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" style=\".*?\" allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>\n",
},
// set class
{
`{{< youtube w7Ft2ymGmfc video>}}`,
- "(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>\n$",
+ "(?s)\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>\n",
},
// set class and autoplay (using named params)
{
`{{< youtube id="w7Ft2ymGmfc" class="video" autoplay="true" >}}`,
- "(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>$",
+ "(?s)\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- matched, err := regexp.MatchString(this.expected, output)
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
}
+
}
func TestShortcodeVimeo(t *testing.T) {
- for i, this := range []struct {
+ t.Parallel()
+
+ for _, this := range []struct {
in, expected string
}{
{
`{{< vimeo 146022717 >}}`,
- "(?s)^\n<div style=\".*?\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" style=\".*?\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n$",
+ "(?s)\n<div style=\".*?\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" style=\".*?\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n",
},
// set class
{
`{{< vimeo 146022717 video >}}`,
- "(?s)^\n<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n$",
+ "(?s)\n<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n",
},
// set class (using named params)
{
`{{< vimeo id="146022717" class="video" >}}`,
- "(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>$",
+ "(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- matched, err := regexp.MatchString(this.expected, output)
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
}
}
func TestShortcodeGist(t *testing.T) {
- for i, this := range []struct {
+ t.Parallel()
+
+ for _, this := range []struct {
in, expected string
}{
{
`{{< gist spf13 7896402 >}}`,
- "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\"></script>$",
+ "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\"></script>",
},
{
`{{< gist spf13 7896402 "img.html" >}}`,
- "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>$",
+ "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>",
},
} {
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(this.in, p)
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- matched, err := regexp.MatchString(this.expected, output)
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
}
}
func TestShortcodeTweet(t *testing.T) {
+ t.Parallel()
+
for i, this := range []struct {
in, resp, expected string
}{
{
`{{< tweet 666616452582129664 >}}`,
`{"url":"https:\/\/twitter.com\/spf13\/status\/666616452582129664","author_name":"Steve Francia","author_url":"https:\/\/twitter.com\/spf13","html":"\u003Cblockquote class=\"twitter-tweet\"\u003E\u003Cp lang=\"en\" dir=\"ltr\"\u003EHugo 0.15 will have 30%+ faster render times thanks to this commit \u003Ca href=\"https:\/\/t.co\/FfzhM8bNhT\"\u003Ehttps:\/\/t.co\/FfzhM8bNhT\u003C\/a\u003E \u003Ca href=\"https:\/\/twitter.com\/hashtag\/gohugo?src=hash\"\u003E#gohugo\u003C\/a\u003E \u003Ca href=\"https:\/\/twitter.com\/hashtag\/golang?src=hash\"\u003E#golang\u003C\/a\u003E \u003Ca href=\"https:\/\/t.co\/ITbMNU2BUf\"\u003Ehttps:\/\/t.co\/ITbMNU2BUf\u003C\/a\u003E\u003C\/p\u003E&mdash; Steve Francia (@spf13) \u003Ca href=\"https:\/\/twitter.com\/spf13\/status\/666616452582129664\"\u003ENovember 17, 2015\u003C\/a\u003E\u003C\/blockquote\u003E\n\u003Cscript async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"\u003E\u003C\/script\u003E","width":550,"height":null,"type":"rich","cache_age":"3153600000","provider_name":"Twitter","provider_url":"https:\/\/twitter.com","version":"1.0"}`,
- `(?s)^<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Hugo 0.15 will have 30%. faster render times thanks to this commit <a href="https://t.co/FfzhM8bNhT">https://t.co/FfzhM8bNhT</a> <a href="https://twitter.com/hashtag/gohugo.src=hash">#gohugo</a> <a href="https://twitter.com/hashtag/golang.src=hash">#golang</a> <a href="https://t.co/ITbMNU2BUf">https://t.co/ITbMNU2BUf</a></p>&mdash; Steve Francia .@spf13. <a href="https://twitter.com/spf13/status/666616452582129664">November 17, 2015</a></blockquote>.*?<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>$`,
+ `(?s)^<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Hugo 0.15 will have 30%. faster render times thanks to this commit <a href="https://t.co/FfzhM8bNhT">https://t.co/FfzhM8bNhT</a> <a href="https://twitter.com/hashtag/gohugo.src=hash">#gohugo</a> <a href="https://twitter.com/hashtag/golang.src=hash">#golang</a> <a href="https://t.co/ITbMNU2BUf">https://t.co/ITbMNU2BUf</a></p>&mdash; Steve Francia .@spf13. <a href="https://twitter.com/spf13/status/666616452582129664">November 17, 2015</a></blockquote>.*?<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>`,
},
} {
// overload getJSON to return mock API response from Twitter
@@ -310,28 +330,32 @@ func TestShortcodeTweet(t *testing.T) {
},
}
- p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
+
+ withTemplate := func(templ tplapi.Template) error {
templ.Funcs(tweetFuncMap)
return nil
- })
+ }
- cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.twitter.com/1/statuses/oembed.json?id=666616452582129664")
- defer os.Remove(cacheFileID)
- output, err := HandleShortcodes(this.in, p)
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
- matched, err := regexp.MatchString(this.expected, output)
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{})
- if err != nil {
- t.Fatalf("[%d] Regexp error", i)
- }
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if !matched {
- t.Errorf("[%d] unexpected rendering, got %s\n", i, output)
- }
}
}
func TestShortcodeInstagram(t *testing.T) {
+ t.Parallel()
+
for i, this := range []struct {
in, hidecaption, resp, expected string
}{
@@ -339,15 +363,13 @@ func TestShortcodeInstagram(t *testing.T) {
`{{< instagram BMokmydjG-M >}}`,
`0`,
`{"provider_url": "https://www.instagram.com", "media_id": "1380514280986406796_25025320", "author_name": "instagram", "height": null, "thumbnail_url": "https://scontent-amt2-1.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/15048135_1880160212214218_7827880881132929024_n.jpg?ig_cache_key=MTM4MDUxNDI4MDk4NjQwNjc5Ng%3D%3D.2", "thumbnail_width": 640, "thumbnail_height": 640, "provider_name": "Instagram", "title": "Today, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories.\nBoomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode.\nYou can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they'll see a pop-up that takes them to that profile.\nYou may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app.\nTo learn more about today\u2019s updates, check out help.instagram.com.\nThese updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.", "html": "\u003cblockquote class=\"instagram-media\" data-instgrm-captioned data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\"\u003e\u003cdiv style=\"padding:8px;\"\u003e \u003cdiv style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;\"\u003e \u003cdiv style=\" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\"\u003e\u003c/div\u003e\u003c/div\u003e \u003cp style=\" margin:8px 0 0 0; padding:0 4px;\"\u003e \u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;\" target=\"_blank\"\u003eToday, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories. Boomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode. You can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they\u0026#39;ll see a pop-up that takes them to that profile. You may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app. To learn more about today\u2019s updates, check out help.instagram.com. These updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.\u003c/a\u003e\u003c/p\u003e \u003cp style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003eA photo posted by Instagram (@instagram) on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
- `<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">Today, we’re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We’re also starting to test links inside some stories. Boomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select “Boomerang” mode. You can also now share who you’re with or who you’re thinking of by mentioning them in your story. When you add text to your story, type “@” followed by a username and select the person you’d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they&#39;ll see a pop-up that takes them to that profile. You may begin to spot “See More” links at the bottom of some stories. This is a test that lets verified accounts add links so it’s easy to learn more. From your favorite chefs’ recipes to articles from top journalists or concert dates from the musicians you love, tap “See More” or swipe up to view the link right inside the app. To learn more about today’s updates, check out help.instagram.com. These updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A photo posted by Instagram (@instagram) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
-<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
+ `(?s)<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" .*defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
},
{
`{{< instagram BMokmydjG-M hidecaption >}}`,
`1`,
`{"provider_url": "https://www.instagram.com", "media_id": "1380514280986406796_25025320", "author_name": "instagram", "height": null, "thumbnail_url": "https://scontent-amt2-1.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/15048135_1880160212214218_7827880881132929024_n.jpg?ig_cache_key=MTM4MDUxNDI4MDk4NjQwNjc5Ng%3D%3D.2", "thumbnail_width": 640, "thumbnail_height": 640, "provider_name": "Instagram", "title": "Today, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories.\nBoomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode.\nYou can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they'll see a pop-up that takes them to that profile.\nYou may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app.\nTo learn more about today\u2019s updates, check out help.instagram.com.\nThese updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.", "html": "\u003cblockquote class=\"instagram-media\" data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\"\u003e\u003cdiv style=\"padding:8px;\"\u003e \u003cdiv style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;\"\u003e \u003cdiv style=\" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cp style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003e\u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
- `<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
-<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
+ `(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
},
} {
// overload getJSON to return mock API response from Instagram
@@ -363,21 +385,25 @@ func TestShortcodeInstagram(t *testing.T) {
},
}
- p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
+
+ withTemplate := func(templ tplapi.Template) error {
templ.Funcs(instagramFuncMap)
return nil
- })
+ }
- cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.instagram.com/oembed/?url=https://instagram.com/p/BMokmydjG-M/&hidecaption="+this.hidecaption)
- defer os.Remove(cacheFileID)
- output, err := HandleShortcodes(this.in, p)
+ writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
+title: Shorty
+---
+%s`, this.in))
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content | safeHTML }}`)
- if err != nil {
- t.Fatalf("[%d] Failed to render shortcodes", i)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{})
+
+ th.assertFileContentRegexp(t, fs, filepath.Join("public", "simple", "index.html"), false, this.expected)
- if this.expected != output {
- t.Errorf("[%d] unexpected rendering, got %s expected: %s", i, output, this.expected)
- }
}
}
diff --git a/hugolib/gitinfo.go b/hugolib/gitinfo.go
index 82baa3250..dfa2b4dc8 100644
--- a/hugolib/gitinfo.go
+++ b/hugolib/gitinfo.go
@@ -20,17 +20,16 @@ import (
"github.com/bep/gitmap"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
)
func (h *HugoSites) assembleGitInfo() {
- if !viper.GetBool("enableGitInfo") {
+ if !h.Cfg.GetBool("enableGitInfo") {
return
}
var (
- workingDir = viper.GetString("workingDir")
- contentDir = viper.GetString("contentDir")
+ workingDir = h.Cfg.GetString("workingDir")
+ contentDir = h.Cfg.GetString("contentDir")
)
gitRepo, err := gitmap.Map(workingDir, "")
diff --git a/hugolib/handler_page.go b/hugolib/handler_page.go
index 6b6b17173..01c041ffc 100644
--- a/hugolib/handler_page.go
+++ b/hugolib/handler_page.go
@@ -19,7 +19,6 @@ import (
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/source"
- "github.com/spf13/viper"
)
func init() {
@@ -43,6 +42,13 @@ func (b basicPageHandler) Read(f *source.File, s *Site) HandledResult {
return HandledResult{file: f, err: err}
}
+ // In a multilanguage setup, we use the first site to
+ // do the initial processing.
+ // That site may be different than where the page will end up,
+ // so we do the assignment here.
+ // We should clean up this, but that will have to wait.
+ s.assignSiteByLanguage(page)
+
return HandledResult{file: f, page: page, err: err}
}
@@ -117,7 +123,7 @@ func commonConvert(p *Page) HandledResult {
// TODO(bep) these page handlers need to be re-evaluated, as it is hard to
// process a page in isolation. See the new preRender func.
- if viper.GetBool("enableEmoji") {
+ if p.s.Cfg.GetBool("enableEmoji") {
p.workContent = helpers.Emojify(p.workContent)
}
diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go
index 01e6793a6..76a9b38d1 100644
--- a/hugolib/handler_test.go
+++ b/hugolib/handler_test.go
@@ -19,18 +19,18 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/viper"
)
func TestDefaultHandler(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
- viper.Set("defaultExtension", "html")
- viper.Set("verbose", true)
- viper.Set("uglyURLs", true)
+ var (
+ cfg, fs = newTestCfg()
+ )
- fs := hugofs.NewMem()
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("verbose", true)
+ cfg.Set("uglyURLs", true)
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"), "<!doctype html><html><body>more content</body></html>")
@@ -46,7 +46,7 @@ func TestDefaultHandler(t *testing.T) {
writeSource(t, fs, filepath.FromSlash("head"), "<head><script src=\"script.js\"></script></head>")
writeSource(t, fs, filepath.FromSlash("head_abs"), "<head><script src=\"/script.js\"></script></head")
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
tests := []struct {
doc string
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index c8ae51d0a..f0feec23e 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -22,9 +22,7 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
-
- "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/i18n"
"github.com/spf13/hugo/tpl"
"github.com/spf13/hugo/tplapi"
)
@@ -48,7 +46,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
return nil, errors.New("Cannot provide Language in Cfg when sites are provided")
}
- langConfig, err := newMultiLingualFromSites(sites...)
+ langConfig, err := newMultiLingualFromSites(cfg.Cfg, sites...)
if err != nil {
return nil, err
@@ -62,6 +60,9 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
s.owner = h
}
+ // TODO(bep)
+ cfg.Cfg.Set("multilingual", sites[0].multilingualEnabled())
+
applyDepsIfNeeded(cfg, sites...)
h.Deps = sites[0].Deps
@@ -70,11 +71,14 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
}
func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
-
if cfg.TemplateProvider == nil {
cfg.TemplateProvider = tpl.DefaultTemplateProvider
}
+ if cfg.TranslationProvider == nil {
+ cfg.TranslationProvider = i18n.NewTranslationProvider()
+ }
+
var (
d *deps.Deps
err error
@@ -89,7 +93,9 @@ func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
cfg.Language = s.Language
cfg.WithTemplate = s.withSiteTemplates(cfg.WithTemplate)
d = deps.New(cfg)
- if err := d.LoadTemplates(); err != nil {
+ s.Deps = d
+
+ if err := d.LoadResources(); err != nil {
return err
}
@@ -98,16 +104,16 @@ func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
if err != nil {
return err
}
+ s.Deps = d
}
- s.Deps = d
+
}
return nil
}
-// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
-// TODO(bep) globals rename this when all the globals are gone.
-func NewHugoSitesFromConfiguration(cfg deps.DepsCfg) (*HugoSites, error) {
+// NewHugoSites creates HugoSites from the given config.
+func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
sites, err := createSitesFromConfig(cfg)
if err != nil {
return nil, err
@@ -141,10 +147,10 @@ func createSitesFromConfig(cfg deps.DepsCfg) ([]*Site, error) {
sites []*Site
)
- multilingual := viper.GetStringMap("languages")
+ multilingual := cfg.Cfg.GetStringMap("languages")
if len(multilingual) == 0 {
- l := helpers.NewDefaultLanguage()
+ l := helpers.NewDefaultLanguage(cfg.Cfg)
cfg.Language = l
s, err := newSite(cfg)
if err != nil {
@@ -156,7 +162,7 @@ func createSitesFromConfig(cfg deps.DepsCfg) ([]*Site, error) {
if len(multilingual) > 0 {
var err error
- languages, err := toSortedLanguages(multilingual)
+ languages, err := toSortedLanguages(cfg.Cfg, multilingual)
if err != nil {
return nil, fmt.Errorf("Failed to parse multilingual config: %s", err)
@@ -190,14 +196,14 @@ func (h *HugoSites) reset() {
func (h *HugoSites) createSitesFromConfig() error {
- depsCfg := deps.DepsCfg{Fs: h.Fs}
+ depsCfg := deps.DepsCfg{Fs: h.Fs, Cfg: h.Cfg}
sites, err := createSitesFromConfig(depsCfg)
if err != nil {
return err
}
- langConfig, err := newMultiLingualFromSites(sites...)
+ langConfig, err := newMultiLingualFromSites(depsCfg.Cfg, sites...)
if err != nil {
return err
@@ -251,12 +257,12 @@ func (h *HugoSites) renderCrossSitesArtifacts() error {
return nil
}
- if viper.GetBool("disableSitemap") {
+ if h.Cfg.GetBool("disableSitemap") {
return nil
}
// TODO(bep) DRY
- sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
+ sitemapDefault := parseSitemap(h.Cfg.GetStringMap("sitemap"))
s := h.Sites[0]
@@ -398,6 +404,24 @@ func (h *HugoSites) createMissingPages() error {
return nil
}
+func (s *Site) assignSiteByLanguage(p *Page) {
+
+ pageLang := p.Lang()
+
+ if pageLang == "" {
+ panic("Page language missing: " + p.Title)
+ }
+
+ for _, site := range s.owner.Sites {
+ if strings.HasPrefix(site.Language.Lang, pageLang) {
+ p.s = site
+ p.Site = &site.Info
+ return
+ }
+ }
+
+}
+
func (h *HugoSites) setupTranslations() {
master := h.Sites[0]
@@ -410,11 +434,11 @@ func (h *HugoSites) setupTranslations() {
shouldBuild := p.shouldBuild()
for i, site := range h.Sites {
- if strings.HasPrefix(site.Language.Lang, p.Lang()) {
+ // The site is assigned by language when read.
+ if site == p.s {
site.updateBuildStats(p)
if shouldBuild {
site.Pages = append(site.Pages, p)
- p.Site = &site.Info
}
}
@@ -577,32 +601,3 @@ func (h *HugoSites) findAllPagesByKind(kind string) Pages {
func (h *HugoSites) findAllPagesByKindNotIn(kind string) Pages {
return h.findPagesByKindNotIn(kind, h.Sites[0].AllPages)
}
-
-// Convenience func used in tests.
-func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages helpers.Languages, cfg deps.DepsCfg) (*HugoSites, error) {
- if len(languages) == 0 {
- panic("Must provide at least one language")
- }
-
- first := &Site{
- Language: languages[0],
- Source: &source.InMemorySource{ByteSource: input},
- }
- if len(languages) == 1 {
- return newHugoSites(cfg, first)
- }
-
- sites := make([]*Site, len(languages))
- sites[0] = first
- for i := 1; i < len(languages); i++ {
- sites[i] = &Site{Language: languages[i]}
- }
-
- return newHugoSites(cfg, sites...)
-
-}
-
-// Convenience func used in tests.
-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 e915d11da..a214e523c 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -121,6 +121,7 @@ func (h *HugoSites) process(config *BuildCfg, events ...fsnotify.Event) error {
// but that seems like a daunting task.
// So for now, if there are more than one site (language),
// we pre-process the first one, then configure all the sites based on that.
+
firstSite := h.Sites[0]
if len(events) > 0 {
@@ -169,9 +170,6 @@ func (h *HugoSites) assemble(config *BuildCfg) error {
}
for _, s := range h.Sites {
- if err := s.setCurrentLanguageConfig(); err != nil {
- return err
- }
s.preparePagesForRender(config)
}
diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go
index 9abb17d5e..ccc056d11 100644
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -14,52 +14,35 @@ import (
"github.com/fortytw2/leaktest"
"github.com/fsnotify/fsnotify"
"github.com/spf13/afero"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
- // jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type testSiteConfig struct {
- DefaultContentLanguage string
- Fs *hugofs.Fs
+ DefaultContentLanguage string
+ DefaultContentLanguageInSubdir bool
+ Fs afero.Fs
}
-func init() {
- testCommonResetState()
-}
-
-func testCommonResetState() {
- viper.Reset()
- // 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)
-
-}
-
-// 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) {
-
+func TestMultiSitesMainLangInRoot(t *testing.T) {
+ t.Parallel()
for _, b := range []bool{true, false} {
doTestMultiSitesMainLangInRoot(t, b)
}
}
func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
- testCommonResetState()
- viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
- fs := hugofs.NewMem()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
+
+ siteConfig := testSiteConfig{Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: defaultInSubDir}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
+ th := testHelper{sites.Cfg}
+ fs := sites.Fs
err := sites.Build(BuildCfg{})
@@ -80,7 +63,6 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
require.Equal(t, "", frSite.Info.LanguagePrefix)
}
- fmt.Println(">>>", enSite.PathSpec)
require.Equal(t, "/blog/en/foo", enSite.PathSpec.RelURL("foo", true))
doc1en := enSite.RegularPages[0]
@@ -94,71 +76,75 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
frPerm := doc1fr.Permalink()
frRelPerm := doc1fr.RelPermalink()
// Main language in root
- require.Equal(t, replaceDefaultContentLanguageValue("http://example.com/blog/fr/sect/doc1/", defaultInSubDir), frPerm)
- require.Equal(t, replaceDefaultContentLanguageValue("/blog/fr/sect/doc1/", defaultInSubDir), frRelPerm)
+ require.Equal(t, th.replaceDefaultContentLanguageValue("http://example.com/blog/fr/sect/doc1/", defaultInSubDir), frPerm)
+ require.Equal(t, th.replaceDefaultContentLanguageValue("/blog/fr/sect/doc1/", defaultInSubDir), frRelPerm)
- 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")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour")
+ th.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, fs, "public/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
+ th.assertFileContent(t, fs, "public/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
} else {
// should have redirect back to root
- assertFileContent(t, fs, "public/fr/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog" />`)
+ th.assertFileContent(t, fs, "public/fr/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog" />`)
}
- assertFileContent(t, fs, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour")
- assertFileContent(t, fs, "public/en/index.html", defaultInSubDir, "Home", "Hello")
+ th.assertFileContent(t, fs, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour")
+ th.assertFileContent(t, fs, "public/en/index.html", defaultInSubDir, "Home", "Hello")
// Check list pages
- 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")
+ th.assertFileContent(t, fs, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour")
+ th.assertFileContent(t, fs, "public/en/sect/index.html", defaultInSubDir, "List", "Hello")
+ th.assertFileContent(t, fs, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour")
+ th.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, fs, "public/sitemap.xml", true,
+ th.assertFileContent(t, fs, "public/sitemap.xml", true,
"<loc>http://example.com/blog/en/sitemap.xml</loc>",
"<loc>http://example.com/blog/fr/sitemap.xml</loc>")
if defaultInSubDir {
- assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/fr/</loc>")
+ th.assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/fr/</loc>")
} else {
- assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/</loc>")
+ th.assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/</loc>")
}
- assertFileContent(t, fs, "public/en/sitemap.xml", true, "<loc>http://example.com/blog/en/</loc>")
+ th.assertFileContent(t, fs, "public/en/sitemap.xml", true, "<loc>http://example.com/blog/en/</loc>")
// Check rss
- assertFileContent(t, fs, "public/fr/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/index.xml"`)
- assertFileContent(t, fs, "public/en/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/index.xml"`)
- assertFileContent(t, fs, "public/fr/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/sect/index.xml"`)
- assertFileContent(t, fs, "public/en/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
- assertFileContent(t, fs, "public/fr/plaques/frtag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`)
- assertFileContent(t, fs, "public/en/tags/tag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
+ th.assertFileContent(t, fs, "public/fr/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/index.xml"`)
+ th.assertFileContent(t, fs, "public/en/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/index.xml"`)
+ th.assertFileContent(t, fs, "public/fr/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/sect/index.xml"`)
+ th.assertFileContent(t, fs, "public/en/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
+ th.assertFileContent(t, fs, "public/fr/plaques/frtag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`)
+ th.assertFileContent(t, fs, "public/en/tags/tag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
// Check paginators
- assertFileContent(t, fs, "public/fr/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/"`)
- assertFileContent(t, fs, "public/en/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/"`)
- assertFileContent(t, fs, "public/fr/page/2/index.html", defaultInSubDir, "Home Page 2", "Bonjour", "http://example.com/blog/fr/")
- assertFileContent(t, fs, "public/en/page/2/index.html", defaultInSubDir, "Home Page 2", "Hello", "http://example.com/blog/en/")
- assertFileContent(t, fs, "public/fr/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/sect/"`)
- assertFileContent(t, fs, "public/en/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/sect/"`)
- assertFileContent(t, fs, "public/fr/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/sect/")
- assertFileContent(t, fs, "public/en/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/sect/")
- assertFileContent(t, fs, "public/fr/plaques/frtag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`)
- assertFileContent(t, fs, "public/en/tags/tag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
- assertFileContent(t, fs, "public/fr/plaques/frtag1/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/plaques/frtag1/")
- assertFileContent(t, fs, "public/en/tags/tag1/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
+ th.assertFileContent(t, fs, "public/fr/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/"`)
+ th.assertFileContent(t, fs, "public/en/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/"`)
+ th.assertFileContent(t, fs, "public/fr/page/2/index.html", defaultInSubDir, "Home Page 2", "Bonjour", "http://example.com/blog/fr/")
+ th.assertFileContent(t, fs, "public/en/page/2/index.html", defaultInSubDir, "Home Page 2", "Hello", "http://example.com/blog/en/")
+ th.assertFileContent(t, fs, "public/fr/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/sect/"`)
+ th.assertFileContent(t, fs, "public/en/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/sect/"`)
+ th.assertFileContent(t, fs, "public/fr/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/sect/")
+ th.assertFileContent(t, fs, "public/en/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/sect/")
+ th.assertFileContent(t, fs, "public/fr/plaques/frtag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`)
+ th.assertFileContent(t, fs, "public/en/tags/tag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
+ th.assertFileContent(t, fs, "public/fr/plaques/frtag1/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/plaques/frtag1/")
+ th.assertFileContent(t, fs, "public/en/tags/tag1/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
// nn (Nynorsk) and nb (Bokmål) have custom pagePath: side ("page" in Norwegian)
- assertFileContent(t, fs, "public/nn/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nn/"`)
- assertFileContent(t, fs, "public/nb/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nb/"`)
+ th.assertFileContent(t, fs, "public/nn/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nn/"`)
+ th.assertFileContent(t, fs, "public/nb/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nb/"`)
}
-func replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) string {
- replace := viper.GetString("defaultContentLanguage") + "/"
+type testHelper struct {
+ Cfg config.Provider
+}
+
+func (th testHelper) replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) string {
+ replace := th.Cfg.GetString("defaultContentLanguage") + "/"
if !defaultInSubDir {
value = strings.Replace(value, replace, "", 1)
@@ -167,36 +153,33 @@ func replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) stri
}
-func assertFileContent(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
- filename = replaceDefaultContentLanguageValue(filename, defaultInSubDir)
+func (th testHelper) assertFileContent(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
+ filename = th.replaceDefaultContentLanguageValue(filename, defaultInSubDir)
content := readDestination(t, fs, filename)
for _, match := range matches {
- match = replaceDefaultContentLanguageValue(match, defaultInSubDir)
+ match = th.replaceDefaultContentLanguageValue(match, defaultInSubDir)
require.True(t, strings.Contains(content, match), fmt.Sprintf("File no match for\n%q in\n%q:\n%s", strings.Replace(match, "%", "%%", -1), filename, strings.Replace(content, "%", "%%", -1)))
}
}
-func assertFileContentRegexp(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
- filename = replaceDefaultContentLanguageValue(filename, defaultInSubDir)
+func (th testHelper) assertFileContentRegexp(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
+ filename = th.replaceDefaultContentLanguageValue(filename, defaultInSubDir)
content := readDestination(t, fs, filename)
for _, match := range matches {
- match = replaceDefaultContentLanguageValue(match, defaultInSubDir)
+ match = th.replaceDefaultContentLanguageValue(match, defaultInSubDir)
r := regexp.MustCompile(match)
- require.True(t, r.MatchString(content), fmt.Sprintf("File no match for\n%q in\n%q:\n%s", match, filename, content))
+ require.True(t, r.MatchString(content), fmt.Sprintf("File no match for\n%q in\n%q:\n%s", strings.Replace(match, "%", "%%", -1), filename, strings.Replace(content, "%", "%%", -1)))
}
}
func TestMultiSitesWithTwoLanguages(t *testing.T) {
- testCommonResetState()
-
- viper.Set("defaultContentLanguage", "nn")
+ t.Parallel()
+ mm := afero.NewMemMapFs()
- fs := hugofs.NewMem()
+ writeToFs(t, mm, "config.toml", `
- depsCfg := deps.DepsCfg{Fs: fs}
- viper.SetFs(depsCfg.Fs.Source)
+defaultContentLanguage = "nn"
- writeSource(t, depsCfg.Fs, "config.toml", `
[languages]
[languages.nn]
languageName = "Nynorsk"
@@ -210,11 +193,12 @@ weight = 2
`,
)
- if err := LoadGlobalConfig("", "config.toml"); err != nil {
- t.Fatalf("Failed to load config: %s", err)
- }
+ cfg, err := LoadConfig(mm, "", "config.toml")
+ require.NoError(t, err)
+
+ fs := hugofs.NewFrom(mm, cfg)
- sites, err := NewHugoSitesFromConfiguration(depsCfg)
+ sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -238,100 +222,108 @@ weight = 2
//
func TestMultiSitesBuild(t *testing.T) {
+ t.Parallel()
+
for _, config := range []struct {
content string
suffix string
}{
{multiSiteTOMLConfigTemplate, "toml"},
- {multiSiteYAMLConfig, "yml"},
- {multiSiteJSONConfig, "json"},
+ {multiSiteYAMLConfigTemplate, "yml"},
+ {multiSiteJSONConfigTemplate, "json"},
} {
doTestMultiSitesBuild(t, config.content, config.suffix)
}
}
func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
- defer leaktest.Check(t)()
- testCommonResetState()
- fs := hugofs.NewMem()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
+ siteConfig := testSiteConfig{Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: true}
sites := createMultiTestSitesForConfig(t, siteConfig, configTemplate, configSuffix)
+ require.Len(t, sites.Sites, 4)
+
+ fs := sites.Fs
+ th := testHelper{sites.Cfg}
err := sites.Build(BuildCfg{})
if err != nil {
t.Fatalf("Failed to build sites: %s", err)
}
+ // Check site config
+ for _, s := range sites.Sites {
+ require.True(t, s.Info.defaultContentLanguageInSubdir, s.Info.Title)
+ }
+
enSite := sites.Sites[0]
enSiteHome := enSite.getPage(KindHome)
require.True(t, enSiteHome.IsTranslated())
- assert.Equal(t, "en", enSite.Language.Lang)
+ require.Equal(t, "en", enSite.Language.Lang)
if len(enSite.RegularPages) != 4 {
t.Fatal("Expected 4 english pages")
}
- assert.Len(t, enSite.Source.Files(), 14, "should have 13 source files")
- assert.Len(t, enSite.AllPages, 28, "should have 28 total pages (including translations and index types)")
+ require.Len(t, enSite.Source.Files(), 14, "should have 13 source files")
+ require.Len(t, enSite.AllPages, 28, "should have 28 total pages (including translations and index types)")
doc1en := enSite.RegularPages[0]
permalink := doc1en.Permalink()
- assert.NoError(t, err, "permalink call failed")
- assert.Equal(t, "http://example.com/blog/en/sect/doc1-slug/", permalink, "invalid doc1.en permalink")
- assert.Len(t, doc1en.Translations(), 1, "doc1-en should have one translation, excluding itself")
+ require.NoError(t, err, "permalink call failed")
+ require.Equal(t, "http://example.com/blog/en/sect/doc1-slug/", permalink, "invalid doc1.en permalink")
+ require.Len(t, doc1en.Translations(), 1, "doc1-en should have one translation, excluding itself")
doc2 := enSite.RegularPages[1]
permalink = doc2.Permalink()
- assert.NoError(t, err, "permalink call failed")
- assert.Equal(t, "http://example.com/blog/en/sect/doc2/", permalink, "invalid doc2 permalink")
+ require.NoError(t, err, "permalink call failed")
+ require.Equal(t, "http://example.com/blog/en/sect/doc2/", permalink, "invalid doc2 permalink")
doc3 := enSite.RegularPages[2]
permalink = doc3.Permalink()
- assert.NoError(t, err, "permalink call failed")
+ require.NoError(t, err, "permalink call failed")
// Note that /superbob is a custom URL set in frontmatter.
// We respect that URL literally (it can be /search.json)
// and do no not do any language code prefixing.
- assert.Equal(t, "http://example.com/blog/superbob", permalink, "invalid doc3 permalink")
+ require.Equal(t, "http://example.com/blog/superbob", permalink, "invalid doc3 permalink")
- assert.Equal(t, "/superbob", doc3.URL(), "invalid url, was specified on doc3")
- assertFileContent(t, fs, "public/superbob/index.html", true, "doc3|Hello|en")
- assert.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
+ require.Equal(t, "/superbob", doc3.URL(), "invalid url, was specified on doc3")
+ th.assertFileContent(t, fs, "public/superbob/index.html", true, "doc3|Hello|en")
+ require.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
doc1fr := doc1en.Translations()[0]
permalink = doc1fr.Permalink()
- assert.NoError(t, err, "permalink call failed")
- assert.Equal(t, "http://example.com/blog/fr/sect/doc1/", permalink, "invalid doc1fr permalink")
+ require.NoError(t, err, "permalink call failed")
+ require.Equal(t, "http://example.com/blog/fr/sect/doc1/", permalink, "invalid doc1fr permalink")
- assert.Equal(t, doc1en.Translations()[0], doc1fr, "doc1-en should have doc1-fr as translation")
- assert.Equal(t, doc1fr.Translations()[0], doc1en, "doc1-fr should have doc1-en as translation")
- assert.Equal(t, "fr", doc1fr.Language().Lang)
+ require.Equal(t, doc1en.Translations()[0], doc1fr, "doc1-en should have doc1-fr as translation")
+ require.Equal(t, doc1fr.Translations()[0], doc1en, "doc1-fr should have doc1-en as translation")
+ require.Equal(t, "fr", doc1fr.Language().Lang)
doc4 := enSite.AllPages[4]
permalink = doc4.Permalink()
- assert.Equal(t, "http://example.com/blog/fr/sect/doc4/", permalink, "invalid doc4 permalink")
- assert.Equal(t, "/blog/fr/sect/doc4/", doc4.URL())
+ require.Equal(t, "http://example.com/blog/fr/sect/doc4/", permalink, "invalid doc4 permalink")
+ require.Equal(t, "/blog/fr/sect/doc4/", doc4.URL())
- assert.Len(t, doc4.Translations(), 0, "found translations for doc4")
+ require.Len(t, doc4.Translations(), 0, "found translations for doc4")
doc5 := enSite.AllPages[5]
permalink = doc5.Permalink()
- assert.Equal(t, "http://example.com/blog/fr/somewhere/else/doc5", permalink, "invalid doc5 permalink")
+ require.Equal(t, "http://example.com/blog/fr/somewhere/else/doc5", permalink, "invalid doc5 permalink")
// Taxonomies and their URLs
- assert.Len(t, enSite.Taxonomies, 1, "should have 1 taxonomy")
+ require.Len(t, enSite.Taxonomies, 1, "should have 1 taxonomy")
tags := enSite.Taxonomies["tags"]
- assert.Len(t, tags, 2, "should have 2 different tags")
- assert.Equal(t, tags["tag1"][0].Page, doc1en, "first tag1 page should be doc1")
+ require.Len(t, tags, 2, "should have 2 different tags")
+ require.Equal(t, tags["tag1"][0].Page, doc1en, "first tag1 page should be doc1")
frSite := sites.Sites[1]
- assert.Equal(t, "fr", frSite.Language.Lang)
- assert.Len(t, frSite.RegularPages, 3, "should have 3 pages")
- assert.Len(t, frSite.AllPages, 28, "should have 28 total pages (including translations and nodes)")
+ require.Equal(t, "fr", frSite.Language.Lang)
+ require.Len(t, frSite.RegularPages, 3, "should have 3 pages")
+ require.Len(t, frSite.AllPages, 28, "should have 28 total pages (including translations and nodes)")
for _, frenchPage := range frSite.RegularPages {
- assert.Equal(t, "fr", frenchPage.Lang())
+ require.Equal(t, "fr", frenchPage.Lang())
}
// Check redirect to main language, French
@@ -339,12 +331,12 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
require.True(t, strings.Contains(languageRedirect, "0; url=http://example.com/blog/fr"), languageRedirect)
// check home page content (including data files rendering)
- assertFileContent(t, fs, "public/en/index.html", true, "Home Page 1", "Hello", "Hugo Rocks!")
- assertFileContent(t, fs, "public/fr/index.html", true, "Home Page 1", "Bonjour", "Hugo Rocks!")
+ th.assertFileContent(t, fs, "public/en/index.html", true, "Home Page 1", "Hello", "Hugo Rocks!")
+ th.assertFileContent(t, fs, "public/fr/index.html", true, "Home Page 1", "Bonjour", "Hugo Rocks!")
// check single page content
- 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")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
+ th.assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
// Check node translations
homeEn := enSite.getPage(KindHome)
@@ -397,9 +389,9 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
readDestination(t, fs, "public/en/tags/tag1/index.html")
// Check Blackfriday config
- assert.True(t, strings.Contains(string(doc1fr.Content), "&laquo;"), string(doc1fr.Content))
- assert.False(t, strings.Contains(string(doc1en.Content), "&laquo;"), string(doc1en.Content))
- assert.True(t, strings.Contains(string(doc1en.Content), "&ldquo;"), string(doc1en.Content))
+ require.True(t, strings.Contains(string(doc1fr.Content), "&laquo;"), string(doc1fr.Content))
+ require.False(t, strings.Contains(string(doc1en.Content), "&laquo;"), string(doc1en.Content))
+ require.True(t, strings.Contains(string(doc1en.Content), "&ldquo;"), string(doc1en.Content))
// Check that the drafts etc. are not built/processed/rendered.
assertShouldNotBuild(t, sites)
@@ -415,13 +407,14 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
}
func TestMultiSitesRebuild(t *testing.T) {
-
+ // t.Parallel() not supported, see https://github.com/fortytw2/leaktest/issues/4
defer leaktest.Check(t)()
- testCommonResetState()
- fs := hugofs.NewMem()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
+
+ siteConfig := testSiteConfig{Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: true}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
+ fs := sites.Fs
cfg := BuildCfg{Watching: true}
+ th := testHelper{sites.Cfg}
err := sites.Build(cfg)
@@ -442,12 +435,12 @@ func TestMultiSitesRebuild(t *testing.T) {
require.Len(t, frSite.RegularPages, 3)
// Verify translations
- assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Hello")
- assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Bonjour")
+ th.assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Hello")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Bonjour")
// check single page content
- 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")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
+ th.assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
for i, this := range []struct {
preFunc func(t *testing.T)
@@ -585,8 +578,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, 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")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Modified Shortcode: Salut")
+ th.assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Modified Shortcode: Hello")
},
},
} {
@@ -630,9 +623,8 @@ func assertShouldNotBuild(t *testing.T, sites *HugoSites) {
}
func TestAddNewLanguage(t *testing.T) {
- testCommonResetState()
- fs := hugofs.NewMem()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
+ t.Parallel()
+ siteConfig := testSiteConfig{Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: true}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{}
@@ -643,6 +635,8 @@ func TestAddNewLanguage(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
+ fs := sites.Fs
+
newConfig := multiSiteTOMLConfigTemplate + `
[Languages.sv]
@@ -657,7 +651,7 @@ title = "Svenska"
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())
+ require.NoError(t, sites.Cfg.(*helpers.Language).Cfg.(*viper.Viper).ReadInConfig())
err = sites.Build(BuildCfg{CreateSitesFromConfig: true})
if err != nil {
@@ -694,11 +688,15 @@ title = "Svenska"
}
func TestChangeDefaultLanguage(t *testing.T) {
- testCommonResetState()
- viper.Set("defaultContentLanguageInSubdir", false)
- fs := hugofs.NewMem()
- sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}, multiSiteTOMLConfigTemplate)
+ t.Parallel()
+ mf := afero.NewMemMapFs()
+
+ sites := createMultiTestSites(t, testSiteConfig{Fs: mf, DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: false}, multiSiteTOMLConfigTemplate)
+
+ require.Equal(t, mf, sites.Fs.Source)
+
cfg := BuildCfg{}
+ th := testHelper{sites.Cfg}
err := sites.Build(cfg)
@@ -706,16 +704,19 @@ func TestChangeDefaultLanguage(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, fs, "public/sect/doc1/index.html", true, "Single", "Bonjour")
- assertFileContent(t, fs, "public/en/sect/doc2/index.html", true, "Single", "Hello")
+ fs := sites.Fs
- newConfig := createConfig(t, testSiteConfig{DefaultContentLanguage: "en"}, multiSiteTOMLConfigTemplate)
+ th.assertFileContent(t, fs, "public/sect/doc1/index.html", false, "Single", "Bonjour")
+ th.assertFileContent(t, fs, "public/en/sect/doc2/index.html", false, "Single", "Hello")
+
+ newConfig := createConfig(t, testSiteConfig{Fs: mf, DefaultContentLanguage: "en", DefaultContentLanguageInSubdir: false}, multiSiteTOMLConfigTemplate)
// replace the config
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())
+ // This does not look pretty, so we should think of something else.
+ require.NoError(t, th.Cfg.(*helpers.Language).Cfg.(*viper.Viper).ReadInConfig())
err = sites.Build(BuildCfg{CreateSitesFromConfig: true})
if err != nil {
@@ -723,19 +724,19 @@ func TestChangeDefaultLanguage(t *testing.T) {
}
// Default language is now en, so that should now be the "root" language
- assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Bonjour")
- assertFileContent(t, fs, "public/sect/doc2/index.html", true, "Single", "Hello")
+ th.assertFileContent(t, fs, "public/fr/sect/doc1/index.html", false, "Single", "Bonjour")
+ th.assertFileContent(t, fs, "public/sect/doc2/index.html", false, "Single", "Hello")
}
func TestTableOfContentsInShortcodes(t *testing.T) {
- testCommonResetState()
- fs := hugofs.NewMem()
+ t.Parallel()
+ mf := afero.NewMemMapFs()
- 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)
+ writeToFs(t, mf, "layouts/shortcodes/toc.html", tocShortcode)
+ writeToFs(t, mf, "content/post/simple.en.md", tocPageSimple)
+ writeToFs(t, mf, "content/post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings)
- sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "en", Fs: fs}, multiSiteTOMLConfigTemplate)
+ sites := createMultiTestSites(t, testSiteConfig{Fs: mf, DefaultContentLanguage: "en", DefaultContentLanguageInSubdir: true}, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{}
@@ -745,8 +746,11 @@ func TestTableOfContentsInShortcodes(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, fs, "public/en/post/simple/index.html", true, tocPageSimpleExpected)
- assertFileContent(t, fs, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected)
+ th := testHelper{sites.Cfg}
+ fs := sites.Fs
+
+ th.assertFileContent(t, fs, "public/en/post/simple/index.html", true, tocPageSimpleExpected)
+ th.assertFileContent(t, fs, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected)
}
var tocShortcode = `
@@ -836,6 +840,7 @@ rssURI = "index.xml"
paginate = 1
defaultContentLanguage = "{{ .DefaultContentLanguage }}"
+defaultContentLanguageInSubdir = {{ .DefaultContentLanguageInSubdir }}
[permalinks]
other = "/somewhere/else/:filename"
@@ -886,7 +891,7 @@ paginatePath = "side"
lag = "lag"
`
-var multiSiteYAMLConfig = `
+var multiSiteYAMLConfigTemplate = `
defaultExtension: "html"
baseURL: "http://example.com/blog"
disableSitemap: false
@@ -894,7 +899,8 @@ disableRSS: false
rssURI: "index.xml"
paginate: 1
-defaultContentLanguage: "fr"
+defaultContentLanguage: "{{ .DefaultContentLanguage }}"
+defaultContentLanguageInSubdir: {{ .DefaultContentLanguageInSubdir }}
permalinks:
other: "/somewhere/else/:filename"
@@ -945,7 +951,7 @@ Languages:
`
-var multiSiteJSONConfig = `
+var multiSiteJSONConfigTemplate = `
{
"defaultExtension": "html",
"baseURL": "http://example.com/blog",
@@ -953,7 +959,8 @@ var multiSiteJSONConfig = `
"disableRSS": false,
"rssURI": "index.xml",
"paginate": 1,
- "defaultContentLanguage": "fr",
+ "defaultContentLanguage": "{{ .DefaultContentLanguage }}",
+ "defaultContentLanguageInSubdir": true,
"permalinks": {
"other": "/somewhere/else/:filename"
},
@@ -1026,25 +1033,26 @@ 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)
+ mf := siteConfig.Fs
+
// Add some layouts
- if err := afero.WriteFile(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
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(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
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(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
filepath.Join("layouts", "index.html"),
[]byte("{{ $p := .Paginator }}Home Page {{ $p.PageNumber }}: {{ .Title }}|{{ .IsHome }}|{{ i18n \"hello\" }}|{{ .Permalink }}|{{ .Site.Data.hugo.slogan }}"),
0755); err != nil {
@@ -1052,7 +1060,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf
}
// Add a shortcode
- if err := afero.WriteFile(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
filepath.Join("layouts", "shortcodes", "shortcode.html"),
[]byte("Shortcode: {{ i18n \"hello\" }}"),
0755); err != nil {
@@ -1060,7 +1068,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf
}
// Add some language files
- if err := afero.WriteFile(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
filepath.Join("i18n", "en.yaml"),
[]byte(`
- id: hello
@@ -1069,7 +1077,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(depsCfg.Fs.Source,
+ if err := afero.WriteFile(mf,
filepath.Join("i18n", "fr.yaml"),
[]byte(`
- id: hello
@@ -1223,26 +1231,25 @@ lag:
}
configFile := "multilangconfig." + configSuffix
- writeSource(t, depsCfg.Fs, configFile, configContent)
+ writeToFs(t, mf, configFile, configContent)
- viper.SetFs(depsCfg.Fs.Source)
+ cfg, err := LoadConfig(mf, "", configFile)
+ require.NoError(t, err)
- if err := LoadGlobalConfig("", configFile); err != nil {
- t.Fatalf("Failed to load config: %s", err)
- }
+ fs := hugofs.NewFrom(mf, cfg)
// Hugo support using ByteSource's directly (for testing),
// but to make it more real, we write them to the mem file system.
for _, s := range sources {
- if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("content", s.Name), s.Content, 0755); err != nil {
+ if err := afero.WriteFile(mf, filepath.Join("content", s.Name), s.Content, 0755); err != nil {
t.Fatalf("Failed to write file: %s", err)
}
}
// Add some data
- writeSource(t, depsCfg.Fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
+ writeSource(t, fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
- sites, err := NewHugoSitesFromConfiguration(depsCfg)
+ sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) //, Logger: newDebugLogger()})
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -1252,11 +1259,19 @@ lag:
t.Fatalf("Got %d sites", len(sites.Sites))
}
+ if sites.Fs.Source != mf {
+ t.Fatal("FS mismatch")
+ }
+
return sites
}
func writeSource(t *testing.T, fs *hugofs.Fs, filename, content string) {
- if err := afero.WriteFile(fs.Source, filepath.FromSlash(filename), []byte(content), 0755); err != nil {
+ writeToFs(t, fs.Source, filename, content)
+}
+
+func writeToFs(t *testing.T, fs afero.Fs, filename, content string) {
+ if err := afero.WriteFile(fs, filepath.FromSlash(filename), []byte(content), 0755); err != nil {
t.Fatalf("Failed to write file: %s", err)
}
}
diff --git a/hugolib/i18n.go b/hugolib/i18n.go
deleted file mode 100644
index d2e1a97df..000000000
--- a/hugolib/i18n.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2016 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 hugolib
-
-import (
- "fmt"
-
- "github.com/nicksnyder/go-i18n/i18n/bundle"
- "github.com/spf13/hugo/source"
- "github.com/spf13/hugo/tpl"
-)
-
-func (s *Site) loadI18n(sources []source.Input) error {
- s.Log.DEBUG.Printf("Load I18n from %q", sources)
-
- i18nBundle := bundle.New()
-
- for _, currentSource := range sources {
- for _, r := range currentSource.Files() {
- err := i18nBundle.ParseTranslationFileBytes(r.LogicalName(), r.Bytes())
- if err != nil {
- return fmt.Errorf("Failed to load translations in file %q: %s", r.LogicalName(), err)
- }
- }
- }
-
- tpl.SetI18nTfuncs(i18nBundle)
-
- return nil
-
-}
diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go
index 8fd94ec48..bb0846b21 100644
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -20,14 +20,10 @@ import (
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
-
"path/filepath"
toml "github.com/pelletier/go-toml"
"github.com/spf13/hugo/source"
- "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -187,7 +183,7 @@ Front Matter with Menu with Identifier`, title, menu, identifier))
// Issue 817 - identifier should trump everything
func TestPageMenuWithIdentifier(t *testing.T) {
-
+ t.Parallel()
toml := []source.ByteSource{
{Name: "sect/doc1.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i1")},
{Name: "sect/doc2.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i2")},
@@ -206,7 +202,6 @@ func TestPageMenuWithIdentifier(t *testing.T) {
}
func doTestPageMenuWithIdentifier(t *testing.T, menuPageSources []source.ByteSource) {
- testCommonResetState()
s := setupMenuTests(t, menuPageSources)
@@ -225,7 +220,7 @@ func doTestPageMenuWithIdentifier(t *testing.T, menuPageSources []source.ByteSou
// Issue 817 contd - name should be second identifier in
func TestPageMenuWithDuplicateName(t *testing.T) {
-
+ t.Parallel()
toml := []source.ByteSource{
{Name: "sect/doc1.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n1")},
{Name: "sect/doc2.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n2")},
@@ -244,7 +239,6 @@ func TestPageMenuWithDuplicateName(t *testing.T) {
}
func doTestPageMenuWithDuplicateName(t *testing.T, menuPageSources []source.ByteSource) {
- testCommonResetState()
s := setupMenuTests(t, menuPageSources)
@@ -262,8 +256,7 @@ func doTestPageMenuWithDuplicateName(t *testing.T, menuPageSources []source.Byte
}
func TestPageMenu(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
s := setupMenuTests(t, menuPageSources)
if len(s.RegularPages) != 3 {
@@ -312,8 +305,7 @@ func TestPageMenu(t *testing.T) {
}
func TestMenuURL(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
s := setupMenuTests(t, menuPageSources)
for i, this := range []struct {
@@ -342,8 +334,7 @@ func TestMenuURL(t *testing.T) {
// Issue #1934
func TestYAMLMenuWithMultipleEntries(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
ps1 := []byte(`---
title: "Yaml 1"
weight: 5
@@ -373,18 +364,15 @@ Yaml Front Matter with Menu Pages`)
// issue #719
func TestMenuWithUnicodeURLs(t *testing.T) {
-
+ t.Parallel()
for _, canonifyURLs := range []bool{true, false} {
doTestMenuWithUnicodeURLs(t, canonifyURLs)
}
}
func doTestMenuWithUnicodeURLs(t *testing.T, canonifyURLs bool) {
- testCommonResetState()
-
- viper.Set("canonifyURLs", canonifyURLs)
- s := setupMenuTests(t, menuPageSources)
+ s := setupMenuTests(t, menuPageSources, "canonifyURLs", canonifyURLs)
unicodeRussian := findTestMenuEntryByID(s, "unicode", "unicode-russian")
@@ -399,18 +387,17 @@ func doTestMenuWithUnicodeURLs(t *testing.T, canonifyURLs bool) {
// Issue #1114
func TestSectionPagesMenu(t *testing.T) {
-
+ t.Parallel()
doTestSectionPagesMenu(true, t)
doTestSectionPagesMenu(false, t)
}
func doTestSectionPagesMenu(canonifyURLs bool, t *testing.T) {
- testCommonResetState()
- viper.Set("sectionPagesMenu", "spm")
-
- viper.Set("canonifyURLs", canonifyURLs)
- s := setupMenuTests(t, menuPageSectionsSources)
+ s := setupMenuTests(t, menuPageSectionsSources,
+ "sectionPagesMenu", "spm",
+ "canonifyURLs", canonifyURLs,
+ )
require.Equal(t, 3, len(s.Sections))
@@ -463,16 +450,15 @@ func doTestSectionPagesMenu(canonifyURLs bool, t *testing.T) {
}
func TestTaxonomyNodeMenu(t *testing.T) {
+ t.Parallel()
+
type taxRenderInfo struct {
key string
singular string
plural string
}
- testCommonResetState()
-
- viper.Set("canonifyURLs", true)
- s := setupMenuTests(t, menuPageSources)
+ s := setupMenuTests(t, menuPageSources, "canonifyURLs", true)
for i, this := range []struct {
menu string
@@ -512,8 +498,7 @@ func TestTaxonomyNodeMenu(t *testing.T) {
}
func TestMenuLimit(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
s := setupMenuTests(t, menuPageSources)
m := *s.Menus["main"]
@@ -528,7 +513,7 @@ func TestMenuLimit(t *testing.T) {
}
func TestMenuSortByN(t *testing.T) {
-
+ t.Parallel()
for i, this := range []struct {
sortFunc func(p Menu) Menu
assertFunc func(p Menu) bool
@@ -554,12 +539,11 @@ func TestMenuSortByN(t *testing.T) {
}
func TestHomeNodeMenu(t *testing.T) {
- testCommonResetState()
-
- viper.Set("canonifyURLs", true)
- viper.Set("uglyURLs", false)
-
- s := setupMenuTests(t, menuPageSources)
+ t.Parallel()
+ s := setupMenuTests(t, menuPageSources,
+ "canonifyURLs", true,
+ "uglyURLs", false,
+ )
home := s.getPage(KindHome)
homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL()}
@@ -596,12 +580,14 @@ func TestHomeNodeMenu(t *testing.T) {
}
func TestHopefullyUniqueID(t *testing.T) {
+ t.Parallel()
assert.Equal(t, "i", (&MenuEntry{Identifier: "i", URL: "u", Name: "n"}).hopefullyUniqueID())
assert.Equal(t, "u", (&MenuEntry{Identifier: "", URL: "u", Name: "n"}).hopefullyUniqueID())
assert.Equal(t, "n", (&MenuEntry{Identifier: "", URL: "", Name: "n"}).hopefullyUniqueID())
}
func TestAddMenuEntryChild(t *testing.T) {
+ t.Parallel()
root := &MenuEntry{Weight: 1}
root.addChild(&MenuEntry{Weight: 2})
root.addChild(&MenuEntry{Weight: 1})
@@ -667,38 +653,28 @@ func findDescendantTestMenuEntry(parent *MenuEntry, id string, matcher func(me *
return found
}
-func setupTestMenuState(t *testing.T) {
- menus, err := tomlToMap(confMenu1)
-
- if err != nil {
- t.Fatalf("Unable to read menus: %v", err)
- }
+func setupMenuTests(t *testing.T, pageSources []source.ByteSource, configKeyValues ...interface{}) *Site {
- viper.Set("menu", menus["menu"])
- viper.Set("baseURL", "http://foo.local/Zoo/")
-}
+ var (
+ cfg, fs = newTestCfg()
+ )
-func setupMenuTests(t *testing.T, pageSources []source.ByteSource) *Site {
+ menus, err := tomlToMap(confMenu1)
+ require.NoError(t, err)
- setupTestMenuState(t)
+ cfg.Set("menu", menus["menu"])
+ cfg.Set("baseURL", "http://foo.local/Zoo/")
- fs := hugofs.NewMem()
+ for i := 0; i < len(configKeyValues); i += 2 {
+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
for _, src := range pageSources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
- return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
-
-}
-
-func createTestSite(pageSources []source.ByteSource) *Site {
-
- return &Site{
- Source: &source.InMemorySource{ByteSource: pageSources},
- Language: helpers.NewDefaultLanguage(),
- }
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
}
diff --git a/hugolib/multilingual.go b/hugolib/multilingual.go
index 29a2ea93a..c5943dbd5 100644
--- a/hugolib/multilingual.go
+++ b/hugolib/multilingual.go
@@ -22,6 +22,7 @@ import (
"fmt"
"github.com/spf13/cast"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
)
@@ -44,7 +45,7 @@ func (ml *Multilingual) Language(lang string) *helpers.Language {
return ml.langMap[lang]
}
-func newMultiLingualFromSites(sites ...*Site) (*Multilingual, error) {
+func newMultiLingualFromSites(cfg config.Provider, sites ...*Site) (*Multilingual, error) {
languages := make(helpers.Languages, len(sites))
for i, s := range sites {
@@ -54,12 +55,14 @@ func newMultiLingualFromSites(sites ...*Site) (*Multilingual, error) {
languages[i] = s.Language
}
- return &Multilingual{Languages: languages, DefaultLang: helpers.NewDefaultLanguage()}, nil
+ defaultLang := cfg.GetString("defaultContentLanguage")
-}
+ if defaultLang == "" {
+ defaultLang = "en"
+ }
+
+ return &Multilingual{Languages: languages, DefaultLang: helpers.NewLanguage(defaultLang, cfg)}, nil
-func newMultiLingualDefaultLanguage() *Multilingual {
- return newMultiLingualForLanguage(helpers.NewDefaultLanguage())
}
func newMultiLingualForLanguage(language *helpers.Language) *Multilingual {
@@ -77,7 +80,7 @@ func (s *Site) multilingualEnabled() bool {
return s.owner.multilingual != nil && s.owner.multilingual.enabled()
}
-func toSortedLanguages(l map[string]interface{}) (helpers.Languages, error) {
+func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (helpers.Languages, error) {
langs := make(helpers.Languages, len(l))
i := 0
@@ -88,7 +91,7 @@ func toSortedLanguages(l map[string]interface{}) (helpers.Languages, error) {
return nil, fmt.Errorf("Language config is not a map: %T", langConf)
}
- language := helpers.NewLanguage(lang)
+ language := helpers.NewLanguage(lang, cfg)
for loki, v := range langsMap {
switch loki {
diff --git a/hugolib/node_as_page_test.go b/hugolib/node_as_page_test.go
index 35588da4d..64b9a2fd8 100644
--- a/hugolib/node_as_page_test.go
+++ b/hugolib/node_as_page_test.go
@@ -21,9 +21,10 @@ import (
"time"
+ "github.com/spf13/afero"
+
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/hugofs"
- "github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -35,6 +36,7 @@ import (
*/
func TestNodesAsPage(t *testing.T) {
+ t.Parallel()
for _, preserveTaxonomyNames := range []bool{false, true} {
for _, ugly := range []bool{true, false} {
doTestNodeAsPage(t, ugly, preserveTaxonomyNames)
@@ -54,25 +56,24 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
*/
- testCommonResetState()
-
- viper.Set("uglyURLs", ugly)
- viper.Set("preserveTaxonomyNames", preserveTaxonomyNames)
-
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks")
- viper.Set("rssURI", "customrss.xml")
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- depsCfg := newTestDepsConfig()
+ cfg.Set("uglyURLs", ugly)
+ cfg.Set("preserveTaxonomyNames", preserveTaxonomyNames)
- viper.SetFs(depsCfg.Fs.Source)
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks")
+ cfg.Set("rssURI", "customrss.xml")
- writeLayoutsForNodeAsPageTests(t, depsCfg.Fs)
- writeNodePagesForNodeAsPageTests(t, depsCfg.Fs, "")
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeNodePagesForNodeAsPageTests(t, fs, "")
- writeRegularPagesForNodeAsPageTests(t, depsCfg.Fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
- sites, err := NewHugoSitesFromConfiguration(depsCfg)
+ sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
@@ -80,7 +81,7 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
// date order: home, sect1, sect2, cat/hugo, cat/web, categories
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "index.html"), false,
+ th.assertFileContent(t, fs, filepath.Join("public", "index.html"), false,
"Index Title: Home Sweet Home!",
"Home <strong>Content!</strong>",
"# Pages: 4",
@@ -89,7 +90,7 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
"GetPage: Section1 ",
)
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1", "regular1"), false, "Single Title: Page 01", "Content Page 01")
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1", "regular1"), false, "Single Title: Page 01", "Content Page 01")
nodes := sites.findAllPagesByKindNotIn(KindPage)
@@ -115,24 +116,24 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
require.True(t, first.IsPage())
// Check Home paginator
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "page", "2"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "page", "2"), false,
"Pag: Page 02")
// Check Sections
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1"), false,
"Section Title: Section", "Section1 <strong>Content!</strong>",
"Date: 2009-01-04",
"Lastmod: 2009-01-05",
)
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect2"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect2"), false,
"Section Title: Section", "Section2 <strong>Content!</strong>",
"Date: 2009-01-06",
"Lastmod: 2009-01-07",
)
// Check Sections paginator
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1", "page", "2"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1", "page", "2"), false,
"Pag: Page 02")
sections := sites.findAllPagesByKind(KindSection)
@@ -140,13 +141,13 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
require.Len(t, sections, 2)
// Check taxonomy lists
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
"Taxonomy Title: Taxonomy Hugo", "Taxonomy Hugo <strong>Content!</strong>",
"Date: 2009-01-08",
"Lastmod: 2009-01-09",
)
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo-rocks"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo-rocks"), false,
"Taxonomy Title: Taxonomy Hugo Rocks",
)
@@ -156,7 +157,7 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
require.NotNil(t, web)
require.Len(t, web.Data["Pages"].(Pages), 4)
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "web"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "web"), false,
"Taxonomy Title: Taxonomy Web",
"Taxonomy Web <strong>Content!</strong>",
"Date: 2009-01-10",
@@ -164,12 +165,12 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
)
// Check taxonomy list paginator
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo", "page", "2"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo", "page", "2"), false,
"Taxonomy Title: Taxonomy Hugo",
"Pag: Page 02")
// Check taxonomy terms
- assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories"), false,
"Taxonomy Terms Title: Taxonomy Term Categories", "Taxonomy Term Categories <strong>Content!</strong>", "k/v: hugo",
"Date: 2009-01-14",
"Lastmod: 2009-01-15",
@@ -178,34 +179,37 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
// There are no pages to paginate over in the taxonomy terms.
// RSS
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "customrss.xml"), false, "Recent content in Home Sweet Home! on Hugo Rocks", "<rss")
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Section1 on Hugo Rocks", "<rss")
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Section2 on Hugo Rocks", "<rss")
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Taxonomy Hugo on Hugo Rocks", "<rss")
- assertFileContent(t, depsCfg.Fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Taxonomy Web on Hugo Rocks", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "customrss.xml"), false, "Recent content in Home Sweet Home! on Hugo Rocks", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Section1 on Hugo Rocks", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Section2 on Hugo Rocks", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Taxonomy Hugo on Hugo Rocks", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Taxonomy Web on Hugo Rocks", "<rss")
}
func TestNodesWithNoContentFile(t *testing.T) {
+ t.Parallel()
for _, ugly := range []bool{false, true} {
doTestNodesWithNoContentFile(t, ugly)
}
}
func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
- testCommonResetState()
- viper.Set("uglyURLs", ugly)
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
- viper.Set("rssURI", "customrss.xml")
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- fs := hugofs.NewMem()
+ cfg.Set("uglyURLs", ugly)
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks!")
+ cfg.Set("rssURI", "customrss.xml")
writeLayoutsForNodeAsPageTests(t, fs)
writeRegularPagesForNodeAsPageTests(t, fs)
- sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
@@ -222,21 +226,21 @@ func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
require.Len(t, homePage.Pages, 4)
require.True(t, homePage.Path() == "")
- assertFileContent(t, fs, filepath.Join("public", "index.html"), false,
+ th.assertFileContent(t, fs, filepath.Join("public", "index.html"), false,
"Index Title: Hugo Rocks!",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
// Taxonomy list
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
"Taxonomy Title: Hugo",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
// Taxonomy terms
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories"), false,
"Taxonomy Terms Title: Categories",
)
@@ -254,28 +258,29 @@ func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
}
// Sections
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1"), false,
"Section Title: Sect1s",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect2"), false,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect2"), false,
"Section Title: Sect2s",
"Date: 2008-07-06",
"Lastmod: 2008-07-09",
)
// RSS
- assertFileContent(t, fs, filepath.Join("public", "customrss.xml"), false, "Hugo Rocks!", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Sect1s on Hugo Rocks!", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Sect2s on Hugo Rocks!", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Hugo on Hugo Rocks!", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Web on Hugo Rocks!", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "customrss.xml"), false, "Hugo Rocks!", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Sect1s on Hugo Rocks!", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Sect2s on Hugo Rocks!", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Hugo on Hugo Rocks!", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Web on Hugo Rocks!", "<rss")
}
func TestNodesAsPageMultilingual(t *testing.T) {
+ t.Parallel()
for _, ugly := range []bool{false, true} {
doTestNodesAsPageMultilingual(t, ugly)
}
@@ -283,15 +288,9 @@ func TestNodesAsPageMultilingual(t *testing.T) {
func doTestNodesAsPageMultilingual(t *testing.T, ugly bool) {
- testCommonResetState()
+ mf := afero.NewMemMapFs()
- fs := hugofs.NewMem()
-
- viper.Set("uglyURLs", ugly)
-
- viper.SetFs(fs.Source)
-
- writeSource(t, fs, "config.toml",
+ writeToFs(t, mf, "config.toml",
`
paginage = 1
title = "Hugo Multilingual Rocks!"
@@ -317,17 +316,22 @@ weight = 3
title = "Deutsche Hugo"
`)
+ cfg, err := LoadConfig(mf, "", "config.toml")
+ require.NoError(t, err)
+
+ cfg.Set("uglyURLs", ugly)
+
+ fs := hugofs.NewFrom(mf, cfg)
+
writeLayoutsForNodeAsPageTests(t, fs)
for _, lang := range []string{"nn", "en"} {
writeRegularPagesForNodeAsPageTestsWithLang(t, fs, lang)
}
- if err := LoadGlobalConfig("", "config.toml"); err != nil {
- t.Fatalf("Failed to load config: %s", err)
- }
+ th := testHelper{cfg}
- 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)
@@ -372,63 +376,65 @@ title = "Deutsche Hugo"
require.Equal(t, expetedPermalink(ugly, "/en/sect1/"), enSect.Permalink())
- assertFileContent(t, fs, filepath.Join("public", "nn", "index.html"), true,
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "index.html"), true,
"Index Title: Hugo på norsk")
- assertFileContent(t, fs, filepath.Join("public", "en", "index.html"), true,
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "index.html"), true,
"Index Title: Home Sweet Home!", "<strong>Content!</strong>")
- assertFileContent(t, fs, filepath.Join("public", "de", "index.html"), true,
+ th.assertFileContent(t, fs, filepath.Join("public", "de", "index.html"), true,
"Index Title: Home Sweet Home!", "<strong>Content!</strong>")
// Taxonomy list
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories", "hugo"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories", "hugo"), true,
"Taxonomy Title: Hugo")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories", "hugo"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories", "hugo"), true,
"Taxonomy Title: Taxonomy Hugo")
// Taxonomy terms
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories"), true,
"Taxonomy Terms Title: Categories")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories"), true,
"Taxonomy Terms Title: Taxonomy Term Categories")
// Sections
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1"), true,
"Section Title: Sect1s")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect2"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect2"), true,
"Section Title: Sect2s")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1"), true,
"Section Title: Section1")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect2"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect2"), true,
"Section Title: Section2")
// Regular pages
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1", "regular1"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1", "regular1"), true,
"Single Title: Page 01")
- assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1", "regular2"), true,
+ th.assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1", "regular2"), true,
"Single Title: Page 02")
// RSS
- assertFileContent(t, fs, filepath.Join("public", "nn", "customrss.xml"), true, "Hugo på norsk", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "nn", "sect1", "customrss.xml"), true, "Recent content in Sect1s on Hugo på norsk", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "nn", "sect2", "customrss.xml"), true, "Recent content in Sect2s on Hugo på norsk", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "hugo", "customrss.xml"), true, "Recent content in Hugo on Hugo på norsk", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "web", "customrss.xml"), true, "Recent content in Web on Hugo på norsk", "<rss")
-
- assertFileContent(t, fs, filepath.Join("public", "en", "customrss.xml"), true, "Recent content in Home Sweet Home! on Hugo in English", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "en", "sect1", "customrss.xml"), true, "Recent content in Section1 on Hugo in English", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "en", "sect2", "customrss.xml"), true, "Recent content in Section2 on Hugo in English", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "en", "categories", "hugo", "customrss.xml"), true, "Recent content in Taxonomy Hugo on Hugo in English", "<rss")
- assertFileContent(t, fs, filepath.Join("public", "en", "categories", "web", "customrss.xml"), true, "Recent content in Taxonomy Web on Hugo in English", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "customrss.xml"), true, "Hugo på norsk", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "sect1", "customrss.xml"), true, "Recent content in Sect1s on Hugo på norsk", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "sect2", "customrss.xml"), true, "Recent content in Sect2s on Hugo på norsk", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "hugo", "customrss.xml"), true, "Recent content in Hugo on Hugo på norsk", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "web", "customrss.xml"), true, "Recent content in Web on Hugo på norsk", "<rss")
+
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "customrss.xml"), true, "Recent content in Home Sweet Home! on Hugo in English", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "sect1", "customrss.xml"), true, "Recent content in Section1 on Hugo in English", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "sect2", "customrss.xml"), true, "Recent content in Section2 on Hugo in English", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "categories", "hugo", "customrss.xml"), true, "Recent content in Taxonomy Hugo on Hugo in English", "<rss")
+ th.assertFileContent(t, fs, filepath.Join("public", "en", "categories", "web", "customrss.xml"), true, "Recent content in Taxonomy Web on Hugo in English", "<rss")
}
func TestNodesWithTaxonomies(t *testing.T) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks!")
writeLayoutsForNodeAsPageTests(t, fs)
writeRegularPagesForNodeAsPageTests(t, fs)
@@ -442,24 +448,26 @@ categories: [
---
`)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy Title: Hugo", "# Pages: 5")
- assertFileContent(t, fs, filepath.Join("public", "categories", "home", "index.html"), true, "Taxonomy Title: Home", "# Pages: 1")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy Title: Hugo", "# Pages: 5")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "home", "index.html"), true, "Taxonomy Title: Home", "# Pages: 1")
}
func TestNodesWithMenu(t *testing.T) {
- testCommonResetState()
-
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- fs := hugofs.NewMem()
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks!")
writeLayoutsForNodeAsPageTests(t, fs)
writeRegularPagesForNodeAsPageTests(t, fs)
@@ -488,26 +496,28 @@ menu:
---
`)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Menu", "Home Menu Item: Go Home!: /")
- assertFileContent(t, fs, filepath.Join("public", "sect1", "index.html"), true, "Sect1 With Menu", "Section Menu Item: Go Sect1!: /sect1/")
- assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy With Menu", "Taxonomy Menu Item: Go Tax Hugo!: /categories/hugo/")
+ th.assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Menu", "Home Menu Item: Go Home!: /")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect1", "index.html"), true, "Sect1 With Menu", "Section Menu Item: Go Sect1!: /sect1/")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy With Menu", "Taxonomy Menu Item: Go Tax Hugo!: /categories/hugo/")
}
func TestNodesWithAlias(t *testing.T) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- viper.Set("paginate", 1)
- viper.Set("baseURL", "http://base/")
- viper.Set("title", "Hugo Rocks!")
+ cfg.Set("paginate", 1)
+ cfg.Set("baseURL", "http://base/")
+ cfg.Set("title", "Hugo Rocks!")
writeLayoutsForNodeAsPageTests(t, fs)
writeRegularPagesForNodeAsPageTests(t, fs)
@@ -519,24 +529,26 @@ aliases:
---
`)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Alias")
- assertFileContent(t, fs, filepath.Join("public", "my", "new", "home.html"), true, "content=\"0; url=http://base/")
+ th.assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Alias")
+ th.assertFileContent(t, fs, filepath.Join("public", "my", "new", "home.html"), true, "content=\"0; url=http://base/")
}
func TestNodesWithSectionWithIndexPageOnly(t *testing.T) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks!")
writeLayoutsForNodeAsPageTests(t, fs)
@@ -546,24 +558,26 @@ title: MySection
My Section Content
`)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
}
func TestNodesWithURLs(t *testing.T) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
- viper.Set("baseURL", "http://bep.is/base/")
+ cfg.Set("paginate", 1)
+ cfg.Set("title", "Hugo Rocks!")
+ cfg.Set("baseURL", "http://bep.is/base/")
writeLayoutsForNodeAsPageTests(t, fs)
writeRegularPagesForNodeAsPageTests(t, fs)
@@ -575,13 +589,13 @@ url: foo.html
My Section Content
`)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
require.NoError(t, err)
require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
s := h.Sites[0]
diff --git a/hugolib/page.go b/hugolib/page.go
index 13d5d0c26..10a75311b 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -39,7 +39,6 @@ import (
"github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/source"
- "github.com/spf13/viper"
)
var (
@@ -195,8 +194,11 @@ type Page struct {
scratch *Scratch
+ // It would be tempting to use the language set on the Site, but in they way we do
+ // multi-site processing, these values may differ during the initial page processing.
language *helpers.Language
- lang string
+
+ lang string
}
// pageInit lazy initializes different parts of the page. It is extracted
@@ -518,10 +520,11 @@ func (p *Page) renderContent(content []byte) []byte {
return p.Site.SourceRelativeLinkFile(ref, p)
}
}
- return helpers.RenderBytes(&helpers.RenderingContext{
+
+ return p.s.ContentSpec.RenderBytes(&helpers.RenderingContext{
Content: content, RenderTOC: true, PageFmt: p.determineMarkupType(),
- ConfigProvider: p.Language(),
- DocumentID: p.UniqueID(), DocumentName: p.Path(),
+ Cfg: p.Language(),
+ DocumentID: p.UniqueID(), DocumentName: p.Path(),
Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn})
}
@@ -532,7 +535,7 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
if p.Language() == nil {
panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang))
}
- p.renderingConfig = helpers.NewBlackfriday(p.Language())
+ p.renderingConfig = p.s.ContentSpec.NewBlackfriday()
if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil {
p.s.Log.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
@@ -544,15 +547,18 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
}
func (s *Site) newPage(filename string) *Page {
+ sp := source.NewSourceSpec(s.Cfg, s.Fs)
page := Page{
pageInit: &pageInit{},
Kind: kindFromFilename(filename),
contentType: "",
- Source: Source{File: *source.NewFile(filename)},
+ Source: Source{File: *sp.NewFile(filename)},
Keywords: []string{}, Sitemap: Sitemap{Priority: -1},
Params: make(map[string]interface{}),
translations: make(Pages, 0),
sections: sectionsFromFilename(filename),
+ Site: &s.Info,
+ s: s,
}
s.Log.DEBUG.Println("Reading from", page.File.Path())
@@ -799,7 +805,7 @@ func (p *Page) Extension() string {
if p.extension != "" {
return p.extension
}
- return viper.GetString("defaultExtension")
+ return p.s.Cfg.GetString("defaultExtension")
}
// AllTranslations returns all translations, including the current Page.
@@ -832,8 +838,8 @@ func (p *Page) LinkTitle() string {
}
func (p *Page) shouldBuild() bool {
- return shouldBuild(viper.GetBool("buildFuture"), viper.GetBool("buildExpired"),
- viper.GetBool("buildDrafts"), p.Draft, p.PublishDate, p.ExpiryDate)
+ return shouldBuild(p.s.Cfg.GetBool("buildFuture"), p.s.Cfg.GetBool("buildExpired"),
+ p.s.Cfg.GetBool("buildDrafts"), p.Draft, p.PublishDate, p.ExpiryDate)
}
func shouldBuild(buildFuture bool, buildExpired bool, buildDrafts bool, Draft bool,
@@ -886,7 +892,7 @@ func (p *Page) URL() string {
func (p *Page) RelPermalink() string {
link := p.getPermalink()
- if viper.GetBool("canonifyURLs") {
+ if p.s.Cfg.GetBool("canonifyURLs") {
// replacements for relpermalink with baseURL on the form http://myhost.com/sub/ will fail later on
// have to return the URL relative from baseURL
relpath, err := helpers.GetRelativePath(link.String(), string(p.Site.BaseURL))
@@ -1047,8 +1053,8 @@ func (p *Page) update(f interface{}) error {
p.Draft = !*published
}
- if p.Date.IsZero() && viper.GetBool("useModTimeAsFallback") {
- fi, err := p.s.Fs.Source.Stat(filepath.Join(helpers.AbsPathify(viper.GetString("contentDir")), p.File.Path()))
+ if p.Date.IsZero() && p.s.Cfg.GetBool("useModTimeAsFallback") {
+ fi, err := p.s.Fs.Source.Stat(filepath.Join(p.s.PathSpec.AbsPathify(p.s.Cfg.GetString("contentDir")), p.File.Path()))
if err == nil {
p.Date = fi.ModTime()
}
@@ -1060,7 +1066,7 @@ func (p *Page) update(f interface{}) error {
if isCJKLanguage != nil {
p.isCJKLanguage = *isCJKLanguage
- } else if viper.GetBool("hasCJKLanguage") {
+ } else if p.s.Cfg.GetBool("hasCJKLanguage") {
if cjk.Match(p.rawContent) {
p.isCJKLanguage = true
} else {
@@ -1378,10 +1384,9 @@ func (p *Page) saveSourceAs(path string, safe bool) error {
func (p *Page) saveSource(by []byte, inpath string, safe bool) (err error) {
if !filepath.IsAbs(inpath) {
- inpath = helpers.AbsPathify(inpath)
+ inpath = p.s.PathSpec.AbsPathify(inpath)
}
p.s.Log.INFO.Println("creating", inpath)
-
if safe {
err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), p.s.Fs.Source)
} else {
@@ -1691,25 +1696,27 @@ func (p *Page) initLanguage() {
if p.language != nil {
return
}
- pageLang := p.lang
+
ml := p.Site.multilingual
if ml == nil {
panic("Multilanguage not set")
}
- if pageLang == "" {
+ if p.lang == "" {
+ p.lang = ml.DefaultLang.Lang
p.language = ml.DefaultLang
return
}
- language := ml.Language(pageLang)
+ language := ml.Language(p.lang)
if language == nil {
// It can be a file named stefano.chiodino.md.
- p.s.Log.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
+ p.s.Log.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", p.lang)
language = ml.DefaultLang
}
p.language = language
+
})
}
@@ -1743,6 +1750,7 @@ func (p *Page) addLangFilepathPrefix(outfile string) string {
if outfile == "" {
outfile = helpers.FilePathSeparator
}
+
if !p.shouldAddLanguagePrefix() {
return outfile
}
@@ -1795,7 +1803,4 @@ func (p *Page) setValuesForKind(s *Site) {
case KindTaxonomyTerm:
p.URLPath.URL = "/" + path.Join(p.sections...) + "/"
}
-
- p.s = s
-
}
diff --git a/hugolib/pageCache_test.go b/hugolib/pageCache_test.go
index 12fa7ae9a..62837394f 100644
--- a/hugolib/pageCache_test.go
+++ b/hugolib/pageCache_test.go
@@ -14,13 +14,15 @@
package hugolib
import (
- "github.com/stretchr/testify/assert"
"sync"
"sync/atomic"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestPageCache(t *testing.T) {
+ t.Parallel()
c1 := newPageCache()
changeFirst := func(p Pages) {
@@ -37,8 +39,10 @@ func TestPageCache(t *testing.T) {
var testPageSets []Pages
+ s := newTestSite(t)
+
for i := 0; i < 50; i++ {
- testPageSets = append(testPageSets, createSortTestPages(i+1))
+ testPageSets = append(testPageSets, createSortTestPages(s, i+1))
}
for j := 0; j < 100; j++ {
diff --git a/hugolib/pageGroup_test.go b/hugolib/pageGroup_test.go
index 07fb92f6b..8cc381b61 100644
--- a/hugolib/pageGroup_test.go
+++ b/hugolib/pageGroup_test.go
@@ -38,24 +38,26 @@ var pageGroupTestSources = []pageGroupTestObject{
}
func preparePageGroupTestPages(t *testing.T) Pages {
+ s := newTestSite(t)
var pages Pages
- for _, s := range pageGroupTestSources {
- p, err := pageTestSite.NewPage(filepath.FromSlash(s.path))
+ for _, src := range pageGroupTestSources {
+ p, err := s.NewPage(filepath.FromSlash(src.path))
if err != nil {
- t.Fatalf("failed to prepare test page %s", s.path)
+ t.Fatalf("failed to prepare test page %s", src.path)
}
- p.Weight = s.weight
- p.Date = cast.ToTime(s.date)
- p.PublishDate = cast.ToTime(s.date)
- p.ExpiryDate = cast.ToTime(s.date)
- p.Params["custom_param"] = s.param
- p.Params["custom_date"] = cast.ToTime(s.date)
+ p.Weight = src.weight
+ p.Date = cast.ToTime(src.date)
+ p.PublishDate = cast.ToTime(src.date)
+ p.ExpiryDate = cast.ToTime(src.date)
+ p.Params["custom_param"] = src.param
+ p.Params["custom_date"] = cast.ToTime(src.date)
pages = append(pages, p)
}
return pages
}
func TestGroupByWithFieldNameArg(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: 1, Pages: Pages{pages[3], pages[4]}},
@@ -73,6 +75,7 @@ func TestGroupByWithFieldNameArg(t *testing.T) {
}
func TestGroupByWithMethodNameArg(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "section1", Pages: Pages{pages[0], pages[1], pages[2]}},
@@ -89,6 +92,7 @@ func TestGroupByWithMethodNameArg(t *testing.T) {
}
func TestGroupByWithSectionArg(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "section1", Pages: Pages{pages[0], pages[1], pages[2]}},
@@ -105,6 +109,7 @@ func TestGroupByWithSectionArg(t *testing.T) {
}
func TestGroupByInReverseOrder(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: 3, Pages: Pages{pages[0], pages[1]}},
@@ -122,6 +127,7 @@ func TestGroupByInReverseOrder(t *testing.T) {
}
func TestGroupByCalledWithEmptyPages(t *testing.T) {
+ t.Parallel()
var pages Pages
groups, err := pages.GroupBy("Weight")
if err != nil {
@@ -133,6 +139,7 @@ func TestGroupByCalledWithEmptyPages(t *testing.T) {
}
func TestGroupByCalledWithUnavailableKey(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
_, err := pages.GroupBy("UnavailableKey")
if err == nil {
@@ -157,6 +164,7 @@ func (page *Page) dummyPageMethodReturnTwoValueForTest() (string, string) {
}
func TestGroupByCalledWithInvalidMethod(t *testing.T) {
+ t.Parallel()
var err error
pages := preparePageGroupTestPages(t)
@@ -182,6 +190,7 @@ func TestGroupByCalledWithInvalidMethod(t *testing.T) {
}
func TestReverse(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
groups1, err := pages.GroupBy("Weight", "desc")
@@ -201,6 +210,7 @@ func TestReverse(t *testing.T) {
}
func TestGroupByParam(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "bar", Pages: Pages{pages[1], pages[3]}},
@@ -218,6 +228,7 @@ func TestGroupByParam(t *testing.T) {
}
func TestGroupByParamInReverseOrder(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "foo", Pages: Pages{pages[0], pages[2]}},
@@ -237,7 +248,8 @@ func TestGroupByParamInReverseOrder(t *testing.T) {
func TestGroupByParamCalledWithCapitalLetterString(t *testing.T) {
testStr := "TestString"
f := "/section1/test_capital.md"
- p, err := pageTestSite.NewPage(filepath.FromSlash(f))
+ s := newTestSite(t)
+ p, err := s.NewPage(filepath.FromSlash(f))
if err != nil {
t.Fatalf("failed to prepare test page %s", f)
}
@@ -254,6 +266,7 @@ func TestGroupByParamCalledWithCapitalLetterString(t *testing.T) {
}
func TestGroupByParamCalledWithSomeUnavailableParams(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
delete(pages[1].Params, "custom_param")
delete(pages[3].Params, "custom_param")
@@ -273,6 +286,7 @@ func TestGroupByParamCalledWithSomeUnavailableParams(t *testing.T) {
}
func TestGroupByParamCalledWithEmptyPages(t *testing.T) {
+ t.Parallel()
var pages Pages
groups, err := pages.GroupByParam("custom_param")
if err != nil {
@@ -284,6 +298,7 @@ func TestGroupByParamCalledWithEmptyPages(t *testing.T) {
}
func TestGroupByParamCalledWithUnavailableParam(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
_, err := pages.GroupByParam("unavailable_param")
if err == nil {
@@ -292,6 +307,7 @@ func TestGroupByParamCalledWithUnavailableParam(t *testing.T) {
}
func TestGroupByDate(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
@@ -309,6 +325,7 @@ func TestGroupByDate(t *testing.T) {
}
func TestGroupByDateInReverseOrder(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-01", Pages: Pages{pages[1]}},
@@ -326,6 +343,7 @@ func TestGroupByDateInReverseOrder(t *testing.T) {
}
func TestGroupByPublishDate(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
@@ -343,6 +361,7 @@ func TestGroupByPublishDate(t *testing.T) {
}
func TestGroupByPublishDateInReverseOrder(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-01", Pages: Pages{pages[1]}},
@@ -360,6 +379,7 @@ func TestGroupByPublishDateInReverseOrder(t *testing.T) {
}
func TestGroupByPublishDateWithEmptyPages(t *testing.T) {
+ t.Parallel()
var pages Pages
groups, err := pages.GroupByPublishDate("2006-01")
if err != nil {
@@ -371,6 +391,7 @@ func TestGroupByPublishDateWithEmptyPages(t *testing.T) {
}
func TestGroupByExpiryDate(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
@@ -388,6 +409,7 @@ func TestGroupByExpiryDate(t *testing.T) {
}
func TestGroupByParamDate(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
@@ -405,6 +427,7 @@ func TestGroupByParamDate(t *testing.T) {
}
func TestGroupByParamDateInReverseOrder(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
expect := PagesGroup{
{Key: "2012-01", Pages: Pages{pages[1]}},
@@ -422,6 +445,7 @@ func TestGroupByParamDateInReverseOrder(t *testing.T) {
}
func TestGroupByParamDateWithEmptyPages(t *testing.T) {
+ t.Parallel()
var pages Pages
groups, err := pages.GroupByParamDate("custom_date", "2006-01")
if err != nil {
diff --git a/hugolib/pageSort.go b/hugolib/pageSort.go
index e12cf68e4..e1ea786b6 100644
--- a/hugolib/pageSort.go
+++ b/hugolib/pageSort.go
@@ -68,7 +68,7 @@ var defaultPageSort = func(p1, p2 *Page) bool {
}
var languagePageSort = func(p1, p2 *Page) bool {
- if p1.language.Weight == p2.language.Weight {
+ if p1.Language().Weight == p2.Language().Weight {
if p1.Date.Unix() == p2.Date.Unix() {
if p1.LinkTitle() == p2.LinkTitle() {
return (p1.FullFilePath() < p2.FullFilePath())
@@ -78,15 +78,15 @@ var languagePageSort = func(p1, p2 *Page) bool {
return p1.Date.Unix() > p2.Date.Unix()
}
- if p2.language.Weight == 0 {
+ if p2.Language().Weight == 0 {
return true
}
- if p1.language.Weight == 0 {
+ if p1.Language().Weight == 0 {
return false
}
- return p1.language.Weight < p2.language.Weight
+ return p1.Language().Weight < p2.Language().Weight
}
func (ps *pageSorter) Len() int { return len(ps.pages) }
diff --git a/hugolib/pageSort_test.go b/hugolib/pageSort_test.go
index c0dedbe2d..f5f28f1d7 100644
--- a/hugolib/pageSort_test.go
+++ b/hugolib/pageSort_test.go
@@ -21,19 +21,19 @@ import (
"time"
"github.com/spf13/cast"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/source"
"github.com/stretchr/testify/assert"
)
func TestDefaultSort(t *testing.T) {
-
+ t.Parallel()
d1 := time.Now()
d2 := d1.Add(-1 * time.Hour)
d3 := d1.Add(-2 * time.Hour)
d4 := d1.Add(-3 * time.Hour)
- p := createSortTestPages(4)
+ s := newTestSite(t)
+
+ p := createSortTestPages(s, 4)
// first by weight
setSortVals([4]time.Time{d1, d2, d3, d4}, [4]string{"b", "a", "c", "d"}, [4]int{4, 3, 2, 1}, p)
@@ -61,13 +61,14 @@ func TestDefaultSort(t *testing.T) {
}
func TestSortByN(t *testing.T) {
-
+ t.Parallel()
+ s := newTestSite(t)
d1 := time.Now()
d2 := d1.Add(-2 * time.Hour)
d3 := d1.Add(-10 * time.Hour)
d4 := d1.Add(-20 * time.Hour)
- p := createSortTestPages(4)
+ p := createSortTestPages(s, 4)
for i, this := range []struct {
sortFunc func(p Pages) Pages
@@ -93,7 +94,9 @@ func TestSortByN(t *testing.T) {
}
func TestLimit(t *testing.T) {
- p := createSortTestPages(10)
+ t.Parallel()
+ s := newTestSite(t)
+ p := createSortTestPages(s, 10)
firstFive := p.Limit(5)
assert.Equal(t, 5, len(firstFive))
for i := 0; i < 5; i++ {
@@ -104,7 +107,9 @@ func TestLimit(t *testing.T) {
}
func TestPageSortReverse(t *testing.T) {
- p1 := createSortTestPages(10)
+ t.Parallel()
+ s := newTestSite(t)
+ p1 := createSortTestPages(s, 10)
assert.Equal(t, 0, p1[0].fuzzyWordCount)
assert.Equal(t, 9, p1[9].fuzzyWordCount)
p2 := p1.Reverse()
@@ -115,9 +120,11 @@ func TestPageSortReverse(t *testing.T) {
}
func TestPageSortByParam(t *testing.T) {
+ t.Parallel()
var k interface{} = "arbitrary"
+ s := newTestSite(t)
- unsorted := createSortTestPages(10)
+ unsorted := createSortTestPages(s, 10)
delete(unsorted[9].Params, cast.ToString(k))
firstSetValue, _ := unsorted[0].Param(k)
@@ -143,8 +150,8 @@ func TestPageSortByParam(t *testing.T) {
}
func BenchmarkSortByWeightAndReverse(b *testing.B) {
-
- p := createSortTestPages(300)
+ s := newTestSite(b)
+ p := createSortTestPages(s, 300)
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -169,32 +176,25 @@ func setSortVals(dates [4]time.Time, titles [4]string, weights [4]int, pages Pag
pages[1].Lastmod = lastLastMod
}
-func createSortTestPages(num int) Pages {
+func createSortTestPages(s *Site, num int) Pages {
pages := make(Pages, num)
- info := newSiteInfo(siteBuilderCfg{baseURL: "http://base", language: helpers.NewDefaultLanguage()})
-
for i := 0; i < num; i++ {
- pages[i] = &Page{
- pageInit: &pageInit{},
- URLPath: URLPath{
- Section: "z",
- URL: fmt.Sprintf("http://base/x/y/p%d.html", i),
- },
- Site: &info,
- Source: Source{File: *source.NewFile(filepath.FromSlash(fmt.Sprintf("/x/y/p%d.md", i)))},
- Params: map[string]interface{}{
- "arbitrary": "xyz" + fmt.Sprintf("%v", 100-i),
- },
+ p := s.newPage(filepath.FromSlash(fmt.Sprintf("/x/y/p%d.md", i)))
+ p.Params = map[string]interface{}{
+ "arbitrary": "xyz" + fmt.Sprintf("%v", 100-i),
}
+
w := 5
if i%2 == 0 {
w = 10
}
- pages[i].fuzzyWordCount = i
- pages[i].Weight = w
- pages[i].Description = "initial"
+ p.fuzzyWordCount = i
+ p.Weight = w
+ p.Description = "initial"
+
+ pages[i] = p
}
return pages
diff --git a/hugolib/page_permalink_test.go b/hugolib/page_permalink_test.go
index 5ed8a8b47..0d7e983da 100644
--- a/hugolib/page_permalink_test.go
+++ b/hugolib/page_permalink_test.go
@@ -14,18 +14,18 @@
package hugolib
import (
+ "fmt"
"html/template"
"path/filepath"
"testing"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/source"
- "github.com/spf13/viper"
+ "github.com/stretchr/testify/require"
+
+ "github.com/spf13/hugo/deps"
)
-// TODO(bep) globals test siteinfo
-func _TestPermalink(t *testing.T) {
- testCommonResetState()
+func TestPermalink(t *testing.T) {
+ t.Parallel()
tests := []struct {
file string
@@ -50,51 +50,53 @@ func _TestPermalink(t *testing.T) {
{"x/y/z/boofar.md", "", "boofar", "", true, false, "/x/y/z/boofar.html", "/x/y/z/boofar.html"},
{"x/y/z/boofar.md", "http://barnew/", "", "", true, false, "http://barnew/x/y/z/boofar.html", "/x/y/z/boofar.html"},
{"x/y/z/boofar.md", "http://barnew/", "boofar", "", true, false, "http://barnew/x/y/z/boofar.html", "/x/y/z/boofar.html"},
- {"x/y/z/boofar.md", "http://barnew/boo/", "boofar", "", true, false, "http://barnew/boo/x/y/z/boofar.html", "/boo/x/y/z/boofar.html"},
- {"x/y/z/boofar.md", "http://barnew/boo/", "boofar", "", false, true, "http://barnew/boo/x/y/z/boofar/", "/x/y/z/boofar/"},
- {"x/y/z/boofar.md", "http://barnew/boo/", "boofar", "", false, false, "http://barnew/boo/x/y/z/boofar/", "/boo/x/y/z/boofar/"},
- {"x/y/z/boofar.md", "http://barnew/boo/", "boofar", "", true, true, "http://barnew/boo/x/y/z/boofar.html", "/x/y/z/boofar.html"},
- {"x/y/z/boofar.md", "http://barnew/boo", "boofar", "", true, true, "http://barnew/boo/x/y/z/boofar.html", "/x/y/z/boofar.html"},
+ {"x/y/z/boofar.md", "http://barnew/boo/", "booslug", "", true, false, "http://barnew/boo/x/y/z/booslug.html", "/boo/x/y/z/booslug.html"},
+ {"x/y/z/boofar.md", "http://barnew/boo/", "booslug", "", false, true, "http://barnew/boo/x/y/z/booslug/", "/x/y/z/booslug/"},
+ {"x/y/z/boofar.md", "http://barnew/boo/", "booslug", "", false, false, "http://barnew/boo/x/y/z/booslug/", "/boo/x/y/z/booslug/"},
+ {"x/y/z/boofar.md", "http://barnew/boo/", "booslug", "", true, true, "http://barnew/boo/x/y/z/booslug.html", "/x/y/z/booslug.html"},
+ {"x/y/z/boofar.md", "http://barnew/boo", "booslug", "", true, true, "http://barnew/boo/x/y/z/booslug.html", "/x/y/z/booslug.html"},
// test URL overrides
{"x/y/z/boofar.md", "", "", "/z/y/q/", false, false, "/z/y/q/", "/z/y/q/"},
}
- viper.Set("defaultExtension", "html")
for i, test := range tests {
- viper.Set("uglyURLs", test.uglyURLs)
- viper.Set("canonifyURLs", test.canonifyURLs)
- info := newSiteInfo(siteBuilderCfg{baseURL: string(test.base), language: helpers.NewDefaultLanguage()})
-
- p := &Page{
- pageInit: &pageInit{},
- Kind: KindPage,
- URLPath: URLPath{
- Section: "z",
- URL: test.url,
- },
- Site: &info,
- Source: Source{File: *source.NewFile(filepath.FromSlash(test.file))},
- }
- if test.slug != "" {
- p.update(map[string]interface{}{
- "slug": test.slug,
- })
- }
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
+
+ cfg.Set("uglyURLs", test.uglyURLs)
+ cfg.Set("canonifyURLs", test.canonifyURLs)
+ cfg.Set("baseURL", test.base)
+
+ pageContent := fmt.Sprintf(`---
+title: Page
+slug: %q
+url: %q
+---
+Content
+`, test.slug, test.url)
+
+ writeSource(t, fs, filepath.Join("content", filepath.FromSlash(test.file)), pageContent)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
+ require.Len(t, s.RegularPages, 1)
+
+ p := s.RegularPages[0]
u := p.Permalink()
expected := test.expectedAbs
if u != expected {
- t.Errorf("Test %d: Expected abs url: %s, got: %s", i, expected, u)
+ t.Fatalf("[%d] Expected abs url: %s, got: %s", i, expected, u)
}
u = p.RelPermalink()
expected = test.expectedRel
if u != expected {
- t.Errorf("Test %d: Expected rel url: %s, got: %s", i, expected, u)
+ t.Errorf("[%d] Expected rel url: %s, got: %s", i, expected, u)
}
}
}
diff --git a/hugolib/page_taxonomy_test.go b/hugolib/page_taxonomy_test.go
index 4998c3ffb..e0dc1ffbc 100644
--- a/hugolib/page_taxonomy_test.go
+++ b/hugolib/page_taxonomy_test.go
@@ -57,6 +57,7 @@ categories = "d"
TOML Front Matter with tags and categories`
func TestParseTaxonomies(t *testing.T) {
+ t.Parallel()
for _, test := range []string{pageTomlWithTaxonomies,
pageJSONWithTaxonomies,
pageYamlWithTaxonomiesA,
@@ -64,7 +65,8 @@ func TestParseTaxonomies(t *testing.T) {
pageYamlWithTaxonomiesC,
} {
- p, _ := pageTestSite.NewPage("page/with/taxonomy")
+ s := newTestSite(t)
+ p, _ := s.NewPage("page/with/taxonomy")
_, err := p.ReadFrom(strings.NewReader(test))
if err != nil {
t.Fatalf("Failed parsing %q: %s", test, err)
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index b40d122a0..90a4d1245 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -28,8 +28,6 @@ import (
"github.com/spf13/cast"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -467,13 +465,6 @@ activity = "exam"
Hi.
`
-func init() {
- testCommonResetState()
- pageTestSite, _ = NewSiteDefaultLang()
-}
-
-var pageTestSite *Site
-
func checkError(t *testing.T, err error, expected string) {
if err == nil {
t.Fatalf("err is nil. Expected: %s", expected)
@@ -484,8 +475,9 @@ func checkError(t *testing.T, err error, expected string) {
}
func TestDegenerateEmptyPageZeroLengthName(t *testing.T) {
-
- _, err := pageTestSite.NewPage("")
+ t.Parallel()
+ s := newTestSite(t)
+ _, err := s.NewPage("")
if err == nil {
t.Fatalf("A zero length page name must return an error")
}
@@ -494,7 +486,9 @@ func TestDegenerateEmptyPageZeroLengthName(t *testing.T) {
}
func TestDegenerateEmptyPage(t *testing.T) {
- _, err := pageTestSite.NewPageFrom(strings.NewReader(emptyPage), "test")
+ t.Parallel()
+ s := newTestSite(t)
+ _, err := s.NewPageFrom(strings.NewReader(emptyPage), "test")
if err != nil {
t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.")
}
@@ -611,19 +605,17 @@ func testAllMarkdownEnginesForPages(t *testing.T,
continue
}
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
if settings != nil {
for k, v := range settings {
- viper.Set(k, v)
+ cfg.Set(k, v)
}
}
contentDir := "content"
- if s := viper.GetString("contentDir"); s != "" {
+ if s := cfg.GetString("contentDir"); s != "" {
contentDir = s
}
@@ -637,7 +629,7 @@ func testAllMarkdownEnginesForPages(t *testing.T,
writeSource(t, fs, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1])
}
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, len(pageSources))
@@ -648,7 +640,7 @@ func testAllMarkdownEnginesForPages(t *testing.T,
}
func TestCreateNewPage(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
@@ -671,7 +663,7 @@ func TestCreateNewPage(t *testing.T) {
}
func TestSplitSummaryAndContent(t *testing.T) {
-
+ t.Parallel()
for i, this := range []struct {
markup string
content string
@@ -727,7 +719,7 @@ func TestSplitSummaryAndContent(t *testing.T) {
}
func TestPageWithDelimiter(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
checkPageTitle(t, p, "Simple")
@@ -743,14 +735,12 @@ func TestPageWithDelimiter(t *testing.T) {
// Issue #1076
func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) {
-
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -767,9 +757,8 @@ func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) {
// Issue #2601
func TestPageRawContent(t *testing.T) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "raw.md"), `---
title: Raw
@@ -778,7 +767,7 @@ title: Raw
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .RawContent }}`)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
p := s.RegularPages[0]
@@ -788,7 +777,7 @@ title: Raw
}
func TestPageWithShortCodeInSummary(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
checkPageTitle(t, p, "Simple")
@@ -802,7 +791,7 @@ func TestPageWithShortCodeInSummary(t *testing.T) {
}
func TestPageWithEmbeddedScriptTag(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
if ext == "ad" || ext == "rst" {
@@ -816,12 +805,12 @@ func TestPageWithEmbeddedScriptTag(t *testing.T) {
}
func TestPageWithAdditionalExtension(t *testing.T) {
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithAdditionalExtension)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -832,11 +821,11 @@ func TestPageWithAdditionalExtension(t *testing.T) {
func TestTableOfContents(t *testing.T) {
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "tocpage.md"), pageWithToC)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -847,7 +836,7 @@ func TestTableOfContents(t *testing.T) {
}
func TestPageWithMoreTag(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
checkPageTitle(t, p, "Simple")
@@ -862,11 +851,12 @@ func TestPageWithMoreTag(t *testing.T) {
}
func TestPageWithDate(t *testing.T) {
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageRFC3339Date)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -877,8 +867,7 @@ func TestPageWithDate(t *testing.T) {
}
func TestWordCountWithAllCJKRunesWithoutHasCJKLanguage(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
if p.WordCount() != 8 {
@@ -890,6 +879,7 @@ func TestWordCountWithAllCJKRunesWithoutHasCJKLanguage(t *testing.T) {
}
func TestWordCountWithAllCJKRunesHasCJKLanguage(t *testing.T) {
+ t.Parallel()
settings := map[string]interface{}{"hasCJKLanguage": true}
assertFunc := func(t *testing.T, ext string, pages Pages) {
@@ -902,6 +892,7 @@ func TestWordCountWithAllCJKRunesHasCJKLanguage(t *testing.T) {
}
func TestWordCountWithMainEnglishWithCJKRunes(t *testing.T) {
+ t.Parallel()
settings := map[string]interface{}{"hasCJKLanguage": true}
assertFunc := func(t *testing.T, ext string, pages Pages) {
@@ -920,8 +911,10 @@ func TestWordCountWithMainEnglishWithCJKRunes(t *testing.T) {
}
func TestWordCountWithIsCJKLanguageFalse(t *testing.T) {
- testCommonResetState()
- viper.Set("hasCJKLanguage", true)
+ t.Parallel()
+ settings := map[string]interface{}{
+ "hasCJKLanguage": true,
+ }
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
@@ -935,12 +928,12 @@ func TestWordCountWithIsCJKLanguageFalse(t *testing.T) {
}
}
- testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithIsCJKLanguageFalse)
+ testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithIsCJKLanguageFalse)
}
func TestWordCount(t *testing.T) {
-
+ t.Parallel()
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
if p.WordCount() != 483 {
@@ -962,6 +955,7 @@ func TestWordCount(t *testing.T) {
}
func TestCreatePage(t *testing.T) {
+ t.Parallel()
var tests = []struct {
r string
}{
@@ -972,7 +966,8 @@ func TestCreatePage(t *testing.T) {
}
for _, test := range tests {
- p, _ := pageTestSite.NewPage("page")
+ s := newTestSite(t)
+ p, _ := s.NewPage("page")
if _, err := p.ReadFrom(strings.NewReader(test.r)); err != nil {
t.Errorf("Unable to parse page: %s", err)
}
@@ -980,6 +975,7 @@ func TestCreatePage(t *testing.T) {
}
func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) {
+ t.Parallel()
var tests = []struct {
r string
err string
@@ -987,14 +983,15 @@ func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) {
{invalidFrontmatterShortDelimEnding, "unable to read frontmatter at filepos 45: EOF"},
}
for _, test := range tests {
-
- p, _ := pageTestSite.NewPage("invalid/front/matter/short/delim")
+ s := newTestSite(t)
+ p, _ := s.NewPage("invalid/front/matter/short/delim")
_, err := p.ReadFrom(strings.NewReader(test.r))
checkError(t, err, test.err)
}
}
func TestShouldRenderContent(t *testing.T) {
+ t.Parallel()
var tests = []struct {
text string
render bool
@@ -1010,8 +1007,8 @@ func TestShouldRenderContent(t *testing.T) {
}
for _, test := range tests {
-
- p, _ := pageTestSite.NewPage("render/front/matter")
+ s := newTestSite(t)
+ p, _ := s.NewPage("render/front/matter")
_, err := p.ReadFrom(strings.NewReader(test.text))
p = pageMust(p, err)
if p.IsRenderable() != test.render {
@@ -1022,13 +1019,15 @@ func TestShouldRenderContent(t *testing.T) {
// Issue #768
func TestCalendarParamsVariants(t *testing.T) {
- pageJSON, _ := pageTestSite.NewPage("test/fileJSON.md")
+ t.Parallel()
+ s := newTestSite(t)
+ pageJSON, _ := s.NewPage("test/fileJSON.md")
_, _ = pageJSON.ReadFrom(strings.NewReader(pageWithCalendarJSONFrontmatter))
- pageYAML, _ := pageTestSite.NewPage("test/fileYAML.md")
+ pageYAML, _ := s.NewPage("test/fileYAML.md")
_, _ = pageYAML.ReadFrom(strings.NewReader(pageWithCalendarYAMLFrontmatter))
- pageTOML, _ := pageTestSite.NewPage("test/fileTOML.md")
+ pageTOML, _ := s.NewPage("test/fileTOML.md")
_, _ = pageTOML.ReadFrom(strings.NewReader(pageWithCalendarTOMLFrontmatter))
assert.True(t, compareObjects(pageJSON.Params, pageYAML.Params))
@@ -1037,7 +1036,9 @@ func TestCalendarParamsVariants(t *testing.T) {
}
func TestDifferentFrontMatterVarTypes(t *testing.T) {
- page, _ := pageTestSite.NewPage("test/file1.md")
+ t.Parallel()
+ s := newTestSite(t)
+ page, _ := s.NewPage("test/file1.md")
_, _ = page.ReadFrom(strings.NewReader(pageWithVariousFrontmatterTypes))
dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
@@ -1066,7 +1067,9 @@ func TestDifferentFrontMatterVarTypes(t *testing.T) {
}
func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) {
- p, _ := pageTestSite.NewPage("invalid/front/matter/leading/ws")
+ t.Parallel()
+ s := newTestSite(t)
+ p, _ := s.NewPage("invalid/front/matter/leading/ws")
_, err := p.ReadFrom(strings.NewReader(invalidFrontmatterLadingWs))
if err != nil {
t.Fatalf("Unable to parse front matter given leading whitespace: %s", err)
@@ -1074,7 +1077,9 @@ func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) {
}
func TestSectionEvaluation(t *testing.T) {
- page, _ := pageTestSite.NewPage(filepath.FromSlash("blue/file1.md"))
+ t.Parallel()
+ s := newTestSite(t)
+ page, _ := s.NewPage(filepath.FromSlash("blue/file1.md"))
page.ReadFrom(strings.NewReader(simplePage))
if page.Section() != "blue" {
t.Errorf("Section should be %s, got: %s", "blue", page.Section())
@@ -1086,6 +1091,7 @@ func L(s ...string) []string {
}
func TestLayoutOverride(t *testing.T) {
+ t.Parallel()
var (
pathContentTwoDir = filepath.Join("content", "dub", "sub", "file1.md")
pathContentOneDir = filepath.Join("content", "gub", "file1.md")
@@ -1119,7 +1125,8 @@ func TestLayoutOverride(t *testing.T) {
{simplePageTypeLayout, pathNoDirectory, L("barfoo/buzfoo.html", "_default/buzfoo.html")},
}
for _, test := range tests {
- p, _ := pageTestSite.NewPage(test.path)
+ s := newTestSite(t)
+ p, _ := s.NewPage(test.path)
_, err := p.ReadFrom(strings.NewReader(test.content))
if err != nil {
t.Fatalf("Unable to parse content:\n%s\n", test.content)
@@ -1135,6 +1142,7 @@ func TestLayoutOverride(t *testing.T) {
}
func TestSliceToLower(t *testing.T) {
+ t.Parallel()
tests := []struct {
value []string
expected []string
@@ -1155,10 +1163,9 @@ func TestSliceToLower(t *testing.T) {
}
func TestPagePaths(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
- viper.Set("defaultExtension", "html")
- siteParmalinksSetting := PermalinkOverrides{
+ siteParmalinksSetting := map[string]string{
"post": ":year/:month/:day/:title/",
}
@@ -1168,34 +1175,41 @@ func TestPagePaths(t *testing.T) {
hasPermalink bool
expected string
}{
- {simplePage, "content/post/x.md", false, "content/post/x.html"},
- {simplePageWithURL, "content/post/x.md", false, "simple/url/index.html"},
- {simplePageWithSlug, "content/post/x.md", false, "content/post/simple-slug.html"},
- {simplePageWithDate, "content/post/x.md", true, "2013/10/15/simple/index.html"},
- {UTF8Page, "content/post/x.md", false, "content/post/x.html"},
- {UTF8PageWithURL, "content/post/x.md", false, "ラーメン/url/index.html"},
- {UTF8PageWithSlug, "content/post/x.md", false, "content/post/ラーメン-slug.html"},
- {UTF8PageWithDate, "content/post/x.md", true, "2013/10/15/ラーメン/index.html"},
+ {simplePage, "post/x.md", false, "post/x.html"},
+ {simplePageWithURL, "post/x.md", false, "simple/url/index.html"},
+ {simplePageWithSlug, "post/x.md", false, "post/simple-slug.html"},
+ {simplePageWithDate, "post/x.md", true, "2013/10/15/simple/index.html"},
+ {UTF8Page, "post/x.md", false, "post/x.html"},
+ {UTF8PageWithURL, "post/x.md", false, "ラーメン/url/index.html"},
+ {UTF8PageWithSlug, "post/x.md", false, "post/ラーメン-slug.html"},
+ {UTF8PageWithDate, "post/x.md", true, "2013/10/15/ラーメン/index.html"},
}
- for _, test := range tests {
- p, _ := pageTestSite.NewPageFrom(strings.NewReader(test.content), filepath.FromSlash(test.path))
- info := newSiteInfo(siteBuilderCfg{language: helpers.NewDefaultLanguage()})
- p.Site = &info
+ for i, test := range tests {
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
if test.hasPermalink {
- p.Site.Permalinks = siteParmalinksSetting
+ cfg.Set("permalinks", siteParmalinksSetting)
}
+ writeSource(t, fs, filepath.Join("content", filepath.FromSlash(test.path)), test.content)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
+ require.Len(t, s.RegularPages, 1)
+
+ p := s.RegularPages[0]
+
expectedTargetPath := filepath.FromSlash(test.expected)
expectedFullFilePath := filepath.FromSlash(test.path)
if p.TargetPath() != expectedTargetPath {
- t.Errorf("%s => TargetPath expected: '%s', got: '%s'", test.content, expectedTargetPath, p.TargetPath())
+ t.Fatalf("[%d] %s => TargetPath expected: '%s', got: '%s'", i, test.content, expectedTargetPath, p.TargetPath())
}
if p.FullFilePath() != expectedFullFilePath {
- t.Errorf("%s => FullFilePath expected: '%s', got: '%s'", test.content, expectedFullFilePath, p.FullFilePath())
+ t.Fatalf("[%d] %s => FullFilePath expected: '%s', got: '%s'", i, test.content, expectedFullFilePath, p.FullFilePath())
}
}
}
@@ -1209,7 +1223,9 @@ some content
`
func TestDraftAndPublishedFrontMatterError(t *testing.T) {
- _, err := pageTestSite.NewPageFrom(strings.NewReader(pageWithDraftAndPublished), "content/post/broken.md")
+ t.Parallel()
+ s := newTestSite(t)
+ _, err := s.NewPageFrom(strings.NewReader(pageWithDraftAndPublished), "content/post/broken.md")
if err != ErrHasDraftAndPublished {
t.Errorf("expected ErrHasDraftAndPublished, was %#v", err)
}
@@ -1229,14 +1245,16 @@ some content
`
func TestPublishedFrontMatter(t *testing.T) {
- p, err := pageTestSite.NewPageFrom(strings.NewReader(pagesWithPublishedFalse), "content/post/broken.md")
+ t.Parallel()
+ s := newTestSite(t)
+ p, err := s.NewPageFrom(strings.NewReader(pagesWithPublishedFalse), "content/post/broken.md")
if err != nil {
t.Fatalf("err during parse: %s", err)
}
if !p.Draft {
t.Errorf("expected true, got %t", p.Draft)
}
- p, err = pageTestSite.NewPageFrom(strings.NewReader(pageWithPublishedTrue), "content/post/broken.md")
+ p, err = s.NewPageFrom(strings.NewReader(pageWithPublishedTrue), "content/post/broken.md")
if err != nil {
t.Fatalf("err during parse: %s", err)
}
@@ -1261,10 +1279,12 @@ some content
}
func TestDraft(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
for _, draft := range []bool{true, false} {
for i, templ := range pagesDraftTemplate {
pageContent := fmt.Sprintf(templ, draft)
- p, err := pageTestSite.NewPageFrom(strings.NewReader(pageContent), "content/post/broken.md")
+ p, err := s.NewPageFrom(strings.NewReader(pageContent), "content/post/broken.md")
if err != nil {
t.Fatalf("err during parse: %s", err)
}
@@ -1314,6 +1334,8 @@ some content
}
func TestPageParams(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
want := map[string]interface{}{
"tags": []string{"hugo", "web"},
// Issue #2752
@@ -1324,13 +1346,15 @@ func TestPageParams(t *testing.T) {
}
for i, c := range pagesParamsTemplate {
- p, err := pageTestSite.NewPageFrom(strings.NewReader(c), "content/post/params.md")
+ p, err := s.NewPageFrom(strings.NewReader(c), "content/post/params.md")
require.NoError(t, err, "err during parse", "#%d", i)
assert.Equal(t, want, p.Params, "#%d", i)
}
}
func TestPageSimpleMethods(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
for i, this := range []struct {
assertFunc func(p *Page) bool
}{
@@ -1340,7 +1364,7 @@ func TestPageSimpleMethods(t *testing.T) {
{func(p *Page) bool { return strings.Join(p.PlainWords(), " ") == "Do Be Do Be Do" }},
} {
- p, _ := pageTestSite.NewPage("Test")
+ p, _ := s.NewPage("Test")
p.Content = "<h1>Do Be Do Be Do</h1>"
if !this.assertFunc(p) {
t.Errorf("[%d] Page method error", i)
@@ -1349,6 +1373,7 @@ func TestPageSimpleMethods(t *testing.T) {
}
func TestIndexPageSimpleMethods(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
assertFunc func(n *Page) bool
}{
@@ -1371,7 +1396,7 @@ func TestIndexPageSimpleMethods(t *testing.T) {
}
func TestKind(t *testing.T) {
-
+ t.Parallel()
// Add tests for these constants to make sure they don't change
require.Equal(t, "page", KindPage)
require.Equal(t, "home", KindHome)
@@ -1382,13 +1407,14 @@ func TestKind(t *testing.T) {
}
func TestChompBOM(t *testing.T) {
+ t.Parallel()
const utf8BOM = "\xef\xbb\xbf"
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "simple.md"), utf8BOM+simplePage)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -1423,6 +1449,7 @@ func compareObjects(a interface{}, b interface{}) bool {
}
func TestShouldBuild(t *testing.T) {
+ t.Parallel()
var past = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
var future = time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC)
var zero = time.Time{}
@@ -1469,12 +1496,13 @@ func TestShouldBuild(t *testing.T) {
}
func BenchmarkParsePage(b *testing.B) {
+ s := newTestSite(b)
f, _ := os.Open("testdata/redis.cn.md")
var buf bytes.Buffer
buf.ReadFrom(f)
b.ResetTimer()
for i := 0; i < b.N; i++ {
- page, _ := pageTestSite.NewPage("bench")
+ page, _ := s.NewPage("bench")
page.ReadFrom(bytes.NewReader(buf.Bytes()))
}
}
diff --git a/hugolib/page_time_integration_test.go b/hugolib/page_time_integration_test.go
index 4544d83e7..dde1587dd 100644
--- a/hugolib/page_time_integration_test.go
+++ b/hugolib/page_time_integration_test.go
@@ -89,13 +89,17 @@ Page With Date HugoLong`
)
func TestDegenerateDateFrontMatter(t *testing.T) {
- p, _ := pageTestSite.NewPageFrom(strings.NewReader(pageWithInvalidDate), "page/with/invalid/date")
+ t.Parallel()
+ s := newTestSite(t)
+ p, _ := s.NewPageFrom(strings.NewReader(pageWithInvalidDate), "page/with/invalid/date")
if p.Date != *new(time.Time) {
t.Fatalf("Date should be set to time.Time zero value. Got: %s", p.Date)
}
}
func TestParsingDateInFrontMatter(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
tests := []struct {
buf string
dt string
@@ -131,7 +135,7 @@ func TestParsingDateInFrontMatter(t *testing.T) {
if e != nil {
t.Fatalf("Unable to parse date time (RFC3339) for running the test: %s", e)
}
- p, err := pageTestSite.NewPageFrom(strings.NewReader(test.buf), "page/with/date")
+ p, err := s.NewPageFrom(strings.NewReader(test.buf), "page/with/date")
if err != nil {
t.Fatalf("Expected to be able to parse page.")
}
diff --git a/hugolib/pagesPrevNext_test.go b/hugolib/pagesPrevNext_test.go
index 46b57d1c2..5945d8fe5 100644
--- a/hugolib/pagesPrevNext_test.go
+++ b/hugolib/pagesPrevNext_test.go
@@ -35,6 +35,7 @@ var pagePNTestSources = []pagePNTestObject{
}
func TestPrev(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
assert.Equal(t, pages.Prev(pages[0]), pages[4])
assert.Equal(t, pages.Prev(pages[1]), pages[0])
@@ -42,6 +43,7 @@ func TestPrev(t *testing.T) {
}
func TestNext(t *testing.T) {
+ t.Parallel()
pages := preparePageGroupTestPages(t)
assert.Equal(t, pages.Next(pages[0]), pages[1])
assert.Equal(t, pages.Next(pages[1]), pages[2])
@@ -49,16 +51,17 @@ func TestNext(t *testing.T) {
}
func prepareWeightedPagesPrevNext(t *testing.T) WeightedPages {
+ s := newTestSite(t)
w := WeightedPages{}
- for _, s := range pagePNTestSources {
- p, err := pageTestSite.NewPage(s.path)
+ for _, src := range pagePNTestSources {
+ p, err := s.NewPage(src.path)
if err != nil {
- t.Fatalf("failed to prepare test page %s", s.path)
+ t.Fatalf("failed to prepare test page %s", src.path)
}
- p.Weight = s.weight
- p.Date = cast.ToTime(s.date)
- p.PublishDate = cast.ToTime(s.date)
+ p.Weight = src.weight
+ p.Date = cast.ToTime(src.date)
+ p.PublishDate = cast.ToTime(src.date)
w = append(w, WeightedPage{p.Weight, p})
}
@@ -67,6 +70,7 @@ func prepareWeightedPagesPrevNext(t *testing.T) WeightedPages {
}
func TestWeightedPagesPrev(t *testing.T) {
+ t.Parallel()
w := prepareWeightedPagesPrevNext(t)
assert.Equal(t, w.Prev(w[0].Page), w[4].Page)
assert.Equal(t, w.Prev(w[1].Page), w[0].Page)
@@ -74,6 +78,7 @@ func TestWeightedPagesPrev(t *testing.T) {
}
func TestWeightedPagesNext(t *testing.T) {
+ t.Parallel()
w := prepareWeightedPagesPrevNext(t)
assert.Equal(t, w.Next(w[0].Page), w[1].Page)
assert.Equal(t, w.Next(w[1].Page), w[2].Page)
diff --git a/hugolib/pagination.go b/hugolib/pagination.go
index acfa2b75e..aa20f8d55 100644
--- a/hugolib/pagination.go
+++ b/hugolib/pagination.go
@@ -21,6 +21,8 @@ import (
"path"
"reflect"
+ "github.com/spf13/hugo/config"
+
"github.com/spf13/cast"
"github.com/spf13/hugo/helpers"
)
@@ -266,7 +268,7 @@ func (p *Page) Paginator(options ...interface{}) (*Pager, error) {
if !p.IsNode() {
return nil, fmt.Errorf("Paginators not supported for pages of type %q (%q)", p.Kind, p.Title)
}
- pagerSize, err := resolvePagerSize(options...)
+ pagerSize, err := resolvePagerSize(p.s.Cfg, options...)
if err != nil {
return nil, err
@@ -310,7 +312,7 @@ func (p *Page) Paginate(seq interface{}, options ...interface{}) (*Pager, error)
return nil, fmt.Errorf("Paginators not supported for pages of type %q (%q)", p.Kind, p.Title)
}
- pagerSize, err := resolvePagerSize(options...)
+ pagerSize, err := resolvePagerSize(p.s.Cfg, options...)
if err != nil {
return nil, err
@@ -353,9 +355,9 @@ func (p *Page) Paginate(seq interface{}, options ...interface{}) (*Pager, error)
return p.paginator, nil
}
-func resolvePagerSize(options ...interface{}) (int, error) {
+func resolvePagerSize(cfg config.Provider, options ...interface{}) (int, error) {
if len(options) == 0 {
- return helpers.Config().GetInt("paginate"), nil
+ return cfg.GetInt("paginate"), nil
}
if len(options) > 1 {
diff --git a/hugolib/pagination_test.go b/hugolib/pagination_test.go
index a7f2d9392..cad0d27da 100644
--- a/hugolib/pagination_test.go
+++ b/hugolib/pagination_test.go
@@ -20,46 +20,44 @@ import (
"testing"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/hugo/source"
- "github.com/spf13/viper"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSplitPages(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
- pages := createTestPages(21)
+ pages := createTestPages(s, 21)
chunks := splitPages(pages, 5)
- assert.Equal(t, 5, len(chunks))
+ require.Equal(t, 5, len(chunks))
for i := 0; i < 4; i++ {
- assert.Equal(t, 5, chunks[i].Len())
+ require.Equal(t, 5, chunks[i].Len())
}
lastChunk := chunks[4]
- assert.Equal(t, 1, lastChunk.Len())
+ require.Equal(t, 1, lastChunk.Len())
}
func TestSplitPageGroups(t *testing.T) {
-
- pages := createTestPages(21)
+ t.Parallel()
+ s := newTestSite(t)
+ pages := createTestPages(s, 21)
groups, _ := pages.GroupBy("Weight", "desc")
chunks := splitPageGroups(groups, 5)
- assert.Equal(t, 5, len(chunks))
+ require.Equal(t, 5, len(chunks))
firstChunk := chunks[0]
// alternate weight 5 and 10
if groups, ok := firstChunk.(PagesGroup); ok {
- assert.Equal(t, 5, groups.Len())
+ require.Equal(t, 5, groups.Len())
for _, pg := range groups {
// first group 10 in weight
- assert.Equal(t, 10, pg.Key)
+ require.Equal(t, 10, pg.Key)
for _, p := range pg.Pages {
- assert.True(t, p.fuzzyWordCount%2 == 0) // magic test
+ require.True(t, p.fuzzyWordCount%2 == 0) // magic test
}
}
} else {
@@ -69,12 +67,12 @@ func TestSplitPageGroups(t *testing.T) {
lastChunk := chunks[4]
if groups, ok := lastChunk.(PagesGroup); ok {
- assert.Equal(t, 1, groups.Len())
+ require.Equal(t, 1, groups.Len())
for _, pg := range groups {
// last should have 5 in weight
- assert.Equal(t, 5, pg.Key)
+ require.Equal(t, 5, pg.Key)
for _, p := range pg.Pages {
- assert.True(t, p.fuzzyWordCount%2 != 0) // magic test
+ require.True(t, p.fuzzyWordCount%2 != 0) // magic test
}
}
} else {
@@ -84,7 +82,9 @@ func TestSplitPageGroups(t *testing.T) {
}
func TestPager(t *testing.T) {
- pages := createTestPages(21)
+ t.Parallel()
+ s := newTestSite(t)
+ pages := createTestPages(s, 21)
groups, _ := pages.GroupBy("Weight", "desc")
urlFactory := func(page int) string {
@@ -92,25 +92,25 @@ func TestPager(t *testing.T) {
}
_, err := newPaginatorFromPages(pages, -1, urlFactory)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
_, err = newPaginatorFromPageGroups(groups, -1, urlFactory)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
pag, err := newPaginatorFromPages(pages, 5, urlFactory)
- assert.Nil(t, err)
+ require.Nil(t, err)
doTestPages(t, pag)
first := pag.Pagers()[0].First()
- assert.Equal(t, "Pager 1", first.String())
- assert.NotEmpty(t, first.Pages())
- assert.Empty(t, first.PageGroups())
+ require.Equal(t, "Pager 1", first.String())
+ require.NotEmpty(t, first.Pages())
+ require.Empty(t, first.PageGroups())
pag, err = newPaginatorFromPageGroups(groups, 5, urlFactory)
- assert.Nil(t, err)
+ require.Nil(t, err)
doTestPages(t, pag)
first = pag.Pagers()[0].First()
- assert.NotEmpty(t, first.PageGroups())
- assert.Empty(t, first.Pages())
+ require.NotEmpty(t, first.PageGroups())
+ require.Empty(t, first.Pages())
}
@@ -118,38 +118,40 @@ func doTestPages(t *testing.T, paginator *paginator) {
paginatorPages := paginator.Pagers()
- assert.Equal(t, 5, len(paginatorPages))
- assert.Equal(t, 21, paginator.TotalNumberOfElements())
- assert.Equal(t, 5, paginator.PageSize())
- assert.Equal(t, 5, paginator.TotalPages())
+ require.Equal(t, 5, len(paginatorPages))
+ require.Equal(t, 21, paginator.TotalNumberOfElements())
+ require.Equal(t, 5, paginator.PageSize())
+ require.Equal(t, 5, paginator.TotalPages())
first := paginatorPages[0]
- assert.Equal(t, template.HTML("page/1/"), first.URL())
- assert.Equal(t, first, first.First())
- assert.True(t, first.HasNext())
- assert.Equal(t, paginatorPages[1], first.Next())
- assert.False(t, first.HasPrev())
- assert.Nil(t, first.Prev())
- assert.Equal(t, 5, first.NumberOfElements())
- assert.Equal(t, 1, first.PageNumber())
+ require.Equal(t, template.HTML("page/1/"), first.URL())
+ require.Equal(t, first, first.First())
+ require.True(t, first.HasNext())
+ require.Equal(t, paginatorPages[1], first.Next())
+ require.False(t, first.HasPrev())
+ require.Nil(t, first.Prev())
+ require.Equal(t, 5, first.NumberOfElements())
+ require.Equal(t, 1, first.PageNumber())
third := paginatorPages[2]
- assert.True(t, third.HasNext())
- assert.True(t, third.HasPrev())
- assert.Equal(t, paginatorPages[1], third.Prev())
+ require.True(t, third.HasNext())
+ require.True(t, third.HasPrev())
+ require.Equal(t, paginatorPages[1], third.Prev())
last := paginatorPages[4]
- assert.Equal(t, template.HTML("page/5/"), last.URL())
- assert.Equal(t, last, last.Last())
- assert.False(t, last.HasNext())
- assert.Nil(t, last.Next())
- assert.True(t, last.HasPrev())
- assert.Equal(t, 1, last.NumberOfElements())
- assert.Equal(t, 5, last.PageNumber())
+ require.Equal(t, template.HTML("page/5/"), last.URL())
+ require.Equal(t, last, last.Last())
+ require.False(t, last.HasNext())
+ require.Nil(t, last.Next())
+ require.True(t, last.HasPrev())
+ require.Equal(t, 1, last.NumberOfElements())
+ require.Equal(t, 5, last.PageNumber())
}
func TestPagerNoPages(t *testing.T) {
- pages := createTestPages(0)
+ t.Parallel()
+ s := newTestSite(t)
+ pages := createTestPages(s, 0)
groups, _ := pages.GroupBy("Weight", "desc")
urlFactory := func(page int) string {
@@ -160,54 +162,55 @@ func TestPagerNoPages(t *testing.T) {
doTestPagerNoPages(t, paginator)
first := paginator.Pagers()[0].First()
- assert.Empty(t, first.PageGroups())
- assert.Empty(t, first.Pages())
+ require.Empty(t, first.PageGroups())
+ require.Empty(t, first.Pages())
paginator, _ = newPaginatorFromPageGroups(groups, 5, urlFactory)
doTestPagerNoPages(t, paginator)
first = paginator.Pagers()[0].First()
- assert.Empty(t, first.PageGroups())
- assert.Empty(t, first.Pages())
+ require.Empty(t, first.PageGroups())
+ require.Empty(t, first.Pages())
}
func doTestPagerNoPages(t *testing.T, paginator *paginator) {
paginatorPages := paginator.Pagers()
- assert.Equal(t, 1, len(paginatorPages))
- assert.Equal(t, 0, paginator.TotalNumberOfElements())
- assert.Equal(t, 5, paginator.PageSize())
- assert.Equal(t, 0, paginator.TotalPages())
+ require.Equal(t, 1, len(paginatorPages))
+ require.Equal(t, 0, paginator.TotalNumberOfElements())
+ require.Equal(t, 5, paginator.PageSize())
+ require.Equal(t, 0, paginator.TotalPages())
// pageOne should be nothing but the first
pageOne := paginatorPages[0]
- assert.NotNil(t, pageOne.First())
- assert.False(t, pageOne.HasNext())
- assert.False(t, pageOne.HasPrev())
- assert.Nil(t, pageOne.Next())
- assert.Equal(t, 1, len(pageOne.Pagers()))
- assert.Equal(t, 0, pageOne.Pages().Len())
- assert.Equal(t, 0, pageOne.NumberOfElements())
- assert.Equal(t, 0, pageOne.TotalNumberOfElements())
- assert.Equal(t, 0, pageOne.TotalPages())
- assert.Equal(t, 1, pageOne.PageNumber())
- assert.Equal(t, 5, pageOne.PageSize())
+ require.NotNil(t, pageOne.First())
+ require.False(t, pageOne.HasNext())
+ require.False(t, pageOne.HasPrev())
+ require.Nil(t, pageOne.Next())
+ require.Equal(t, 1, len(pageOne.Pagers()))
+ require.Equal(t, 0, pageOne.Pages().Len())
+ require.Equal(t, 0, pageOne.NumberOfElements())
+ require.Equal(t, 0, pageOne.TotalNumberOfElements())
+ require.Equal(t, 0, pageOne.TotalPages())
+ require.Equal(t, 1, pageOne.PageNumber())
+ require.Equal(t, 5, pageOne.PageSize())
}
func TestPaginationURLFactory(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
+ cfg, fs := newTestCfg()
- viper.Set("paginatePath", "zoo")
+ cfg.Set("paginatePath", "zoo")
- pathSpec := newTestPathSpec()
+ pathSpec := newTestPathSpec(fs, cfg)
unicode := newPaginationURLFactory(pathSpec, "новости проекта")
fooBar := newPaginationURLFactory(pathSpec, "foo", "bar")
- assert.Equal(t, "/foo/bar/", fooBar(1))
- assert.Equal(t, "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0/zoo/4/", unicode(4))
+ require.Equal(t, "/foo/bar/", fooBar(1))
+ require.Equal(t, "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0/zoo/4/", unicode(4))
unicoded := unicode(4)
unicodedExpected := "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0/zoo/4/"
@@ -216,30 +219,32 @@ func TestPaginationURLFactory(t *testing.T) {
t.Fatal("Expected\n", unicodedExpected, "\nGot\n", unicoded)
}
- assert.Equal(t, "/foo/bar/zoo/12345/", fooBar(12345))
+ require.Equal(t, "/foo/bar/zoo/12345/", fooBar(12345))
}
func TestPaginator(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
for _, useViper := range []bool{false, true} {
doTestPaginator(t, useViper)
}
}
func doTestPaginator(t *testing.T, useViper bool) {
- testCommonResetState()
+
+ cfg, fs := newTestCfg()
pagerSize := 5
if useViper {
- viper.Set("paginate", pagerSize)
+ cfg.Set("paginate", pagerSize)
} else {
- viper.Set("paginate", -1)
+ cfg.Set("paginate", -1)
}
- pages := createTestPages(12)
- s, err := NewSiteDefaultLang()
+
+ s, err := NewSiteForCfg(deps.DepsCfg{Cfg: cfg, Fs: fs})
require.NoError(t, err)
+
+ pages := createTestPages(s, 12)
n1 := s.newHomePage()
n2 := s.newHomePage()
n1.Data["Pages"] = pages
@@ -252,50 +257,45 @@ func doTestPaginator(t *testing.T, useViper bool) {
paginator1, err = n1.Paginator(pagerSize)
}
- assert.Nil(t, err)
- assert.NotNil(t, paginator1)
- assert.Equal(t, 3, paginator1.TotalPages())
- assert.Equal(t, 12, paginator1.TotalNumberOfElements())
+ require.Nil(t, err)
+ require.NotNil(t, paginator1)
+ require.Equal(t, 3, paginator1.TotalPages())
+ require.Equal(t, 12, paginator1.TotalNumberOfElements())
n2.paginator = paginator1.Next()
paginator2, err := n2.Paginator()
- assert.Nil(t, err)
- assert.Equal(t, paginator2, paginator1.Next())
+ require.Nil(t, err)
+ require.Equal(t, paginator2, paginator1.Next())
- n1.Data["Pages"] = createTestPages(1)
+ n1.Data["Pages"] = createTestPages(s, 1)
samePaginator, _ := n1.Paginator()
- assert.Equal(t, paginator1, samePaginator)
+ require.Equal(t, paginator1, samePaginator)
- p, _ := pageTestSite.NewPage("test")
+ p, _ := s.NewPage("test")
_, err = p.Paginator()
- assert.NotNil(t, err)
+ require.NotNil(t, err)
}
func TestPaginatorWithNegativePaginate(t *testing.T) {
- testCommonResetState()
-
- viper.Set("paginate", -1)
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
- _, err = s.newHomePage().Paginator()
+ t.Parallel()
+ s := newTestSite(t, "paginate", -1)
+ _, err := s.newHomePage().Paginator()
require.Error(t, err)
}
func TestPaginate(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
for _, useViper := range []bool{false, true} {
doTestPaginate(t, useViper)
}
}
func TestPaginatorURL(t *testing.T) {
+ t.Parallel()
+ cfg, fs := newTestCfg()
- testCommonResetState()
- viper.Set("paginate", 2)
- viper.Set("paginatePath", "testing")
-
- fs := hugofs.NewMem()
+ cfg.Set("paginate", 2)
+ cfg.Set("paginatePath", "testing")
for i := 0; i < 10; i++ {
// Issue #2177, do not double encode URLs
@@ -318,23 +318,29 @@ Pages: {{ .Paginator.TotalPages }}
{{ end }}
</body></html>`)
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
- assertFileContent(t, fs, filepath.Join("public", "阅读", "testing", "2", "index.html"), false, "2: /%E9%98%85%E8%AF%BB/testing/2/")
+ th := testHelper{s.Cfg}
+
+ th.assertFileContent(t, fs, filepath.Join("public", "阅读", "testing", "2", "index.html"), false, "2: /%E9%98%85%E8%AF%BB/testing/2/")
}
func doTestPaginate(t *testing.T, useViper bool) {
pagerSize := 5
+
+ var (
+ s *Site
+ err error
+ )
+
if useViper {
- viper.Set("paginate", pagerSize)
+ s = newTestSite(t, "paginate", pagerSize)
} else {
- viper.Set("paginate", -1)
+ s = newTestSite(t, "paginate", -1)
}
- pages := createTestPages(6)
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
+ pages := createTestPages(s, 6)
n1 := s.newHomePage()
n2 := s.newHomePage()
@@ -346,10 +352,10 @@ func doTestPaginate(t *testing.T, useViper bool) {
paginator1, err = n1.Paginate(pages, pagerSize)
}
- assert.Nil(t, err)
- assert.NotNil(t, paginator1)
- assert.Equal(t, 2, paginator1.TotalPages())
- assert.Equal(t, 6, paginator1.TotalNumberOfElements())
+ require.Nil(t, err)
+ require.NotNil(t, paginator1)
+ require.Equal(t, 2, paginator1.TotalPages())
+ require.Equal(t, 6, paginator1.TotalNumberOfElements())
n2.paginator = paginator1.Next()
if useViper {
@@ -357,101 +363,102 @@ func doTestPaginate(t *testing.T, useViper bool) {
} else {
paginator2, err = n2.Paginate(pages, pagerSize)
}
- assert.Nil(t, err)
- assert.Equal(t, paginator2, paginator1.Next())
+ require.Nil(t, err)
+ require.Equal(t, paginator2, paginator1.Next())
- p, _ := pageTestSite.NewPage("test")
+ p, _ := s.NewPage("test")
_, err = p.Paginate(pages)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
}
func TestInvalidOptions(t *testing.T) {
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
+ t.Parallel()
+ s := newTestSite(t)
n1 := s.newHomePage()
- _, err = n1.Paginate(createTestPages(1), 1, 2)
- assert.NotNil(t, err)
+ _, err := n1.Paginate(createTestPages(s, 1), 1, 2)
+ require.NotNil(t, err)
_, err = n1.Paginator(1, 2)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
_, err = n1.Paginator(-1)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
}
func TestPaginateWithNegativePaginate(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
+ cfg, fs := newTestCfg()
+ cfg.Set("paginate", -1)
- viper.Set("paginate", -1)
- s, err := NewSiteDefaultLang()
+ s, err := NewSiteForCfg(deps.DepsCfg{Cfg: cfg, Fs: fs})
require.NoError(t, err)
- _, err = s.newHomePage().Paginate(createTestPages(2))
- assert.NotNil(t, err)
+
+ _, err = s.newHomePage().Paginate(createTestPages(s, 2))
+ require.NotNil(t, err)
}
func TestPaginatePages(t *testing.T) {
- groups, _ := createTestPages(31).GroupBy("Weight", "desc")
- pathSpec := newTestPathSpec()
+ t.Parallel()
+ s := newTestSite(t)
- for i, seq := range []interface{}{createTestPages(11), groups, WeightedPages{}, PageGroup{}, &Pages{}} {
- v, err := paginatePages(pathSpec, seq, 11, "t")
- assert.NotNil(t, v, "Val %d", i)
- assert.Nil(t, err, "Err %d", i)
+ groups, _ := createTestPages(s, 31).GroupBy("Weight", "desc")
+
+ for i, seq := range []interface{}{createTestPages(s, 11), groups, WeightedPages{}, PageGroup{}, &Pages{}} {
+ v, err := paginatePages(s.PathSpec, seq, 11, "t")
+ require.NotNil(t, v, "Val %d", i)
+ require.Nil(t, err, "Err %d", i)
}
- _, err := paginatePages(pathSpec, Site{}, 11, "t")
- assert.NotNil(t, err)
+ _, err := paginatePages(s.PathSpec, Site{}, 11, "t")
+ require.NotNil(t, err)
}
// Issue #993
func TestPaginatorFollowedByPaginateShouldFail(t *testing.T) {
- testCommonResetState()
-
- viper.Set("paginate", 10)
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
+ t.Parallel()
+ s := newTestSite(t, "paginate", 10)
n1 := s.newHomePage()
n2 := s.newHomePage()
- _, err = n1.Paginator()
- assert.Nil(t, err)
- _, err = n1.Paginate(createTestPages(2))
- assert.NotNil(t, err)
+ _, err := n1.Paginator()
+ require.Nil(t, err)
+ _, err = n1.Paginate(createTestPages(s, 2))
+ require.NotNil(t, err)
- _, err = n2.Paginate(createTestPages(2))
- assert.Nil(t, err)
+ _, err = n2.Paginate(createTestPages(s, 2))
+ require.Nil(t, err)
}
func TestPaginateFollowedByDifferentPaginateShouldFail(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
+ s := newTestSite(t, "paginate", 10)
- viper.Set("paginate", 10)
- s, err := NewSiteDefaultLang()
- require.NoError(t, err)
n1 := s.newHomePage()
n2 := s.newHomePage()
- p1 := createTestPages(2)
- p2 := createTestPages(10)
+ p1 := createTestPages(s, 2)
+ p2 := createTestPages(s, 10)
- _, err = n1.Paginate(p1)
- assert.Nil(t, err)
+ _, err := n1.Paginate(p1)
+ require.Nil(t, err)
_, err = n1.Paginate(p1)
- assert.Nil(t, err)
+ require.Nil(t, err)
_, err = n1.Paginate(p2)
- assert.NotNil(t, err)
+ require.NotNil(t, err)
_, err = n2.Paginate(p2)
- assert.Nil(t, err)
+ require.Nil(t, err)
}
func TestProbablyEqualPageLists(t *testing.T) {
- fivePages := createTestPages(5)
- zeroPages := createTestPages(0)
- zeroPagesByWeight, _ := createTestPages(0).GroupBy("Weight", "asc")
- fivePagesByWeight, _ := createTestPages(5).GroupBy("Weight", "asc")
- ninePagesByWeight, _ := createTestPages(9).GroupBy("Weight", "asc")
+ t.Parallel()
+ s := newTestSite(t)
+ fivePages := createTestPages(s, 5)
+ zeroPages := createTestPages(s, 0)
+ zeroPagesByWeight, _ := createTestPages(s, 0).GroupBy("Weight", "asc")
+ fivePagesByWeight, _ := createTestPages(s, 5).GroupBy("Weight", "asc")
+ ninePagesByWeight, _ := createTestPages(s, 9).GroupBy("Weight", "asc")
for i, this := range []struct {
v1 interface{}
@@ -462,7 +469,7 @@ func TestProbablyEqualPageLists(t *testing.T) {
{"a", "b", true},
{"a", fivePages, false},
{fivePages, "a", false},
- {fivePages, createTestPages(2), false},
+ {fivePages, createTestPages(s, 2), false},
{fivePages, fivePages, true},
{zeroPages, zeroPages, true},
{fivePagesByWeight, fivePagesByWeight, true},
@@ -481,12 +488,15 @@ func TestProbablyEqualPageLists(t *testing.T) {
}
func TestPage(t *testing.T) {
+ t.Parallel()
urlFactory := func(page int) string {
return fmt.Sprintf("page/%d/", page)
}
- fivePages := createTestPages(7)
- fivePagesFuzzyWordCount, _ := createTestPages(7).GroupBy("FuzzyWordCount", "asc")
+ s := newTestSite(t)
+
+ fivePages := createTestPages(s, 7)
+ fivePagesFuzzyWordCount, _ := createTestPages(s, 7).GroupBy("FuzzyWordCount", "asc")
p1, _ := newPaginatorFromPages(fivePages, 2, urlFactory)
p2, _ := newPaginatorFromPageGroups(fivePagesFuzzyWordCount, 2, urlFactory)
@@ -500,33 +510,26 @@ func TestPage(t *testing.T) {
page21, _ := f2.page(1)
page2Nil, _ := f2.page(3)
- assert.Equal(t, 3, page11.fuzzyWordCount)
- assert.Nil(t, page1Nil)
+ require.Equal(t, 3, page11.fuzzyWordCount)
+ require.Nil(t, page1Nil)
- assert.Equal(t, 3, page21.fuzzyWordCount)
- assert.Nil(t, page2Nil)
+ require.Equal(t, 3, page21.fuzzyWordCount)
+ require.Nil(t, page2Nil)
}
-func createTestPages(num int) Pages {
+func createTestPages(s *Site, num int) Pages {
pages := make(Pages, num)
- info := newSiteInfo(siteBuilderCfg{baseURL: "http://base/", language: helpers.NewDefaultLanguage()})
for i := 0; i < num; i++ {
- pages[i] = &Page{
- pageInit: &pageInit{},
- URLPath: URLPath{
- Section: "z",
- URL: fmt.Sprintf("http://base/x/y/p%d.html", i),
- },
- Site: &info,
- Source: Source{File: *source.NewFile(filepath.FromSlash(fmt.Sprintf("/x/y/p%d.md", i)))},
- }
+ p := s.newPage(filepath.FromSlash(fmt.Sprintf("/x/y/z/p%d.md", i)))
w := 5
if i%2 == 0 {
w = 10
}
- pages[i].fuzzyWordCount = i + 2
- pages[i].Weight = w
+ p.fuzzyWordCount = i + 2
+ p.Weight = w
+ pages[i] = p
+
}
return pages
diff --git a/hugolib/path_separators_test.go b/hugolib/path_separators_test.go
index 55790c9da..bc1bcec0d 100644
--- a/hugolib/path_separators_test.go
+++ b/hugolib/path_separators_test.go
@@ -26,7 +26,9 @@ Sample Text
`
func TestDegenerateMissingFolderInPageFilename(t *testing.T) {
- p, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageYAML), filepath.Join("foobar"))
+ t.Parallel()
+ s := newTestSite(t)
+ p, err := s.NewPageFrom(strings.NewReader(simplePageYAML), filepath.Join("foobar"))
if err != nil {
t.Fatalf("Error in NewPageFrom")
}
@@ -36,6 +38,8 @@ func TestDegenerateMissingFolderInPageFilename(t *testing.T) {
}
func TestNewPageWithFilePath(t *testing.T) {
+ t.Parallel()
+ s := newTestSite(t)
toCheck := []struct {
input string
section string
@@ -48,7 +52,7 @@ func TestNewPageWithFilePath(t *testing.T) {
}
for i, el := range toCheck {
- p, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageYAML), el.input)
+ p, err := s.NewPageFrom(strings.NewReader(simplePageYAML), el.input)
if err != nil {
t.Errorf("[%d] Reading from simplePageYAML resulted in an error: %s", i, err)
}
diff --git a/hugolib/path_separators_windows_test.go b/hugolib/path_separators_windows_test.go
index 2fe07b3a4..d28589a42 100644
--- a/hugolib/path_separators_windows_test.go
+++ b/hugolib/path_separators_windows_test.go
@@ -14,8 +14,9 @@
package hugolib
import (
- "github.com/spf13/hugo/tpl"
"testing"
+
+ "github.com/spf13/hugo/tpl"
)
const (
@@ -24,6 +25,7 @@ const (
)
func TestTemplatePathSeparator(t *testing.T) {
+ t.Parallel()
tmpl := new(tpl.GoHTMLTemplate)
if name := tmpl.GenerateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
t.Fatalf("Template name incorrect. got %s but expected %s", name, "sub1/index.html")
diff --git a/hugolib/permalinks_test.go b/hugolib/permalinks_test.go
index 2622dc7c8..7a4bf78c2 100644
--- a/hugolib/permalinks_test.go
+++ b/hugolib/permalinks_test.go
@@ -16,8 +16,6 @@ package hugolib
import (
"strings"
"testing"
-
- "github.com/spf13/hugo/helpers"
)
// testdataPermalinks is used by a couple of tests; the expandsTo content is
@@ -54,6 +52,7 @@ var testdataPermalinks = []struct {
}
func TestPermalinkValidation(t *testing.T) {
+ t.Parallel()
for _, item := range testdataPermalinks {
pp := pathPattern(item.spec)
have := pp.validate()
@@ -71,9 +70,10 @@ func TestPermalinkValidation(t *testing.T) {
}
func TestPermalinkExpansion(t *testing.T) {
- page, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageJSON), "blue/test-page.md")
- info := newSiteInfo(siteBuilderCfg{language: helpers.NewDefaultLanguage()})
- page.Site = &info
+ t.Parallel()
+ s := newTestSite(t)
+ page, err := s.NewPageFrom(strings.NewReader(simplePageJSON), "blue/test-page.md")
+
if err != nil {
t.Fatalf("failed before we began, could not parse SIMPLE_PAGE_JSON: %s", err)
}
diff --git a/hugolib/robotstxt_test.go b/hugolib/robotstxt_test.go
index 1b42011ca..ace5162c8 100644
--- a/hugolib/robotstxt_test.go
+++ b/hugolib/robotstxt_test.go
@@ -17,10 +17,7 @@ import (
"path/filepath"
"testing"
- "github.com/spf13/hugo/hugofs"
-
"github.com/spf13/hugo/deps"
- "github.com/spf13/viper"
)
const robotTxtTemplate = `User-agent: Googlebot
@@ -30,18 +27,20 @@ const robotTxtTemplate = `User-agent: Googlebot
`
func TestRobotsTXTOutput(t *testing.T) {
- testCommonResetState()
-
- viper.Set("baseURL", "http://auth/bub/")
- viper.Set("enableRobotsTXT", true)
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
- fs := hugofs.NewMem()
+ cfg.Set("baseURL", "http://auth/bub/")
+ cfg.Set("enableRobotsTXT", true)
writeSource(t, fs, filepath.Join("layouts", "robots.txt"), robotTxtTemplate)
writeSourcesToSource(t, "content", fs, weightedSources...)
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
- assertFileContent(t, fs, "public/robots.txt", true, "User-agent: Googlebot")
+ th.assertFileContent(t, fs, "public/robots.txt", true, "User-agent: Googlebot")
}
diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go
index 74a59be6d..0a7f84a42 100644
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_test.go
@@ -18,31 +18,32 @@ import (
"testing"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/viper"
)
func TestRSSOutput(t *testing.T) {
- testCommonResetState()
+ t.Parallel()
+ var (
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
+ )
rssURI := "customrss.xml"
- viper.Set("baseURL", "http://auth/bub/")
- viper.Set("rssURI", rssURI)
- viper.Set("title", "RSSTest")
- fs := hugofs.NewMem()
+ cfg.Set("baseURL", "http://auth/bub/")
+ cfg.Set("rssURI", rssURI)
+ cfg.Set("title", "RSSTest")
for _, src := range weightedSources {
writeSource(t, fs, filepath.Join("content", "sect", src.Name), string(src.Content))
}
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
// Home RSS
- assertFileContent(t, fs, filepath.Join("public", rssURI), true, "<?xml", "rss version", "RSSTest")
+ th.assertFileContent(t, fs, filepath.Join("public", rssURI), true, "<?xml", "rss version", "RSSTest")
// Section RSS
- assertFileContent(t, fs, filepath.Join("public", "sect", rssURI), true, "<?xml", "rss version", "Sects on RSSTest")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", rssURI), true, "<?xml", "rss version", "Sects on RSSTest")
// Taxonomy RSS
- assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", rssURI), true, "<?xml", "rss version", "Hugo on RSSTest")
+ th.assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", rssURI), true, "<?xml", "rss version", "Hugo on RSSTest")
}
diff --git a/hugolib/scratch_test.go b/hugolib/scratch_test.go
index e7b04d3ab..f65c2ddfe 100644
--- a/hugolib/scratch_test.go
+++ b/hugolib/scratch_test.go
@@ -14,13 +14,15 @@
package hugolib
import (
- "github.com/stretchr/testify/assert"
"reflect"
"sync"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestScratchAdd(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
scratch.Add("int1", 10)
scratch.Add("int1", 20)
@@ -50,6 +52,7 @@ func TestScratchAdd(t *testing.T) {
}
func TestScratchAddSlice(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
_, err := scratch.Add("intSlice", []int{1, 2})
@@ -78,6 +81,7 @@ func TestScratchAddSlice(t *testing.T) {
}
func TestScratchSet(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
scratch.Set("key", "val")
assert.Equal(t, "val", scratch.Get("key"))
@@ -119,6 +123,7 @@ func TestScratchInParallel(t *testing.T) {
}
func TestScratchGet(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
nothing := scratch.Get("nothing")
if nothing != nil {
@@ -127,6 +132,7 @@ func TestScratchGet(t *testing.T) {
}
func TestScratchSetInMap(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
scratch.SetInMap("key", "lux", "Lux")
scratch.SetInMap("key", "abc", "Abc")
@@ -137,6 +143,7 @@ func TestScratchSetInMap(t *testing.T) {
}
func TestScratchGetSortedMapValues(t *testing.T) {
+ t.Parallel()
scratch := newScratch()
nothing := scratch.GetSortedMapValues("nothing")
if nothing != nil {
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index afee1884f..4ce6df7e9 100644
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -149,31 +149,6 @@ func (sc shortcode) String() string {
return fmt.Sprintf("%s(%q, %t){%s}", sc.name, params, sc.doMarkup, sc.inner)
}
-// HandleShortcodes does all in one go: extract, render and replace
-// only used for testing
-func HandleShortcodes(stringToParse string, page *Page) (string, error) {
- tmpContent, tmpShortcodes, err := extractAndRenderShortcodes(stringToParse, page)
-
- if err != nil {
- return "", err
- }
-
- if len(tmpShortcodes) > 0 {
- shortcodes, err := executeShortcodeFuncMap(tmpShortcodes)
- if err != nil {
- return "", err
- }
- tmpContentWithTokensReplaced, err := replaceShortcodeTokens([]byte(tmpContent), shortcodePlaceholderPrefix, shortcodes)
-
- if err != nil {
- return "", fmt.Errorf("Failed to replace shortcode tokens in %s:\n%s", page.BaseFileName(), err.Error())
- }
- return string(tmpContentWithTokensReplaced), nil
- }
-
- return tmpContent, nil
-}
-
var isInnerShortcodeCache = struct {
sync.RWMutex
m map[string]bool
@@ -239,12 +214,12 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page) string {
}
if sc.doMarkup {
- newInner := helpers.RenderBytes(&helpers.RenderingContext{
+ newInner := p.s.ContentSpec.RenderBytes(&helpers.RenderingContext{
Content: []byte(inner), PageFmt: p.determineMarkupType(),
- ConfigProvider: p.Language(),
- DocumentID: p.UniqueID(),
- DocumentName: p.Path(),
- Config: p.getRenderingConfig()})
+ Cfg: p.Language(),
+ DocumentID: p.UniqueID(),
+ DocumentName: p.Path(),
+ Config: p.getRenderingConfig()})
// If the type is “unknown” or “markdown”, we assume the markdown
// generation has been performed. Given the input: `a line`, markdown
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
index d4494dba2..b1f28d53b 100644
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -24,20 +24,22 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tplapi"
- "github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
// TODO(bep) remove
func pageFromString(in, filename string, withTemplate ...func(templ tplapi.Template) error) (*Page, error) {
- s := pageTestSite
+ s := newTestSite(nil)
if len(withTemplate) > 0 {
// Have to create a new site
var err error
- s, err = NewSiteDefaultLang(withTemplate...)
+ cfg, fs := newTestCfg()
+
+ d := deps.DepsCfg{Language: helpers.NewLanguage("en", cfg), Fs: fs, WithTemplate: withTemplate[0]}
+
+ s, err = NewSiteForCfg(d)
if err != nil {
return nil, err
}
@@ -50,9 +52,8 @@ func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func
}
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tplapi.Template) error, expectError bool) {
- testCommonResetState()
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
// Need some front matter, see https://github.com/spf13/hugo/issues/2337
contentFile := `---
@@ -62,7 +63,7 @@ title: "Title"
writeSource(t, fs, "content/simple.md", contentFile)
- h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs, WithTemplate: withTemplate})
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate})
require.NoError(t, err)
require.Len(t, h.Sites, 1)
@@ -90,43 +91,15 @@ title: "Title"
}
}
-func TestShortcodeGoFuzzReports(t *testing.T) {
-
- p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
- return templ.AddInternalShortcode("sc.html", `foo`)
- })
-
- for i, this := range []struct {
- data string
- expectErr bool
- }{
- {"{{</*/", true},
- } {
- output, err := HandleShortcodes(this.data, p)
-
- if this.expectErr && err == nil {
- t.Errorf("[%d] should have errored", i)
- }
-
- if !this.expectErr && err != nil {
- t.Errorf("[%d] should not have errored: %s", i, err)
- }
-
- if !this.expectErr && err == nil && len(output) == 0 {
- t.Errorf("[%d] empty result", i)
- }
- }
-
-}
-
func TestNonSC(t *testing.T) {
-
+ t.Parallel()
// notice the syntax diff from 0.12, now comment delims must be added
CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", nil)
}
// Issue #929
func TestHyphenatedSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
return nil
@@ -137,6 +110,7 @@ func TestHyphenatedSC(t *testing.T) {
// Issue #1753
func TestNoTrailingNewline(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
return nil
@@ -146,6 +120,7 @@ func TestNoTrailingNewline(t *testing.T) {
}
func TestPositionalParamSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
return nil
@@ -159,6 +134,7 @@ func TestPositionalParamSC(t *testing.T) {
}
func TestPositionalParamIndexOutOfBounds(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
return nil
@@ -169,6 +145,7 @@ func TestPositionalParamIndexOutOfBounds(t *testing.T) {
// some repro issues for panics in Go Fuzz testing
func TestNamedParamSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
return nil
@@ -183,6 +160,7 @@ func TestNamedParamSC(t *testing.T) {
// Issue #2294
func TestNestedNamedMissingParam(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("acc.html", `<div class="acc">{{ .Inner }}</div>`)
tem.AddInternalShortcode("div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
@@ -195,6 +173,7 @@ func TestNestedNamedMissingParam(t *testing.T) {
}
func TestIsNamedParamsSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("byposition.html", `<div id="{{ .Get 0 }}">`)
tem.AddInternalShortcode("byname.html", `<div id="{{ .Get "id" }}">`)
@@ -210,6 +189,7 @@ func TestIsNamedParamsSC(t *testing.T) {
}
func TestInnerSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
@@ -220,6 +200,7 @@ func TestInnerSC(t *testing.T) {
}
func TestInnerSCWithMarkdown(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
@@ -233,6 +214,7 @@ func TestInnerSCWithMarkdown(t *testing.T) {
}
func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
@@ -256,12 +238,14 @@ This is **plain** text.
}
func TestEmbeddedSC(t *testing.T) {
+ t.Parallel()
CheckShortCodeMatch(t, "{{% test %}}", "This is a simple Test", nil)
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" />\n \n \n</figure>\n", nil)
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"This is a caption\" />\n \n \n <figcaption>\n <p>\n This is a caption\n \n \n \n </p> \n </figcaption>\n \n</figure>\n", nil)
}
func TestNestedSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)
@@ -273,6 +257,7 @@ func TestNestedSC(t *testing.T) {
}
func TestNestedComplexSC(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`)
tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`)
@@ -288,6 +273,7 @@ func TestNestedComplexSC(t *testing.T) {
}
func TestParentShortcode(t *testing.T) {
+ t.Parallel()
wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
tem.AddInternalShortcode("r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
@@ -300,43 +286,14 @@ func TestParentShortcode(t *testing.T) {
}
func TestFigureImgWidth(t *testing.T) {
+ t.Parallel()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"apple\" width=\"100px\" />\n \n \n</figure>\n", nil)
}
-func TestHighlight(t *testing.T) {
- testCommonResetState()
-
- if !helpers.HasPygments() {
- t.Skip("Skip test as Pygments is not installed")
- }
- viper.Set("pygmentsStyle", "bw")
- viper.Set("pygmentsUseClasses", false)
-
- code := `
-{{< highlight java >}}
-void do();
-{{< /highlight >}}`
-
- p, _ := pageFromString(simplePage, "simple.md")
- output, err := HandleShortcodes(code, p)
-
- if err != nil {
- t.Fatal("Handle shortcode error", err)
- }
- matched, err := regexp.MatchString("(?s)^\n<div class=\"highlight\" style=\"background: #ffffff\"><pre style=\"line-height: 125%\">.*?void</span> do().*?</pre></div>\n$", output)
-
- if err != nil {
- t.Fatal("Regexp error", err)
- }
-
- if !matched {
- t.Errorf("Hightlight mismatch, got (escaped to see invisible chars)\n%+q", output)
- }
-}
-
const testScPlaceholderRegexp = "HAHAHUGOSHORTCODE-\\d+HBHB"
func TestExtractShortcodes(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
name string
input string
@@ -455,17 +412,8 @@ func TestExtractShortcodes(t *testing.T) {
}
func TestShortcodesInSite(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
baseURL := "http://foo/bar"
- viper.Set("defaultExtension", "html")
- viper.Set("defaultContentLanguage", "en")
- viper.Set("baseURL", baseURL)
- viper.Set("uglyURLs", false)
- viper.Set("verbose", true)
-
- viper.Set("pygmentsUseClasses", true)
- viper.Set("pygmentsCodefences", true)
tests := []struct {
contentPath string
@@ -579,11 +527,21 @@ tags:
}
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("defaultContentLanguage", "en")
+ cfg.Set("baseURL", baseURL)
+ cfg.Set("uglyURLs", false)
+ cfg.Set("verbose", true)
+
+ cfg.Set("pygmentsUseClasses", true)
+ cfg.Set("pygmentsCodefences", true)
writeSourcesToSource(t, "content", fs, sources...)
- buildSingleSite(t, deps.DepsCfg{WithTemplate: addTemplates, Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{WithTemplate: addTemplates, Fs: fs, Cfg: cfg}, BuildCfg{})
+ th := testHelper{s.Cfg}
for _, test := range tests {
if strings.HasSuffix(test.contentPath, ".ad") && !helpers.HasAsciidoc() {
@@ -597,7 +555,7 @@ tags:
continue
}
- assertFileContent(t, fs, test.outFile, true, test.expected)
+ th.assertFileContent(t, fs, test.outFile, true, test.expected)
}
}
@@ -665,6 +623,7 @@ func BenchmarkReplaceShortcodeTokens(b *testing.B) {
}
func TestReplaceShortcodeTokens(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
input string
prefix string
diff --git a/hugolib/shortcodeparser_test.go b/hugolib/shortcodeparser_test.go
index 662ce0f64..3103fd4de 100644
--- a/hugolib/shortcodeparser_test.go
+++ b/hugolib/shortcodeparser_test.go
@@ -152,6 +152,7 @@ var shortCodeLexerTests = []shortCodeLexerTest{
}
func TestShortcodeLexer(t *testing.T) {
+ t.Parallel()
for i, test := range shortCodeLexerTests {
items := collect(&test)
if !equal(items, test.items) {
diff --git a/hugolib/site.go b/hugolib/site.go
index 76fcb3c00..bd0156849 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -37,11 +37,9 @@ import (
bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/parser"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
- "github.com/spf13/hugo/tpl"
"github.com/spf13/hugo/tplapi"
"github.com/spf13/hugo/transform"
"github.com/spf13/nitro"
@@ -113,7 +111,7 @@ type Site struct {
// reset returns a new Site prepared for rebuild.
func (s *Site) reset() *Site {
- return &Site{Deps: s.Deps, owner: s.owner, PageCollections: newPageCollections()}
+ return &Site{Deps: s.Deps, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
}
// newSite creates a new site with the given configuration.
@@ -121,7 +119,7 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
c := newPageCollections()
if cfg.Language == nil {
- cfg.Language = helpers.NewDefaultLanguage()
+ cfg.Language = helpers.NewDefaultLanguage(cfg.Cfg)
}
s := &Site{PageCollections: c, Language: cfg.Language}
@@ -148,37 +146,22 @@ func NewSite(cfg deps.DepsCfg) (*Site, error) {
return s, nil
}
-// TODO(bep) globals clean below...
// NewSiteDefaultLang creates a new site in the default language.
// The site will have a template system loaded and ready to use.
// Note: This is mainly used in single site tests.
func NewSiteDefaultLang(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
- return newSiteForLang(helpers.NewDefaultLanguage(), withTemplate...)
+ v := viper.New()
+ loadDefaultSettingsFor(v)
+ return newSiteForLang(helpers.NewDefaultLanguage(v), withTemplate...)
}
// NewEnglishSite creates a new site in English language.
// The site will have a template system loaded and ready to use.
// Note: This is mainly used in single site tests.
func NewEnglishSite(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
- return newSiteForLang(helpers.NewLanguage("en"), withTemplate...)
-}
-
-// NewEnglishSite creates a new site in the English language with in-memory Fs.
-// The site will have a template system loaded and ready to use.
-// Note: This is mainly used in single site tests.
-func NewEnglishSiteMem(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
- withTemplates := func(templ tplapi.Template) error {
- for _, wt := range withTemplate {
- if err := wt(templ); err != nil {
- return err
- }
- }
- return nil
- }
-
- cfg := deps.DepsCfg{WithTemplate: withTemplates, Language: helpers.NewLanguage("en"), Fs: hugofs.NewMem()}
-
- return newSiteForCfg(cfg)
+ v := viper.New()
+ loadDefaultSettingsFor(v)
+ return newSiteForLang(helpers.NewLanguage("en", v), withTemplate...)
}
// newSiteForLang creates a new site in the given language.
@@ -191,13 +174,17 @@ func newSiteForLang(lang *helpers.Language, withTemplate ...func(templ tplapi.Te
}
return nil
}
+
cfg := deps.DepsCfg{WithTemplate: withTemplates, Language: lang}
- return newSiteForCfg(cfg)
+ return NewSiteForCfg(cfg)
}
-func newSiteForCfg(cfg deps.DepsCfg) (*Site, error) {
+// NewSiteForCfg creates a new site for the given configuration.
+// The site will have a template system loaded and ready to use.
+// Note: This is mainly used in single site tests.
+func NewSiteForCfg(cfg deps.DepsCfg) (*Site, error) {
s, err := newSite(cfg)
if err != nil {
@@ -269,21 +256,20 @@ func (s *SiteInfo) String() string {
// Used in tests.
type siteBuilderCfg struct {
- language *helpers.Language
- // TOD(bep) globals fs
+ language *helpers.Language
s *Site
- fs *hugofs.Fs
pageCollections *PageCollections
baseURL string
}
-// TODO(bep) globals get rid of this
+// TODO(bep) get rid of this
func newSiteInfo(cfg siteBuilderCfg) SiteInfo {
return SiteInfo{
s: cfg.s,
BaseURL: template.URL(cfg.baseURL),
multilingual: newMultiLingualForLanguage(cfg.language),
PageCollections: cfg.pageCollections,
+ Params: make(map[string]interface{}),
}
}
@@ -586,12 +572,12 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
}
}
- if len(tmplChanged) > 0 {
+ if len(tmplChanged) > 0 || len(i18nChanged) > 0 {
sites := s.owner.Sites
first := sites[0]
// TOD(bep) globals clean
- if err := first.Deps.LoadTemplates(); err != nil {
+ if err := first.Deps.LoadResources(); err != nil {
s.Log.ERROR.Println(err)
}
@@ -613,12 +599,6 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
s.readDataFromSourceFS()
}
- if len(i18nChanged) > 0 {
- if err := s.readI18nSources(); err != nil {
- s.Log.ERROR.Println(err)
- }
- }
-
// If a content file changes, we need to reload only it and re-render the entire site.
// First step is to read the changed files and (re)place them in site.AllPages
@@ -648,7 +628,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
wg2.Add(4)
for i := 0; i < 2; i++ {
go fileConverter(s, fileConvChan, convertResults, wg2)
- go pageConverter(s, pageChan, convertResults, wg2)
+ go pageConverter(pageChan, convertResults, wg2)
}
for _, ev := range sourceChanged {
@@ -732,7 +712,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
}
func (s *Site) loadData(sources []source.Input) (err error) {
- s.Log.DEBUG.Printf("Load Data from %q", sources)
+ s.Log.DEBUG.Printf("Load Data from %d source(s)", len(sources))
s.Data = make(map[string]interface{})
var current map[string]interface{}
for _, currentSource := range sources {
@@ -790,35 +770,19 @@ func (s *Site) readData(f *source.File) (interface{}, error) {
case "toml":
return parser.HandleTOMLMetaData(f.Bytes())
default:
- s.Log.WARN.Printf("Data not supported for extension '%s'", f.Extension())
- return nil, nil
- }
-}
-
-func (s *Site) readI18nSources() error {
-
- i18nSources := []source.Input{source.NewFilesystem(s.Fs, s.absI18nDir())}
-
- themeI18nDir, err := s.PathSpec.GetThemeI18nDirPath()
- if err == nil {
- i18nSources = []source.Input{source.NewFilesystem(s.Fs, themeI18nDir), i18nSources[0]}
+ return nil, fmt.Errorf("Data not supported for extension '%s'", f.Extension())
}
-
- if err = s.loadI18n(i18nSources); err != nil {
- return err
- }
-
- return nil
}
func (s *Site) readDataFromSourceFS() error {
+ sp := source.NewSourceSpec(s.Cfg, s.Fs)
dataSources := make([]source.Input, 0, 2)
- dataSources = append(dataSources, source.NewFilesystem(s.Fs, s.absDataDir()))
+ dataSources = append(dataSources, sp.NewFilesystem(s.absDataDir()))
// have to be last - duplicate keys in earlier entries will win
themeDataDir, err := s.PathSpec.GetThemeDataDirPath()
if err == nil {
- dataSources = append(dataSources, source.NewFilesystem(s.Fs, themeDataDir))
+ dataSources = append(dataSources, sp.NewFilesystem(themeDataDir))
}
err = s.loadData(dataSources)
@@ -837,10 +801,6 @@ func (s *Site) process(config BuildCfg) (err error) {
return
}
- if err = s.readI18nSources(); err != nil {
- return
- }
-
s.timerStep("load i18n")
return s.createPages()
@@ -858,20 +818,7 @@ func (s *Site) setupPrevNext() {
}
}
-func (s *Site) setCurrentLanguageConfig() error {
- // There are sadly some global template funcs etc. that need the language information.
- viper.Set("multilingual", s.multilingualEnabled())
- viper.Set("currentContentLanguage", s.Language)
- // Cache the current config.
- helpers.InitConfigProviderForCurrentContentLanguage()
- return tpl.SetTranslateLang(s.Language)
-}
-
func (s *Site) render() (err error) {
- if err = s.setCurrentLanguageConfig(); err != nil {
- return
- }
-
if err = s.preparePages(); err != nil {
return
}
@@ -927,9 +874,10 @@ func (s *Site) initialize() (err error) {
return err
}
- staticDir := helpers.AbsPathify(viper.GetString("staticDir") + "/")
+ staticDir := s.PathSpec.AbsPathify(s.Cfg.GetString("staticDir") + "/")
- s.Source = source.NewFilesystem(s.Fs, s.absContentDir(), staticDir)
+ sp := source.NewSourceSpec(s.Cfg, s.Fs)
+ s.Source = sp.NewFilesystem(s.absContentDir(), staticDir)
return
}
@@ -945,7 +893,7 @@ func (s *SiteInfo) HomeAbsURL() string {
// SitemapAbsURL is a convenience method giving the absolute URL to the sitemap.
func (s *SiteInfo) SitemapAbsURL() string {
- sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
+ sitemapDefault := parseSitemap(s.s.Cfg.GetStringMap("sitemap"))
p := s.HomeAbsURL()
if !strings.HasSuffix(p, "/") {
p += "/"
@@ -967,12 +915,12 @@ func (s *Site) initializeSiteInfo() {
params := lang.Params()
permalinks := make(PermalinkOverrides)
- for k, v := range viper.GetStringMapString("permalinks") {
+ for k, v := range s.Cfg.GetStringMapString("permalinks") {
permalinks[k] = pathPattern(v)
}
- defaultContentInSubDir := viper.GetBool("defaultContentLanguageInSubdir")
- defaultContentLanguage := viper.GetString("defaultContentLanguage")
+ defaultContentInSubDir := s.Cfg.GetBool("defaultContentLanguageInSubdir")
+ defaultContentLanguage := s.Cfg.GetString("defaultContentLanguage")
languagePrefix := ""
if s.multilingualEnabled() && (defaultContentInSubDir || lang.Lang != defaultContentLanguage) {
@@ -985,7 +933,7 @@ func (s *Site) initializeSiteInfo() {
}
s.Info = SiteInfo{
- BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("baseURL"))),
+ BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(s.Cfg.GetString("baseURL"))),
Title: lang.GetString("title"),
Author: lang.GetStringMap("author"),
Social: lang.GetStringMapString("social"),
@@ -999,8 +947,8 @@ func (s *Site) initializeSiteInfo() {
defaultContentLanguageInSubdir: defaultContentInSubDir,
sectionPagesMenu: lang.GetString("sectionPagesMenu"),
GoogleAnalytics: lang.GetString("googleAnalytics"),
- BuildDrafts: viper.GetBool("buildDrafts"),
- canonifyURLs: viper.GetBool("canonifyURLs"),
+ BuildDrafts: s.Cfg.GetBool("buildDrafts"),
+ canonifyURLs: s.Cfg.GetBool("canonifyURLs"),
preserveTaxonomyNames: lang.GetBool("preserveTaxonomyNames"),
PageCollections: s.PageCollections,
Files: &s.Files,
@@ -1016,22 +964,22 @@ func (s *Site) initializeSiteInfo() {
}
func (s *Site) hasTheme() bool {
- return viper.GetString("theme") != ""
+ return s.Cfg.GetString("theme") != ""
}
func (s *Site) dataDir() string {
- return viper.GetString("dataDir")
+ return s.Cfg.GetString("dataDir")
}
func (s *Site) absDataDir() string {
- return helpers.AbsPathify(s.dataDir())
+ return s.PathSpec.AbsPathify(s.dataDir())
}
func (s *Site) i18nDir() string {
- return viper.GetString("i18nDir")
+ return s.Cfg.GetString("i18nDir")
}
func (s *Site) absI18nDir() string {
- return helpers.AbsPathify(s.i18nDir())
+ return s.PathSpec.AbsPathify(s.i18nDir())
}
func (s *Site) isI18nEvent(e fsnotify.Event) bool {
@@ -1049,7 +997,7 @@ func (s *Site) getThemeI18nDir(path string) string {
if !s.hasTheme() {
return ""
}
- return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.i18nDir())), path)
+ return s.getRealDir(s.PathSpec.AbsPathify(filepath.Join(s.themeDir(), s.i18nDir())), path)
}
func (s *Site) isDataDirEvent(e fsnotify.Event) bool {
@@ -1067,23 +1015,23 @@ func (s *Site) getThemeDataDir(path string) string {
if !s.hasTheme() {
return ""
}
- return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.dataDir())), path)
+ return s.getRealDir(s.PathSpec.AbsPathify(filepath.Join(s.themeDir(), s.dataDir())), path)
}
func (s *Site) themeDir() string {
- return viper.GetString("themesDir") + "/" + viper.GetString("theme")
+ return s.Cfg.GetString("themesDir") + "/" + s.Cfg.GetString("theme")
}
func (s *Site) absThemeDir() string {
- return helpers.AbsPathify(s.themeDir())
+ return s.PathSpec.AbsPathify(s.themeDir())
}
func (s *Site) layoutDir() string {
- return viper.GetString("layoutDir")
+ return s.Cfg.GetString("layoutDir")
}
func (s *Site) absLayoutDir() string {
- return helpers.AbsPathify(s.layoutDir())
+ return s.PathSpec.AbsPathify(s.layoutDir())
}
func (s *Site) isLayoutDirEvent(e fsnotify.Event) bool {
@@ -1101,11 +1049,11 @@ func (s *Site) getThemeLayoutDir(path string) string {
if !s.hasTheme() {
return ""
}
- return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.layoutDir())), path)
+ return s.getRealDir(s.PathSpec.AbsPathify(filepath.Join(s.themeDir(), s.layoutDir())), path)
}
func (s *Site) absContentDir() string {
- return helpers.AbsPathify(viper.GetString("contentDir"))
+ return s.PathSpec.AbsPathify(s.Cfg.GetString("contentDir"))
}
func (s *Site) isContentDirEvent(e fsnotify.Event) bool {
@@ -1141,7 +1089,7 @@ func (s *Site) getRealDir(base, path string) string {
}
func (s *Site) absPublishDir() string {
- return helpers.AbsPathify(viper.GetString("publishDir"))
+ return s.PathSpec.AbsPathify(s.Cfg.GetString("publishDir"))
}
func (s *Site) checkDirectories() (err error) {
@@ -1160,7 +1108,9 @@ func (s *Site) reReadFile(absFilePath string) (*source.File, error) {
if err != nil {
return nil, err
}
- file, err = source.NewFileFromAbs(s.getContentDir(absFilePath), absFilePath, reader)
+
+ sp := source.NewSourceSpec(s.Cfg, s.Fs)
+ file, err = sp.NewFileFromAbs(s.getContentDir(absFilePath), absFilePath, reader)
if err != nil {
return nil, err
@@ -1219,7 +1169,7 @@ func (s *Site) convertSource() chan error {
wg.Add(2 * procs * 4)
for i := 0; i < procs*4; i++ {
go fileConverter(s, fileConvChan, results, wg)
- go pageConverter(s, pageChan, results, wg)
+ go pageConverter(pageChan, results, wg)
}
go converterCollator(s, results, errs)
@@ -1278,7 +1228,7 @@ func readSourceFile(s *Site, file *source.File, results chan<- HandledResult) {
}
}
-func pageConverter(s *Site, pages <-chan *Page, results HandleResults, wg *sync.WaitGroup) {
+func pageConverter(pages <-chan *Page, results HandleResults, wg *sync.WaitGroup) {
defer wg.Done()
for page := range pages {
var h *MetaHandle
@@ -1288,7 +1238,10 @@ func pageConverter(s *Site, pages <-chan *Page, results HandleResults, wg *sync.
h = NewMetaHandler(page.File.Extension())
}
if h != nil {
- h.Convert(page, s, results)
+ // Note that we convert pages from the site's rawAllPages collection
+ // Which may contain pages from multiple sites, so we use the Page's site
+ // for the conversion.
+ h.Convert(page, page.s, results)
}
}
}
@@ -1478,7 +1431,6 @@ func (s *Site) assembleMenus() {
//creating flat hash
pages := s.Pages
for _, p := range pages {
-
if sectionPagesMenu != "" {
if _, ok := sectionPagesMenus[p.Section()]; !ok {
if p.Section() != "" {
@@ -1750,7 +1702,7 @@ func (s *SiteInfo) permalink(plink string) string {
func (s *SiteInfo) permalinkStr(plink string) string {
return helpers.MakePermalink(
- viper.GetString("baseURL"),
+ s.s.Cfg.GetString("baseURL"),
s.s.PathSpec.URLizeAndPrep(plink)).String()
}
@@ -1770,10 +1722,10 @@ func (s *Site) renderAndWriteXML(name string, dest string, d interface{}, layout
defer bp.PutBuffer(outBuffer)
var path []byte
- if viper.GetBool("relativeURLs") {
+ if s.Cfg.GetBool("relativeURLs") {
path = []byte(helpers.GetDottedRelativePath(dest))
} else {
- s := viper.GetString("baseURL")
+ s := s.Cfg.GetString("baseURL")
if !strings.HasSuffix(s, "/") {
s += "/"
}
@@ -1811,31 +1763,31 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
transformLinks := transform.NewEmptyTransforms()
- if viper.GetBool("relativeURLs") || viper.GetBool("canonifyURLs") {
+ if s.Cfg.GetBool("relativeURLs") || s.Cfg.GetBool("canonifyURLs") {
transformLinks = append(transformLinks, transform.AbsURL)
}
- if s.running() && viper.GetBool("watch") && !viper.GetBool("disableLiveReload") {
- transformLinks = append(transformLinks, transform.LiveReloadInject)
+ if s.running() && s.Cfg.GetBool("watch") && !s.Cfg.GetBool("disableLiveReload") {
+ transformLinks = append(transformLinks, transform.LiveReloadInject(s.Cfg.GetInt("port")))
}
// For performance reasons we only inject the Hugo generator tag on the home page.
if n, ok := d.(*Page); ok && n.IsHome() {
- if !viper.GetBool("disableHugoGeneratorInject") {
+ if !s.Cfg.GetBool("disableHugoGeneratorInject") {
transformLinks = append(transformLinks, transform.HugoGeneratorInject)
}
}
var path []byte
- if viper.GetBool("relativeURLs") {
+ if s.Cfg.GetBool("relativeURLs") {
translated, err := pageTarget.(target.OptionalTranslator).TranslateRelative(dest)
if err != nil {
return err
}
path = []byte(helpers.GetDottedRelativePath(translated))
- } else if viper.GetBool("canonifyURLs") {
- s := viper.GetString("baseURL")
+ } else if s.Cfg.GetBool("canonifyURLs") {
+ s := s.Cfg.GetString("baseURL")
if !strings.HasSuffix(s, "/") {
s += "/"
}
@@ -1850,7 +1802,7 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
s.Log.WARN.Printf("%s is rendered empty\n", dest)
if dest == "/" {
debugAddend := ""
- if !viper.GetBool("verbose") {
+ if !s.Cfg.GetBool("verbose") {
debugAddend = "* For more debugging information, run \"hugo -v\""
}
distinctFeedbackLogger.Printf(`=============================================================
@@ -1860,7 +1812,7 @@ Your rendered home page is blank: /index.html is zero-length
%s
=============================================================`,
filepath.Base(viper.ConfigFileUsed()),
- viper.GetString("theme"),
+ s.Cfg.GetString("theme"),
debugAddend)
}
@@ -1956,7 +1908,7 @@ func (s *Site) initTargetList() {
s.targets.page = &target.PagePub{
Fs: s.Fs,
PublishDir: s.absPublishDir(),
- UglyURLs: viper.GetBool("uglyURLs"),
+ UglyURLs: s.Cfg.GetBool("uglyURLs"),
LangDir: langDir,
}
}
@@ -2007,9 +1959,9 @@ func (s *Site) writeDestAlias(path, permalink string, p *Page) (err error) {
}
func (s *Site) publishDestAlias(aliasPublisher target.AliasPublisher, path, permalink string, p *Page) (err error) {
- if viper.GetBool("relativeURLs") {
+ if s.Cfg.GetBool("relativeURLs") {
// convert `permalink` into URI relative to location of `path`
- baseURL := helpers.SanitizeURLKeepTrailingSlash(viper.GetString("baseURL"))
+ baseURL := helpers.SanitizeURLKeepTrailingSlash(s.Cfg.GetString("baseURL"))
if strings.HasPrefix(permalink, baseURL) {
permalink = "/" + strings.TrimPrefix(permalink, baseURL)
}
@@ -2035,7 +1987,7 @@ func (s *Site) draftStats() string {
msg = fmt.Sprintf("%d drafts rendered", s.draftCount)
}
- if viper.GetBool("buildDrafts") {
+ if s.Cfg.GetBool("buildDrafts") {
return fmt.Sprintf("%d of ", s.draftCount) + msg
}
@@ -2054,7 +2006,7 @@ func (s *Site) futureStats() string {
msg = fmt.Sprintf("%d futures rendered", s.futureCount)
}
- if viper.GetBool("buildFuture") {
+ if s.Cfg.GetBool("buildFuture") {
return fmt.Sprintf("%d of ", s.futureCount) + msg
}
@@ -2073,7 +2025,7 @@ func (s *Site) expiredStats() string {
msg = fmt.Sprintf("%d expired rendered", s.expiredCount)
}
- if viper.GetBool("buildExpired") {
+ if s.Cfg.GetBool("buildExpired") {
return fmt.Sprintf("%d of ", s.expiredCount) + msg
}
@@ -2091,11 +2043,11 @@ func getGoMaxProcs() int {
func (s *Site) newNodePage(typ string) *Page {
return &Page{
+ language: s.Language,
pageInit: &pageInit{},
Kind: typ,
Data: make(map[string]interface{}),
Site: &s.Info,
- language: s.Language,
s: s}
}
@@ -2148,7 +2100,7 @@ func (s *Site) newSectionPage(name string, section WeightedPages) *Page {
}
sectionName = helpers.FirstUpper(sectionName)
- if viper.GetBool("pluralizeListTitles") {
+ if s.Cfg.GetBool("pluralizeListTitles") {
p.Title = inflect.Pluralize(sectionName)
} else {
p.Title = sectionName
diff --git a/hugolib/siteJSONEncode_test.go b/hugolib/siteJSONEncode_test.go
index 1218bfd34..4b01a9144 100644
--- a/hugolib/siteJSONEncode_test.go
+++ b/hugolib/siteJSONEncode_test.go
@@ -20,15 +20,14 @@ import (
"path/filepath"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
)
// Issue #1123
// Testing prevention of cyclic refs in JSON encoding
// May be smart to run with: -timeout 4000ms
func TestEncodePage(t *testing.T) {
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
// borrowed from menu_test.go
for _, src := range menuPageSources {
@@ -36,7 +35,7 @@ func TestEncodePage(t *testing.T) {
}
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
_, err := json.Marshal(s)
check(t, err)
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index 84df78c1c..fa398aa04 100644
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -21,8 +21,6 @@ import (
"time"
bp "github.com/spf13/hugo/bufferpool"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
)
// renderPages renders pages each corresponding to a markdown file.
@@ -65,6 +63,7 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
defer wg.Done()
for p := range pages {
targetPath := p.TargetPath()
+
layouts := p.layouts()
s.Log.DEBUG.Printf("Render %s to %q with layouts %q", p.Kind, targetPath, layouts)
@@ -89,12 +88,12 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
func (s *Site) renderPaginator(p *Page) error {
if p.paginator != nil {
s.Log.DEBUG.Printf("Render paginator for page %q", p.Path())
- paginatePath := helpers.Config().GetString("paginatePath")
+ paginatePath := s.Cfg.GetString("paginatePath")
// write alias for page 1
// TODO(bep) ml all of these n.addLang ... fix.
- aliasPath := p.addLangPathPrefix(helpers.PaginateAliasPath(path.Join(p.sections...), 1))
+ aliasPath := p.addLangPathPrefix(s.PathSpec.PaginateAliasPath(path.Join(p.sections...), 1))
link := p.Permalink()
s.writeDestAlias(aliasPath, link, nil)
@@ -131,7 +130,7 @@ func (s *Site) renderPaginator(p *Page) error {
func (s *Site) renderRSS(p *Page) error {
- if viper.GetBool("disableRSS") {
+ if s.Cfg.GetBool("disableRSS") {
return nil
}
@@ -168,7 +167,7 @@ func (s *Site) renderRSS(p *Page) error {
}
func (s *Site) render404() error {
- if viper.GetBool("disable404") {
+ if s.Cfg.GetBool("disable404") {
return nil
}
@@ -185,11 +184,11 @@ func (s *Site) render404() error {
}
func (s *Site) renderSitemap() error {
- if viper.GetBool("disableSitemap") {
+ if s.Cfg.GetBool("disableSitemap") {
return nil
}
- sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
+ sitemapDefault := parseSitemap(s.Cfg.GetStringMap("sitemap"))
n := s.newNodePage(kindSitemap)
@@ -228,7 +227,7 @@ func (s *Site) renderSitemap() error {
}
func (s *Site) renderRobotsTXT() error {
- if !viper.GetBool("enableRobotsTXT") {
+ if !s.Cfg.GetBool("enableRobotsTXT") {
return nil
}
@@ -265,9 +264,9 @@ func (s *Site) renderAliases() error {
}
if s.owner.multilingual.enabled() {
- mainLang := s.owner.multilingual.DefaultLang.Lang
+ mainLang := s.owner.multilingual.DefaultLang
if s.Info.defaultContentLanguageInSubdir {
- mainLangURL := s.PathSpec.AbsURL(mainLang, false)
+ mainLangURL := s.PathSpec.AbsURL(mainLang.Lang, false)
s.Log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
return err
@@ -275,7 +274,7 @@ func (s *Site) renderAliases() error {
} else {
mainLangURL := s.PathSpec.AbsURL("", false)
s.Log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
- if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
+ if err := s.publishDestAlias(s.languageAliasTarget(), mainLang.Lang, mainLangURL, nil); err != nil {
return err
}
}
diff --git a/hugolib/site_test.go b/hugolib/site_test.go
index ddf9da5b5..8418e2a8c 100644
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -27,7 +27,6 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/hugofs"
- "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -54,12 +53,12 @@ func pageMust(p *Page, err error) *Page {
}
func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "a", "file.md"), pageSimpleTitle)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
require.Len(t, s.RegularPages, 1)
@@ -72,18 +71,18 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
}
func TestRenderWithInvalidTemplate(t *testing.T) {
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "foo.md"), "foo")
withTemplate := createWithTemplateFromNameValues("missing", templateMissingFunc)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs, WithTemplate: withTemplate}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{})
errCount := s.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
- // TODO(bep) globals clean up the template error handling
+ // TODO(bep) clean up the template error handling
// The template errors are stored in a slice etc. so we get 4 log entries
// When we should get only 1
if errCount == 0 {
@@ -92,8 +91,7 @@ func TestRenderWithInvalidTemplate(t *testing.T) {
}
func TestDraftAndFutureRender(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.md"), Content: []byte("---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*")},
{Name: filepath.FromSlash("sect/doc2.md"), Content: []byte("---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*")},
@@ -101,19 +99,23 @@ func TestDraftAndFutureRender(t *testing.T) {
{Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc4\ndraft: false\npublishdate: \"2012-05-29\"\n---\n# doc4\n*some content*")},
}
- siteSetup := func(t *testing.T) *Site {
- fs := hugofs.NewMem()
+ siteSetup := func(t *testing.T, configKeyValues ...interface{}) *Site {
+ cfg, fs := newTestCfg()
+
+ cfg.Set("baseURL", "http://auth/bub")
+
+ for i := 0; i < len(configKeyValues); i += 2 {
+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
- return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
}
- viper.Set("baseURL", "http://auth/bub")
-
// Testing Defaults.. Only draft:true and publishDate in the past should be rendered
s := siteSetup(t)
if len(s.RegularPages) != 1 {
@@ -121,54 +123,50 @@ func TestDraftAndFutureRender(t *testing.T) {
}
// only publishDate in the past should be rendered
- viper.Set("buildDrafts", true)
- s = siteSetup(t)
+ s = siteSetup(t, "buildDrafts", true)
if len(s.RegularPages) != 2 {
t.Fatal("Future Dated Posts published unexpectedly")
}
// drafts should not be rendered, but all dates should
- viper.Set("buildDrafts", false)
- viper.Set("buildFuture", true)
- s = siteSetup(t)
+ s = siteSetup(t,
+ "buildDrafts", false,
+ "buildFuture", true)
+
if len(s.RegularPages) != 2 {
t.Fatal("Draft posts published unexpectedly")
}
// all 4 should be included
- viper.Set("buildDrafts", true)
- viper.Set("buildFuture", true)
- s = siteSetup(t)
+ s = siteSetup(t,
+ "buildDrafts", true,
+ "buildFuture", true)
+
if len(s.RegularPages) != 4 {
t.Fatal("Drafts or Future posts not included as expected")
}
- //setting defaults back
- viper.Set("buildDrafts", false)
- viper.Set("buildFuture", false)
}
func TestFutureExpirationRender(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc3.md"), Content: []byte("---\ntitle: doc1\nexpirydate: \"2400-05-29\"\n---\n# doc1\n*some content*")},
{Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc2\nexpirydate: \"2000-05-29\"\n---\n# doc2\n*some content*")},
}
siteSetup := func(t *testing.T) *Site {
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
+ cfg.Set("baseURL", "http://auth/bub")
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
- return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
}
- viper.Set("baseURL", "http://auth/bub")
-
s := siteSetup(t)
if len(s.AllPages) != 1 {
@@ -188,6 +186,7 @@ func TestFutureExpirationRender(t *testing.T) {
// Issue #957
func TestCrossrefs(t *testing.T) {
+ t.Parallel()
for _, uglyURLs := range []bool{true, false} {
for _, relative := range []bool{true, false} {
doTestCrossrefs(t, relative, uglyURLs)
@@ -196,13 +195,8 @@ func TestCrossrefs(t *testing.T) {
}
func doTestCrossrefs(t *testing.T, relative, uglyURLs bool) {
- testCommonResetState()
baseURL := "http://foo/bar"
- viper.Set("defaultExtension", "html")
- viper.Set("baseURL", baseURL)
- viper.Set("uglyURLs", uglyURLs)
- viper.Set("verbose", true)
var refShortcode string
var expectedBase string
@@ -246,7 +240,12 @@ THE END.`, refShortcode)),
},
}
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("baseURL", baseURL)
+ cfg.Set("uglyURLs", uglyURLs)
+ cfg.Set("verbose", true)
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
@@ -256,6 +255,7 @@ THE END.`, refShortcode)),
t,
deps.DepsCfg{
Fs: fs,
+ Cfg: cfg,
WithTemplate: createWithTemplateFromNameValues("_default/single.html", "{{.Content}}")},
BuildCfg{})
@@ -263,6 +263,8 @@ THE END.`, refShortcode)),
t.Fatalf("Expected 3 got %d pages", len(s.AllPages))
}
+ th := testHelper{s.Cfg}
+
tests := []struct {
doc string
expected string
@@ -273,7 +275,7 @@ THE END.`, refShortcode)),
}
for _, test := range tests {
- assertFileContent(t, fs, test.doc, true, test.expected)
+ th.assertFileContent(t, fs, test.doc, true, test.expected)
}
@@ -282,33 +284,33 @@ THE END.`, refShortcode)),
// Issue #939
// Issue #1923
func TestShouldAlwaysHaveUglyURLs(t *testing.T) {
+ t.Parallel()
for _, uglyURLs := range []bool{true, false} {
doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
}
}
func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
- testCommonResetState()
-
- viper.Set("defaultExtension", "html")
- viper.Set("verbose", true)
- viper.Set("baseURL", "http://auth/bub")
- viper.Set("disableSitemap", false)
- viper.Set("disableRSS", false)
- viper.Set("rssURI", "index.xml")
- viper.Set("blackfriday",
+
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("verbose", true)
+ cfg.Set("baseURL", "http://auth/bub")
+ cfg.Set("disableSitemap", false)
+ cfg.Set("disableRSS", false)
+ cfg.Set("rssURI", "index.xml")
+ cfg.Set("blackfriday",
map[string]interface{}{
"plainIDAnchors": true})
- viper.Set("uglyURLs", uglyURLs)
+ cfg.Set("uglyURLs", uglyURLs)
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.md"), Content: []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
{Name: filepath.FromSlash("sect/doc2.md"), Content: []byte("---\nurl: /ugly.html\nmarkup: markdown\n---\n# title\ndoc2 *content*")},
}
- fs := hugofs.NewMem()
-
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
@@ -319,7 +321,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
writeSource(t, fs, filepath.Join("layouts", "rss.xml"), "<root>RSS</root>")
writeSource(t, fs, filepath.Join("layouts", "sitemap.xml"), "<root>SITEMAP</root>")
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
var expectedPagePath string
if uglyURLs {
@@ -356,6 +358,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
}
func TestNewSiteDefaultLang(t *testing.T) {
+ t.Parallel()
s, err := NewSiteDefaultLang()
require.NoError(t, err)
require.Equal(t, hugofs.Os, s.Fs.Source)
@@ -364,7 +367,7 @@ func TestNewSiteDefaultLang(t *testing.T) {
// Issue #1176
func TestSectionNaming(t *testing.T) {
-
+ t.Parallel()
for _, canonify := range []bool{true, false} {
for _, uglify := range []bool{true, false} {
for _, pluralize := range []bool{true, false} {
@@ -375,13 +378,6 @@ func TestSectionNaming(t *testing.T) {
}
func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
- testCommonResetState()
-
- viper.Set("baseURL", "http://auth/sub/")
- viper.Set("defaultExtension", "html")
- viper.Set("uglyURLs", uglify)
- viper.Set("pluralizeListTitles", pluralize)
- viper.Set("canonifyURLs", canonify)
var expectedPathSuffix string
@@ -397,7 +393,13 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
{Name: filepath.FromSlash("ラーメン/doc3.html"), Content: []byte("doc3")},
}
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
+
+ cfg.Set("baseURL", "http://auth/sub/")
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("uglyURLs", uglify)
+ cfg.Set("pluralizeListTitles", pluralize)
+ cfg.Set("canonifyURLs", canonify)
for _, source := range sources {
writeSource(t, fs, filepath.Join("content", source.Name), string(source.Content))
@@ -406,8 +408,8 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}")
writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "{{.Title}}")
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
-
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+ th := testHelper{s.Cfg}
tests := []struct {
doc string
pluralAware bool
@@ -427,13 +429,12 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
test.expected = inflect.Pluralize(test.expected)
}
- assertFileContent(t, fs, filepath.Join("public", test.doc), true, test.expected)
+ th.assertFileContent(t, fs, filepath.Join("public", test.doc), true, test.expected)
}
}
func TestSkipRender(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
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("<!doctype html><html><body>more content</body></html>")},
@@ -447,13 +448,13 @@ func TestSkipRender(t *testing.T) {
{Name: filepath.FromSlash("doc9.html"), Content: []byte("<html><body>doc9: {{< myshortcode >}}</body></html>")},
}
- viper.Set("defaultExtension", "html")
- viper.Set("verbose", true)
- viper.Set("canonifyURLs", true)
- viper.Set("uglyURLs", true)
- viper.Set("baseURL", "http://auth/bub")
+ cfg, fs := newTestCfg()
- fs := hugofs.NewMem()
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("verbose", true)
+ cfg.Set("canonifyURLs", true)
+ cfg.Set("uglyURLs", true)
+ cfg.Set("baseURL", "http://auth/bub")
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
@@ -465,7 +466,7 @@ func TestSkipRender(t *testing.T) {
writeSource(t, fs, filepath.Join("layouts", "head_abs"), "<head><script src=\"/script.js\"></script></head>")
writeSource(t, fs, filepath.Join("layouts", "shortcodes", "myshortcode.html"), "SHORT")
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
tests := []struct {
doc string
@@ -497,21 +498,20 @@ func TestSkipRender(t *testing.T) {
}
func TestAbsURLify(t *testing.T) {
- testCommonResetState()
-
- viper.Set("defaultExtension", "html")
- viper.Set("uglyURLs", true)
-
+ t.Parallel()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>")},
{Name: filepath.FromSlash("blue/doc2.html"), Content: []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>")},
}
for _, baseURL := range []string{"http://auth/bub", "http://base", "//base"} {
for _, canonify := range []bool{true, false} {
- viper.Set("canonifyURLs", canonify)
- viper.Set("baseURL", baseURL)
- fs := hugofs.NewMem()
+ cfg, fs := newTestCfg()
+
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("uglyURLs", true)
+ cfg.Set("canonifyURLs", canonify)
+ cfg.Set("baseURL", baseURL)
for _, src := range sources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
@@ -520,7 +520,8 @@ func TestAbsURLify(t *testing.T) {
writeSource(t, fs, filepath.Join("layouts", "blue/single.html"), templateWithURLAbs)
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+ th := testHelper{s.Cfg}
tests := []struct {
file, expected string
@@ -541,7 +542,7 @@ func TestAbsURLify(t *testing.T) {
expected = strings.Replace(expected, baseURL, "", -1)
}
- assertFileContent(t, fs, test.file, true, expected)
+ th.assertFileContent(t, fs, test.file, true, expected)
}
}
@@ -594,18 +595,16 @@ var weightedSources = []source.ByteSource{
}
func TestOrderedPages(t *testing.T) {
- testCommonResetState()
-
- viper.Set("baseURL", "http://auth/bub")
-
- fs := hugofs.NewMem()
+ t.Parallel()
+ cfg, fs := newTestCfg()
+ cfg.Set("baseURL", "http://auth/bub")
for _, src := range weightedSources {
writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
if s.Sections["sect"][0].Weight != 2 || s.Sections["sect"][3].Weight != 6 {
t.Errorf("Pages in unexpected order. First should be '%d', got '%d'", 2, s.Sections["sect"][0].Weight)
@@ -656,19 +655,18 @@ var groupedSources = []source.ByteSource{
}
func TestGroupedPages(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
- viper.Set("baseURL", "http://auth/bub")
+ cfg, fs := newTestCfg()
+ cfg.Set("baseURL", "http://auth/bub")
- fs := hugofs.NewMem()
writeSourcesToSource(t, "content", fs, groupedSources...)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
rbysection, err := s.RegularPages.GroupBy("Section", "desc")
if err != nil {
@@ -832,8 +830,7 @@ date = 2010-05-27T07:32:00Z
Front Matter with weighted tags and categories`)
func TestWeightedTaxonomies(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.md"), Content: pageWithWeightedTaxonomies2},
{Name: filepath.FromSlash("sect/doc2.md"), Content: pageWithWeightedTaxonomies1},
@@ -844,12 +841,13 @@ func TestWeightedTaxonomies(t *testing.T) {
taxonomies["tag"] = "tags"
taxonomies["category"] = "categories"
- viper.Set("baseURL", "http://auth/bub")
- viper.Set("taxonomies", taxonomies)
+ cfg, fs := newTestCfg()
+
+ cfg.Set("baseURL", "http://auth/bub")
+ cfg.Set("taxonomies", taxonomies)
- fs := hugofs.NewMem()
writeSourcesToSource(t, "content", fs, sources...)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
if s.Taxonomies["tags"]["a"][0].Page.Title != "foo" {
t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies["tags"]["a"][0].Page.Title)
@@ -865,7 +863,8 @@ func TestWeightedTaxonomies(t *testing.T) {
}
func findPage(site *Site, f string) *Page {
- currentPath := source.NewFile(filepath.FromSlash(f))
+ sp := source.NewSourceSpec(site.Cfg, site.Fs)
+ currentPath := sp.NewFile(filepath.FromSlash(f))
//t.Logf("looking for currentPath: %s", currentPath.Path())
for _, page := range site.Pages {
@@ -901,24 +900,24 @@ func setupLinkingMockSite(t *testing.T) *Site {
{Name: filepath.FromSlash("level2/level3/common.png"), Content: []byte("")},
}
- viper.Set("baseURL", "http://auth/")
- viper.Set("defaultExtension", "html")
- viper.Set("uglyURLs", false)
- viper.Set("pluralizeListTitles", false)
- viper.Set("canonifyURLs", false)
- viper.Set("blackfriday",
+ cfg, fs := newTestCfg()
+
+ cfg.Set("baseURL", "http://auth/")
+ cfg.Set("defaultExtension", "html")
+ cfg.Set("uglyURLs", false)
+ cfg.Set("pluralizeListTitles", false)
+ cfg.Set("canonifyURLs", false)
+ cfg.Set("blackfriday",
map[string]interface{}{
"sourceRelativeLinksProjectFolder": "/docs"})
- fs := hugofs.NewMem()
writeSourcesToSource(t, "content", fs, sources...)
- return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
}
func TestRefLinking(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
site := setupLinkingMockSite(t)
currentPage := findPage(site, "level2/level3/index.md")
@@ -941,8 +940,7 @@ func TestRefLinking(t *testing.T) {
}
func TestSourceRelativeLinksing(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
site := setupLinkingMockSite(t)
type resultMap map[string]string
@@ -1077,8 +1075,7 @@ func TestSourceRelativeLinksing(t *testing.T) {
}
func TestSourceRelativeLinkFileing(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
site := setupLinkingMockSite(t)
type resultMap map[string]string
diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go
index 5706b9fb5..6f75846e7 100644
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -20,9 +20,7 @@ import (
"html/template"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
- "github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -35,14 +33,6 @@ slug: slug-doc-2
slug doc 2 content
`
-const indexTemplate = "{{ range .Data.Pages }}.{{ end }}"
-
-func must(err error) {
- if err != nil {
- panic(err)
- }
-}
-
var urlFakeSource = []source.ByteSource{
{Name: filepath.FromSlash("content/blue/doc1.md"), Content: []byte(slugDoc1)},
{Name: filepath.FromSlash("content/blue/doc2.md"), Content: []byte(slugDoc2)},
@@ -50,8 +40,7 @@ var urlFakeSource = []source.ByteSource{
// Issue #1105
func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) {
- testCommonResetState()
-
+ t.Parallel()
for i, this := range []struct {
in string
expected string
@@ -61,8 +50,10 @@ func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) {
{"http://base.com/sub", "http://base.com/sub"},
{"http://base.com", "http://base.com"}} {
- viper.Set("baseURL", this.in)
- s, err := NewSiteDefaultLang()
+ cfg, fs := newTestCfg()
+ cfg.Set("baseURL", this.in)
+ d := deps.DepsCfg{Cfg: cfg, Fs: fs}
+ s, err := NewSiteForCfg(d)
require.NoError(t, err)
s.initializeSiteInfo()
@@ -70,18 +61,16 @@ func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) {
t.Errorf("[%d] got %s expected %s", i, s.Info.BaseURL, this.expected)
}
}
-
}
func TestPageCount(t *testing.T) {
- testCommonResetState()
-
- viper.Set("uglyURLs", false)
- viper.Set("paginate", 10)
+ t.Parallel()
+ cfg, fs := newTestCfg()
+ cfg.Set("uglyURLs", false)
+ cfg.Set("paginate", 10)
- fs := hugofs.NewMem()
writeSourcesToSource(t, "content", fs, urlFakeSource...)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
_, err := s.Fs.Destination.Open("public/blue")
if err != nil {
diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go
index 15d71cc6f..8bbcb487b 100644
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -19,9 +19,7 @@ import (
"reflect"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/tplapi"
- "github.com/spf13/viper"
)
const sitemapTemplate = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
@@ -36,19 +34,18 @@ const sitemapTemplate = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/
</urlset>`
func TestSitemapOutput(t *testing.T) {
+ t.Parallel()
for _, internal := range []bool{false, true} {
doTestSitemapOutput(t, internal)
}
}
func doTestSitemapOutput(t *testing.T, internal bool) {
- testCommonResetState()
- viper.Set("baseURL", "http://auth/bub/")
+ cfg, fs := newTestCfg()
+ cfg.Set("baseURL", "http://auth/bub/")
- fs := hugofs.NewMem()
-
- depsCfg := deps.DepsCfg{Fs: fs}
+ depsCfg := deps.DepsCfg{Fs: fs, Cfg: cfg}
if !internal {
depsCfg.WithTemplate = func(templ tplapi.Template) error {
@@ -59,8 +56,9 @@ func doTestSitemapOutput(t *testing.T, internal bool) {
writeSourcesToSource(t, "content", fs, weightedSources...)
s := buildSingleSite(t, depsCfg, BuildCfg{})
+ th := testHelper{s.Cfg}
- assertFileContent(t, s.Fs, "public/sitemap.xml", true,
+ th.assertFileContent(t, s.Fs, "public/sitemap.xml", true,
// Regular page
" <loc>http://auth/bub/sect/doc1/</loc>",
// Home page
@@ -76,6 +74,7 @@ func doTestSitemapOutput(t *testing.T, internal bool) {
}
func TestParseSitemap(t *testing.T) {
+ t.Parallel()
expected := Sitemap{Priority: 3.0, Filename: "doo.xml", ChangeFreq: "3"}
input := map[string]interface{}{
"changefreq": "3",
diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go
index 5cbd58d10..f5abe2af0 100644
--- a/hugolib/taxonomy_test.go
+++ b/hugolib/taxonomy_test.go
@@ -19,26 +19,22 @@ import (
"testing"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
-
- "github.com/spf13/viper"
)
func TestByCountOrderOfTaxonomies(t *testing.T) {
- defer testCommonResetState()
-
+ t.Parallel()
taxonomies := make(map[string]string)
taxonomies["tag"] = "tags"
taxonomies["category"] = "categories"
- viper.Set("taxonomies", taxonomies)
+ cfg, fs := newTestCfg()
- fs := hugofs.NewMem()
+ cfg.Set("taxonomies", taxonomies)
writeSource(t, fs, filepath.Join("content", "page.md"), pageYamlWithTaxonomiesA)
- s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
st := make([]string, 0)
for _, t := range s.Taxonomies["tags"].ByCount() {
diff --git a/hugolib/template_engines_test.go b/hugolib/template_engines_test.go
index 424f2562a..e70104366 100644
--- a/hugolib/template_engines_test.go
+++ b/hugolib/template_engines_test.go
@@ -20,13 +20,11 @@ import (
"strings"
- "github.com/spf13/viper"
-
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
)
func TestAllTemplateEngines(t *testing.T) {
+ t.Parallel()
noOp := func(s string) string {
return s
}
@@ -57,10 +55,7 @@ func TestAllTemplateEngines(t *testing.T) {
func doTestTemplateEngine(t *testing.T, suffix string, templateFixer func(s string) string) {
- testCommonResetState()
-
- fs := hugofs.NewMem()
- viper.SetFs(fs.Source)
+ cfg, fs := newTestCfg()
writeSource(t, fs, filepath.Join("content", "p.md"), `
---
@@ -88,9 +83,10 @@ p
writeSource(t, fs, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ)
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+ th := testHelper{s.Cfg}
- assertFileContent(t, fs, filepath.Join("public", "p", "index.html"), true,
+ th.assertFileContent(t, fs, filepath.Join("public", "p", "index.html"), true,
"Page Title: My Title",
"My Content",
"Hello World",
diff --git a/hugolib/template_test.go b/hugolib/template_test.go
index 2690b172a..f9e14c8f9 100644
--- a/hugolib/template_test.go
+++ b/hugolib/template_test.go
@@ -24,8 +24,12 @@ import (
)
func TestBaseGoTemplate(t *testing.T) {
-
- var fs *hugofs.Fs
+ t.Parallel()
+ var (
+ fs *hugofs.Fs
+ cfg *viper.Viper
+ th testHelper
+ )
// Variants:
// 1. <current-path>/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
@@ -44,7 +48,7 @@ func TestBaseGoTemplate(t *testing.T) {
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
},
},
{
@@ -55,7 +59,7 @@ func TestBaseGoTemplate(t *testing.T) {
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "index.html"), false, "Base: index")
+ th.assertFileContent(t, fs, filepath.Join("public", "index.html"), false, "Base: index")
},
},
{
@@ -66,7 +70,7 @@ func TestBaseGoTemplate(t *testing.T) {
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
@@ -77,64 +81,63 @@ func TestBaseGoTemplate(t *testing.T) {
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
// Variant 1, theme, use project's base
func(t *testing.T) {
- viper.Set("theme", "mytheme")
+ cfg.Set("theme", "mytheme")
writeSource(t, fs, filepath.Join("layouts", "section", "sect-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
},
},
{
// Variant 1, theme, use theme's base
func(t *testing.T) {
- viper.Set("theme", "mytheme")
+ cfg.Set("theme", "mytheme")
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: sect")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: sect")
},
},
{
// Variant 4, theme, use project's base
func(t *testing.T) {
- viper.Set("theme", "mytheme")
+ cfg.Set("theme", "mytheme")
writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
// Variant 4, theme, use themes's base
func(t *testing.T) {
- viper.Set("theme", "mytheme")
+ cfg.Set("theme", "mytheme")
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: list")
+ th.assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: list")
},
},
} {
- testCommonResetState()
-
- fs = hugofs.NewMem()
+ cfg, fs = newTestCfg()
+ th = testHelper{cfg}
writeSource(t, fs, filepath.Join("content", "sect", "page.md"), `---
title: Template test
@@ -143,7 +146,7 @@ Some content
`)
this.setup(t)
- buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+ buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
this.assert(t)
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 1d775aca8..33e78e121 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -6,20 +6,64 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tplapi"
"github.com/spf13/viper"
+ "io/ioutil"
+ "os"
+
+ "log"
+
+ "github.com/spf13/hugo/hugofs"
+ jww "github.com/spf13/jwalterweatherman"
"github.com/stretchr/testify/require"
)
-func newTestDepsConfig() deps.DepsCfg {
- return deps.DepsCfg{Fs: hugofs.NewMem()}
+func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *helpers.PathSpec {
+ l := helpers.NewDefaultLanguage(v)
+ return helpers.NewPathSpec(fs, l)
}
-func newTestPathSpec() *helpers.PathSpec {
- return helpers.NewPathSpec(hugofs.NewMem(), viper.GetViper())
+func newTestCfg() (*viper.Viper, *hugofs.Fs) {
+
+ v := viper.New()
+ fs := hugofs.NewMem(v)
+
+ v.SetFs(fs.Source)
+
+ loadDefaultSettingsFor(v)
+
+ // Default is false, but true is easier to use as default in tests
+ v.Set("defaultContentLanguageInSubdir", true)
+
+ return v, fs
+
+}
+
+// newTestSite creates a new site in the English language with in-memory Fs.
+// The site will have a template system loaded and ready to use.
+// Note: This is only used in single site tests.
+func newTestSite(t testing.TB, configKeyValues ...interface{}) *Site {
+
+ cfg, fs := newTestCfg()
+
+ for i := 0; i < len(configKeyValues); i += 2 {
+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
+
+ d := deps.DepsCfg{Language: helpers.NewLanguage("en", cfg), Fs: fs}
+
+ s, err := NewSiteForCfg(d)
+
+ if err != nil {
+ t.Fatalf("Failed to create Site: %s", err)
+ }
+ return s
+}
+
+func newDebugLogger() *jww.Notepad {
+ return jww.NewNotepad(jww.LevelDebug, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
}
func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tplapi.Template) error {
@@ -36,11 +80,21 @@ func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ
}
func buildSingleSite(t *testing.T, depsCfg deps.DepsCfg, buildCfg BuildCfg) *Site {
- h, err := NewHugoSitesFromConfiguration(depsCfg)
+ return buildSingleSiteExpected(t, false, depsCfg, buildCfg)
+}
+
+func buildSingleSiteExpected(t *testing.T, expectBuildError bool, depsCfg deps.DepsCfg, buildCfg BuildCfg) *Site {
+ h, err := NewHugoSites(depsCfg)
require.NoError(t, err)
require.Len(t, h.Sites, 1)
+ if expectBuildError {
+ require.Error(t, h.Build(buildCfg))
+ return nil
+
+ }
+
require.NoError(t, h.Build(buildCfg))
return h.Sites[0]
diff --git a/tpl/template_i18n.go b/i18n/i18n.go
index a7ec0df98..ce268fac3 100644
--- a/tpl/template_i18n.go
+++ b/i18n/i18n.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2017 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.
@@ -11,48 +11,52 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package tpl
+package i18n
import (
"github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
var (
- // Logi18nWarnings set to true to print warnings about missing language strings
- Logi18nWarnings bool
i18nWarningLogger = helpers.NewDistinctFeedbackLogger()
- currentLanguage *helpers.Language
)
-type translate struct {
+// Translator handles i18n translations.
+type Translator struct {
translateFuncs map[string]bundle.TranslateFunc
-
- current bundle.TranslateFunc
+ cfg config.Provider
+ logger *jww.Notepad
}
-// TODO(bep) global translator
-var translator *translate
+// NewTranslator creates a new Translator for the given language bundle and configuration.
+func NewTranslator(b *bundle.Bundle, cfg config.Provider, logger *jww.Notepad) Translator {
+ t := Translator{cfg: cfg, logger: logger, translateFuncs: make(map[string]bundle.TranslateFunc)}
+ t.initFuncs(b)
+ return t
+}
-// SetTranslateLang sets the translations language to use during template processing.
-// This construction is unfortunate, but the template system is currently global.
-func SetTranslateLang(language *helpers.Language) error {
- currentLanguage = language
- if f, ok := translator.translateFuncs[language.Lang]; ok {
- translator.current = f
- } else {
- jww.WARN.Printf("Translation func for language %v not found, use default.", language.Lang)
- translator.current = translator.translateFuncs[viper.GetString("defaultContentLanguage")]
+// Func gets the translate func for the given language, or for the default
+// configured language if not found.
+func (t Translator) Func(lang string) bundle.TranslateFunc {
+ if f, ok := t.translateFuncs[lang]; ok {
+ return f
+ }
+ t.logger.WARN.Printf("Translation func for language %v not found, use default.", lang)
+ if f, ok := t.translateFuncs[t.cfg.GetString("defaultContentLanguage")]; ok {
+ return f
+ }
+ t.logger.WARN.Println("i18n not initialized, check that you have language file (in i18n) that matches the site language or the default language.")
+ return func(translationID string, args ...interface{}) string {
+ return ""
}
- return nil
+
}
-// SetI18nTfuncs sets the language bundle to be used for i18n.
-func SetI18nTfuncs(bndl *bundle.Bundle) {
- translator = &translate{translateFuncs: make(map[string]bundle.TranslateFunc)}
- defaultContentLanguage := viper.GetString("defaultContentLanguage")
+func (t Translator) initFuncs(bndl *bundle.Bundle) {
+ defaultContentLanguage := t.cfg.GetString("defaultContentLanguage")
var (
defaultT bundle.TranslateFunc
err error
@@ -64,18 +68,18 @@ func SetI18nTfuncs(bndl *bundle.Bundle) {
jww.WARN.Printf("No translation bundle found for default language %q", defaultContentLanguage)
}
- enableMissingTranslationPlaceholders := viper.GetBool("enableMissingTranslationPlaceholders")
+ enableMissingTranslationPlaceholders := t.cfg.GetBool("enableMissingTranslationPlaceholders")
for _, lang := range bndl.LanguageTags() {
currentLang := lang
- translator.translateFuncs[currentLang] = func(translationID string, args ...interface{}) string {
+ t.translateFuncs[currentLang] = func(translationID string, args ...interface{}) string {
tFunc, err := bndl.Tfunc(currentLang)
if err != nil {
jww.WARN.Printf("could not load translations for language %q (%s), will use default content language.\n", lang, err)
} else if translated := tFunc(translationID, args...); translated != translationID {
return translated
}
- if Logi18nWarnings {
+ if t.cfg.GetBool("logI18nWarnings") {
i18nWarningLogger.Printf("i18n|MISSING_TRANSLATION|%s|%s", currentLang, translationID)
}
if enableMissingTranslationPlaceholders {
@@ -90,11 +94,3 @@ func SetI18nTfuncs(bndl *bundle.Bundle) {
}
}
}
-
-func i18nTranslate(id string, args ...interface{}) (string, error) {
- if translator == nil || translator.current == nil {
- helpers.DistinctErrorLog.Printf("i18n not initialized, check that you have language file (in i18n) that matches the site language or the default language.")
- return "", nil
- }
- return translator.current(id, args...), nil
-}
diff --git a/tpl/template_i18n_test.go b/i18n/i18n_test.go
index fb7ff5845..fd9c91b3e 100644
--- a/tpl/template_i18n_test.go
+++ b/i18n/i18n_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2017 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.
@@ -11,17 +11,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package tpl
+package i18n
import (
"testing"
+ "io/ioutil"
+ "os"
+
+ "log"
+
"github.com/nicksnyder/go-i18n/i18n/bundle"
- "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/config"
+ jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
- "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
+var logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+
type i18nTest struct {
data map[string][]byte
args interface{}
@@ -106,7 +114,7 @@ var i18nTests = []i18nTest{
},
}
-func doTestI18nTranslate(t *testing.T, data map[string][]byte, lang, id string, args interface{}) string {
+func doTestI18nTranslate(t *testing.T, data map[string][]byte, lang, id string, args interface{}, cfg config.Provider) string {
i18nBundle := bundle.New()
for file, content := range data {
@@ -116,25 +124,23 @@ func doTestI18nTranslate(t *testing.T, data map[string][]byte, lang, id string,
}
}
- SetI18nTfuncs(i18nBundle)
- SetTranslateLang(helpers.NewLanguage(lang))
+ translator := NewTranslator(i18nBundle, cfg, logger)
+
+ f := translator.Func(lang)
+
+ translated := f(id, args)
- translated, err := i18nTranslate(id, args)
- if err != nil {
- t.Errorf("Error translating '%s': %s", id, err)
- }
return translated
}
func TestI18nTranslate(t *testing.T) {
var actual, expected string
-
- viper.SetDefault("defaultContentLanguage", "en")
- viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
+ v := viper.New()
+ v.SetDefault("defaultContentLanguage", "en")
// Test without and with placeholders
for _, enablePlaceholders := range []bool{false, true} {
- viper.Set("enableMissingTranslationPlaceholders", enablePlaceholders)
+ v.Set("enableMissingTranslationPlaceholders", enablePlaceholders)
for _, test := range i18nTests {
if enablePlaceholders {
@@ -142,8 +148,8 @@ func TestI18nTranslate(t *testing.T) {
} else {
expected = test.expected
}
- actual = doTestI18nTranslate(t, test.data, test.lang, test.id, test.args)
- assert.Equal(t, expected, actual)
+ actual = doTestI18nTranslate(t, test.data, test.lang, test.id, test.args, v)
+ require.Equal(t, expected, actual)
}
}
}
diff --git a/i18n/translationProvider.go b/i18n/translationProvider.go
new file mode 100644
index 000000000..34558cffb
--- /dev/null
+++ b/i18n/translationProvider.go
@@ -0,0 +1,73 @@
+// Copyright 2017 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 i18n
+
+import (
+ "fmt"
+
+ "github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/source"
+)
+
+// TranslationProvider provides translation handling, i.e. loading
+// of bundles etc.
+type TranslationProvider struct {
+ t Translator
+}
+
+// NewTranslationProvider creates a new translation provider.
+func NewTranslationProvider() *TranslationProvider {
+ return &TranslationProvider{}
+}
+
+// Update updates the i18n func in the provided Deps.
+func (tp *TranslationProvider) Update(d *deps.Deps) error {
+ dir := d.PathSpec.AbsPathify(d.Cfg.GetString("i18nDir"))
+ sp := source.NewSourceSpec(d.Cfg, d.Fs)
+ sources := []source.Input{sp.NewFilesystem(dir)}
+
+ themeI18nDir, err := d.PathSpec.GetThemeI18nDirPath()
+
+ if err == nil {
+ sources = []source.Input{sp.NewFilesystem(themeI18nDir), sources[0]}
+ }
+
+ d.Log.DEBUG.Printf("Load I18n from %q", sources)
+
+ i18nBundle := bundle.New()
+
+ for _, currentSource := range sources {
+ for _, r := range currentSource.Files() {
+ err := i18nBundle.ParseTranslationFileBytes(r.LogicalName(), r.Bytes())
+ if err != nil {
+ return fmt.Errorf("Failed to load translations in file %q: %s", r.LogicalName(), err)
+ }
+ }
+ }
+
+ tp.t = NewTranslator(i18nBundle, d.Cfg, d.Log)
+
+ d.Translate = tp.t.Func(d.Language.Lang)
+
+ return nil
+
+}
+
+// Clone sets the language func for the new language.
+func (tp *TranslationProvider) Clone(d *deps.Deps) error {
+ d.Translate = tp.t.Func(d.Language.Lang)
+
+ return nil
+}
diff --git a/source/content_directory_test.go b/source/content_directory_test.go
index 107ca9837..48c3ae903 100644
--- a/source/content_directory_test.go
+++ b/source/content_directory_test.go
@@ -14,13 +14,13 @@
package source
import (
- "github.com/spf13/viper"
"testing"
+
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/viper"
)
func TestIgnoreDotFilesAndDirectories(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
tests := []struct {
path string
@@ -49,9 +49,12 @@ func TestIgnoreDotFilesAndDirectories(t *testing.T) {
for _, test := range tests {
- viper.Set("ignoreFiles", test.ignoreFilesRegexpes)
+ v := viper.New()
+ v.Set("ignoreFiles", test.ignoreFilesRegexpes)
+
+ s := NewSourceSpec(v, hugofs.NewMem(v))
- if ignored := isNonProcessablePath(test.path); test.ignore != ignored {
+ if ignored := s.isNonProcessablePath(test.path); test.ignore != ignored {
t.Errorf("File not ignored. Expected: %t, got: %t", test.ignore, ignored)
}
}
diff --git a/source/file.go b/source/file.go
index 18ed9d6f9..1dff9ac0b 100644
--- a/source/file.go
+++ b/source/file.go
@@ -18,10 +18,21 @@ import (
"path/filepath"
"strings"
+ "github.com/spf13/hugo/hugofs"
+
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
)
+type SourceSpec struct {
+ Cfg config.Provider
+ Fs *hugofs.Fs
+}
+
+func NewSourceSpec(cfg config.Provider, fs *hugofs.Fs) SourceSpec {
+ return SourceSpec{Cfg: cfg, Fs: fs}
+}
+
// File represents a source content file.
// All paths are relative from the source directory base
type File struct {
@@ -110,15 +121,15 @@ func (f *File) Path() string {
// NewFileWithContents creates a new File pointer with the given relative path and
// content. The language defaults to "en".
-func NewFileWithContents(relpath string, content io.Reader) *File {
- file := NewFile(relpath)
+func (sp SourceSpec) NewFileWithContents(relpath string, content io.Reader) *File {
+ file := sp.NewFile(relpath)
file.Contents = content
file.lang = "en"
return file
}
// NewFile creates a new File pointer with the given relative path.
-func NewFile(relpath string) *File {
+func (sp SourceSpec) NewFile(relpath string) *File {
f := &File{
relpath: relpath,
}
@@ -128,8 +139,8 @@ func NewFile(relpath string) *File {
f.baseName = helpers.Filename(f.LogicalName())
lang := strings.TrimPrefix(filepath.Ext(f.baseName), ".")
- if _, ok := viper.GetStringMap("languages")[lang]; lang == "" || !ok {
- f.lang = viper.GetString("defaultContentLanguage")
+ if _, ok := sp.Cfg.GetStringMap("languages")[lang]; lang == "" || !ok {
+ f.lang = sp.Cfg.GetString("defaultContentLanguage")
f.translationBaseName = f.baseName
} else {
f.lang = lang
@@ -144,11 +155,11 @@ func NewFile(relpath string) *File {
// NewFileFromAbs creates a new File pointer with the given full file path path and
// content.
-func NewFileFromAbs(base, fullpath string, content io.Reader) (f *File, err error) {
+func (sp SourceSpec) NewFileFromAbs(base, fullpath string, content io.Reader) (f *File, err error) {
var name string
if name, err = helpers.GetRelativePath(fullpath, base); err != nil {
return nil, err
}
- return NewFileWithContents(name, content), nil
+ return sp.NewFileWithContents(name, content), nil
}
diff --git a/source/file_test.go b/source/file_test.go
index 517474012..d87a154f5 100644
--- a/source/file_test.go
+++ b/source/file_test.go
@@ -18,28 +18,40 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/viper"
+
"github.com/stretchr/testify/assert"
)
func TestFileUniqueID(t *testing.T) {
+ ss := newTestSourceSpec()
+
f1 := File{uniqueID: "123"}
- f2 := NewFile("a")
+ f2 := ss.NewFile("a")
assert.Equal(t, "123", f1.UniqueID())
assert.Equal(t, "0cc175b9c0f1b6a831c399e269772661", f2.UniqueID())
- f3 := NewFile(filepath.FromSlash("test1/index.md"))
- f4 := NewFile(filepath.FromSlash("test2/index.md"))
+ f3 := ss.NewFile(filepath.FromSlash("test1/index.md"))
+ f4 := ss.NewFile(filepath.FromSlash("test2/index.md"))
assert.NotEqual(t, f3.UniqueID(), f4.UniqueID())
}
func TestFileString(t *testing.T) {
- assert.Equal(t, "abc", NewFileWithContents("a", strings.NewReader("abc")).String())
- assert.Equal(t, "", NewFile("a").String())
+ ss := newTestSourceSpec()
+ assert.Equal(t, "abc", ss.NewFileWithContents("a", strings.NewReader("abc")).String())
+ assert.Equal(t, "", ss.NewFile("a").String())
}
func TestFileBytes(t *testing.T) {
- assert.Equal(t, []byte("abc"), NewFileWithContents("a", strings.NewReader("abc")).Bytes())
- assert.Equal(t, []byte(""), NewFile("a").Bytes())
+ ss := newTestSourceSpec()
+ assert.Equal(t, []byte("abc"), ss.NewFileWithContents("a", strings.NewReader("abc")).Bytes())
+ assert.Equal(t, []byte(""), ss.NewFile("a").Bytes())
+}
+
+func newTestSourceSpec() SourceSpec {
+ v := viper.New()
+ return SourceSpec{Fs: hugofs.NewMem(v), Cfg: v}
}
diff --git a/source/filesystem.go b/source/filesystem.go
index 6089824a0..a13128144 100644
--- a/source/filesystem.go
+++ b/source/filesystem.go
@@ -21,13 +21,10 @@ import (
"runtime"
"strings"
- "github.com/spf13/hugo/hugofs"
- "golang.org/x/text/unicode/norm"
-
- "github.com/spf13/viper"
-
+ "github.com/spf13/cast"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
+ "golang.org/x/text/unicode/norm"
)
type Input interface {
@@ -39,11 +36,11 @@ type Filesystem struct {
Base string
AvoidPaths []string
- fs *hugofs.Fs
+ SourceSpec
}
-func NewFilesystem(fs *hugofs.Fs, base string, avoidPaths ...string) *Filesystem {
- return &Filesystem{fs: fs, Base: base, AvoidPaths: avoidPaths}
+func (sp SourceSpec) NewFilesystem(base string, avoidPaths ...string) *Filesystem {
+ return &Filesystem{SourceSpec: sp, Base: base, AvoidPaths: avoidPaths}
}
func (f *Filesystem) FilesByExts(exts ...string) []*File {
@@ -79,7 +76,7 @@ func (f *Filesystem) add(name string, reader io.Reader) (err error) {
name = norm.NFC.String(name)
}
- file, err = NewFileFromAbs(f.Base, name, reader)
+ file, err = f.SourceSpec.NewFileFromAbs(f.Base, name, reader)
if err == nil {
f.files = append(f.files, file)
@@ -98,7 +95,7 @@ func (f *Filesystem) captureFiles() {
return err
}
if b {
- rd, err := NewLazyFileReader(f.fs.Source, filePath)
+ rd, err := NewLazyFileReader(f.Fs.Source, filePath)
if err != nil {
return err
}
@@ -107,10 +104,10 @@ func (f *Filesystem) captureFiles() {
return err
}
- if f.fs == nil {
+ if f.Fs == nil {
panic("Must have a fs")
}
- err := helpers.SymbolicWalk(f.fs.Source, f.Base, walker)
+ err := helpers.SymbolicWalk(f.Fs.Source, f.Base, walker)
if err != nil {
jww.ERROR.Println(err)
@@ -128,7 +125,7 @@ func (f *Filesystem) shouldRead(filePath string, fi os.FileInfo) (bool, error) {
jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", filePath, err)
return false, nil
}
- linkfi, err := f.fs.Source.Stat(link)
+ linkfi, err := f.Fs.Source.Stat(link)
if err != nil {
jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return false, nil
@@ -140,13 +137,13 @@ func (f *Filesystem) shouldRead(filePath string, fi os.FileInfo) (bool, error) {
}
if fi.IsDir() {
- if f.avoid(filePath) || isNonProcessablePath(filePath) {
+ if f.avoid(filePath) || f.isNonProcessablePath(filePath) {
return false, filepath.SkipDir
}
return false, nil
}
- if isNonProcessablePath(filePath) {
+ if f.isNonProcessablePath(filePath) {
return false, nil
}
return true, nil
@@ -161,14 +158,14 @@ func (f *Filesystem) avoid(filePath string) bool {
return false
}
-func isNonProcessablePath(filePath string) bool {
+func (s SourceSpec) isNonProcessablePath(filePath string) bool {
base := filepath.Base(filePath)
if strings.HasPrefix(base, ".") ||
strings.HasPrefix(base, "#") ||
strings.HasSuffix(base, "~") {
return true
}
- ignoreFiles := viper.GetStringSlice("ignoreFiles")
+ ignoreFiles := cast.ToStringSlice(s.Cfg.Get("ignoreFiles"))
if len(ignoreFiles) > 0 {
for _, ignorePattern := range ignoreFiles {
match, err := regexp.MatchString(ignorePattern, filePath)
diff --git a/source/filesystem_test.go b/source/filesystem_test.go
index 598a1b81d..90512ce3f 100644
--- a/source/filesystem_test.go
+++ b/source/filesystem_test.go
@@ -19,12 +19,11 @@ import (
"runtime"
"strings"
"testing"
-
- "github.com/spf13/hugo/hugofs"
)
func TestEmptySourceFilesystem(t *testing.T) {
- src := NewFilesystem(hugofs.NewMem(), "Empty")
+ ss := newTestSourceSpec()
+ src := ss.NewFilesystem("Empty")
if len(src.Files()) != 0 {
t.Errorf("new filesystem should contain 0 files.")
}
@@ -39,12 +38,12 @@ type TestPath struct {
}
func TestAddFile(t *testing.T) {
- fs := hugofs.NewMem()
+ ss := newTestSourceSpec()
tests := platformPaths
for _, test := range tests {
base := platformBase
- srcDefault := NewFilesystem(fs, "")
- srcWithBase := NewFilesystem(fs, base)
+ srcDefault := ss.NewFilesystem("")
+ srcWithBase := ss.NewFilesystem(base)
for _, src := range []*Filesystem{srcDefault, srcWithBase} {
@@ -100,10 +99,10 @@ func TestUnicodeNorm(t *testing.T) {
{NFC: "é", NFD: "\x65\xcc\x81"},
}
- fs := hugofs.NewMem()
+ ss := newTestSourceSpec()
for _, path := range paths {
- src := NewFilesystem(fs, "")
+ src := ss.NewFilesystem("")
_ = src.add(path.NFD, strings.NewReader(""))
f := src.Files()[0]
if f.BaseFileName() != path.NFC {
diff --git a/source/inmemory.go b/source/inmemory.go
index 9d3af5194..431236a56 100644
--- a/source/inmemory.go
+++ b/source/inmemory.go
@@ -13,8 +13,6 @@
package source
-import "bytes"
-
type ByteSource struct {
Name string
Content []byte
@@ -23,15 +21,3 @@ type ByteSource struct {
func (b *ByteSource) String() string {
return b.Name + " " + string(b.Content)
}
-
-type InMemorySource struct {
- ByteSource []ByteSource
-}
-
-func (i *InMemorySource) Files() (files []*File) {
- files = make([]*File, len(i.ByteSource))
- for i, fake := range i.ByteSource {
- files[i] = NewFileWithContents(fake.Name, bytes.NewReader(fake.Content))
- }
- return
-}
diff --git a/target/page_test.go b/target/page_test.go
index 844120048..687c4d917 100644
--- a/target/page_test.go
+++ b/target/page_test.go
@@ -18,10 +18,11 @@ import (
"testing"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/viper"
)
func TestPageTranslator(t *testing.T) {
- fs := hugofs.NewMem()
+ fs := hugofs.NewMem(viper.New())
tests := []struct {
content string
diff --git a/tpl/template.go b/tpl/template.go
index 844608014..9a6364d5a 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -21,6 +21,8 @@ import (
"path/filepath"
"strings"
+ "sync"
+
"github.com/eknkc/amber"
"github.com/spf13/afero"
bp "github.com/spf13/hugo/bufferpool"
@@ -31,6 +33,9 @@ import (
// TODO(bep) globals get rid of the rest of the jww.ERR etc.
+// Protecting global map access (Amber)
+var amberMu sync.Mutex
+
type templateErr struct {
name string
err error
@@ -132,6 +137,7 @@ func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
t.amberFuncMap = template.FuncMap{}
+ amberMu.Lock()
for k, v := range amber.FuncMap {
t.amberFuncMap[k] = v
}
@@ -143,6 +149,7 @@ func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
panic("should never be invoked")
}
}
+ amberMu.Unlock()
}
@@ -362,7 +369,9 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
return err
}
+ amberMu.Lock()
templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
+ amberMu.Unlock()
if err != nil {
return err
}
@@ -482,11 +491,11 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
}
if needsBase {
- layoutDir := helpers.GetLayoutDirPath()
+ layoutDir := t.PathSpec.GetLayoutDirPath()
currBaseFilename := fmt.Sprintf("%s-%s", helpers.Filename(path), baseFileName)
templateDir := filepath.Dir(path)
- themeDir := filepath.Join(helpers.GetThemeDir())
- relativeThemeLayoutsDir := filepath.Join(helpers.GetRelativeThemeDir(), "layouts")
+ themeDir := filepath.Join(t.PathSpec.GetThemeDir())
+ relativeThemeLayoutsDir := filepath.Join(t.PathSpec.GetRelativeThemeDir(), "layouts")
var baseTemplatedDir string
diff --git a/tpl/template_ast_transformers_test.go b/tpl/template_ast_transformers_test.go
index 8ffb1cab1..43d78284c 100644
--- a/tpl/template_ast_transformers_test.go
+++ b/tpl/template_ast_transformers_test.go
@@ -113,6 +113,7 @@ F3: {{ Echo (printf "themes/%s-theme" .Site.Params.LOWER) }}
)
func TestParamsKeysToLower(t *testing.T) {
+ t.Parallel()
require.Error(t, applyTemplateTransformers(nil))
@@ -190,6 +191,7 @@ func BenchmarkTemplateParamsKeysToLower(b *testing.B) {
}
func TestParamsKeysToLowerVars(t *testing.T) {
+ t.Parallel()
var (
ctx = map[string]interface{}{
"Params": map[string]interface{}{
@@ -227,6 +229,7 @@ Blue: {{ $__amber_1.Blue}}
}
func TestParamsKeysToLowerInBlockTemplate(t *testing.T) {
+ t.Parallel()
var (
ctx = map[string]interface{}{
diff --git a/tpl/template_func_truncate_test.go b/tpl/template_func_truncate_test.go
index 95368f83e..9213c6faa 100644
--- a/tpl/template_func_truncate_test.go
+++ b/tpl/template_func_truncate_test.go
@@ -21,6 +21,7 @@ import (
)
func TestTruncate(t *testing.T) {
+ t.Parallel()
var err error
cases := []struct {
v1 interface{}
diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go
index 01b975665..9777bf619 100644
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -46,7 +46,6 @@ import (
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
// Importing image codecs for image.DecodeConfig
_ "image/gif"
@@ -58,7 +57,6 @@ import (
type templateFuncster struct {
funcMap template.FuncMap
cachedPartials partialCache
-
*deps.Deps
}
@@ -398,6 +396,7 @@ func intersect(l1, l2 interface{}) (interface{}, error) {
}
// ResetCaches resets all caches that might be used during build.
+// TODO(bep) globals move image config cache to funcster
func ResetCaches() {
resetImageConfigCache()
}
@@ -1357,31 +1356,29 @@ func returnWhenSet(a, k interface{}) interface{} {
}
// highlight returns an HTML string with syntax highlighting applied.
-func highlight(in interface{}, lang, opts string) (template.HTML, error) {
+func (t *templateFuncster) highlight(in interface{}, lang, opts string) (template.HTML, error) {
str, err := cast.ToStringE(in)
if err != nil {
return "", err
}
- return template.HTML(helpers.Highlight(html.UnescapeString(str), lang, opts)), nil
+ return template.HTML(helpers.Highlight(t.Cfg, html.UnescapeString(str), lang, opts)), nil
}
var markdownTrimPrefix = []byte("<p>")
var markdownTrimSuffix = []byte("</p>\n")
// markdownify renders a given string from Markdown to HTML.
-func markdownify(in interface{}) (template.HTML, error) {
+func (t *templateFuncster) markdownify(in interface{}) (template.HTML, error) {
text, err := cast.ToStringE(in)
if err != nil {
return "", err
}
- language := viper.Get("currentContentLanguage").(*helpers.Language)
-
- m := helpers.RenderBytes(&helpers.RenderingContext{
- ConfigProvider: language,
- Content: []byte(text), PageFmt: "markdown"})
+ m := t.ContentSpec.RenderBytes(&helpers.RenderingContext{
+ Cfg: t.Cfg,
+ Content: []byte(text), PageFmt: "markdown"})
m = bytes.TrimPrefix(m, markdownTrimPrefix)
m = bytes.TrimSuffix(m, markdownTrimSuffix)
return template.HTML(m), nil
@@ -2143,7 +2140,7 @@ func (t *templateFuncster) initFuncMap() {
"getenv": getenv,
"gt": gt,
"hasPrefix": hasPrefix,
- "highlight": highlight,
+ "highlight": t.highlight,
"htmlEscape": htmlEscape,
"htmlUnescape": htmlUnescape,
"humanize": humanize,
@@ -2159,7 +2156,7 @@ func (t *templateFuncster) initFuncMap() {
"le": le,
"lower": lower,
"lt": lt,
- "markdownify": markdownify,
+ "markdownify": t.markdownify,
"md5": md5,
"mod": mod,
"modBool": modBool,
@@ -2211,8 +2208,8 @@ func (t *templateFuncster) initFuncMap() {
"upper": upper,
"urlize": t.PathSpec.URLize,
"where": where,
- "i18n": i18nTranslate,
- "T": i18nTranslate,
+ "i18n": t.Translate,
+ "T": t.Translate,
}
t.funcMap = funcMap
diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go
index e4af75b30..35214b649 100644
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -42,7 +42,9 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cast"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/i18n"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
@@ -53,12 +55,16 @@ var (
logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
)
-func newDefaultDepsCfg() deps.DepsCfg {
+func newDepsConfig(cfg config.Provider) deps.DepsCfg {
+ l := helpers.NewLanguage("en", cfg)
+ l.Set("i18nDir", "i18n")
return deps.DepsCfg{
- Language: helpers.NewLanguage("en"),
- Fs: hugofs.NewMem(),
- Logger: logger,
- TemplateProvider: DefaultTemplateProvider,
+ Language: l,
+ Cfg: cfg,
+ Fs: hugofs.NewMem(l),
+ Logger: logger,
+ TemplateProvider: DefaultTemplateProvider,
+ TranslationProvider: i18n.NewTranslationProvider(),
}
}
@@ -88,22 +94,17 @@ func tstIsLt(tp tstCompareType) bool {
return tp == tstLt || tp == tstLe
}
-func tstInitTemplates() {
- viper.Set("CurrentContentLanguage", helpers.NewLanguage("en"))
- helpers.ResetConfigProvider()
-}
-
func TestFuncsInTemplate(t *testing.T) {
-
- testReset()
+ t.Parallel()
workingDir := "/home/hugo"
- viper.Set("workingDir", workingDir)
- viper.Set("currentContentLanguage", helpers.NewDefaultLanguage())
- viper.Set("multilingual", true)
+ v := viper.New()
- fs := hugofs.NewMem()
+ v.Set("workingDir", workingDir)
+ v.Set("multilingual", true)
+
+ fs := hugofs.NewMem(v)
afero.WriteFile(fs.Source, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
@@ -268,11 +269,10 @@ urlize: bat-man
data.Section = "blog"
data.Params = map[string]interface{}{"langCode": "en"}
- viper.Set("baseURL", "http://mysite.com/hugo/")
-
- tstInitTemplates()
+ v.Set("baseURL", "http://mysite.com/hugo/")
+ v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
- config := newDefaultDepsCfg()
+ config := newDepsConfig(v)
config.WithTemplate = func(templ tplapi.Template) error {
if _, err := templ.New("test").Parse(in); err != nil {
t.Fatal("Got error on parse", err)
@@ -282,7 +282,7 @@ urlize: bat-man
config.Fs = fs
d := deps.New(config)
- if err := d.LoadTemplates(); err != nil {
+ if err := d.LoadResources(); err != nil {
t.Fatal(err)
}
@@ -300,6 +300,7 @@ urlize: bat-man
}
func TestCompare(t *testing.T) {
+ t.Parallel()
for _, this := range []struct {
tstCompareType
funcUnderTest func(a, b interface{}) bool
@@ -370,6 +371,7 @@ func doTestCompare(t *testing.T, tp tstCompareType, funcUnderTest func(a, b inte
}
func TestMod(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
a interface{}
b interface{}
@@ -405,6 +407,7 @@ func TestMod(t *testing.T) {
}
func TestModBool(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
a interface{}
b interface{}
@@ -445,6 +448,7 @@ func TestModBool(t *testing.T) {
}
func TestFirst(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
count interface{}
sequence interface{}
@@ -480,6 +484,7 @@ func TestFirst(t *testing.T) {
}
func TestLast(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
count interface{}
sequence interface{}
@@ -515,6 +520,7 @@ func TestLast(t *testing.T) {
}
func TestAfter(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
count interface{}
sequence interface{}
@@ -550,6 +556,7 @@ func TestAfter(t *testing.T) {
}
func TestShuffleInputAndOutputFormat(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
sequence interface{}
success bool
@@ -588,6 +595,7 @@ func TestShuffleInputAndOutputFormat(t *testing.T) {
}
func TestShuffleRandomising(t *testing.T) {
+ t.Parallel()
// Note that this test can fail with false negative result if the shuffle
// of the sequence happens to be the same as the original sequence. However
// the propability of the event is 10^-158 which is negligible.
@@ -615,6 +623,7 @@ func TestShuffleRandomising(t *testing.T) {
}
func TestDictionary(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
v1 []interface{}
expecterr bool
@@ -647,13 +656,15 @@ func blankImage(width, height int) []byte {
}
func TestImageConfig(t *testing.T) {
- testReset()
+ t.Parallel()
workingDir := "/home/hugo"
- viper.Set("workingDir", workingDir)
+ v := viper.New()
- f := newTestFuncster()
+ v.Set("workingDir", workingDir)
+
+ f := newTestFuncsterWithViper(v)
for i, this := range []struct {
resetCache bool
@@ -754,6 +765,7 @@ func TestImageConfig(t *testing.T) {
}
func TestIn(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
v1 interface{}
v2 interface{}
@@ -783,6 +795,7 @@ func TestIn(t *testing.T) {
}
func TestSlicestr(t *testing.T) {
+ t.Parallel()
var err error
for i, this := range []struct {
v1 interface{}
@@ -848,6 +861,7 @@ func TestSlicestr(t *testing.T) {
}
func TestHasPrefix(t *testing.T) {
+ t.Parallel()
cases := []struct {
s interface{}
prefix interface{}
@@ -875,6 +889,7 @@ func TestHasPrefix(t *testing.T) {
}
func TestSubstr(t *testing.T) {
+ t.Parallel()
var err error
var n int
for i, this := range []struct {
@@ -952,6 +967,7 @@ func TestSubstr(t *testing.T) {
}
func TestSplit(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
v1 interface{}
v2 string
@@ -982,6 +998,7 @@ func TestSplit(t *testing.T) {
}
func TestIntersect(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
sequence1 interface{}
sequence2 interface{}
@@ -1025,6 +1042,7 @@ func TestIntersect(t *testing.T) {
}
func TestIsSet(t *testing.T) {
+ t.Parallel()
aSlice := []interface{}{1, 2, 3, 5}
aMap := map[string]interface{}{"a": 1, "b": 2}
@@ -1074,6 +1092,7 @@ type TstX struct {
}
func TestTimeUnix(t *testing.T) {
+ t.Parallel()
var sec int64 = 1234567890
tv := reflect.ValueOf(time.Unix(sec, 0))
i := 1
@@ -1096,6 +1115,7 @@ func TestTimeUnix(t *testing.T) {
}
func TestEvaluateSubElem(t *testing.T) {
+ t.Parallel()
tstx := TstX{A: "foo", B: "bar"}
var inner struct {
S fmt.Stringer
@@ -1146,6 +1166,7 @@ func TestEvaluateSubElem(t *testing.T) {
}
func TestCheckCondition(t *testing.T) {
+ t.Parallel()
type expect struct {
result bool
isError bool
@@ -1266,6 +1287,7 @@ func TestCheckCondition(t *testing.T) {
}
func TestWhere(t *testing.T) {
+ t.Parallel()
type Mid struct {
Tst TstX
@@ -1671,6 +1693,7 @@ func TestWhere(t *testing.T) {
}
func TestDelimit(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
sequence interface{}
delimiter interface{}
@@ -1720,6 +1743,7 @@ func TestDelimit(t *testing.T) {
}
func TestSort(t *testing.T) {
+ t.Parallel()
type ts struct {
MyInt int
MyFloat float64
@@ -1932,6 +1956,7 @@ func TestSort(t *testing.T) {
}
func TestReturnWhenSet(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
data interface{}
key interface{}
@@ -1957,7 +1982,10 @@ func TestReturnWhenSet(t *testing.T) {
}
func TestMarkdownify(t *testing.T) {
- viper.Set("currentContentLanguage", helpers.NewDefaultLanguage())
+ t.Parallel()
+ v := viper.New()
+
+ f := newTestFuncsterWithViper(v)
for i, this := range []struct {
in interface{}
@@ -1966,7 +1994,7 @@ func TestMarkdownify(t *testing.T) {
{"Hello **World!**", template.HTML("Hello <strong>World!</strong>")},
{[]byte("Hello Bytes **World!**"), template.HTML("Hello Bytes <strong>World!</strong>")},
} {
- result, err := markdownify(this.in)
+ result, err := f.markdownify(this.in)
if err != nil {
t.Fatalf("[%d] unexpected error in markdownify: %s", i, err)
}
@@ -1975,12 +2003,13 @@ func TestMarkdownify(t *testing.T) {
}
}
- if _, err := markdownify(t); err == nil {
+ if _, err := f.markdownify(t); err == nil {
t.Fatalf("markdownify should have errored")
}
}
func TestApply(t *testing.T) {
+ t.Parallel()
f := newTestFuncster()
@@ -2024,6 +2053,7 @@ func TestApply(t *testing.T) {
}
func TestChomp(t *testing.T) {
+ t.Parallel()
base := "\n This is\na story "
for i, item := range []string{
"\n", "\n\n",
@@ -2046,6 +2076,7 @@ func TestChomp(t *testing.T) {
}
func TestLower(t *testing.T) {
+ t.Parallel()
cases := []struct {
s interface{}
want string
@@ -2069,6 +2100,7 @@ func TestLower(t *testing.T) {
}
func TestTitle(t *testing.T) {
+ t.Parallel()
cases := []struct {
s interface{}
want string
@@ -2092,6 +2124,7 @@ func TestTitle(t *testing.T) {
}
func TestUpper(t *testing.T) {
+ t.Parallel()
cases := []struct {
s interface{}
want string
@@ -2115,8 +2148,12 @@ func TestUpper(t *testing.T) {
}
func TestHighlight(t *testing.T) {
+ t.Parallel()
code := "func boo() {}"
- highlighted, err := highlight(code, "go", "")
+
+ f := newTestFuncster()
+
+ highlighted, err := f.highlight(code, "go", "")
if err != nil {
t.Fatal("Highlight returned error:", err)
@@ -2127,7 +2164,7 @@ func TestHighlight(t *testing.T) {
t.Errorf("Highlight mismatch, got %v", highlighted)
}
- _, err = highlight(t, "go", "")
+ _, err = f.highlight(t, "go", "")
if err == nil {
t.Error("Expected highlight error")
@@ -2135,6 +2172,7 @@ func TestHighlight(t *testing.T) {
}
func TestInflect(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
inflectFunc func(i interface{}) (string, error)
in interface{}
@@ -2169,6 +2207,7 @@ func TestInflect(t *testing.T) {
}
func TestCounterFuncs(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
countFunc func(i interface{}) (int, error)
in string
@@ -2195,6 +2234,7 @@ func TestCounterFuncs(t *testing.T) {
}
func TestReplace(t *testing.T) {
+ t.Parallel()
v, _ := replace("aab", "a", "b")
assert.Equal(t, "bbb", v)
v, _ = replace("11a11", 1, 2)
@@ -2210,6 +2250,7 @@ func TestReplace(t *testing.T) {
}
func TestReplaceRE(t *testing.T) {
+ t.Parallel()
for i, val := range []struct {
pattern interface{}
repl interface{}
@@ -2234,6 +2275,7 @@ func TestReplaceRE(t *testing.T) {
}
func TestFindRE(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
expr string
content interface{}
@@ -2264,6 +2306,7 @@ func TestFindRE(t *testing.T) {
}
func TestTrim(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
v1 interface{}
@@ -2294,6 +2337,7 @@ func TestTrim(t *testing.T) {
}
func TestDateFormat(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
layout string
value interface{}
@@ -2328,6 +2372,7 @@ func TestDateFormat(t *testing.T) {
}
func TestDefaultFunc(t *testing.T) {
+ t.Parallel()
then := time.Now()
now := time.Now()
@@ -2385,6 +2430,7 @@ func TestDefaultFunc(t *testing.T) {
}
func TestDefault(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
input interface{}
tpl string
@@ -2414,6 +2460,7 @@ func TestDefault(t *testing.T) {
}
func TestSafeHTML(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
str string
tmplStr string
@@ -2454,6 +2501,7 @@ func TestSafeHTML(t *testing.T) {
}
func TestSafeHTMLAttr(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
str string
tmplStr string
@@ -2494,6 +2542,7 @@ func TestSafeHTMLAttr(t *testing.T) {
}
func TestSafeCSS(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
str string
tmplStr string
@@ -2535,6 +2584,7 @@ func TestSafeCSS(t *testing.T) {
// TODO(bep) what is this? Also look above.
func TestSafeJS(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
str string
tmplStr string
@@ -2576,6 +2626,7 @@ func TestSafeJS(t *testing.T) {
// TODO(bep) what is this?
func TestSafeURL(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
str string
tmplStr string
@@ -2616,6 +2667,7 @@ func TestSafeURL(t *testing.T) {
}
func TestBase64Decode(t *testing.T) {
+ t.Parallel()
testStr := "abc123!?$*&()'-=@~"
enc := base64.StdEncoding.EncodeToString([]byte(testStr))
result, err := base64Decode(enc)
@@ -2635,6 +2687,7 @@ func TestBase64Decode(t *testing.T) {
}
func TestBase64Encode(t *testing.T) {
+ t.Parallel()
testStr := "YWJjMTIzIT8kKiYoKSctPUB+"
dec, err := base64.StdEncoding.DecodeString(testStr)
@@ -2659,6 +2712,7 @@ func TestBase64Encode(t *testing.T) {
}
func TestMD5(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
input string
expectedHash string
@@ -2683,6 +2737,7 @@ func TestMD5(t *testing.T) {
}
func TestSHA1(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
input string
expectedHash string
@@ -2707,6 +2762,7 @@ func TestSHA1(t *testing.T) {
}
func TestSHA256(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
input string
expectedHash string
@@ -2731,13 +2787,15 @@ func TestSHA256(t *testing.T) {
}
func TestReadFile(t *testing.T) {
- testReset()
+ t.Parallel()
workingDir := "/home/hugo"
- viper.Set("workingDir", workingDir)
+ v := viper.New()
- f := newTestFuncster()
+ v.Set("workingDir", workingDir)
+
+ f := newTestFuncsterWithViper(v)
afero.WriteFile(f.Fs.Source, filepath.Join(workingDir, "/f/f1.txt"), []byte("f1-content"), 0755)
afero.WriteFile(f.Fs.Source, filepath.Join("/home", "f2.txt"), []byte("f2-content"), 0755)
@@ -2770,6 +2828,7 @@ func TestReadFile(t *testing.T) {
}
func TestPartialCached(t *testing.T) {
+ t.Parallel()
testCases := []struct {
name string
partial string
@@ -2793,7 +2852,6 @@ func TestPartialCached(t *testing.T) {
data.Section = "blog"
data.Params = map[string]interface{}{"langCode": "en"}
- tstInitTemplates()
for i, tc := range testCases {
var tmp string
if tc.variant != "" {
@@ -2802,9 +2860,9 @@ func TestPartialCached(t *testing.T) {
tmp = tc.tmpl
}
- cfg := newDefaultDepsCfg()
+ config := newDepsConfig(viper.New())
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config.WithTemplate = func(templ tplapi.Template) error {
err := templ.AddTemplate("testroot", tmp)
if err != nil {
return err
@@ -2817,8 +2875,8 @@ func TestPartialCached(t *testing.T) {
return nil
}
- de := deps.New(cfg)
- require.NoError(t, de.LoadTemplates())
+ de := deps.New(config)
+ require.NoError(t, de.LoadResources())
buf := new(bytes.Buffer)
templ := de.Tmpl.Lookup("testroot")
@@ -2842,8 +2900,8 @@ func TestPartialCached(t *testing.T) {
}
func BenchmarkPartial(b *testing.B) {
- cfg := newDefaultDepsCfg()
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config := newDepsConfig(viper.New())
+ config.WithTemplate = func(templ tplapi.Template) error {
err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
if err != nil {
return err
@@ -2856,8 +2914,8 @@ func BenchmarkPartial(b *testing.B) {
return nil
}
- de := deps.New(cfg)
- require.NoError(b, de.LoadTemplates())
+ de := deps.New(config)
+ require.NoError(b, de.LoadResources())
buf := new(bytes.Buffer)
tmpl := de.Tmpl.Lookup("testroot")
@@ -2873,8 +2931,8 @@ func BenchmarkPartial(b *testing.B) {
}
func BenchmarkPartialCached(b *testing.B) {
- cfg := newDefaultDepsCfg()
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config := newDepsConfig(viper.New())
+ config.WithTemplate = func(templ tplapi.Template) error {
err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
if err != nil {
return err
@@ -2887,8 +2945,8 @@ func BenchmarkPartialCached(b *testing.B) {
return nil
}
- de := deps.New(cfg)
- require.NoError(b, de.LoadTemplates())
+ de := deps.New(config)
+ require.NoError(b, de.LoadResources())
buf := new(bytes.Buffer)
tmpl := de.Tmpl.Lookup("testroot")
@@ -2904,9 +2962,14 @@ func BenchmarkPartialCached(b *testing.B) {
}
func newTestFuncster() *templateFuncster {
- cfg := newDefaultDepsCfg()
- d := deps.New(cfg)
- if err := d.LoadTemplates(); err != nil {
+ return newTestFuncsterWithViper(viper.New())
+}
+
+func newTestFuncsterWithViper(v *viper.Viper) *templateFuncster {
+ config := newDepsConfig(v)
+ d := deps.New(config)
+
+ if err := d.LoadResources(); err != nil {
panic(err)
}
@@ -2914,8 +2977,8 @@ func newTestFuncster() *templateFuncster {
}
func newTestTemplate(t *testing.T, name, template string) *template.Template {
- cfg := newDefaultDepsCfg()
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config := newDepsConfig(viper.New())
+ config.WithTemplate = func(templ tplapi.Template) error {
err := templ.AddTemplate(name, template)
if err != nil {
return err
@@ -2923,8 +2986,8 @@ func newTestTemplate(t *testing.T, name, template string) *template.Template {
return nil
}
- de := deps.New(cfg)
- require.NoError(t, de.LoadTemplates())
+ de := deps.New(config)
+ require.NoError(t, de.LoadResources())
return de.Tmpl.Lookup(name)
}
diff --git a/tpl/template_resources.go b/tpl/template_resources.go
index 13ebfb698..bb1b11eb3 100644
--- a/tpl/template_resources.go
+++ b/tpl/template_resources.go
@@ -27,9 +27,9 @@ import (
"time"
"github.com/spf13/afero"
+ "github.com/spf13/hugo/config"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
var (
@@ -63,17 +63,17 @@ func (l *remoteLock) URLUnlock(url string) {
}
// getCacheFileID returns the cache ID for a string
-func getCacheFileID(id string) string {
- return viper.GetString("cacheDir") + url.QueryEscape(id)
+func getCacheFileID(cfg config.Provider, id string) string {
+ return cfg.GetString("cacheDir") + url.QueryEscape(id)
}
// resGetCache returns the content for an ID from the file cache or an error
// if the file is not found returns nil,nil
-func resGetCache(id string, fs afero.Fs, ignoreCache bool) ([]byte, error) {
+func resGetCache(id string, fs afero.Fs, cfg config.Provider, ignoreCache bool) ([]byte, error) {
if ignoreCache {
return nil, nil
}
- fID := getCacheFileID(id)
+ fID := getCacheFileID(cfg, id)
isExists, err := helpers.Exists(fID, fs)
if err != nil {
return nil, err
@@ -87,11 +87,11 @@ func resGetCache(id string, fs afero.Fs, ignoreCache bool) ([]byte, error) {
}
// resWriteCache writes bytes to an ID into the file cache
-func resWriteCache(id string, c []byte, fs afero.Fs, ignoreCache bool) error {
+func resWriteCache(id string, c []byte, fs afero.Fs, cfg config.Provider, ignoreCache bool) error {
if ignoreCache {
return nil
}
- fID := getCacheFileID(id)
+ fID := getCacheFileID(cfg, id)
f, err := fs.Create(fID)
if err != nil {
return errors.New("Error: " + err.Error() + ". Failed to create file: " + fID)
@@ -107,13 +107,13 @@ func resWriteCache(id string, c []byte, fs afero.Fs, ignoreCache bool) error {
return nil
}
-func resDeleteCache(id string, fs afero.Fs) error {
- return fs.Remove(getCacheFileID(id))
+func resDeleteCache(id string, fs afero.Fs, cfg config.Provider) error {
+ return fs.Remove(getCacheFileID(cfg, id))
}
// resGetRemote loads the content of a remote file. This method is thread safe.
-func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
- c, err := resGetCache(url, fs, viper.GetBool("ignoreCache"))
+func resGetRemote(url string, fs afero.Fs, cfg config.Provider, hc *http.Client) ([]byte, error) {
+ c, err := resGetCache(url, fs, cfg, cfg.GetBool("ignoreCache"))
if c != nil && err == nil {
return c, nil
}
@@ -126,7 +126,7 @@ func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
defer func() { remoteURLLock.URLUnlock(url) }()
// avoid multiple locks due to calling resGetCache twice
- c, err = resGetCache(url, fs, viper.GetBool("ignoreCache"))
+ c, err = resGetCache(url, fs, cfg, cfg.GetBool("ignoreCache"))
if c != nil && err == nil {
return c, nil
}
@@ -144,17 +144,17 @@ func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
if err != nil {
return nil, err
}
- err = resWriteCache(url, c, fs, viper.GetBool("ignoreCache"))
+ err = resWriteCache(url, c, fs, cfg, cfg.GetBool("ignoreCache"))
if err != nil {
return nil, err
}
- jww.INFO.Printf("... and cached to: %s", getCacheFileID(url))
+ jww.INFO.Printf("... and cached to: %s", getCacheFileID(cfg, url))
return c, nil
}
// resGetLocal loads the content of a local file
-func resGetLocal(url string, fs afero.Fs) ([]byte, error) {
- filename := filepath.Join(viper.GetString("workingDir"), url)
+func resGetLocal(url string, fs afero.Fs, cfg config.Provider) ([]byte, error) {
+ filename := filepath.Join(cfg.GetString("workingDir"), url)
if e, err := helpers.Exists(filename, fs); !e {
return nil, err
}
@@ -169,9 +169,9 @@ func (t *templateFuncster) resGetResource(url string) ([]byte, error) {
return nil, nil
}
if strings.Contains(url, "://") {
- return resGetRemote(url, t.Fs.Source, http.DefaultClient)
+ return resGetRemote(url, t.Fs.Source, t.Cfg, http.DefaultClient)
}
- return resGetLocal(url, t.Fs.Source)
+ return resGetLocal(url, t.Fs.Source, t.Cfg)
}
// getJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
@@ -193,7 +193,7 @@ func (t *templateFuncster) getJSON(urlParts ...string) interface{} {
jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, t.Fs.Source)
+ resDeleteCache(url, t.Fs.Source, t.Cfg)
continue
}
break
@@ -226,7 +226,7 @@ func (t *templateFuncster) getCSV(sep string, urlParts ...string) [][]string {
var clearCacheSleep = func(i int, u string) {
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, t.Fs.Source)
+ resDeleteCache(url, t.Fs.Source, t.Cfg)
}
for i := 0; i <= resRetries; i++ {
diff --git a/tpl/template_resources_test.go b/tpl/template_resources_test.go
index 5a92f98d3..82764977f 100644
--- a/tpl/template_resources_test.go
+++ b/tpl/template_resources_test.go
@@ -19,10 +19,8 @@ import (
"net/http"
"net/http/httptest"
"net/url"
- "os"
"strings"
"testing"
- "time"
"github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
@@ -32,6 +30,7 @@ import (
)
func TestScpCache(t *testing.T) {
+ t.Parallel()
tests := []struct {
path string
@@ -50,7 +49,8 @@ func TestScpCache(t *testing.T) {
fs := new(afero.MemMapFs)
for _, test := range tests {
- c, err := resGetCache(test.path, fs, test.ignore)
+ cfg := viper.New()
+ c, err := resGetCache(test.path, fs, cfg, test.ignore)
if err != nil {
t.Errorf("Error getting cache: %s", err)
}
@@ -58,12 +58,12 @@ func TestScpCache(t *testing.T) {
t.Errorf("There is content where there should not be anything: %s", string(c))
}
- err = resWriteCache(test.path, test.content, fs, test.ignore)
+ err = resWriteCache(test.path, test.content, fs, cfg, test.ignore)
if err != nil {
t.Errorf("Error writing cache: %s", err)
}
- c, err = resGetCache(test.path, fs, test.ignore)
+ c, err = resGetCache(test.path, fs, cfg, test.ignore)
if err != nil {
t.Errorf("Error getting cache after writing: %s", err)
}
@@ -80,8 +80,9 @@ func TestScpCache(t *testing.T) {
}
func TestScpGetLocal(t *testing.T) {
- testReset()
- fs := hugofs.NewMem()
+ t.Parallel()
+ v := viper.New()
+ fs := hugofs.NewMem(v)
ps := helpers.FilePathSeparator
tests := []struct {
@@ -102,7 +103,7 @@ func TestScpGetLocal(t *testing.T) {
t.Error(err)
}
- c, err := resGetLocal(test.path, fs.Source)
+ c, err := resGetLocal(test.path, fs.Source, v)
if err != nil {
t.Errorf("Error getting resource content: %s", err)
}
@@ -126,6 +127,7 @@ func getTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*httpt
}
func TestScpGetRemote(t *testing.T) {
+ t.Parallel()
fs := new(afero.MemMapFs)
tests := []struct {
@@ -146,14 +148,16 @@ func TestScpGetRemote(t *testing.T) {
})
defer func() { srv.Close() }()
- c, err := resGetRemote(test.path, fs, cl)
+ cfg := viper.New()
+
+ c, err := resGetRemote(test.path, fs, cfg, cl)
if err != nil {
t.Errorf("Error getting resource content: %s", err)
}
if !bytes.Equal(c, test.content) {
t.Errorf("\nNet Expected: %s\nNet Actual: %s\n", string(test.content), string(c))
}
- cc, cErr := resGetCache(test.path, fs, test.ignore)
+ cc, cErr := resGetCache(test.path, fs, cfg, test.ignore)
if cErr != nil {
t.Error(cErr)
}
@@ -170,6 +174,7 @@ func TestScpGetRemote(t *testing.T) {
}
func TestParseCSV(t *testing.T) {
+ t.Parallel()
tests := []struct {
csv []byte
@@ -208,29 +213,11 @@ func TestParseCSV(t *testing.T) {
}
}
-// https://twitter.com/francesc/status/603066617124126720
-// for the construct: defer testRetryWhenDone().Reset()
-type wd struct {
- Reset func()
-}
-
-func testRetryWhenDone(f *templateFuncster) wd {
- cd := viper.GetString("cacheDir")
- viper.Set("cacheDir", helpers.GetTempDir("", f.Fs.Source))
- var tmpSleep time.Duration
- tmpSleep, resSleep = resSleep, time.Millisecond
- return wd{func() {
- viper.Set("cacheDir", cd)
- resSleep = tmpSleep
- }}
-}
-
func TestGetJSONFailParse(t *testing.T) {
+ t.Parallel()
f := newTestFuncster()
- defer testRetryWhenDone(f).Reset()
-
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if reqCount > 0 {
@@ -244,7 +231,6 @@ func TestGetJSONFailParse(t *testing.T) {
}))
defer ts.Close()
url := ts.URL + "/test.json"
- defer os.Remove(getCacheFileID(url))
want := map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}}
have := f.getJSON(url)
@@ -255,10 +241,9 @@ func TestGetJSONFailParse(t *testing.T) {
}
func TestGetCSVFailParseSep(t *testing.T) {
+ t.Parallel()
f := newTestFuncster()
- defer testRetryWhenDone(f).Reset()
-
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if reqCount > 0 {
@@ -275,7 +260,6 @@ func TestGetCSVFailParseSep(t *testing.T) {
}))
defer ts.Close()
url := ts.URL + "/test.csv"
- defer os.Remove(getCacheFileID(url))
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
have := f.getCSV(",", url)
@@ -286,11 +270,10 @@ func TestGetCSVFailParseSep(t *testing.T) {
}
func TestGetCSVFailParse(t *testing.T) {
+ t.Parallel()
f := newTestFuncster()
- defer testRetryWhenDone(f).Reset()
-
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-type", "application/json")
@@ -309,7 +292,6 @@ func TestGetCSVFailParse(t *testing.T) {
}))
defer ts.Close()
url := ts.URL + "/test.csv"
- defer os.Remove(getCacheFileID(url))
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
have := f.getCSV(",", url)
diff --git a/tpl/template_test.go b/tpl/template_test.go
index 60950bb3b..5bb6d89d3 100644
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -26,21 +26,15 @@ import (
"github.com/spf13/afero"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/helpers"
+
"github.com/spf13/hugo/tplapi"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
-func testReset() {
- viper.Reset()
-
- // TODO(bep) viper-globals
- viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
-}
-
// Some tests for Issue #1178 -- Ace
func TestAceTemplates(t *testing.T) {
+ t.Parallel()
for i, this := range []struct {
basePath string
@@ -79,7 +73,7 @@ html lang=en
d := "DATA"
- config := newDefaultDepsCfg()
+ config := newDepsConfig(viper.New())
config.WithTemplate = func(templ tplapi.Template) error {
return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
[]byte(this.baseContent), []byte(this.innerContent))
@@ -87,7 +81,7 @@ html lang=en
a := deps.New(config)
- if err := a.LoadTemplates(); err != nil {
+ if err := a.LoadResources(); err != nil {
t.Fatal(err)
}
@@ -124,6 +118,7 @@ func isAtLeastGo16() bool {
}
func TestAddTemplateFileWithMaster(t *testing.T) {
+ t.Parallel()
if !isAtLeastGo16() {
t.Skip("This test only runs on Go >= 1.6")
@@ -148,8 +143,8 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
masterTplName := "mt"
finalTplName := "tp"
- cfg := newDefaultDepsCfg()
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config := newDepsConfig(viper.New())
+ config.WithTemplate = func(templ tplapi.Template) error {
err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
@@ -189,13 +184,13 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
}
if this.writeSkipper != 1 {
- afero.WriteFile(cfg.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
+ afero.WriteFile(config.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
}
if this.writeSkipper != 2 {
- afero.WriteFile(cfg.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
+ afero.WriteFile(config.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
}
- deps.New(cfg)
+ deps.New(config)
}
@@ -204,6 +199,7 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
// A Go stdlib test for linux/arm. Will remove later.
// See #1771
func TestBigIntegerFunc(t *testing.T) {
+ t.Parallel()
var func1 = func(v int64) error {
return nil
}
@@ -234,6 +230,7 @@ func (b BI) A(v int64) error {
return nil
}
func TestBigIntegerMethod(t *testing.T) {
+ t.Parallel()
data := &BI{}
@@ -253,6 +250,7 @@ func TestBigIntegerMethod(t *testing.T) {
// Test for bugs discovered by https://github.com/dvyukov/go-fuzz
func TestTplGoFuzzReports(t *testing.T) {
+ t.Parallel()
// The following test case(s) also fail
// See https://github.com/golang/go/issues/10634
@@ -284,13 +282,14 @@ func TestTplGoFuzzReports(t *testing.T) {
H: "a,b,c,d,e,f",
}
- cfg := newDefaultDepsCfg()
- cfg.WithTemplate = func(templ tplapi.Template) error {
+ config := newDepsConfig(viper.New())
+
+ config.WithTemplate = func(templ tplapi.Template) error {
return templ.AddTemplate("fuzz", this.data)
}
- de := deps.New(cfg)
- require.NoError(t, de.LoadTemplates())
+ de := deps.New(config)
+ require.NoError(t, de.LoadResources())
templ := de.Tmpl.(*GoHTMLTemplate)
diff --git a/transform/livereloadinject.go b/transform/livereloadinject.go
index 95608185d..83fe7c106 100644
--- a/transform/livereloadinject.go
+++ b/transform/livereloadinject.go
@@ -16,24 +16,23 @@ package transform
import (
"bytes"
"fmt"
-
- "github.com/spf13/viper"
)
-func LiveReloadInject(ct contentTransformer) {
- endBodyTag := "</body>"
- match := []byte(endBodyTag)
- port := viper.Get("port")
- replaceTemplate := `<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10"></' + 'script>')</script>%s`
- replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
-
- newcontent := bytes.Replace(ct.Content(), match, replace, 1)
- if len(newcontent) == len(ct.Content()) {
- endBodyTag = "</BODY>"
- replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
+func LiveReloadInject(port int) func(ct contentTransformer) {
+ return func(ct contentTransformer) {
+ endBodyTag := "</body>"
match := []byte(endBodyTag)
- newcontent = bytes.Replace(ct.Content(), match, replace, 1)
- }
+ replaceTemplate := `<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10"></' + 'script>')</script>%s`
+ replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
+
+ newcontent := bytes.Replace(ct.Content(), match, replace, 1)
+ if len(newcontent) == len(ct.Content()) {
+ endBodyTag = "</BODY>"
+ replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
+ match := []byte(endBodyTag)
+ newcontent = bytes.Replace(ct.Content(), match, replace, 1)
+ }
- ct.Write(newcontent)
+ ct.Write(newcontent)
+ }
}
diff --git a/transform/livereloadinject_test.go b/transform/livereloadinject_test.go
index 9f28e05e2..3337243bd 100644
--- a/transform/livereloadinject_test.go
+++ b/transform/livereloadinject_test.go
@@ -18,8 +18,6 @@ import (
"fmt"
"strings"
"testing"
-
- "github.com/spf13/viper"
)
func TestLiveReloadInject(t *testing.T) {
@@ -28,11 +26,10 @@ func TestLiveReloadInject(t *testing.T) {
}
func doTestLiveReloadInject(t *testing.T, bodyEndTag string) {
- viper.Set("port", 1313)
out := new(bytes.Buffer)
in := strings.NewReader(bodyEndTag)
- tr := NewChain(LiveReloadInject)
+ tr := NewChain(LiveReloadInject(1313))
tr.Apply(out, in, []byte("path"))
expected := fmt.Sprintf(`<script data-no-instant>document.write('<script src="/livereload.js?port=1313&mindelay=10"></' + 'script>')</script>%s`, bodyEndTag)