mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-30 22:39:58 +02:00
all: Refactor to nonglobal Viper, i18n etc.
This is a final rewrite that removes all the global state in Hugo, which also enables the use if `t.Parallel` in tests. Updates #2701 Fixes #3016
This commit is contained in:
@@ -21,6 +21,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/eknkc/amber"
|
||||
"github.com/spf13/afero"
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
@@ -31,6 +33,9 @@ import (
|
||||
|
||||
// TODO(bep) globals get rid of the rest of the jww.ERR etc.
|
||||
|
||||
// Protecting global map access (Amber)
|
||||
var amberMu sync.Mutex
|
||||
|
||||
type templateErr struct {
|
||||
name string
|
||||
err error
|
||||
@@ -132,6 +137,7 @@ func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
|
||||
|
||||
t.amberFuncMap = template.FuncMap{}
|
||||
|
||||
amberMu.Lock()
|
||||
for k, v := range amber.FuncMap {
|
||||
t.amberFuncMap[k] = v
|
||||
}
|
||||
@@ -143,6 +149,7 @@ func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
|
||||
panic("should never be invoked")
|
||||
}
|
||||
}
|
||||
amberMu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
@@ -362,7 +369,9 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
|
||||
return err
|
||||
}
|
||||
|
||||
amberMu.Lock()
|
||||
templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
|
||||
amberMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -482,11 +491,11 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
|
||||
}
|
||||
if needsBase {
|
||||
|
||||
layoutDir := helpers.GetLayoutDirPath()
|
||||
layoutDir := t.PathSpec.GetLayoutDirPath()
|
||||
currBaseFilename := fmt.Sprintf("%s-%s", helpers.Filename(path), baseFileName)
|
||||
templateDir := filepath.Dir(path)
|
||||
themeDir := filepath.Join(helpers.GetThemeDir())
|
||||
relativeThemeLayoutsDir := filepath.Join(helpers.GetRelativeThemeDir(), "layouts")
|
||||
themeDir := filepath.Join(t.PathSpec.GetThemeDir())
|
||||
relativeThemeLayoutsDir := filepath.Join(t.PathSpec.GetRelativeThemeDir(), "layouts")
|
||||
|
||||
var baseTemplatedDir string
|
||||
|
||||
|
@@ -113,6 +113,7 @@ F3: {{ Echo (printf "themes/%s-theme" .Site.Params.LOWER) }}
|
||||
)
|
||||
|
||||
func TestParamsKeysToLower(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Error(t, applyTemplateTransformers(nil))
|
||||
|
||||
@@ -190,6 +191,7 @@ func BenchmarkTemplateParamsKeysToLower(b *testing.B) {
|
||||
}
|
||||
|
||||
func TestParamsKeysToLowerVars(t *testing.T) {
|
||||
t.Parallel()
|
||||
var (
|
||||
ctx = map[string]interface{}{
|
||||
"Params": map[string]interface{}{
|
||||
@@ -227,6 +229,7 @@ Blue: {{ $__amber_1.Blue}}
|
||||
}
|
||||
|
||||
func TestParamsKeysToLowerInBlockTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ctx = map[string]interface{}{
|
||||
|
@@ -21,6 +21,7 @@ import (
|
||||
)
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
t.Parallel()
|
||||
var err error
|
||||
cases := []struct {
|
||||
v1 interface{}
|
||||
|
@@ -46,7 +46,6 @@ import (
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
// Importing image codecs for image.DecodeConfig
|
||||
_ "image/gif"
|
||||
@@ -58,7 +57,6 @@ import (
|
||||
type templateFuncster struct {
|
||||
funcMap template.FuncMap
|
||||
cachedPartials partialCache
|
||||
|
||||
*deps.Deps
|
||||
}
|
||||
|
||||
@@ -398,6 +396,7 @@ func intersect(l1, l2 interface{}) (interface{}, error) {
|
||||
}
|
||||
|
||||
// ResetCaches resets all caches that might be used during build.
|
||||
// TODO(bep) globals move image config cache to funcster
|
||||
func ResetCaches() {
|
||||
resetImageConfigCache()
|
||||
}
|
||||
@@ -1357,31 +1356,29 @@ func returnWhenSet(a, k interface{}) interface{} {
|
||||
}
|
||||
|
||||
// highlight returns an HTML string with syntax highlighting applied.
|
||||
func highlight(in interface{}, lang, opts string) (template.HTML, error) {
|
||||
func (t *templateFuncster) highlight(in interface{}, lang, opts string) (template.HTML, error) {
|
||||
str, err := cast.ToStringE(in)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.HTML(helpers.Highlight(html.UnescapeString(str), lang, opts)), nil
|
||||
return template.HTML(helpers.Highlight(t.Cfg, html.UnescapeString(str), lang, opts)), nil
|
||||
}
|
||||
|
||||
var markdownTrimPrefix = []byte("<p>")
|
||||
var markdownTrimSuffix = []byte("</p>\n")
|
||||
|
||||
// markdownify renders a given string from Markdown to HTML.
|
||||
func markdownify(in interface{}) (template.HTML, error) {
|
||||
func (t *templateFuncster) markdownify(in interface{}) (template.HTML, error) {
|
||||
text, err := cast.ToStringE(in)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
language := viper.Get("currentContentLanguage").(*helpers.Language)
|
||||
|
||||
m := helpers.RenderBytes(&helpers.RenderingContext{
|
||||
ConfigProvider: language,
|
||||
Content: []byte(text), PageFmt: "markdown"})
|
||||
m := t.ContentSpec.RenderBytes(&helpers.RenderingContext{
|
||||
Cfg: t.Cfg,
|
||||
Content: []byte(text), PageFmt: "markdown"})
|
||||
m = bytes.TrimPrefix(m, markdownTrimPrefix)
|
||||
m = bytes.TrimSuffix(m, markdownTrimSuffix)
|
||||
return template.HTML(m), nil
|
||||
@@ -2143,7 +2140,7 @@ func (t *templateFuncster) initFuncMap() {
|
||||
"getenv": getenv,
|
||||
"gt": gt,
|
||||
"hasPrefix": hasPrefix,
|
||||
"highlight": highlight,
|
||||
"highlight": t.highlight,
|
||||
"htmlEscape": htmlEscape,
|
||||
"htmlUnescape": htmlUnescape,
|
||||
"humanize": humanize,
|
||||
@@ -2159,7 +2156,7 @@ func (t *templateFuncster) initFuncMap() {
|
||||
"le": le,
|
||||
"lower": lower,
|
||||
"lt": lt,
|
||||
"markdownify": markdownify,
|
||||
"markdownify": t.markdownify,
|
||||
"md5": md5,
|
||||
"mod": mod,
|
||||
"modBool": modBool,
|
||||
@@ -2211,8 +2208,8 @@ func (t *templateFuncster) initFuncMap() {
|
||||
"upper": upper,
|
||||
"urlize": t.PathSpec.URLize,
|
||||
"where": where,
|
||||
"i18n": i18nTranslate,
|
||||
"T": i18nTranslate,
|
||||
"i18n": t.Translate,
|
||||
"T": t.Translate,
|
||||
}
|
||||
|
||||
t.funcMap = funcMap
|
||||
|
@@ -42,7 +42,9 @@ import (
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/hugo/config"
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
"github.com/spf13/hugo/i18n"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -53,12 +55,16 @@ var (
|
||||
logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
)
|
||||
|
||||
func newDefaultDepsCfg() deps.DepsCfg {
|
||||
func newDepsConfig(cfg config.Provider) deps.DepsCfg {
|
||||
l := helpers.NewLanguage("en", cfg)
|
||||
l.Set("i18nDir", "i18n")
|
||||
return deps.DepsCfg{
|
||||
Language: helpers.NewLanguage("en"),
|
||||
Fs: hugofs.NewMem(),
|
||||
Logger: logger,
|
||||
TemplateProvider: DefaultTemplateProvider,
|
||||
Language: l,
|
||||
Cfg: cfg,
|
||||
Fs: hugofs.NewMem(l),
|
||||
Logger: logger,
|
||||
TemplateProvider: DefaultTemplateProvider,
|
||||
TranslationProvider: i18n.NewTranslationProvider(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,22 +94,17 @@ func tstIsLt(tp tstCompareType) bool {
|
||||
return tp == tstLt || tp == tstLe
|
||||
}
|
||||
|
||||
func tstInitTemplates() {
|
||||
viper.Set("CurrentContentLanguage", helpers.NewLanguage("en"))
|
||||
helpers.ResetConfigProvider()
|
||||
}
|
||||
|
||||
func TestFuncsInTemplate(t *testing.T) {
|
||||
|
||||
testReset()
|
||||
t.Parallel()
|
||||
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
viper.Set("workingDir", workingDir)
|
||||
viper.Set("currentContentLanguage", helpers.NewDefaultLanguage())
|
||||
viper.Set("multilingual", true)
|
||||
v := viper.New()
|
||||
|
||||
fs := hugofs.NewMem()
|
||||
v.Set("workingDir", workingDir)
|
||||
v.Set("multilingual", true)
|
||||
|
||||
fs := hugofs.NewMem(v)
|
||||
|
||||
afero.WriteFile(fs.Source, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
|
||||
|
||||
@@ -268,11 +269,10 @@ urlize: bat-man
|
||||
data.Section = "blog"
|
||||
data.Params = map[string]interface{}{"langCode": "en"}
|
||||
|
||||
viper.Set("baseURL", "http://mysite.com/hugo/")
|
||||
v.Set("baseURL", "http://mysite.com/hugo/")
|
||||
v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
|
||||
|
||||
tstInitTemplates()
|
||||
|
||||
config := newDefaultDepsCfg()
|
||||
config := newDepsConfig(v)
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
if _, err := templ.New("test").Parse(in); err != nil {
|
||||
t.Fatal("Got error on parse", err)
|
||||
@@ -282,7 +282,7 @@ urlize: bat-man
|
||||
config.Fs = fs
|
||||
|
||||
d := deps.New(config)
|
||||
if err := d.LoadTemplates(); err != nil {
|
||||
if err := d.LoadResources(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -300,6 +300,7 @@ urlize: bat-man
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, this := range []struct {
|
||||
tstCompareType
|
||||
funcUnderTest func(a, b interface{}) bool
|
||||
@@ -370,6 +371,7 @@ func doTestCompare(t *testing.T, tp tstCompareType, funcUnderTest func(a, b inte
|
||||
}
|
||||
|
||||
func TestMod(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
@@ -405,6 +407,7 @@ func TestMod(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModBool(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
@@ -445,6 +448,7 @@ func TestModBool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
count interface{}
|
||||
sequence interface{}
|
||||
@@ -480,6 +484,7 @@ func TestFirst(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLast(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
count interface{}
|
||||
sequence interface{}
|
||||
@@ -515,6 +520,7 @@ func TestLast(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
count interface{}
|
||||
sequence interface{}
|
||||
@@ -550,6 +556,7 @@ func TestAfter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShuffleInputAndOutputFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
sequence interface{}
|
||||
success bool
|
||||
@@ -588,6 +595,7 @@ func TestShuffleInputAndOutputFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShuffleRandomising(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Note that this test can fail with false negative result if the shuffle
|
||||
// of the sequence happens to be the same as the original sequence. However
|
||||
// the propability of the event is 10^-158 which is negligible.
|
||||
@@ -615,6 +623,7 @@ func TestShuffleRandomising(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDictionary(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
v1 []interface{}
|
||||
expecterr bool
|
||||
@@ -647,13 +656,15 @@ func blankImage(width, height int) []byte {
|
||||
}
|
||||
|
||||
func TestImageConfig(t *testing.T) {
|
||||
testReset()
|
||||
t.Parallel()
|
||||
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
viper.Set("workingDir", workingDir)
|
||||
v := viper.New()
|
||||
|
||||
f := newTestFuncster()
|
||||
v.Set("workingDir", workingDir)
|
||||
|
||||
f := newTestFuncsterWithViper(v)
|
||||
|
||||
for i, this := range []struct {
|
||||
resetCache bool
|
||||
@@ -754,6 +765,7 @@ func TestImageConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIn(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
v1 interface{}
|
||||
v2 interface{}
|
||||
@@ -783,6 +795,7 @@ func TestIn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSlicestr(t *testing.T) {
|
||||
t.Parallel()
|
||||
var err error
|
||||
for i, this := range []struct {
|
||||
v1 interface{}
|
||||
@@ -848,6 +861,7 @@ func TestSlicestr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHasPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
s interface{}
|
||||
prefix interface{}
|
||||
@@ -875,6 +889,7 @@ func TestHasPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSubstr(t *testing.T) {
|
||||
t.Parallel()
|
||||
var err error
|
||||
var n int
|
||||
for i, this := range []struct {
|
||||
@@ -952,6 +967,7 @@ func TestSubstr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
v1 interface{}
|
||||
v2 string
|
||||
@@ -982,6 +998,7 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntersect(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
sequence1 interface{}
|
||||
sequence2 interface{}
|
||||
@@ -1025,6 +1042,7 @@ func TestIntersect(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
aSlice := []interface{}{1, 2, 3, 5}
|
||||
aMap := map[string]interface{}{"a": 1, "b": 2}
|
||||
|
||||
@@ -1074,6 +1092,7 @@ type TstX struct {
|
||||
}
|
||||
|
||||
func TestTimeUnix(t *testing.T) {
|
||||
t.Parallel()
|
||||
var sec int64 = 1234567890
|
||||
tv := reflect.ValueOf(time.Unix(sec, 0))
|
||||
i := 1
|
||||
@@ -1096,6 +1115,7 @@ func TestTimeUnix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateSubElem(t *testing.T) {
|
||||
t.Parallel()
|
||||
tstx := TstX{A: "foo", B: "bar"}
|
||||
var inner struct {
|
||||
S fmt.Stringer
|
||||
@@ -1146,6 +1166,7 @@ func TestEvaluateSubElem(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckCondition(t *testing.T) {
|
||||
t.Parallel()
|
||||
type expect struct {
|
||||
result bool
|
||||
isError bool
|
||||
@@ -1266,6 +1287,7 @@ func TestCheckCondition(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWhere(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Mid struct {
|
||||
Tst TstX
|
||||
@@ -1671,6 +1693,7 @@ func TestWhere(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
sequence interface{}
|
||||
delimiter interface{}
|
||||
@@ -1720,6 +1743,7 @@ func TestDelimit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
type ts struct {
|
||||
MyInt int
|
||||
MyFloat float64
|
||||
@@ -1932,6 +1956,7 @@ func TestSort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReturnWhenSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
data interface{}
|
||||
key interface{}
|
||||
@@ -1957,7 +1982,10 @@ func TestReturnWhenSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMarkdownify(t *testing.T) {
|
||||
viper.Set("currentContentLanguage", helpers.NewDefaultLanguage())
|
||||
t.Parallel()
|
||||
v := viper.New()
|
||||
|
||||
f := newTestFuncsterWithViper(v)
|
||||
|
||||
for i, this := range []struct {
|
||||
in interface{}
|
||||
@@ -1966,7 +1994,7 @@ func TestMarkdownify(t *testing.T) {
|
||||
{"Hello **World!**", template.HTML("Hello <strong>World!</strong>")},
|
||||
{[]byte("Hello Bytes **World!**"), template.HTML("Hello Bytes <strong>World!</strong>")},
|
||||
} {
|
||||
result, err := markdownify(this.in)
|
||||
result, err := f.markdownify(this.in)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] unexpected error in markdownify: %s", i, err)
|
||||
}
|
||||
@@ -1975,12 +2003,13 @@ func TestMarkdownify(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := markdownify(t); err == nil {
|
||||
if _, err := f.markdownify(t); err == nil {
|
||||
t.Fatalf("markdownify should have errored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApply(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := newTestFuncster()
|
||||
|
||||
@@ -2024,6 +2053,7 @@ func TestApply(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChomp(t *testing.T) {
|
||||
t.Parallel()
|
||||
base := "\n This is\na story "
|
||||
for i, item := range []string{
|
||||
"\n", "\n\n",
|
||||
@@ -2046,6 +2076,7 @@ func TestChomp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLower(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
s interface{}
|
||||
want string
|
||||
@@ -2069,6 +2100,7 @@ func TestLower(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
s interface{}
|
||||
want string
|
||||
@@ -2092,6 +2124,7 @@ func TestTitle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpper(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
s interface{}
|
||||
want string
|
||||
@@ -2115,8 +2148,12 @@ func TestUpper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHighlight(t *testing.T) {
|
||||
t.Parallel()
|
||||
code := "func boo() {}"
|
||||
highlighted, err := highlight(code, "go", "")
|
||||
|
||||
f := newTestFuncster()
|
||||
|
||||
highlighted, err := f.highlight(code, "go", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Highlight returned error:", err)
|
||||
@@ -2127,7 +2164,7 @@ func TestHighlight(t *testing.T) {
|
||||
t.Errorf("Highlight mismatch, got %v", highlighted)
|
||||
}
|
||||
|
||||
_, err = highlight(t, "go", "")
|
||||
_, err = f.highlight(t, "go", "")
|
||||
|
||||
if err == nil {
|
||||
t.Error("Expected highlight error")
|
||||
@@ -2135,6 +2172,7 @@ func TestHighlight(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInflect(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
inflectFunc func(i interface{}) (string, error)
|
||||
in interface{}
|
||||
@@ -2169,6 +2207,7 @@ func TestInflect(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCounterFuncs(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
countFunc func(i interface{}) (int, error)
|
||||
in string
|
||||
@@ -2195,6 +2234,7 @@ func TestCounterFuncs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplace(t *testing.T) {
|
||||
t.Parallel()
|
||||
v, _ := replace("aab", "a", "b")
|
||||
assert.Equal(t, "bbb", v)
|
||||
v, _ = replace("11a11", 1, 2)
|
||||
@@ -2210,6 +2250,7 @@ func TestReplace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplaceRE(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, val := range []struct {
|
||||
pattern interface{}
|
||||
repl interface{}
|
||||
@@ -2234,6 +2275,7 @@ func TestReplaceRE(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindRE(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
expr string
|
||||
content interface{}
|
||||
@@ -2264,6 +2306,7 @@ func TestFindRE(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTrim(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for i, this := range []struct {
|
||||
v1 interface{}
|
||||
@@ -2294,6 +2337,7 @@ func TestTrim(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDateFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
layout string
|
||||
value interface{}
|
||||
@@ -2328,6 +2372,7 @@ func TestDateFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
then := time.Now()
|
||||
now := time.Now()
|
||||
|
||||
@@ -2385,6 +2430,7 @@ func TestDefaultFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
input interface{}
|
||||
tpl string
|
||||
@@ -2414,6 +2460,7 @@ func TestDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSafeHTML(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
str string
|
||||
tmplStr string
|
||||
@@ -2454,6 +2501,7 @@ func TestSafeHTML(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSafeHTMLAttr(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
str string
|
||||
tmplStr string
|
||||
@@ -2494,6 +2542,7 @@ func TestSafeHTMLAttr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSafeCSS(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
str string
|
||||
tmplStr string
|
||||
@@ -2535,6 +2584,7 @@ func TestSafeCSS(t *testing.T) {
|
||||
|
||||
// TODO(bep) what is this? Also look above.
|
||||
func TestSafeJS(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
str string
|
||||
tmplStr string
|
||||
@@ -2576,6 +2626,7 @@ func TestSafeJS(t *testing.T) {
|
||||
|
||||
// TODO(bep) what is this?
|
||||
func TestSafeURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
str string
|
||||
tmplStr string
|
||||
@@ -2616,6 +2667,7 @@ func TestSafeURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBase64Decode(t *testing.T) {
|
||||
t.Parallel()
|
||||
testStr := "abc123!?$*&()'-=@~"
|
||||
enc := base64.StdEncoding.EncodeToString([]byte(testStr))
|
||||
result, err := base64Decode(enc)
|
||||
@@ -2635,6 +2687,7 @@ func TestBase64Decode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBase64Encode(t *testing.T) {
|
||||
t.Parallel()
|
||||
testStr := "YWJjMTIzIT8kKiYoKSctPUB+"
|
||||
dec, err := base64.StdEncoding.DecodeString(testStr)
|
||||
|
||||
@@ -2659,6 +2712,7 @@ func TestBase64Encode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
input string
|
||||
expectedHash string
|
||||
@@ -2683,6 +2737,7 @@ func TestMD5(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
input string
|
||||
expectedHash string
|
||||
@@ -2707,6 +2762,7 @@ func TestSHA1(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSHA256(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, this := range []struct {
|
||||
input string
|
||||
expectedHash string
|
||||
@@ -2731,13 +2787,15 @@ func TestSHA256(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
testReset()
|
||||
t.Parallel()
|
||||
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
viper.Set("workingDir", workingDir)
|
||||
v := viper.New()
|
||||
|
||||
f := newTestFuncster()
|
||||
v.Set("workingDir", workingDir)
|
||||
|
||||
f := newTestFuncsterWithViper(v)
|
||||
|
||||
afero.WriteFile(f.Fs.Source, filepath.Join(workingDir, "/f/f1.txt"), []byte("f1-content"), 0755)
|
||||
afero.WriteFile(f.Fs.Source, filepath.Join("/home", "f2.txt"), []byte("f2-content"), 0755)
|
||||
@@ -2770,6 +2828,7 @@ func TestReadFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPartialCached(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
name string
|
||||
partial string
|
||||
@@ -2793,7 +2852,6 @@ func TestPartialCached(t *testing.T) {
|
||||
data.Section = "blog"
|
||||
data.Params = map[string]interface{}{"langCode": "en"}
|
||||
|
||||
tstInitTemplates()
|
||||
for i, tc := range testCases {
|
||||
var tmp string
|
||||
if tc.variant != "" {
|
||||
@@ -2802,9 +2860,9 @@ func TestPartialCached(t *testing.T) {
|
||||
tmp = tc.tmpl
|
||||
}
|
||||
|
||||
cfg := newDefaultDepsCfg()
|
||||
config := newDepsConfig(viper.New())
|
||||
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
err := templ.AddTemplate("testroot", tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2817,8 +2875,8 @@ func TestPartialCached(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
de := deps.New(cfg)
|
||||
require.NoError(t, de.LoadTemplates())
|
||||
de := deps.New(config)
|
||||
require.NoError(t, de.LoadResources())
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
templ := de.Tmpl.Lookup("testroot")
|
||||
@@ -2842,8 +2900,8 @@ func TestPartialCached(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkPartial(b *testing.B) {
|
||||
cfg := newDefaultDepsCfg()
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2856,8 +2914,8 @@ func BenchmarkPartial(b *testing.B) {
|
||||
return nil
|
||||
}
|
||||
|
||||
de := deps.New(cfg)
|
||||
require.NoError(b, de.LoadTemplates())
|
||||
de := deps.New(config)
|
||||
require.NoError(b, de.LoadResources())
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tmpl := de.Tmpl.Lookup("testroot")
|
||||
@@ -2873,8 +2931,8 @@ func BenchmarkPartial(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkPartialCached(b *testing.B) {
|
||||
cfg := newDefaultDepsCfg()
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2887,8 +2945,8 @@ func BenchmarkPartialCached(b *testing.B) {
|
||||
return nil
|
||||
}
|
||||
|
||||
de := deps.New(cfg)
|
||||
require.NoError(b, de.LoadTemplates())
|
||||
de := deps.New(config)
|
||||
require.NoError(b, de.LoadResources())
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tmpl := de.Tmpl.Lookup("testroot")
|
||||
@@ -2904,9 +2962,14 @@ func BenchmarkPartialCached(b *testing.B) {
|
||||
}
|
||||
|
||||
func newTestFuncster() *templateFuncster {
|
||||
cfg := newDefaultDepsCfg()
|
||||
d := deps.New(cfg)
|
||||
if err := d.LoadTemplates(); err != nil {
|
||||
return newTestFuncsterWithViper(viper.New())
|
||||
}
|
||||
|
||||
func newTestFuncsterWithViper(v *viper.Viper) *templateFuncster {
|
||||
config := newDepsConfig(v)
|
||||
d := deps.New(config)
|
||||
|
||||
if err := d.LoadResources(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -2914,8 +2977,8 @@ func newTestFuncster() *templateFuncster {
|
||||
}
|
||||
|
||||
func newTestTemplate(t *testing.T, name, template string) *template.Template {
|
||||
cfg := newDefaultDepsCfg()
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
err := templ.AddTemplate(name, template)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2923,8 +2986,8 @@ func newTestTemplate(t *testing.T, name, template string) *template.Template {
|
||||
return nil
|
||||
}
|
||||
|
||||
de := deps.New(cfg)
|
||||
require.NoError(t, de.LoadTemplates())
|
||||
de := deps.New(config)
|
||||
require.NoError(t, de.LoadResources())
|
||||
|
||||
return de.Tmpl.Lookup(name)
|
||||
}
|
||||
|
@@ -1,100 +0,0 @@
|
||||
// Copyright 2015 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 tpl
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n/bundle"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logi18nWarnings set to true to print warnings about missing language strings
|
||||
Logi18nWarnings bool
|
||||
i18nWarningLogger = helpers.NewDistinctFeedbackLogger()
|
||||
currentLanguage *helpers.Language
|
||||
)
|
||||
|
||||
type translate struct {
|
||||
translateFuncs map[string]bundle.TranslateFunc
|
||||
|
||||
current bundle.TranslateFunc
|
||||
}
|
||||
|
||||
// TODO(bep) global translator
|
||||
var translator *translate
|
||||
|
||||
// SetTranslateLang sets the translations language to use during template processing.
|
||||
// This construction is unfortunate, but the template system is currently global.
|
||||
func SetTranslateLang(language *helpers.Language) error {
|
||||
currentLanguage = language
|
||||
if f, ok := translator.translateFuncs[language.Lang]; ok {
|
||||
translator.current = f
|
||||
} else {
|
||||
jww.WARN.Printf("Translation func for language %v not found, use default.", language.Lang)
|
||||
translator.current = translator.translateFuncs[viper.GetString("defaultContentLanguage")]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetI18nTfuncs sets the language bundle to be used for i18n.
|
||||
func SetI18nTfuncs(bndl *bundle.Bundle) {
|
||||
translator = &translate{translateFuncs: make(map[string]bundle.TranslateFunc)}
|
||||
defaultContentLanguage := viper.GetString("defaultContentLanguage")
|
||||
var (
|
||||
defaultT bundle.TranslateFunc
|
||||
err error
|
||||
)
|
||||
|
||||
defaultT, err = bndl.Tfunc(defaultContentLanguage)
|
||||
|
||||
if err != nil {
|
||||
jww.WARN.Printf("No translation bundle found for default language %q", defaultContentLanguage)
|
||||
}
|
||||
|
||||
enableMissingTranslationPlaceholders := viper.GetBool("enableMissingTranslationPlaceholders")
|
||||
for _, lang := range bndl.LanguageTags() {
|
||||
currentLang := lang
|
||||
|
||||
translator.translateFuncs[currentLang] = func(translationID string, args ...interface{}) string {
|
||||
tFunc, err := bndl.Tfunc(currentLang)
|
||||
if err != nil {
|
||||
jww.WARN.Printf("could not load translations for language %q (%s), will use default content language.\n", lang, err)
|
||||
} else if translated := tFunc(translationID, args...); translated != translationID {
|
||||
return translated
|
||||
}
|
||||
if Logi18nWarnings {
|
||||
i18nWarningLogger.Printf("i18n|MISSING_TRANSLATION|%s|%s", currentLang, translationID)
|
||||
}
|
||||
if enableMissingTranslationPlaceholders {
|
||||
return "[i18n] " + translationID
|
||||
}
|
||||
if defaultT != nil {
|
||||
if translated := defaultT(translationID, args...); translated != translationID {
|
||||
return translated
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func i18nTranslate(id string, args ...interface{}) (string, error) {
|
||||
if translator == nil || translator.current == nil {
|
||||
helpers.DistinctErrorLog.Printf("i18n not initialized, check that you have language file (in i18n) that matches the site language or the default language.")
|
||||
return "", nil
|
||||
}
|
||||
return translator.current(id, args...), nil
|
||||
}
|
@@ -1,149 +0,0 @@
|
||||
// Copyright 2015 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 tpl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/i18n/bundle"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type i18nTest struct {
|
||||
data map[string][]byte
|
||||
args interface{}
|
||||
lang, id, expected, expectedFlag string
|
||||
}
|
||||
|
||||
var i18nTests = []i18nTest{
|
||||
// All translations present
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte("- id: \"hello\"\n translation: \"Hello, World!\""),
|
||||
"es.yaml": []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\""),
|
||||
},
|
||||
args: nil,
|
||||
lang: "es",
|
||||
id: "hello",
|
||||
expected: "¡Hola, Mundo!",
|
||||
expectedFlag: "¡Hola, Mundo!",
|
||||
},
|
||||
// Translation missing in current language but present in default
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte("- id: \"hello\"\n translation: \"Hello, World!\""),
|
||||
"es.yaml": []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\""),
|
||||
},
|
||||
args: nil,
|
||||
lang: "es",
|
||||
id: "hello",
|
||||
expected: "Hello, World!",
|
||||
expectedFlag: "[i18n] hello",
|
||||
},
|
||||
// Translation missing in default language but present in current
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte("- id: \"goodybe\"\n translation: \"Goodbye, World!\""),
|
||||
"es.yaml": []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\""),
|
||||
},
|
||||
args: nil,
|
||||
lang: "es",
|
||||
id: "hello",
|
||||
expected: "¡Hola, Mundo!",
|
||||
expectedFlag: "¡Hola, Mundo!",
|
||||
},
|
||||
// Translation missing in both default and current language
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte("- id: \"goodbye\"\n translation: \"Goodbye, World!\""),
|
||||
"es.yaml": []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\""),
|
||||
},
|
||||
args: nil,
|
||||
lang: "es",
|
||||
id: "hello",
|
||||
expected: "",
|
||||
expectedFlag: "[i18n] hello",
|
||||
},
|
||||
// Default translation file missing or empty
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte(""),
|
||||
},
|
||||
args: nil,
|
||||
lang: "es",
|
||||
id: "hello",
|
||||
expected: "",
|
||||
expectedFlag: "[i18n] hello",
|
||||
},
|
||||
// Context provided
|
||||
{
|
||||
data: map[string][]byte{
|
||||
"en.yaml": []byte("- id: \"wordCount\"\n translation: \"Hello, {{.WordCount}} people!\""),
|
||||
"es.yaml": []byte("- id: \"wordCount\"\n translation: \"¡Hola, {{.WordCount}} gente!\""),
|
||||
},
|
||||
args: struct {
|
||||
WordCount int
|
||||
}{
|
||||
50,
|
||||
},
|
||||
lang: "es",
|
||||
id: "wordCount",
|
||||
expected: "¡Hola, 50 gente!",
|
||||
expectedFlag: "¡Hola, 50 gente!",
|
||||
},
|
||||
}
|
||||
|
||||
func doTestI18nTranslate(t *testing.T, data map[string][]byte, lang, id string, args interface{}) string {
|
||||
i18nBundle := bundle.New()
|
||||
|
||||
for file, content := range data {
|
||||
err := i18nBundle.ParseTranslationFileBytes(file, content)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing translation file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
SetI18nTfuncs(i18nBundle)
|
||||
SetTranslateLang(helpers.NewLanguage(lang))
|
||||
|
||||
translated, err := i18nTranslate(id, args)
|
||||
if err != nil {
|
||||
t.Errorf("Error translating '%s': %s", id, err)
|
||||
}
|
||||
return translated
|
||||
}
|
||||
|
||||
func TestI18nTranslate(t *testing.T) {
|
||||
var actual, expected string
|
||||
|
||||
viper.SetDefault("defaultContentLanguage", "en")
|
||||
viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
|
||||
|
||||
// Test without and with placeholders
|
||||
for _, enablePlaceholders := range []bool{false, true} {
|
||||
viper.Set("enableMissingTranslationPlaceholders", enablePlaceholders)
|
||||
|
||||
for _, test := range i18nTests {
|
||||
if enablePlaceholders {
|
||||
expected = test.expectedFlag
|
||||
} else {
|
||||
expected = test.expected
|
||||
}
|
||||
actual = doTestI18nTranslate(t, test.data, test.lang, test.id, test.args)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
@@ -27,9 +27,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/hugo/config"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -63,17 +63,17 @@ func (l *remoteLock) URLUnlock(url string) {
|
||||
}
|
||||
|
||||
// getCacheFileID returns the cache ID for a string
|
||||
func getCacheFileID(id string) string {
|
||||
return viper.GetString("cacheDir") + url.QueryEscape(id)
|
||||
func getCacheFileID(cfg config.Provider, id string) string {
|
||||
return cfg.GetString("cacheDir") + url.QueryEscape(id)
|
||||
}
|
||||
|
||||
// resGetCache returns the content for an ID from the file cache or an error
|
||||
// if the file is not found returns nil,nil
|
||||
func resGetCache(id string, fs afero.Fs, ignoreCache bool) ([]byte, error) {
|
||||
func resGetCache(id string, fs afero.Fs, cfg config.Provider, ignoreCache bool) ([]byte, error) {
|
||||
if ignoreCache {
|
||||
return nil, nil
|
||||
}
|
||||
fID := getCacheFileID(id)
|
||||
fID := getCacheFileID(cfg, id)
|
||||
isExists, err := helpers.Exists(fID, fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -87,11 +87,11 @@ func resGetCache(id string, fs afero.Fs, ignoreCache bool) ([]byte, error) {
|
||||
}
|
||||
|
||||
// resWriteCache writes bytes to an ID into the file cache
|
||||
func resWriteCache(id string, c []byte, fs afero.Fs, ignoreCache bool) error {
|
||||
func resWriteCache(id string, c []byte, fs afero.Fs, cfg config.Provider, ignoreCache bool) error {
|
||||
if ignoreCache {
|
||||
return nil
|
||||
}
|
||||
fID := getCacheFileID(id)
|
||||
fID := getCacheFileID(cfg, id)
|
||||
f, err := fs.Create(fID)
|
||||
if err != nil {
|
||||
return errors.New("Error: " + err.Error() + ". Failed to create file: " + fID)
|
||||
@@ -107,13 +107,13 @@ func resWriteCache(id string, c []byte, fs afero.Fs, ignoreCache bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resDeleteCache(id string, fs afero.Fs) error {
|
||||
return fs.Remove(getCacheFileID(id))
|
||||
func resDeleteCache(id string, fs afero.Fs, cfg config.Provider) error {
|
||||
return fs.Remove(getCacheFileID(cfg, id))
|
||||
}
|
||||
|
||||
// resGetRemote loads the content of a remote file. This method is thread safe.
|
||||
func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
|
||||
c, err := resGetCache(url, fs, viper.GetBool("ignoreCache"))
|
||||
func resGetRemote(url string, fs afero.Fs, cfg config.Provider, hc *http.Client) ([]byte, error) {
|
||||
c, err := resGetCache(url, fs, cfg, cfg.GetBool("ignoreCache"))
|
||||
if c != nil && err == nil {
|
||||
return c, nil
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
|
||||
defer func() { remoteURLLock.URLUnlock(url) }()
|
||||
|
||||
// avoid multiple locks due to calling resGetCache twice
|
||||
c, err = resGetCache(url, fs, viper.GetBool("ignoreCache"))
|
||||
c, err = resGetCache(url, fs, cfg, cfg.GetBool("ignoreCache"))
|
||||
if c != nil && err == nil {
|
||||
return c, nil
|
||||
}
|
||||
@@ -144,17 +144,17 @@ func resGetRemote(url string, fs afero.Fs, hc *http.Client) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = resWriteCache(url, c, fs, viper.GetBool("ignoreCache"))
|
||||
err = resWriteCache(url, c, fs, cfg, cfg.GetBool("ignoreCache"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jww.INFO.Printf("... and cached to: %s", getCacheFileID(url))
|
||||
jww.INFO.Printf("... and cached to: %s", getCacheFileID(cfg, url))
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// resGetLocal loads the content of a local file
|
||||
func resGetLocal(url string, fs afero.Fs) ([]byte, error) {
|
||||
filename := filepath.Join(viper.GetString("workingDir"), url)
|
||||
func resGetLocal(url string, fs afero.Fs, cfg config.Provider) ([]byte, error) {
|
||||
filename := filepath.Join(cfg.GetString("workingDir"), url)
|
||||
if e, err := helpers.Exists(filename, fs); !e {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,9 +169,9 @@ func (t *templateFuncster) resGetResource(url string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
if strings.Contains(url, "://") {
|
||||
return resGetRemote(url, t.Fs.Source, http.DefaultClient)
|
||||
return resGetRemote(url, t.Fs.Source, t.Cfg, http.DefaultClient)
|
||||
}
|
||||
return resGetLocal(url, t.Fs.Source)
|
||||
return resGetLocal(url, t.Fs.Source, t.Cfg)
|
||||
}
|
||||
|
||||
// getJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
|
||||
@@ -193,7 +193,7 @@ func (t *templateFuncster) getJSON(urlParts ...string) interface{} {
|
||||
jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)
|
||||
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
|
||||
time.Sleep(resSleep)
|
||||
resDeleteCache(url, t.Fs.Source)
|
||||
resDeleteCache(url, t.Fs.Source, t.Cfg)
|
||||
continue
|
||||
}
|
||||
break
|
||||
@@ -226,7 +226,7 @@ func (t *templateFuncster) getCSV(sep string, urlParts ...string) [][]string {
|
||||
var clearCacheSleep = func(i int, u string) {
|
||||
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
|
||||
time.Sleep(resSleep)
|
||||
resDeleteCache(url, t.Fs.Source)
|
||||
resDeleteCache(url, t.Fs.Source, t.Cfg)
|
||||
}
|
||||
|
||||
for i := 0; i <= resRetries; i++ {
|
||||
|
@@ -19,10 +19,8 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
@@ -32,6 +30,7 @@ import (
|
||||
)
|
||||
|
||||
func TestScpCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
@@ -50,7 +49,8 @@ func TestScpCache(t *testing.T) {
|
||||
fs := new(afero.MemMapFs)
|
||||
|
||||
for _, test := range tests {
|
||||
c, err := resGetCache(test.path, fs, test.ignore)
|
||||
cfg := viper.New()
|
||||
c, err := resGetCache(test.path, fs, cfg, test.ignore)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting cache: %s", err)
|
||||
}
|
||||
@@ -58,12 +58,12 @@ func TestScpCache(t *testing.T) {
|
||||
t.Errorf("There is content where there should not be anything: %s", string(c))
|
||||
}
|
||||
|
||||
err = resWriteCache(test.path, test.content, fs, test.ignore)
|
||||
err = resWriteCache(test.path, test.content, fs, cfg, test.ignore)
|
||||
if err != nil {
|
||||
t.Errorf("Error writing cache: %s", err)
|
||||
}
|
||||
|
||||
c, err = resGetCache(test.path, fs, test.ignore)
|
||||
c, err = resGetCache(test.path, fs, cfg, test.ignore)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting cache after writing: %s", err)
|
||||
}
|
||||
@@ -80,8 +80,9 @@ func TestScpCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestScpGetLocal(t *testing.T) {
|
||||
testReset()
|
||||
fs := hugofs.NewMem()
|
||||
t.Parallel()
|
||||
v := viper.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
ps := helpers.FilePathSeparator
|
||||
|
||||
tests := []struct {
|
||||
@@ -102,7 +103,7 @@ func TestScpGetLocal(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
c, err := resGetLocal(test.path, fs.Source)
|
||||
c, err := resGetLocal(test.path, fs.Source, v)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting resource content: %s", err)
|
||||
}
|
||||
@@ -126,6 +127,7 @@ func getTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*httpt
|
||||
}
|
||||
|
||||
func TestScpGetRemote(t *testing.T) {
|
||||
t.Parallel()
|
||||
fs := new(afero.MemMapFs)
|
||||
|
||||
tests := []struct {
|
||||
@@ -146,14 +148,16 @@ func TestScpGetRemote(t *testing.T) {
|
||||
})
|
||||
defer func() { srv.Close() }()
|
||||
|
||||
c, err := resGetRemote(test.path, fs, cl)
|
||||
cfg := viper.New()
|
||||
|
||||
c, err := resGetRemote(test.path, fs, cfg, cl)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting resource content: %s", err)
|
||||
}
|
||||
if !bytes.Equal(c, test.content) {
|
||||
t.Errorf("\nNet Expected: %s\nNet Actual: %s\n", string(test.content), string(c))
|
||||
}
|
||||
cc, cErr := resGetCache(test.path, fs, test.ignore)
|
||||
cc, cErr := resGetCache(test.path, fs, cfg, test.ignore)
|
||||
if cErr != nil {
|
||||
t.Error(cErr)
|
||||
}
|
||||
@@ -170,6 +174,7 @@ func TestScpGetRemote(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseCSV(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
csv []byte
|
||||
@@ -208,29 +213,11 @@ func TestParseCSV(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://twitter.com/francesc/status/603066617124126720
|
||||
// for the construct: defer testRetryWhenDone().Reset()
|
||||
type wd struct {
|
||||
Reset func()
|
||||
}
|
||||
|
||||
func testRetryWhenDone(f *templateFuncster) wd {
|
||||
cd := viper.GetString("cacheDir")
|
||||
viper.Set("cacheDir", helpers.GetTempDir("", f.Fs.Source))
|
||||
var tmpSleep time.Duration
|
||||
tmpSleep, resSleep = resSleep, time.Millisecond
|
||||
return wd{func() {
|
||||
viper.Set("cacheDir", cd)
|
||||
resSleep = tmpSleep
|
||||
}}
|
||||
}
|
||||
|
||||
func TestGetJSONFailParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := newTestFuncster()
|
||||
|
||||
defer testRetryWhenDone(f).Reset()
|
||||
|
||||
reqCount := 0
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if reqCount > 0 {
|
||||
@@ -244,7 +231,6 @@ func TestGetJSONFailParse(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
url := ts.URL + "/test.json"
|
||||
defer os.Remove(getCacheFileID(url))
|
||||
|
||||
want := map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}}
|
||||
have := f.getJSON(url)
|
||||
@@ -255,10 +241,9 @@ func TestGetJSONFailParse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetCSVFailParseSep(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := newTestFuncster()
|
||||
|
||||
defer testRetryWhenDone(f).Reset()
|
||||
|
||||
reqCount := 0
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if reqCount > 0 {
|
||||
@@ -275,7 +260,6 @@ func TestGetCSVFailParseSep(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
url := ts.URL + "/test.csv"
|
||||
defer os.Remove(getCacheFileID(url))
|
||||
|
||||
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
|
||||
have := f.getCSV(",", url)
|
||||
@@ -286,11 +270,10 @@ func TestGetCSVFailParseSep(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetCSVFailParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := newTestFuncster()
|
||||
|
||||
defer testRetryWhenDone(f).Reset()
|
||||
|
||||
reqCount := 0
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-type", "application/json")
|
||||
@@ -309,7 +292,6 @@ func TestGetCSVFailParse(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
url := ts.URL + "/test.csv"
|
||||
defer os.Remove(getCacheFileID(url))
|
||||
|
||||
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
|
||||
have := f.getCSV(",", url)
|
||||
|
@@ -26,21 +26,15 @@ import (
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testReset() {
|
||||
viper.Reset()
|
||||
|
||||
// TODO(bep) viper-globals
|
||||
viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
|
||||
}
|
||||
|
||||
// Some tests for Issue #1178 -- Ace
|
||||
func TestAceTemplates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for i, this := range []struct {
|
||||
basePath string
|
||||
@@ -79,7 +73,7 @@ html lang=en
|
||||
|
||||
d := "DATA"
|
||||
|
||||
config := newDefaultDepsCfg()
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
|
||||
[]byte(this.baseContent), []byte(this.innerContent))
|
||||
@@ -87,7 +81,7 @@ html lang=en
|
||||
|
||||
a := deps.New(config)
|
||||
|
||||
if err := a.LoadTemplates(); err != nil {
|
||||
if err := a.LoadResources(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -124,6 +118,7 @@ func isAtLeastGo16() bool {
|
||||
}
|
||||
|
||||
func TestAddTemplateFileWithMaster(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !isAtLeastGo16() {
|
||||
t.Skip("This test only runs on Go >= 1.6")
|
||||
@@ -148,8 +143,8 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
|
||||
masterTplName := "mt"
|
||||
finalTplName := "tp"
|
||||
|
||||
cfg := newDefaultDepsCfg()
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
|
||||
err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
|
||||
|
||||
@@ -189,13 +184,13 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
|
||||
}
|
||||
|
||||
if this.writeSkipper != 1 {
|
||||
afero.WriteFile(cfg.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
|
||||
afero.WriteFile(config.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
|
||||
}
|
||||
if this.writeSkipper != 2 {
|
||||
afero.WriteFile(cfg.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
|
||||
afero.WriteFile(config.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
|
||||
}
|
||||
|
||||
deps.New(cfg)
|
||||
deps.New(config)
|
||||
|
||||
}
|
||||
|
||||
@@ -204,6 +199,7 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
|
||||
// A Go stdlib test for linux/arm. Will remove later.
|
||||
// See #1771
|
||||
func TestBigIntegerFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
var func1 = func(v int64) error {
|
||||
return nil
|
||||
}
|
||||
@@ -234,6 +230,7 @@ func (b BI) A(v int64) error {
|
||||
return nil
|
||||
}
|
||||
func TestBigIntegerMethod(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := &BI{}
|
||||
|
||||
@@ -253,6 +250,7 @@ func TestBigIntegerMethod(t *testing.T) {
|
||||
|
||||
// Test for bugs discovered by https://github.com/dvyukov/go-fuzz
|
||||
func TestTplGoFuzzReports(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The following test case(s) also fail
|
||||
// See https://github.com/golang/go/issues/10634
|
||||
@@ -284,13 +282,14 @@ func TestTplGoFuzzReports(t *testing.T) {
|
||||
H: "a,b,c,d,e,f",
|
||||
}
|
||||
|
||||
cfg := newDefaultDepsCfg()
|
||||
cfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
config := newDepsConfig(viper.New())
|
||||
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
return templ.AddTemplate("fuzz", this.data)
|
||||
}
|
||||
|
||||
de := deps.New(cfg)
|
||||
require.NoError(t, de.LoadTemplates())
|
||||
de := deps.New(config)
|
||||
require.NoError(t, de.LoadResources())
|
||||
|
||||
templ := de.Tmpl.(*GoHTMLTemplate)
|
||||
|
||||
|
Reference in New Issue
Block a user