Create a struct with all of Hugo's config options

Primary motivation is documentation, but it will also hopefully simplify the code.

Also,

* Lower case the default output format names; this is in line with the custom ones (map keys) and how
it's treated all the places. This avoids doing `stringds.EqualFold` everywhere.

Closes #10896
Closes #10620
This commit is contained in:
Bjørn Erik Pedersen
2023-01-04 18:24:36 +01:00
parent 6aededf6b4
commit 241b21b0fd
337 changed files with 13377 additions and 14898 deletions

View File

@@ -50,30 +50,18 @@ type ContentSpec struct {
anchorNameSanitizer converter.AnchorNameSanitizer
getRenderer func(t hooks.RendererType, id any) any
// SummaryLength is the length of the summary that Hugo extracts from a content.
summaryLength int
BuildFuture bool
BuildExpired bool
BuildDrafts bool
Cfg config.Provider
Cfg config.AllProvider
}
// NewContentSpec returns a ContentSpec initialized
// with the appropriate fields from the given config.Provider.
func NewContentSpec(cfg config.Provider, logger loggers.Logger, contentFs afero.Fs, ex *hexec.Exec) (*ContentSpec, error) {
func NewContentSpec(cfg config.AllProvider, logger loggers.Logger, contentFs afero.Fs, ex *hexec.Exec) (*ContentSpec, error) {
spec := &ContentSpec{
summaryLength: cfg.GetInt("summaryLength"),
BuildFuture: cfg.GetBool("buildFuture"),
BuildExpired: cfg.GetBool("buildExpired"),
BuildDrafts: cfg.GetBool("buildDrafts"),
Cfg: cfg,
}
converterProvider, err := markup.NewConverterProvider(converter.ProviderConfig{
Cfg: cfg,
Conf: cfg,
ContentFs: contentFs,
Logger: logger,
Exec: ex,
@@ -157,6 +145,9 @@ func (c *ContentSpec) SanitizeAnchorName(s string) string {
}
func (c *ContentSpec) ResolveMarkup(in string) string {
if c == nil {
panic("nil ContentSpec")
}
in = strings.ToLower(in)
switch in {
case "md", "markdown", "mdown":
@@ -194,17 +185,17 @@ func (c *ContentSpec) TruncateWordsByRune(in []string) (string, bool) {
count := 0
for index, word := range words {
if count >= c.summaryLength {
if count >= c.Cfg.SummaryLength() {
return strings.Join(words[:index], " "), true
}
runeCount := utf8.RuneCountInString(word)
if len(word) == runeCount {
count++
} else if count+runeCount < c.summaryLength {
} else if count+runeCount < c.Cfg.SummaryLength() {
count += runeCount
} else {
for ri := range word {
if count >= c.summaryLength {
if count >= c.Cfg.SummaryLength() {
truncatedWords := append(words[:index], word[:ri])
return strings.Join(truncatedWords, " "), true
}
@@ -229,7 +220,7 @@ func (c *ContentSpec) TruncateWordsToWholeSentence(s string) (string, bool) {
wordCount++
lastWordIndex = i
if wordCount >= c.summaryLength {
if wordCount >= c.Cfg.SummaryLength() {
break
}
@@ -283,19 +274,19 @@ func isEndOfSentence(r rune) bool {
func (c *ContentSpec) truncateWordsToWholeSentenceOld(content string) (string, bool) {
words := strings.Fields(content)
if c.summaryLength >= len(words) {
if c.Cfg.SummaryLength() >= len(words) {
return strings.Join(words, " "), false
}
for counter, word := range words[c.summaryLength:] {
for counter, word := range words[c.Cfg.SummaryLength():] {
if strings.HasSuffix(word, ".") ||
strings.HasSuffix(word, "?") ||
strings.HasSuffix(word, ".\"") ||
strings.HasSuffix(word, "!") {
upper := c.summaryLength + counter + 1
upper := c.Cfg.SummaryLength() + counter + 1
return strings.Join(words[:upper], " "), (upper < len(words))
}
}
return strings.Join(words[:c.summaryLength], " "), true
return strings.Join(words[:c.Cfg.SummaryLength()], " "), true
}

View File

@@ -1,4 +1,4 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
// Copyright 2023 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,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package helpers
package helpers_test
import (
"bytes"
@@ -19,12 +19,9 @@ import (
"strings"
"testing"
"github.com/spf13/afero"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
)
const tstHTMLContent = "<!DOCTYPE html><html><head><script src=\"http://two/foobar.js\"></script></head><body><nav><ul><li hugo-nav=\"section_0\"></li><li hugo-nav=\"section_1\"></li></ul></nav><article>content <a href=\"http://two/foobar\">foobar</a>. Follow up</article><p>This is some text.<br>And some more.</p></body></html>"
@@ -43,7 +40,7 @@ func TestTrimShortHTML(t *testing.T) {
{[]byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")},
}
c := newTestContentSpec()
c := newTestContentSpec(nil)
for i, test := range tests {
output := c.TrimShortHTML(test.input)
if !bytes.Equal(test.output, output) {
@@ -52,55 +49,23 @@ func TestTrimShortHTML(t *testing.T) {
}
}
func TestStripEmptyNav(t *testing.T) {
c := qt.New(t)
cleaned := stripEmptyNav([]byte("do<nav>\n</nav>\n\nbedobedo"))
c.Assert(cleaned, qt.DeepEquals, []byte("dobedobedo"))
}
func TestBytesToHTML(t *testing.T) {
c := qt.New(t)
c.Assert(BytesToHTML([]byte("dobedobedo")), qt.Equals, template.HTML("dobedobedo"))
}
func TestNewContentSpec(t *testing.T) {
cfg := config.NewWithTestDefaults()
c := qt.New(t)
cfg.Set("summaryLength", 32)
cfg.Set("buildFuture", true)
cfg.Set("buildExpired", true)
cfg.Set("buildDrafts", true)
spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil)
c.Assert(err, qt.IsNil)
c.Assert(spec.summaryLength, qt.Equals, 32)
c.Assert(spec.BuildFuture, qt.Equals, true)
c.Assert(spec.BuildExpired, qt.Equals, true)
c.Assert(spec.BuildDrafts, qt.Equals, true)
c.Assert(helpers.BytesToHTML([]byte("dobedobedo")), qt.Equals, template.HTML("dobedobedo"))
}
var benchmarkTruncateString = strings.Repeat("This is a sentence about nothing.", 20)
func BenchmarkTestTruncateWordsToWholeSentence(b *testing.B) {
c := newTestContentSpec()
c := newTestContentSpec(nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.TruncateWordsToWholeSentence(benchmarkTruncateString)
}
}
func BenchmarkTestTruncateWordsToWholeSentenceOld(b *testing.B) {
c := newTestContentSpec()
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.truncateWordsToWholeSentenceOld(benchmarkTruncateString)
}
}
func TestTruncateWordsToWholeSentence(t *testing.T) {
c := newTestContentSpec()
type test struct {
input, expected string
max int
@@ -118,7 +83,9 @@ func TestTruncateWordsToWholeSentence(t *testing.T) {
{"This... is a more difficult test?", "This... is a more difficult test?", 1, false},
}
for i, d := range data {
c.summaryLength = d.max
cfg := config.New()
cfg.Set("summaryLength", d.max)
c := newTestContentSpec(cfg)
output, truncated := c.TruncateWordsToWholeSentence(d.input)
if d.expected != output {
t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
@@ -131,7 +98,7 @@ func TestTruncateWordsToWholeSentence(t *testing.T) {
}
func TestTruncateWordsByRune(t *testing.T) {
c := newTestContentSpec()
type test struct {
input, expected string
max int
@@ -153,7 +120,9 @@ func TestTruncateWordsByRune(t *testing.T) {
{" \nThis is not a sentence\n ", "This is not", 3, true},
}
for i, d := range data {
c.summaryLength = d.max
cfg := config.New()
cfg.Set("summaryLength", d.max)
c := newTestContentSpec(cfg)
output, truncated := c.TruncateWordsByRune(strings.Fields(d.input))
if d.expected != output {
t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
@@ -168,7 +137,7 @@ func TestTruncateWordsByRune(t *testing.T) {
func TestExtractTOCNormalContent(t *testing.T) {
content := []byte("<nav>\n<ul>\nTOC<li><a href=\"#")
actualTocLessContent, actualToc := ExtractTOC(content)
actualTocLessContent, actualToc := helpers.ExtractTOC(content)
expectedTocLess := []byte("TOC<li><a href=\"#")
expectedToc := []byte("<nav id=\"TableOfContents\">\n<ul>\n")
@@ -184,7 +153,7 @@ func TestExtractTOCNormalContent(t *testing.T) {
func TestExtractTOCGreaterThanSeventy(t *testing.T) {
content := []byte("<nav>\n<ul>\nTOC This is a very long content which will definitely be greater than seventy, I promise you that.<li><a href=\"#")
actualTocLessContent, actualToc := ExtractTOC(content)
actualTocLessContent, actualToc := helpers.ExtractTOC(content)
// Because the start of Toc is greater than 70+startpoint of <li> content and empty TOC will be returned
expectedToc := []byte("")
@@ -200,7 +169,7 @@ func TestExtractTOCGreaterThanSeventy(t *testing.T) {
func TestExtractNoTOC(t *testing.T) {
content := []byte("TOC")
actualTocLessContent, actualToc := ExtractTOC(content)
actualTocLessContent, actualToc := helpers.ExtractTOC(content)
expectedToc := []byte("")
if !bytes.Equal(actualTocLessContent, content) {
@@ -225,7 +194,7 @@ func TestTotalWords(t *testing.T) {
{"One, Two, Three", 3},
{totalWordsBenchmarkString, 400},
} {
actualWordCount := TotalWords(this.s)
actualWordCount := helpers.TotalWords(this.s)
if actualWordCount != this.words {
t.Errorf("[%d] Actual word count (%d) for test string (%s) did not match %d", i, actualWordCount, this.s, this.words)
@@ -236,7 +205,7 @@ func TestTotalWords(t *testing.T) {
func BenchmarkTotalWords(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
wordCount := TotalWords(totalWordsBenchmarkString)
wordCount := helpers.TotalWords(totalWordsBenchmarkString)
if wordCount != 400 {
b.Fatal("Wordcount error")
}

View File

@@ -43,20 +43,6 @@ import (
// FilePathSeparator as defined by os.Separator.
const FilePathSeparator = string(filepath.Separator)
// FindAvailablePort returns an available and valid TCP port.
func FindAvailablePort() (*net.TCPAddr, error) {
l, err := net.Listen("tcp", ":0")
if err == nil {
defer l.Close()
addr := l.Addr()
if a, ok := addr.(*net.TCPAddr); ok {
return a, nil
}
return nil, fmt.Errorf("unable to obtain a valid tcp port: %v", addr)
}
return nil, err
}
// TCPListen starts listening on a valid TCP port.
func TCPListen() (net.Listener, *net.TCPAddr, error) {
l, err := net.Listen("tcp", ":0")

View File

@@ -1,4 +1,4 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
// Copyright 2023 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,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package helpers
package helpers_test
import (
"fmt"
@@ -21,17 +21,14 @@ import (
"time"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest"
"github.com/spf13/afero"
)
func TestResolveMarkup(t *testing.T) {
c := qt.New(t)
cfg := config.NewWithTestDefaults()
spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil)
c.Assert(err, qt.IsNil)
spec := newTestContentSpec(nil)
for i, this := range []struct {
in string
@@ -61,7 +58,7 @@ func TestResolveMarkup(t *testing.T) {
func TestDistinctLoggerDoesNotLockOnWarningPanic(t *testing.T) {
// Testing to make sure logger mutex doesn't lock if warnings cause panics.
// func Warnf() of DistinctLogger is defined in general.go
l := NewDistinctLogger(loggers.NewWarningLogger())
l := helpers.NewDistinctLogger(loggers.NewWarningLogger())
// Set PanicOnWarning to true to reproduce issue 9380
// Ensure global variable loggers.PanicOnWarning is reset to old value after test
@@ -123,7 +120,7 @@ func TestFirstUpper(t *testing.T) {
{"", ""},
{"å", "Å"},
} {
result := FirstUpper(this.in)
result := helpers.FirstUpper(this.in)
if result != this.expect {
t.Errorf("[%d] got %s but expected %s", i, result, this.expect)
}
@@ -143,7 +140,7 @@ func TestHasStringsPrefix(t *testing.T) {
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, true},
{[]string{"abra", "ca"}, []string{"abra", "ca", "dabra"}, false},
} {
result := HasStringsPrefix(this.s, this.prefix)
result := helpers.HasStringsPrefix(this.s, this.prefix)
if result != this.expect {
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
}
@@ -162,7 +159,7 @@ func TestHasStringsSuffix(t *testing.T) {
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, false},
{[]string{"abra", "ca", "dabra"}, []string{"ca", "dabra"}, true},
} {
result := HasStringsSuffix(this.s, this.suffix)
result := helpers.HasStringsSuffix(this.s, this.suffix)
if result != this.expect {
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
}
@@ -239,7 +236,7 @@ func TestSliceToLower(t *testing.T) {
}
for _, test := range tests {
res := SliceToLower(test.value)
res := helpers.SliceToLower(test.value)
for i, val := range res {
if val != test.expected[i] {
t.Errorf("Case mismatch. Expected %s, got %s", test.expected[i], res[i])
@@ -251,34 +248,34 @@ func TestSliceToLower(t *testing.T) {
func TestReaderContains(t *testing.T) {
c := qt.New(t)
for i, this := range append(containsBenchTestData, containsAdditionalTestData...) {
result := ReaderContains(strings.NewReader(this.v1), this.v2)
result := helpers.ReaderContains(strings.NewReader(this.v1), this.v2)
if result != this.expect {
t.Errorf("[%d] got %t but expected %t", i, result, this.expect)
}
}
c.Assert(ReaderContains(nil, []byte("a")), qt.Equals, false)
c.Assert(ReaderContains(nil, nil), qt.Equals, false)
c.Assert(helpers.ReaderContains(nil, []byte("a")), qt.Equals, false)
c.Assert(helpers.ReaderContains(nil, nil), qt.Equals, false)
}
func TestGetTitleFunc(t *testing.T) {
title := "somewhere over the rainbow"
c := qt.New(t)
c.Assert(GetTitleFunc("go")(title), qt.Equals, "Somewhere Over The Rainbow")
c.Assert(GetTitleFunc("chicago")(title), qt.Equals, "Somewhere over the Rainbow")
c.Assert(GetTitleFunc("Chicago")(title), qt.Equals, "Somewhere over the Rainbow")
c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(GetTitleFunc("")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(GetTitleFunc("unknown")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(helpers.GetTitleFunc("go")(title), qt.Equals, "Somewhere Over The Rainbow")
c.Assert(helpers.GetTitleFunc("chicago")(title), qt.Equals, "Somewhere over the Rainbow")
c.Assert(helpers.GetTitleFunc("Chicago")(title), qt.Equals, "Somewhere over the Rainbow")
c.Assert(helpers.GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(helpers.GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(helpers.GetTitleFunc("")(title), qt.Equals, "Somewhere Over the Rainbow")
c.Assert(helpers.GetTitleFunc("unknown")(title), qt.Equals, "Somewhere Over the Rainbow")
}
func BenchmarkReaderContains(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i, this := range containsBenchTestData {
result := ReaderContains(strings.NewReader(this.v1), this.v2)
result := helpers.ReaderContains(strings.NewReader(this.v1), this.v2)
if result != this.expect {
b.Errorf("[%d] got %t but expected %t", i, result, this.expect)
}
@@ -288,7 +285,7 @@ func BenchmarkReaderContains(b *testing.B) {
func TestUniqueStrings(t *testing.T) {
in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
output := UniqueStrings(in)
output := helpers.UniqueStrings(in)
expected := []string{"a", "b", "c", "", "d"}
if !reflect.DeepEqual(output, expected) {
t.Errorf("Expected %#v, got %#v\n", expected, output)
@@ -297,7 +294,7 @@ func TestUniqueStrings(t *testing.T) {
func TestUniqueStringsReuse(t *testing.T) {
in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
output := UniqueStringsReuse(in)
output := helpers.UniqueStringsReuse(in)
expected := []string{"a", "b", "c", "", "d"}
if !reflect.DeepEqual(output, expected) {
t.Errorf("Expected %#v, got %#v\n", expected, output)
@@ -307,18 +304,10 @@ func TestUniqueStringsReuse(t *testing.T) {
func TestUniqueStringsSorted(t *testing.T) {
c := qt.New(t)
in := []string{"a", "a", "b", "c", "b", "", "a", "", "d"}
output := UniqueStringsSorted(in)
output := helpers.UniqueStringsSorted(in)
expected := []string{"", "a", "b", "c", "d"}
c.Assert(output, qt.DeepEquals, expected)
c.Assert(UniqueStringsSorted(nil), qt.IsNil)
}
func TestFindAvailablePort(t *testing.T) {
c := qt.New(t)
addr, err := FindAvailablePort()
c.Assert(err, qt.IsNil)
c.Assert(addr, qt.Not(qt.IsNil))
c.Assert(addr.Port > 0, qt.Equals, true)
c.Assert(helpers.UniqueStringsSorted(nil), qt.IsNil)
}
func TestFastMD5FromFile(t *testing.T) {
@@ -357,23 +346,23 @@ func TestFastMD5FromFile(t *testing.T) {
defer bf1.Close()
defer bf2.Close()
m1, err := MD5FromFileFast(sf1)
m1, err := helpers.MD5FromFileFast(sf1)
c.Assert(err, qt.IsNil)
c.Assert(m1, qt.Equals, "e9c8989b64b71a88b4efb66ad05eea96")
m2, err := MD5FromFileFast(sf2)
m2, err := helpers.MD5FromFileFast(sf2)
c.Assert(err, qt.IsNil)
c.Assert(m2, qt.Not(qt.Equals), m1)
m3, err := MD5FromFileFast(bf1)
m3, err := helpers.MD5FromFileFast(bf1)
c.Assert(err, qt.IsNil)
c.Assert(m3, qt.Not(qt.Equals), m2)
m4, err := MD5FromFileFast(bf2)
m4, err := helpers.MD5FromFileFast(bf2)
c.Assert(err, qt.IsNil)
c.Assert(m4, qt.Not(qt.Equals), m3)
m5, err := MD5FromReader(bf2)
m5, err := helpers.MD5FromReader(bf2)
c.Assert(err, qt.IsNil)
c.Assert(m5, qt.Not(qt.Equals), m4)
}
@@ -394,11 +383,11 @@ func BenchmarkMD5FromFileFast(b *testing.B) {
}
b.StartTimer()
if full {
if _, err := MD5FromReader(f); err != nil {
if _, err := helpers.MD5FromReader(f); err != nil {
b.Fatal(err)
}
} else {
if _, err := MD5FromFileFast(f); err != nil {
if _, err := helpers.MD5FromFileFast(f); err != nil {
b.Fatal(err)
}
}
@@ -413,7 +402,7 @@ func BenchmarkUniqueStrings(b *testing.B) {
b.Run("Safe", func(b *testing.B) {
for i := 0; i < b.N; i++ {
result := UniqueStrings(input)
result := helpers.UniqueStrings(input)
if len(result) != 6 {
b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
}
@@ -432,7 +421,7 @@ func BenchmarkUniqueStrings(b *testing.B) {
for i := 0; i < b.N; i++ {
inputc := inputs[i]
result := UniqueStringsReuse(inputc)
result := helpers.UniqueStringsReuse(inputc)
if len(result) != 6 {
b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
}
@@ -451,7 +440,7 @@ func BenchmarkUniqueStrings(b *testing.B) {
for i := 0; i < b.N; i++ {
inputc := inputs[i]
result := UniqueStringsSorted(inputc)
result := helpers.UniqueStringsSorted(inputc)
if len(result) != 6 {
b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
}

View File

@@ -1,4 +1,4 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
// Copyright 2023 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.
@@ -28,8 +28,6 @@ import (
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/hugio"
@@ -54,7 +52,7 @@ func (p *PathSpec) MakePathsSanitized(paths []string) {
// MakePathSanitized creates a Unicode-sanitized string, with the spaces replaced
func (p *PathSpec) MakePathSanitized(s string) string {
if p.DisablePathToLower {
if p.Cfg.DisablePathToLower() {
return p.MakePath(s)
}
return strings.ToLower(p.MakePath(s))
@@ -91,7 +89,7 @@ func ishex(c rune) bool {
// Hyphens in the original input are maintained.
// Spaces will be replaced with a single hyphen, and sequential replacement hyphens will be reduced to one.
func (p *PathSpec) UnicodeSanitize(s string) string {
if p.RemovePathAccents {
if p.Cfg.RemovePathAccents() {
s = text.RemoveAccentsString(s)
}
@@ -128,7 +126,7 @@ func (p *PathSpec) UnicodeSanitize(s string) string {
return string(target)
}
func makePathRelative(inPath string, possibleDirectories ...string) (string, error) {
func MakePathRelative(inPath string, possibleDirectories ...string) (string, error) {
for _, currentPath := range possibleDirectories {
if strings.HasPrefix(inPath, currentPath) {
return strings.TrimPrefix(inPath, currentPath), nil
@@ -394,8 +392,8 @@ func OpenFileForWriting(fs afero.Fs, filename string) (afero.File, error) {
// GetCacheDir returns a cache dir from the given filesystem and config.
// The dir will be created if it does not exist.
func GetCacheDir(fs afero.Fs, cfg config.Provider) (string, error) {
cacheDir := getCacheDir(cfg)
func GetCacheDir(fs afero.Fs, cacheDir string) (string, error) {
cacheDir = cacheDirDefault(cacheDir)
if cacheDir != "" {
exists, err := DirExists(cacheDir, fs)
if err != nil {
@@ -414,9 +412,8 @@ func GetCacheDir(fs afero.Fs, cfg config.Provider) (string, error) {
return GetTempDir("hugo_cache", fs), nil
}
func getCacheDir(cfg config.Provider) string {
func cacheDirDefault(cacheDir string) string {
// Always use the cacheDir config if set.
cacheDir := cfg.GetString("cacheDir")
if len(cacheDir) > 1 {
return addTrailingFileSeparator(cacheDir)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 The Hugo Authors. All rights reserved.
// Copyright 2023 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,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package helpers
package helpers_test
import (
"fmt"
@@ -24,16 +24,12 @@ import (
"testing"
"time"
"github.com/gohugoio/hugo/langs"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/helpers"
"github.com/spf13/afero"
)
func TestMakePath(t *testing.T) {
c := qt.New(t)
tests := []struct {
input string
expected string
@@ -60,13 +56,7 @@ func TestMakePath(t *testing.T) {
}
for _, test := range tests {
v := newTestCfg()
v.Set("removePathAccents", test.removeAccents)
l := langs.NewDefaultLanguage(v)
p, err := NewPathSpec(hugofs.NewMem(v), l, nil)
c.Assert(err, qt.IsNil)
p := newTestPathSpec("removePathAccents", test.removeAccents)
output := p.MakePath(test.input)
if output != test.expected {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
@@ -75,9 +65,7 @@ func TestMakePath(t *testing.T) {
}
func TestMakePathSanitized(t *testing.T) {
v := newTestCfg()
p, _ := NewPathSpec(hugofs.NewMem(v), v, nil)
p := newTestPathSpec()
tests := []struct {
input string
@@ -100,12 +88,7 @@ func TestMakePathSanitized(t *testing.T) {
}
func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
v := newTestCfg()
v.Set("disablePathToLower", true)
l := langs.NewDefaultLanguage(v)
p, _ := NewPathSpec(hugofs.NewMem(v), l, nil)
p := newTestPathSpec("disablePathToLower", true)
tests := []struct {
input string
@@ -138,12 +121,12 @@ func TestMakePathRelative(t *testing.T) {
}
for i, d := range data {
output, _ := makePathRelative(d.inPath, d.path1, d.path2)
output, _ := helpers.MakePathRelative(d.inPath, d.path1, d.path2)
if d.output != output {
t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output)
}
}
_, error := makePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f")
_, error := helpers.MakePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f")
if error == nil {
t.Errorf("Test failed, expected error")
@@ -181,7 +164,7 @@ func doTestGetDottedRelativePath(urlFixer func(string) string, t *testing.T) {
{"/404.html", "./"},
}
for i, d := range data {
output := GetDottedRelativePath(d.input)
output := helpers.GetDottedRelativePath(d.input)
if d.expected != output {
t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
}
@@ -198,7 +181,7 @@ func TestMakeTitle(t *testing.T) {
{"make_title", "make_title"},
}
for i, d := range data {
output := MakeTitle(d.input)
output := helpers.MakeTitle(d.input)
if d.expected != output {
t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
}
@@ -219,7 +202,7 @@ func TestDirExists(t *testing.T) {
{"./..", true},
{"./../", true},
{os.TempDir(), true},
{os.TempDir() + FilePathSeparator, true},
{os.TempDir() + helpers.FilePathSeparator, true},
{"/", true},
{"/some-really-random-directory-name", false},
{"/some/really/random/directory/name", false},
@@ -228,7 +211,7 @@ func TestDirExists(t *testing.T) {
}
for i, d := range data {
exists, _ := DirExists(filepath.FromSlash(d.input), new(afero.OsFs))
exists, _ := helpers.DirExists(filepath.FromSlash(d.input), new(afero.OsFs))
if d.expected != exists {
t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists)
}
@@ -249,7 +232,7 @@ func TestIsDir(t *testing.T) {
for i, d := range data {
exists, _ := IsDir(d.input, new(afero.OsFs))
exists, _ := helpers.IsDir(d.input, new(afero.OsFs))
if d.expected != exists {
t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists)
}
@@ -310,7 +293,7 @@ func TestExists(t *testing.T) {
{nonExistentDir, false, nil},
}
for i, d := range data {
exists, err := Exists(d.input, new(afero.OsFs))
exists, err := helpers.Exists(d.input, new(afero.OsFs))
if d.expectedResult != exists {
t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists)
}
@@ -341,7 +324,7 @@ func TestAbsPathify(t *testing.T) {
for i, d := range data {
// todo see comment in AbsPathify
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
ps := newTestPathSpec("workingDir", d.workingDir)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
@@ -351,7 +334,7 @@ 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 {
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
ps := newTestPathSpec("workingDir", d.workingDir)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
@@ -360,7 +343,7 @@ func TestAbsPathify(t *testing.T) {
}
} else {
for i, d := range unixData {
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
ps := newTestPathSpec("workingDir", d.workingDir)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
@@ -383,7 +366,7 @@ func TestExtractAndGroupRootPaths(t *testing.T) {
inCopy := make([]string, len(in))
copy(inCopy, in)
result := ExtractAndGroupRootPaths(in)
result := helpers.ExtractAndGroupRootPaths(in)
c := qt.New(t)
c.Assert(fmt.Sprint(result), qt.Equals, filepath.FromSlash("[/a/b/{c,e} /c/d/e]"))
@@ -405,7 +388,7 @@ func TestExtractRootPaths(t *testing.T) {
}}
for _, test := range tests {
output := ExtractRootPaths(test.input)
output := helpers.ExtractRootPaths(test.input)
if !reflect.DeepEqual(output, test.expected) {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
}
@@ -426,7 +409,7 @@ func TestFindCWD(t *testing.T) {
// I really don't know a better way to test this function. - SPF 2014.11.04
}
for i, d := range data {
dir, err := FindCWD()
dir, err := helpers.FindCWD()
if d.expectedDir != dir {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir)
}
@@ -459,7 +442,7 @@ func TestSafeWriteToDisk(t *testing.T) {
}
for i, d := range data {
e := SafeWriteToDisk(d.filename, reader, new(afero.OsFs))
e := helpers.SafeWriteToDisk(d.filename, reader, new(afero.OsFs))
if d.expectedErr != nil {
if d.expectedErr.Error() != e.Error() {
t.Errorf("Test %d failed. Expected error %q but got %q", i, d.expectedErr.Error(), e.Error())
@@ -498,7 +481,7 @@ func TestWriteToDisk(t *testing.T) {
}
for i, d := range data {
e := WriteToDisk(d.filename, reader, new(afero.OsFs))
e := helpers.WriteToDisk(d.filename, reader, new(afero.OsFs))
if d.expectedErr != e {
t.Errorf("Test %d failed. WriteToDisk Error Expected %q but got %q", i, d.expectedErr, e)
}
@@ -515,27 +498,27 @@ func TestWriteToDisk(t *testing.T) {
func TestGetTempDir(t *testing.T) {
dir := os.TempDir()
if FilePathSeparator != dir[len(dir)-1:] {
dir = dir + FilePathSeparator
if helpers.FilePathSeparator != dir[len(dir)-1:] {
dir = dir + helpers.FilePathSeparator
}
testDir := "hugoTestFolder" + FilePathSeparator
testDir := "hugoTestFolder" + helpers.FilePathSeparator
tests := []struct {
input string
expected string
}{
{"", dir},
{testDir + " Foo bar ", dir + testDir + " Foo bar " + FilePathSeparator},
{testDir + "Foo.Bar/foo_Bar-Foo", dir + testDir + "Foo.Bar/foo_Bar-Foo" + FilePathSeparator},
{testDir + "fOO,bar:foo%bAR", dir + testDir + "fOObarfoo%bAR" + FilePathSeparator},
{testDir + "fOO,bar:foobAR", dir + testDir + "fOObarfoobAR" + FilePathSeparator},
{testDir + "FOo/BaR.html", dir + testDir + "FOo/BaR.html" + FilePathSeparator},
{testDir + "трям/трям", dir + testDir + "трям/трям" + FilePathSeparator},
{testDir + "은행", dir + testDir + "은행" + FilePathSeparator},
{testDir + "Банковский кассир", dir + testDir + "Банковский кассир" + FilePathSeparator},
{testDir + " Foo bar ", dir + testDir + " Foo bar " + helpers.FilePathSeparator},
{testDir + "Foo.Bar/foo_Bar-Foo", dir + testDir + "Foo.Bar/foo_Bar-Foo" + helpers.FilePathSeparator},
{testDir + "fOO,bar:foo%bAR", dir + testDir + "fOObarfoo%bAR" + helpers.FilePathSeparator},
{testDir + "fOO,bar:foobAR", dir + testDir + "fOObarfoobAR" + helpers.FilePathSeparator},
{testDir + "FOo/BaR.html", dir + testDir + "FOo/BaR.html" + helpers.FilePathSeparator},
{testDir + "трям/трям", dir + testDir + "трям/трям" + helpers.FilePathSeparator},
{testDir + "은행", dir + testDir + "은행" + helpers.FilePathSeparator},
{testDir + "Банковский кассир", dir + testDir + "Банковский кассир" + helpers.FilePathSeparator},
}
for _, test := range tests {
output := GetTempDir(test.input, new(afero.MemMapFs))
output := helpers.GetTempDir(test.input, new(afero.MemMapFs))
if output != test.expected {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
}

View File

@@ -34,17 +34,17 @@ type PathSpec struct {
Fs *hugofs.Fs
// The config provider to use
Cfg config.Provider
Cfg config.AllProvider
}
// NewPathSpec creates a new PathSpec from the given filesystems and language.
func NewPathSpec(fs *hugofs.Fs, cfg config.Provider, logger loggers.Logger) (*PathSpec, error) {
func NewPathSpec(fs *hugofs.Fs, cfg config.AllProvider, logger loggers.Logger) (*PathSpec, error) {
return NewPathSpecWithBaseBaseFsProvided(fs, cfg, logger, nil)
}
// NewPathSpecWithBaseBaseFsProvided creates a new PathSpec from the given filesystems and language.
// If an existing BaseFs is provided, parts of that is reused.
func NewPathSpecWithBaseBaseFsProvided(fs *hugofs.Fs, cfg config.Provider, logger loggers.Logger, baseBaseFs *filesystems.BaseFs) (*PathSpec, error) {
func NewPathSpecWithBaseBaseFsProvided(fs *hugofs.Fs, cfg config.AllProvider, logger loggers.Logger, baseBaseFs *filesystems.BaseFs) (*PathSpec, error) {
p, err := paths.New(fs, cfg)
if err != nil {
return nil, err
@@ -69,11 +69,6 @@ func NewPathSpecWithBaseBaseFsProvided(fs *hugofs.Fs, cfg config.Provider, logge
ProcessingStats: NewProcessingStats(p.Lang()),
}
basePath := ps.BaseURL.Path()
if basePath != "" && basePath != "/" {
ps.BasePath = basePath
}
return ps, nil
}

View File

@@ -1,62 +0,0 @@
// Copyright 2018 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 helpers
import (
"path/filepath"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
)
func TestNewPathSpecFromConfig(t *testing.T) {
c := qt.New(t)
v := newTestCfg()
l := langs.NewLanguage("no", v)
v.Set("disablePathToLower", true)
v.Set("removePathAccents", true)
v.Set("uglyURLs", true)
v.Set("canonifyURLs", true)
v.Set("paginatePath", "side")
v.Set("baseURL", "http://base.com/foo")
v.Set("themesDir", "thethemes")
v.Set("layoutDir", "thelayouts")
v.Set("workingDir", "thework")
v.Set("staticDir", "thestatic")
v.Set("theme", "thetheme")
langs.LoadLanguageSettings(v, nil)
fs := hugofs.NewMem(v)
fs.Source.MkdirAll(filepath.FromSlash("thework/thethemes/thetheme"), 0777)
p, err := NewPathSpec(fs, l, nil)
c.Assert(err, qt.IsNil)
c.Assert(p.CanonifyURLs, qt.Equals, true)
c.Assert(p.DisablePathToLower, qt.Equals, true)
c.Assert(p.RemovePathAccents, qt.Equals, true)
c.Assert(p.UglyURLs, qt.Equals, true)
c.Assert(p.Language.Lang, qt.Equals, "no")
c.Assert(p.PaginatePath, qt.Equals, "side")
c.Assert(p.BaseURL.String(), qt.Equals, "http://base.com/foo")
c.Assert(p.BaseURLString, qt.Equals, "http://base.com/foo")
c.Assert(p.BaseURLNoPathString, qt.Equals, "http://base.com")
c.Assert(p.ThemesDir, qt.Equals, "thethemes")
c.Assert(p.WorkingDir, qt.Equals, "thework")
}

View File

@@ -1,47 +1,47 @@
package helpers
package helpers_test
import (
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/spf13/afero"
"github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/modules"
"github.com/spf13/afero"
)
func newTestPathSpec(fs *hugofs.Fs, v config.Provider) *PathSpec {
l := langs.NewDefaultLanguage(v)
ps, _ := NewPathSpec(fs, l, nil)
return ps
}
func newTestPathSpecFromCfgAndLang(cfg config.Provider, lang string) *helpers.PathSpec {
mfs := afero.NewMemMapFs()
func newTestDefaultPathSpec(configKeyValues ...any) *PathSpec {
cfg := newTestCfg()
fs := hugofs.NewMem(cfg)
for i := 0; i < len(configKeyValues); i += 2 {
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
configs := testconfig.GetTestConfigs(mfs, cfg)
var conf config.AllProvider
if lang == "" {
conf = configs.GetFirstLanguageConfig()
} else {
conf = configs.GetByLang(lang)
if conf == nil {
panic("no config for lang " + lang)
}
}
return newTestPathSpec(fs, cfg)
}
func newTestCfg() config.Provider {
v := config.NewWithTestDefaults()
langs.LoadLanguageSettings(v, nil)
langs.LoadLanguageSettings(v, nil)
mod, err := modules.CreateProjectModule(v)
fs := hugofs.NewFrom(mfs, conf.BaseConfig())
ps, err := helpers.NewPathSpec(fs, conf, loggers.NewErrorLogger())
if err != nil {
panic(err)
}
v.Set("allModules", modules.Modules{mod})
return v
return ps
}
func newTestContentSpec() *ContentSpec {
v := config.NewWithTestDefaults()
spec, err := NewContentSpec(v, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil)
func newTestPathSpec(configKeyValues ...any) *helpers.PathSpec {
cfg := config.New()
for i := 0; i < len(configKeyValues); i += 2 {
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
}
return newTestPathSpecFromCfgAndLang(cfg, "")
}
func newTestContentSpec(cfg config.Provider) *helpers.ContentSpec {
fs := afero.NewMemMapFs()
conf := testconfig.GetTestConfig(fs, cfg)
spec, err := helpers.NewContentSpec(conf, loggers.NewErrorLogger(), fs, nil)
if err != nil {
panic(err)
}

View File

@@ -71,8 +71,9 @@ func SanitizeURLKeepTrailingSlash(in string) string {
// URLize is similar to MakePath, but with Unicode handling
// Example:
// uri: Vim (text editor)
// urlize: vim-text-editor
//
// uri: Vim (text editor)
// urlize: vim-text-editor
func (p *PathSpec) URLize(uri string) string {
return p.URLEscape(p.MakePathSanitized(uri))
}
@@ -141,16 +142,16 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
func (p *PathSpec) getBaseURLRoot(path string) string {
if strings.HasPrefix(path, "/") {
// Treat it as relative to the server root.
return p.BaseURLNoPathString
return p.Cfg.BaseURL().WithoutPath
} else {
// Treat it as relative to the baseURL.
return p.BaseURLString
return p.Cfg.BaseURL().WithPath
}
}
func (p *PathSpec) RelURL(in string, addLanguage bool) string {
baseURL := p.getBaseURLRoot(in)
canonifyURLs := p.CanonifyURLs
canonifyURLs := p.Cfg.CanonifyURLs()
if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
return in
}
@@ -217,25 +218,3 @@ func (p *PathSpec) PrependBasePath(rel string, isAbs bool) string {
}
return rel
}
// URLizeAndPrep applies misc sanitation to the given URL to get it in line
// with the Hugo standard.
func (p *PathSpec) URLizeAndPrep(in string) string {
return p.URLPrep(p.URLize(in))
}
// URLPrep applies misc sanitation to the given URL.
func (p *PathSpec) URLPrep(in string) string {
if p.UglyURLs {
return paths.Uglify(SanitizeURL(in))
}
pretty := paths.PrettifyURL(SanitizeURL(in))
if path.Ext(pretty) == ".xml" {
return pretty
}
url, err := purell.NormalizeURLString(pretty, purell.FlagAddTrailingSlash)
if err != nil {
return pretty
}
return url
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 The Hugo Authors. All rights reserved.
// Copyright 2023 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,21 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package helpers
package helpers_test
import (
"fmt"
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
)
func TestURLize(t *testing.T) {
v := newTestCfg()
l := langs.NewDefaultLanguage(v)
p, _ := NewPathSpec(hugofs.NewMem(v), l, nil)
p := newTestPathSpec()
tests := []struct {
input string
@@ -61,10 +60,6 @@ func TestAbsURL(t *testing.T) {
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
c := qt.New(t)
v := newTestCfg()
v.Set("multilingual", multilingual)
v.Set("defaultContentLanguage", "en")
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
tests := []struct {
input string
@@ -103,24 +98,42 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
}
for _, test := range tests {
v.Set("baseURL", test.baseURL)
v.Set("contentDir", "content")
l := langs.NewLanguage(lang, v)
p, _ := NewPathSpec(hugofs.NewMem(v), l, nil)
output := p.AbsURL(test.input, addLanguage)
expected := test.expected
if multilingual && addLanguage {
if !defaultInSubDir && lang == "en" {
expected = strings.Replace(expected, "MULTI", "", 1)
} else {
expected = strings.Replace(expected, "MULTI", lang+"/", 1)
c.Run(fmt.Sprintf("%v/%t-%t-%t/%s", test, defaultInSubDir, addLanguage, multilingual, lang), func(c *qt.C) {
v := config.New()
if multilingual {
v.Set("languages", map[string]any{
"fr": map[string]interface{}{
"weight": 20,
},
"en": map[string]interface{}{
"weight": 10,
},
})
}
} else {
expected = strings.Replace(expected, "MULTI", "", 1)
}
v.Set("defaultContentLanguage", "en")
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
v.Set("baseURL", test.baseURL)
c.Assert(output, qt.Equals, expected)
var configLang string
if multilingual {
configLang = lang
}
p := newTestPathSpecFromCfgAndLang(v, configLang)
output := p.AbsURL(test.input, addLanguage)
expected := test.expected
if multilingual && addLanguage {
if !defaultInSubDir && lang == "en" {
expected = strings.Replace(expected, "MULTI", "", 1)
} else {
expected = strings.Replace(expected, "MULTI", lang+"/", 1)
}
} else {
expected = strings.Replace(expected, "MULTI", "", 1)
}
c.Assert(output, qt.Equals, expected)
})
}
}
@@ -137,9 +150,19 @@ func TestRelURL(t *testing.T) {
}
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
t.Helper()
c := qt.New(t)
v := newTestCfg()
v.Set("multilingual", multilingual)
v := config.New()
if multilingual {
v.Set("languages", map[string]any{
"fr": map[string]interface{}{
"weight": 20,
},
"en": map[string]interface{}{
"weight": 10,
},
})
}
v.Set("defaultContentLanguage", "en")
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
@@ -182,25 +205,31 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
}
for i, test := range tests {
v.Set("baseURL", test.baseURL)
v.Set("canonifyURLs", test.canonify)
l := langs.NewLanguage(lang, v)
p, _ := NewPathSpec(hugofs.NewMem(v), l, nil)
c.Run(fmt.Sprintf("%v/%t%t%t/%s", test, defaultInSubDir, addLanguage, multilingual, lang), func(c *qt.C) {
output := p.RelURL(test.input, addLanguage)
expected := test.expected
if multilingual && addLanguage {
if !defaultInSubDir && lang == "en" {
expected = strings.Replace(expected, "MULTI", "", 1)
} else {
expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
v.Set("baseURL", test.baseURL)
v.Set("canonifyURLs", test.canonify)
var configLang string
if multilingual {
configLang = lang
}
} else {
expected = strings.Replace(expected, "MULTI", "", 1)
}
p := newTestPathSpecFromCfgAndLang(v, configLang)
c.Assert(output, qt.Equals, expected, qt.Commentf("[%d] %s", i, test.input))
output := p.RelURL(test.input, addLanguage)
expected := test.expected
if multilingual && addLanguage {
if !defaultInSubDir && lang == "en" {
expected = strings.Replace(expected, "MULTI", "", 1)
} else {
expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
}
} else {
expected = strings.Replace(expected, "MULTI", "", 1)
}
c.Assert(output, qt.Equals, expected, qt.Commentf("[%d] %s", i, test.input))
})
}
}
@@ -216,8 +245,8 @@ func TestSanitizeURL(t *testing.T) {
}
for i, test := range tests {
o1 := SanitizeURL(test.input)
o2 := SanitizeURLKeepTrailingSlash(test.input)
o1 := helpers.SanitizeURL(test.input)
o2 := helpers.SanitizeURLKeepTrailingSlash(test.input)
expected2 := test.expected
@@ -233,28 +262,3 @@ func TestSanitizeURL(t *testing.T) {
}
}
}
func TestURLPrep(t *testing.T) {
type test struct {
ugly bool
input string
output string
}
data := []test{
{false, "/section/name.html", "/section/name/"},
{true, "/section/name/index.html", "/section/name.html"},
}
for i, d := range data {
v := newTestCfg()
v.Set("uglyURLs", d.ugly)
l := langs.NewDefaultLanguage(v)
p, _ := NewPathSpec(hugofs.NewMem(v), l, nil)
output := p.URLPrep(d.input)
if d.output != output {
t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output)
}
}
}