summaryrefslogtreecommitdiffstats
path: root/hugolib/testhelpers_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'hugolib/testhelpers_test.go')
-rw-r--r--hugolib/testhelpers_test.go268
1 files changed, 201 insertions, 67 deletions
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 8c72e10d0..ac511367d 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -2,13 +2,17 @@ package hugolib
import (
"io"
- "io/ioutil"
"path/filepath"
"runtime"
"strconv"
"testing"
"unicode/utf8"
+ "github.com/gohugoio/hugo/parser/metadecoders"
+
+ "github.com/gohugoio/hugo/parser"
+ "github.com/pkg/errors"
+
"bytes"
"fmt"
"regexp"
@@ -39,9 +43,12 @@ import (
)
type sitesBuilder struct {
- Cfg config.Provider
- Fs *hugofs.Fs
- T testing.TB
+ Cfg config.Provider
+ environ []string
+
+ Fs *hugofs.Fs
+ T testing.TB
+ depsCfg deps.DepsCfg
*require.Assertions
@@ -60,13 +67,16 @@ type sitesBuilder struct {
theme string
// Default toml
- configFormat string
+ configFormat string
+ configFileSet bool
+ viperSet bool
// Default is empty.
// TODO(bep) revisit this and consider always setting it to something.
// Consider this in relation to using the BaseFs.PublishFs to all publishing.
workingDir string
+ addNothing bool
// Base data/content
contentFilePairs []string
templateFilePairs []string
@@ -94,18 +104,22 @@ func newTestSitesBuilder(t testing.TB) *sitesBuilder {
return &sitesBuilder{T: t, Assertions: require.New(t), Fs: fs, configFormat: "toml", dumper: litterOptions}
}
-func createTempDir(prefix string) (string, func(), error) {
- workDir, err := ioutil.TempDir("", prefix)
- if err != nil {
- return "", nil, err
- }
+func newTestSitesBuilderFromDepsCfg(t testing.TB, d deps.DepsCfg) *sitesBuilder {
+ assert := require.New(t)
- if runtime.GOOS == "darwin" && !strings.HasPrefix(workDir, "/private") {
- // To get the entry folder in line with the rest. This its a little bit
- // mysterious, but so be it.
- workDir = "/private" + workDir
+ litterOptions := litter.Options{
+ HidePrivateFields: true,
+ StripPackageNames: true,
+ Separator: " ",
}
- return workDir, func() { os.RemoveAll(workDir) }, nil
+
+ b := &sitesBuilder{T: t, Assertions: assert, depsCfg: d, Fs: d.Fs, dumper: litterOptions}
+ workingDir := d.Cfg.GetString("workingDir")
+
+ b.WithWorkingDir(workingDir)
+
+ return b.WithViper(d.Cfg.(*viper.Viper))
+
}
func (s *sitesBuilder) Running() *sitesBuilder {
@@ -113,17 +127,31 @@ func (s *sitesBuilder) Running() *sitesBuilder {
return s
}
+func (s *sitesBuilder) WithNothingAdded() *sitesBuilder {
+ s.addNothing = true
+ return s
+}
+
func (s *sitesBuilder) WithLogger(logger *loggers.Logger) *sitesBuilder {
s.logger = logger
return s
}
func (s *sitesBuilder) WithWorkingDir(dir string) *sitesBuilder {
- s.workingDir = dir
+ s.workingDir = filepath.FromSlash(dir)
+ return s
+}
+
+func (s *sitesBuilder) WithEnviron(env ...string) *sitesBuilder {
+ for i := 0; i < len(env); i += 2 {
+ s.environ = append(s.environ, fmt.Sprintf("%s=%s", env[i], env[i+1]))
+ }
return s
}
func (s *sitesBuilder) WithConfigTemplate(data interface{}, format, configTemplate string) *sitesBuilder {
+ s.T.Helper()
+
if format == "" {
format = "toml"
}
@@ -138,32 +166,59 @@ func (s *sitesBuilder) WithConfigTemplate(data interface{}, format, configTempla
}
func (s *sitesBuilder) WithViper(v *viper.Viper) *sitesBuilder {
- loadDefaultSettingsFor(v)
- s.Cfg = v
+ s.T.Helper()
+ if s.configFileSet {
+ s.T.Fatal("WithViper: use Viper or config.toml, not both")
+ }
+ defer func() {
+ s.viperSet = true
+ }()
- return s
+ // Write to a config file to make sure the tests follow the same code path.
+ var buff bytes.Buffer
+ m := v.AllSettings()
+ s.Assertions.NoError(parser.InterfaceToConfig(m, metadecoders.TOML, &buff))
+ return s.WithConfigFile("toml", buff.String())
}
func (s *sitesBuilder) WithConfigFile(format, conf string) *sitesBuilder {
- writeSource(s.T, s.Fs, "config."+format, conf)
+ s.T.Helper()
+ if s.viperSet {
+ s.T.Fatal("WithConfigFile: use Viper or config.toml, not both")
+ }
+ s.configFileSet = true
+ filename := s.absFilename("config." + format)
+ writeSource(s.T, s.Fs, filename, conf)
s.configFormat = format
return s
}
func (s *sitesBuilder) WithThemeConfigFile(format, conf string) *sitesBuilder {
+ s.T.Helper()
if s.theme == "" {
s.theme = "test-theme"
}
filename := filepath.Join("themes", s.theme, "config."+format)
- writeSource(s.T, s.Fs, filename, conf)
+ writeSource(s.T, s.Fs, s.absFilename(filename), conf)
return s
}
-func (s *sitesBuilder) WithSourceFile(filename, content string) *sitesBuilder {
- writeSource(s.T, s.Fs, filepath.FromSlash(filename), content)
+func (s *sitesBuilder) WithSourceFile(filenameContent ...string) *sitesBuilder {
+ s.T.Helper()
+ for i := 0; i < len(filenameContent); i += 2 {
+ writeSource(s.T, s.Fs, s.absFilename(filenameContent[i]), filenameContent[i+1])
+ }
return s
}
+func (s *sitesBuilder) absFilename(filename string) string {
+ filename = filepath.FromSlash(filename)
+ if s.workingDir != "" && !strings.HasPrefix(filename, s.workingDir) {
+ filename = filepath.Join(s.workingDir, filename)
+ }
+ return filename
+}
+
const commonConfigSections = `
[services]
@@ -191,10 +246,12 @@ privacyEnhanced = true
`
func (s *sitesBuilder) WithSimpleConfigFile() *sitesBuilder {
+ s.T.Helper()
return s.WithSimpleConfigFileAndBaseURL("http://example.com/")
}
func (s *sitesBuilder) WithSimpleConfigFileAndBaseURL(baseURL string) *sitesBuilder {
+ s.T.Helper()
config := fmt.Sprintf("baseURL = %q", baseURL)
config = config + commonConfigSections
@@ -323,7 +380,7 @@ func (s *sitesBuilder) EditFiles(filenameContent ...string) *sitesBuilder {
for i := 0; i < len(filenameContent); i += 2 {
filename, content := filepath.FromSlash(filenameContent[i]), filenameContent[i+1]
changedFiles = append(changedFiles, filename)
- writeSource(s.T, s.Fs, filename, content)
+ writeSource(s.T, s.Fs, s.absFilename(filename), content)
}
s.changedFiles = changedFiles
@@ -354,6 +411,7 @@ func (s *sitesBuilder) writeFilePairs(folder string, filenameContent []string) *
func (s *sitesBuilder) CreateSites() *sitesBuilder {
if err := s.CreateSitesE(); err != nil {
+ herrors.PrintStackTrace(err)
s.Fatalf("Failed to create sites: %s", err)
}
@@ -361,34 +419,72 @@ func (s *sitesBuilder) CreateSites() *sitesBuilder {
}
func (s *sitesBuilder) LoadConfig() error {
- cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: s.Fs.Source, Filename: "config." + s.configFormat})
+ if !s.configFileSet {
+ s.WithSimpleConfigFile()
+ }
+
+ cfg, _, err := LoadConfig(ConfigSourceDescriptor{
+ WorkingDir: s.workingDir,
+ Fs: s.Fs.Source,
+ Logger: s.logger,
+ Environ: s.environ,
+ Filename: "config." + s.configFormat}, func(cfg config.Provider) error {
+
+ return nil
+ })
+
if err != nil {
return err
}
+
s.Cfg = cfg
+
return nil
}
func (s *sitesBuilder) CreateSitesE() error {
- s.addDefaults()
- s.writeFilePairs("content", s.contentFilePairs)
- s.writeFilePairs("content", s.contentFilePairsAdded)
- s.writeFilePairs("layouts", s.templateFilePairs)
- s.writeFilePairs("layouts", s.templateFilePairsAdded)
- s.writeFilePairs("data", s.dataFilePairs)
- s.writeFilePairs("data", s.dataFilePairsAdded)
- s.writeFilePairs("i18n", s.i18nFilePairs)
- s.writeFilePairs("i18n", s.i18nFilePairsAdded)
-
- if s.Cfg == nil {
- if err := s.LoadConfig(); err != nil {
- return err
+ if !s.addNothing {
+ if _, ok := s.Fs.Source.(*afero.OsFs); ok {
+ for _, dir := range []string{
+ "content/sect",
+ "layouts/_default",
+ "layouts/partials",
+ "layouts/shortcodes",
+ "data",
+ "i18n",
+ } {
+ if err := os.MkdirAll(filepath.Join(s.workingDir, dir), 0777); err != nil {
+ return errors.Wrapf(err, "failed to create %q", dir)
+ }
+ }
}
+
+ s.addDefaults()
+ s.writeFilePairs("content", s.contentFilePairsAdded)
+ s.writeFilePairs("layouts", s.templateFilePairsAdded)
+ s.writeFilePairs("data", s.dataFilePairsAdded)
+ s.writeFilePairs("i18n", s.i18nFilePairsAdded)
+
+ s.writeFilePairs("i18n", s.i18nFilePairs)
+ s.writeFilePairs("data", s.dataFilePairs)
+ s.writeFilePairs("content", s.contentFilePairs)
+ s.writeFilePairs("layouts", s.templateFilePairs)
+
}
- sites, err := NewHugoSites(deps.DepsCfg{Fs: s.Fs, Cfg: s.Cfg, Logger: s.logger, Running: s.running})
+ if err := s.LoadConfig(); err != nil {
+ return errors.Wrap(err, "failed to load config")
+ }
+
+ depsCfg := s.depsCfg
+ depsCfg.Fs = s.Fs
+ depsCfg.Cfg = s.Cfg
+ depsCfg.Logger = s.logger
+ depsCfg.Running = s.running
+
+ sites, err := NewHugoSites(depsCfg)
if err != nil {
- return err
+ return errors.Wrap(err, "failed to create sites")
}
s.H = sites
@@ -404,10 +500,12 @@ func (s *sitesBuilder) BuildE(cfg BuildCfg) error {
}
func (s *sitesBuilder) Build(cfg BuildCfg) *sitesBuilder {
+ s.T.Helper()
return s.build(cfg, false)
}
func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder {
+ s.T.Helper()
return s.build(cfg, true)
}
@@ -528,14 +626,8 @@ hello:
}
func (s *sitesBuilder) Fatalf(format string, args ...interface{}) {
- Fatalf(s.T, format, args...)
-}
-
-func Fatalf(t testing.TB, format string, args ...interface{}) {
- trace := stackTrace()
- format = format + "\n%s"
- args = append(args, trace)
- t.Fatalf(format, args...)
+ s.T.Helper()
+ s.T.Fatalf(format, args...)
}
func stackTrace() string {
@@ -543,9 +635,10 @@ func stackTrace() string {
}
func (s *sitesBuilder) AssertFileContentFn(filename string, f func(s string) bool) {
+ s.T.Helper()
content := s.FileContent(filename)
if !f(content) {
- s.Fatalf("Assert failed for %q", filename)
+ s.Fatalf("Assert failed for %q in content\n%s", filename, content)
}
}
@@ -554,6 +647,7 @@ func (s *sitesBuilder) AssertHome(matches ...string) {
}
func (s *sitesBuilder) AssertFileContent(filename string, matches ...string) {
+ s.T.Helper()
content := s.FileContent(filename)
for _, match := range matches {
if !strings.Contains(content, match) {
@@ -563,10 +657,16 @@ func (s *sitesBuilder) AssertFileContent(filename string, matches ...string) {
}
func (s *sitesBuilder) FileContent(filename string) string {
+ s.T.Helper()
+ filename = filepath.FromSlash(filename)
+ if !strings.HasPrefix(filename, s.workingDir) {
+ filename = filepath.Join(s.workingDir, filename)
+ }
return readDestination(s.T, s.Fs, filename)
}
func (s *sitesBuilder) AssertObject(expected string, object interface{}) {
+ s.T.Helper()
got := s.dumper.Sdump(object)
expected = strings.TrimSpace(expected)
@@ -633,17 +733,41 @@ func (th testHelper) replaceDefaultContentLanguageValue(value string) string {
return value
}
-func newTestCfg() (*viper.Viper, *hugofs.Fs) {
+func loadTestConfig(fs afero.Fs, withConfig ...func(cfg config.Provider) error) (*viper.Viper, error) {
+ v, _, err := LoadConfig(ConfigSourceDescriptor{Fs: fs}, withConfig...)
+ return v, err
+}
+func newTestCfgBasic() (*viper.Viper, *hugofs.Fs) {
+ mm := afero.NewMemMapFs()
v := viper.New()
- fs := hugofs.NewMem(v)
+ v.Set("defaultContentLanguageInSubdir", true)
- v.SetFs(fs.Source)
+ fs := hugofs.NewFrom(hugofs.NewBaseFileDecorator(mm), v)
- loadDefaultSettingsFor(v)
+ return v, fs
- // Default is false, but true is easier to use as default in tests
- v.Set("defaultContentLanguageInSubdir", true)
+}
+
+func newTestCfg(withConfig ...func(cfg config.Provider) error) (*viper.Viper, *hugofs.Fs) {
+ mm := afero.NewMemMapFs()
+
+ v, err := loadTestConfig(mm, func(cfg config.Provider) error {
+ // Default is false, but true is easier to use as default in tests
+ cfg.Set("defaultContentLanguageInSubdir", true)
+
+ for _, w := range withConfig {
+ w(cfg)
+ }
+
+ return nil
+ })
+
+ if err != nil && err != ErrNoConfigFile {
+ panic(err)
+ }
+
+ fs := hugofs.NewFrom(hugofs.NewBaseFileDecorator(mm), v)
return v, fs
@@ -651,9 +775,10 @@ func newTestCfg() (*viper.Viper, *hugofs.Fs) {
func newTestSitesFromConfig(t testing.TB, afs afero.Fs, tomlConfig string, layoutPathContentPairs ...string) (testHelper, *HugoSites) {
if len(layoutPathContentPairs)%2 != 0 {
- Fatalf(t, "Layouts must be provided in pairs")
+ t.Fatalf("Layouts must be provided in pairs")
}
+ writeToFs(t, afs, filepath.Join("content", ".gitkeep"), "")
writeToFs(t, afs, "config.toml", tomlConfig)
cfg, err := LoadConfigDefault(afs)
@@ -673,14 +798,6 @@ func newTestSitesFromConfig(t testing.TB, afs afero.Fs, tomlConfig string, layou
return th, h
}
-func newTestSitesFromConfigWithDefaultTemplates(t testing.TB, tomlConfig string) (testHelper, *HugoSites) {
- return newTestSitesFromConfig(t, afero.NewMemMapFs(), tomlConfig,
- "layouts/_default/single.html", "Single|{{ .Title }}|{{ .Content }}",
- "layouts/_default/list.html", "List|{{ .Title }}|{{ .Content }}",
- "layouts/_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}",
- )
-}
-
func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tpl.TemplateHandler) error {
return func(templ tpl.TemplateHandler) error {
@@ -694,12 +811,16 @@ func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ
}
}
+// TODO(bep) replace these with the builder
func buildSingleSite(t testing.TB, depsCfg deps.DepsCfg, buildCfg BuildCfg) *Site {
return buildSingleSiteExpected(t, false, false, depsCfg, buildCfg)
}
func buildSingleSiteExpected(t testing.TB, expectSiteInitEror, expectBuildError bool, depsCfg deps.DepsCfg, buildCfg BuildCfg) *Site {
- h, err := NewHugoSites(depsCfg)
+ t.Helper()
+ b := newTestSitesBuilderFromDepsCfg(t, depsCfg).WithNothingAdded()
+
+ err := b.CreateSitesE()
if expectSiteInitEror {
require.Error(t, err)
@@ -708,6 +829,8 @@ func buildSingleSiteExpected(t testing.TB, expectSiteInitEror, expectBuildError
require.NoError(t, err)
}
+ h := b.H
+
require.Len(t, h.Sites, 1)
if expectBuildError {
@@ -751,9 +874,13 @@ func content(c resource.ContentProvider) string {
func dumpPages(pages ...page.Page) {
fmt.Println("---------")
for i, p := range pages {
- fmt.Printf("%d: Kind: %s Title: %-10s RelPermalink: %-10s Path: %-10s sections: %s\n",
+ var meta interface{}
+ if p.File() != nil && p.File().FileInfo() != nil {
+ meta = p.File().FileInfo().Meta()
+ }
+ fmt.Printf("%d: Kind: %s Title: %-10s RelPermalink: %-10s Path: %-10s sections: %s Lang: %s Meta: %v\n",
i+1,
- p.Kind(), p.Title(), p.RelPermalink(), p.Path(), p.SectionsPath())
+ p.Kind(), p.Title(), p.RelPermalink(), p.Path(), p.SectionsPath(), p.Lang(), meta)
}
}
@@ -802,3 +929,10 @@ func parallel(t *testing.T) {
t.Parallel()
}
}
+
+func skipSymlink(t *testing.T) {
+ if runtime.GOOS == "windows" && os.Getenv("CI") == "" {
+ t.Skip("skip symlink test on local Windows (needs admin)")
+ }
+
+}