diff options
Diffstat (limited to 'hugolib/testhelpers_test.go')
-rw-r--r-- | hugolib/testhelpers_test.go | 268 |
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)") + } + +} |