mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-29 22:29:56 +02:00
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:
@@ -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
|
||||
}
|
||||
|
@@ -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")
|
||||
}
|
||||
|
@@ -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")
|
||||
|
@@ -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)))
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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")
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user