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:
Bjørn Erik Pedersen
2017-02-05 10:20:06 +07:00
parent e34af6ee30
commit 93ca7c9e95
99 changed files with 2843 additions and 2458 deletions

View File

@@ -1,63 +0,0 @@
// Copyright 2016-present 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 implements general utility functions that work with
// and on content. The helper functions defined here lay down the
// foundation of how Hugo works with files and filepaths, and perform
// string operations on content.
package helpers
import (
"github.com/spf13/viper"
)
// A cached version of the current ConfigProvider (language) and relatives. These globals
// are unfortunate, but we still have some places that needs this that does
// not have access to the site configuration.
// These values will be set on initialization when rendering a new language.
//
// TODO(bep) Get rid of these.
var (
currentConfigProvider ConfigProvider
)
// ConfigProvider provides the configuration settings for Hugo.
type ConfigProvider interface {
GetString(key string) string
GetInt(key string) int
GetBool(key string) bool
GetStringMap(key string) map[string]interface{}
GetStringMapString(key string) map[string]string
Get(key string) interface{}
}
// Config returns the currently active Hugo config. This will be set
// per site (language) rendered.
func Config() ConfigProvider {
if currentConfigProvider != nil {
return currentConfigProvider
}
// Some tests rely on this. We will fix that, eventually.
return viper.Get("currentContentLanguage").(ConfigProvider)
}
// InitConfigProviderForCurrentContentLanguage does what it says.
func InitConfigProviderForCurrentContentLanguage() {
currentConfigProvider = viper.Get("CurrentContentLanguage").(ConfigProvider)
}
// ResetConfigProvider is used in tests.
func ResetConfigProvider() {
currentConfigProvider = nil
}

View File

@@ -24,12 +24,13 @@ import (
"unicode"
"unicode/utf8"
"github.com/spf13/hugo/config"
"github.com/miekg/mmark"
"github.com/mitchellh/mapstructure"
"github.com/russross/blackfriday"
bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"strings"
"sync"
@@ -41,6 +42,14 @@ var SummaryLength = 70
// SummaryDivider denotes where content summarization should end. The default is "<!--more-->".
var SummaryDivider = []byte("<!--more-->")
type ContentSpec struct {
cfg config.Provider
}
func NewContentSpec(cfg config.Provider) *ContentSpec {
return &ContentSpec{cfg}
}
// Blackfriday holds configuration values for Blackfriday rendering.
type Blackfriday struct {
Smartypants bool
@@ -58,7 +67,7 @@ type Blackfriday struct {
}
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
func NewBlackfriday(c ConfigProvider) *Blackfriday {
func (c ContentSpec) NewBlackfriday() *Blackfriday {
defaultParam := map[string]interface{}{
"smartypants": true,
@@ -75,7 +84,7 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday {
ToLowerMap(defaultParam)
siteParam := c.GetStringMap("blackfriday")
siteParam := c.cfg.GetStringMap("blackfriday")
siteConfig := make(map[string]interface{})
@@ -187,10 +196,10 @@ func BytesToHTML(b []byte) template.HTML {
}
// getHTMLRenderer creates a new Blackfriday HTML Renderer with the given configuration.
func getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
func (c ContentSpec) getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
renderParameters := blackfriday.HtmlRendererParameters{
FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
}
b := len(ctx.DocumentID) != 0
@@ -265,21 +274,21 @@ func getMarkdownExtensions(ctx *RenderingContext) int {
return flags
}
func markdownRender(ctx *RenderingContext) []byte {
func (c ContentSpec) markdownRender(ctx *RenderingContext) []byte {
if ctx.RenderTOC {
return blackfriday.Markdown(ctx.Content,
getHTMLRenderer(blackfriday.HTML_TOC, ctx),
c.getHTMLRenderer(blackfriday.HTML_TOC, ctx),
getMarkdownExtensions(ctx))
}
return blackfriday.Markdown(ctx.Content, getHTMLRenderer(0, ctx),
return blackfriday.Markdown(ctx.Content, c.getHTMLRenderer(0, ctx),
getMarkdownExtensions(ctx))
}
// getMmarkHTMLRenderer creates a new mmark HTML Renderer with the given configuration.
func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
func (c ContentSpec) getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
renderParameters := mmark.HtmlRendererParameters{
FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
}
b := len(ctx.DocumentID) != 0
@@ -294,6 +303,7 @@ func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Rendere
return &HugoMmarkHTMLRenderer{
mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters),
c.cfg,
}
}
@@ -321,8 +331,8 @@ func getMmarkExtensions(ctx *RenderingContext) int {
return flags
}
func mmarkRender(ctx *RenderingContext) []byte {
return mmark.Parse(ctx.Content, getMmarkHTMLRenderer(0, ctx),
func (c ContentSpec) mmarkRender(ctx *RenderingContext) []byte {
return mmark.Parse(ctx.Content, c.getMmarkHTMLRenderer(0, ctx),
getMmarkExtensions(ctx)).Bytes()
}
@@ -365,42 +375,44 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
// RenderingContext holds contextual information, like content and configuration,
// for a given content rendering.
type RenderingContext struct {
Content []byte
PageFmt string
DocumentID string
DocumentName string
Config *Blackfriday
RenderTOC bool
FileResolver FileResolverFunc
LinkResolver LinkResolverFunc
ConfigProvider ConfigProvider
configInit sync.Once
Content []byte
PageFmt string
DocumentID string
DocumentName string
Config *Blackfriday
RenderTOC bool
FileResolver FileResolverFunc
LinkResolver LinkResolverFunc
Cfg config.Provider
configInit sync.Once
}
func newViperProvidedRenderingContext() *RenderingContext {
return &RenderingContext{ConfigProvider: viper.GetViper()}
func newRenderingContext(cfg config.Provider) *RenderingContext {
return &RenderingContext{Cfg: cfg}
}
func (c *RenderingContext) getConfig() *Blackfriday {
// TODO(bep) get rid of this
c.configInit.Do(func() {
if c.Config == nil {
c.Config = NewBlackfriday(c.ConfigProvider)
cs := NewContentSpec(c.Cfg)
c.Config = cs.NewBlackfriday()
}
})
return c.Config
}
// RenderBytes renders a []byte.
func RenderBytes(ctx *RenderingContext) []byte {
func (c ContentSpec) RenderBytes(ctx *RenderingContext) []byte {
switch ctx.PageFmt {
default:
return markdownRender(ctx)
return c.markdownRender(ctx)
case "markdown":
return markdownRender(ctx)
return c.markdownRender(ctx)
case "asciidoc":
return getAsciidocContent(ctx)
case "mmark":
return mmarkRender(ctx)
return c.mmarkRender(ctx)
case "rst":
return getRstContent(ctx)
}

View File

@@ -19,8 +19,8 @@ import (
"github.com/miekg/mmark"
"github.com/russross/blackfriday"
"github.com/spf13/hugo/config"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
type LinkResolverFunc func(ref string) (string, error)
@@ -33,49 +33,49 @@ type HugoHTMLRenderer struct {
blackfriday.Renderer
}
func (renderer *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
opts := viper.GetString("pygmentsOptions")
func (r *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
opts := r.Cfg.GetString("pygmentsOptions")
str := html.UnescapeString(string(text))
out.WriteString(Highlight(str, lang, opts))
out.WriteString(Highlight(r.RenderingContext.Cfg, str, lang, opts))
} else {
renderer.Renderer.BlockCode(out, text, lang)
r.Renderer.BlockCode(out, text, lang)
}
}
func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
if renderer.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
func (r *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
if r.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
// Use the blackfriday built in Link handler
renderer.Renderer.Link(out, link, title, content)
r.Renderer.Link(out, link, title, content)
} else {
// set by SourceRelativeLinksEval
newLink, err := renderer.LinkResolver(string(link))
newLink, err := r.LinkResolver(string(link))
if err != nil {
newLink = string(link)
jww.ERROR.Printf("LinkResolver: %s", err)
}
renderer.Renderer.Link(out, []byte(newLink), title, content)
r.Renderer.Link(out, []byte(newLink), title, content)
}
}
func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
if renderer.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
func (r *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
if r.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
// Use the blackfriday built in Image handler
renderer.Renderer.Image(out, link, title, alt)
r.Renderer.Image(out, link, title, alt)
} else {
// set by SourceRelativeLinksEval
newLink, err := renderer.FileResolver(string(link))
newLink, err := r.FileResolver(string(link))
if err != nil {
newLink = string(link)
jww.ERROR.Printf("FileResolver: %s", err)
}
renderer.Renderer.Image(out, []byte(newLink), title, alt)
r.Renderer.Image(out, []byte(newLink), title, alt)
}
}
// ListItem adds task list support to the Blackfriday renderer.
func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
if !renderer.Config.TaskLists {
renderer.Renderer.ListItem(out, text, flags)
func (r *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
if !r.Config.TaskLists {
r.Renderer.ListItem(out, text, flags)
return
}
@@ -87,17 +87,17 @@ func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags
text = append([]byte(`<input type="checkbox" checked disabled class="task-list-item">`), text[3:]...)
}
renderer.Renderer.ListItem(out, text, flags)
r.Renderer.ListItem(out, text, flags)
}
// List adds task list support to the Blackfriday renderer.
func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
if !renderer.Config.TaskLists {
renderer.Renderer.List(out, text, flags)
func (r *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
if !r.Config.TaskLists {
r.Renderer.List(out, text, flags)
return
}
marker := out.Len()
renderer.Renderer.List(out, text, flags)
r.Renderer.List(out, text, flags)
if out.Len() > marker {
list := out.Bytes()[marker:]
if bytes.Contains(list, []byte("task-list-item")) {
@@ -114,13 +114,14 @@ func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flag
// Enabling Hugo to customise the rendering experience
type HugoMmarkHTMLRenderer struct {
mmark.Renderer
Cfg config.Provider
}
func (renderer *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
func (r *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
str := html.UnescapeString(string(text))
out.WriteString(Highlight(str, lang, ""))
out.WriteString(Highlight(r.Cfg, str, lang, ""))
} else {
renderer.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
r.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
}
}

View File

@@ -22,9 +22,9 @@ import (
)
// Renders a codeblock using Blackfriday
func render(input string) string {
ctx := newViperProvidedRenderingContext()
render := getHTMLRenderer(0, ctx)
func (c ContentSpec) render(input string) string {
ctx := newRenderingContext(c.cfg)
render := c.getHTMLRenderer(0, ctx)
buf := &bytes.Buffer{}
render.BlockCode(buf, []byte(input), "html")
@@ -32,9 +32,9 @@ func render(input string) string {
}
// Renders a codeblock using Mmark
func renderWithMmark(input string) string {
ctx := newViperProvidedRenderingContext()
render := getMmarkHTMLRenderer(0, ctx)
func (c ContentSpec) renderWithMmark(input string) string {
ctx := newRenderingContext(c.cfg)
render := c.getMmarkHTMLRenderer(0, ctx)
buf := &bytes.Buffer{}
render.BlockCode(buf, []byte(input), "html", []byte(""), false, false)
@@ -59,16 +59,16 @@ func TestCodeFence(t *testing.T) {
{false, "<html></html>", `(?s)^<pre><code class="language-html">.*?</code></pre>\n$`},
}
viper.Reset()
defer viper.Reset()
viper.Set("pygmentsStyle", "monokai")
viper.Set("pygmentsUseClasses", true)
for i, d := range data {
viper.Set("pygmentsCodeFences", d.enabled)
v := viper.New()
result := render(d.input)
v.Set("pygmentsStyle", "monokai")
v.Set("pygmentsUseClasses", true)
v.Set("pygmentsCodeFences", d.enabled)
c := NewContentSpec(v)
result := c.render(d.input)
expectedRe, err := regexp.Compile(d.expected)
@@ -81,7 +81,7 @@ func TestCodeFence(t *testing.T) {
t.Errorf("Test %d failed. BlackFriday enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
}
result = renderWithMmark(d.input)
result = c.renderWithMmark(d.input)
matched = expectedRe.MatchString(result)
if !matched {
t.Errorf("Test %d failed. Mmark enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
@@ -90,6 +90,8 @@ func TestCodeFence(t *testing.T) {
}
func TestBlackfridayTaskList(t *testing.T) {
c := newTestContentSpec()
for i, this := range []struct {
markdown string
taskListEnabled bool
@@ -118,11 +120,11 @@ END
</ul>
`},
} {
blackFridayConfig := NewBlackfriday(viper.GetViper())
blackFridayConfig := c.NewBlackfriday()
blackFridayConfig.TaskLists = this.taskListEnabled
ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig}
result := string(RenderBytes(ctx))
result := string(c.RenderBytes(ctx))
if result != this.expect {
t.Errorf("[%d] got \n%v but expected \n%v", i, result, this.expect)

View File

@@ -21,7 +21,6 @@ import (
"github.com/miekg/mmark"
"github.com/russross/blackfriday"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
@@ -152,8 +151,9 @@ func TestTruncateWordsByRune(t *testing.T) {
}
func TestGetHTMLRendererFlags(t *testing.T) {
ctx := newViperProvidedRenderingContext()
renderer := getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
renderer := c.getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
flags := renderer.GetFlags()
if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
t.Errorf("Test flag: %d was not found amongs set flags:%d; Result: %d", blackfriday.HTML_USE_XHTML, flags, flags&blackfriday.HTML_USE_XHTML)
@@ -161,6 +161,8 @@ func TestGetHTMLRendererFlags(t *testing.T) {
}
func TestGetHTMLRendererAllFlags(t *testing.T) {
c := newTestContentSpec()
type data struct {
testFlag int
}
@@ -176,7 +178,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
{blackfriday.HTML_SMARTYPANTS_LATEX_DASHES},
}
defaultFlags := blackfriday.HTML_USE_XHTML
ctx := newViperProvidedRenderingContext()
ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.AngledQuotes = true
ctx.Config.Fractions = true
@@ -186,7 +188,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
ctx.Config.SmartDashes = true
ctx.Config.Smartypants = true
ctx.Config.SourceRelativeLinksEval = true
renderer := getHTMLRenderer(defaultFlags, ctx)
renderer := c.getHTMLRenderer(defaultFlags, ctx)
actualFlags := renderer.GetFlags()
var expectedFlags int
//OR-ing flags together...
@@ -199,12 +201,13 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
}
func TestGetHTMLRendererAnchors(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.DocumentID = "testid"
ctx.Config = ctx.getConfig()
ctx.Config.PlainIDAnchors = false
actualRenderer := getHTMLRenderer(0, ctx)
actualRenderer := c.getHTMLRenderer(0, ctx)
headerBuffer := &bytes.Buffer{}
footnoteBuffer := &bytes.Buffer{}
expectedFootnoteHref := []byte("href=\"#fn:testid:href\"")
@@ -223,11 +226,12 @@ func TestGetHTMLRendererAnchors(t *testing.T) {
}
func TestGetMmarkHTMLRenderer(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.DocumentID = "testid"
ctx.Config = ctx.getConfig()
ctx.Config.PlainIDAnchors = false
actualRenderer := getMmarkHTMLRenderer(0, ctx)
actualRenderer := c.getMmarkHTMLRenderer(0, ctx)
headerBuffer := &bytes.Buffer{}
footnoteBuffer := &bytes.Buffer{}
@@ -247,7 +251,8 @@ func TestGetMmarkHTMLRenderer(t *testing.T) {
}
func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"headerId"}
ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"}
@@ -262,7 +267,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
type data struct {
testFlag int
}
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{""}
ctx.Config.ExtensionsMask = []string{""}
@@ -294,7 +300,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
}
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"definitionLists"}
ctx.Config.ExtensionsMask = []string{""}
@@ -306,10 +313,11 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
}
func TestGetMarkdownRenderer(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
actualRenderedMarkdown := markdownRender(ctx)
actualRenderedMarkdown := c.markdownRender(ctx)
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
@@ -317,10 +325,11 @@ func TestGetMarkdownRenderer(t *testing.T) {
}
func TestGetMarkdownRendererWithTOC(t *testing.T) {
ctx := &RenderingContext{RenderTOC: true, ConfigProvider: viper.GetViper()}
c := newTestContentSpec()
ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg}
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
actualRenderedMarkdown := markdownRender(ctx)
actualRenderedMarkdown := c.markdownRender(ctx)
expectedRenderedMarkdown := []byte("<nav>\n</nav>\n\n<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
@@ -332,7 +341,8 @@ func TestGetMmarkExtensions(t *testing.T) {
type data struct {
testFlag int
}
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Config = ctx.getConfig()
ctx.Config.Extensions = []string{"tables"}
ctx.Config.ExtensionsMask = []string{""}
@@ -361,10 +371,11 @@ func TestGetMmarkExtensions(t *testing.T) {
}
func TestMmarkRender(t *testing.T) {
ctx := newViperProvidedRenderingContext()
c := newTestContentSpec()
ctx := newRenderingContext(c.cfg)
ctx.Content = []byte("testContent")
ctx.Config = ctx.getConfig()
actualRenderedMarkdown := mmarkRender(ctx)
actualRenderedMarkdown := c.mmarkRender(ctx)
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)

View File

@@ -32,7 +32,6 @@ import (
bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
// FilePathSeparator as defined by os.Separator.
@@ -196,8 +195,8 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
}
// ThemeSet checks whether a theme is in use or not.
func ThemeSet() bool {
return viper.GetString("theme") != ""
func (p *PathSpec) ThemeSet() bool {
return p.theme != ""
}
type logPrinter interface {

View File

@@ -19,8 +19,7 @@ import (
"sync"
"github.com/spf13/cast"
"github.com/spf13/viper"
"github.com/spf13/hugo/config"
)
// These are the settings that should only be looked up in the global Viper
@@ -41,26 +40,28 @@ type Language struct {
LanguageName string
Title string
Weight int
params map[string]interface{}
paramsInit sync.Once
Cfg config.Provider
params map[string]interface{}
paramsInit sync.Once
}
func (l *Language) String() string {
return l.Lang
}
func NewLanguage(lang string) *Language {
return &Language{Lang: lang, params: make(map[string]interface{})}
func NewLanguage(lang string, cfg config.Provider) *Language {
return &Language{Lang: lang, Cfg: cfg, params: make(map[string]interface{})}
}
func NewDefaultLanguage() *Language {
defaultLang := viper.GetString("defaultContentLanguage")
func NewDefaultLanguage(cfg config.Provider) *Language {
defaultLang := cfg.GetString("defaultContentLanguage")
if defaultLang == "" {
defaultLang = "en"
}
return NewLanguage(defaultLang)
return NewLanguage(defaultLang, cfg)
}
type Languages []*Language
@@ -83,7 +84,7 @@ func (l *Language) Params() map[string]interface{} {
// Merge with global config.
// TODO(bep) consider making this part of a constructor func.
globalParams := viper.GetStringMap("params")
globalParams := l.Cfg.GetStringMap("params")
for k, v := range globalParams {
if _, ok := l.params[k]; !ok {
l.params[k] = v
@@ -132,5 +133,28 @@ func (l *Language) Get(key string) interface{} {
return v
}
}
return viper.Get(key)
return l.Cfg.Get(key)
}
// Set sets the value for the key in the language's params.
func (l *Language) Set(key string, value interface{}) {
if l == nil {
panic("language not set")
}
key = strings.ToLower(key)
l.params[key] = value
}
// IsSet checks whether the key is set in the language or the related config store.
func (l *Language) IsSet(key string) bool {
key = strings.ToLower(key)
key = strings.ToLower(key)
if !globalOnlySettings[key] {
if _, ok := l.params[key]; ok {
return true
}
}
return l.Cfg.IsSet(key)
}

View File

@@ -21,11 +21,12 @@ import (
)
func TestGetGlobalOnlySetting(t *testing.T) {
lang := NewDefaultLanguage()
v := viper.New()
lang := NewDefaultLanguage(v)
lang.SetParam("defaultContentLanguageInSubdir", false)
lang.SetParam("paginatePath", "side")
viper.Set("defaultContentLanguageInSubdir", true)
viper.Set("paginatePath", "page")
v.Set("defaultContentLanguageInSubdir", true)
v.Set("paginatePath", "page")
require.True(t, lang.GetBool("defaultContentLanguageInSubdir"))
require.Equal(t, "side", lang.GetString("paginatePath"))

View File

@@ -24,7 +24,6 @@ import (
"unicode"
"github.com/spf13/afero"
"github.com/spf13/viper"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
@@ -153,41 +152,41 @@ func ReplaceExtension(path string, newExt string) string {
// AbsPathify creates an absolute path if given a relative path. If already
// absolute, the path is just cleaned.
func AbsPathify(inPath string) string {
func (p *PathSpec) AbsPathify(inPath string) string {
if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}
// TODO(bep): Consider moving workingDir to argument list
return filepath.Clean(filepath.Join(viper.GetString("workingDir"), inPath))
return filepath.Clean(filepath.Join(p.workingDir, inPath))
}
// GetLayoutDirPath returns the absolute path to the layout file dir
// for the current Hugo project.
func GetLayoutDirPath() string {
return AbsPathify(viper.GetString("layoutDir"))
func (p *PathSpec) GetLayoutDirPath() string {
return p.AbsPathify(p.layoutDir)
}
// GetStaticDirPath returns the absolute path to the static file dir
// for the current Hugo project.
func GetStaticDirPath() string {
return AbsPathify(viper.GetString("staticDir"))
func (p *PathSpec) GetStaticDirPath() string {
return p.AbsPathify(p.staticDir)
}
// GetThemeDir gets the root directory of the current theme, if there is one.
// If there is no theme, returns the empty string.
func GetThemeDir() string {
if ThemeSet() {
return AbsPathify(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")))
func (p *PathSpec) GetThemeDir() string {
if p.ThemeSet() {
return p.AbsPathify(filepath.Join(p.themesDir, p.theme))
}
return ""
}
// GetRelativeThemeDir gets the relative root directory of the current theme, if there is one.
// If there is no theme, returns the empty string.
func GetRelativeThemeDir() string {
if ThemeSet() {
return strings.TrimPrefix(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")), FilePathSeparator)
func (p *PathSpec) GetRelativeThemeDir() string {
if p.ThemeSet() {
return strings.TrimPrefix(filepath.Join(p.themesDir, p.theme), FilePathSeparator)
}
return ""
}
@@ -211,13 +210,13 @@ func (p *PathSpec) GetThemeI18nDirPath() (string, error) {
}
func (p *PathSpec) getThemeDirPath(path string) (string, error) {
if !ThemeSet() {
if !p.ThemeSet() {
return "", ErrThemeUndefined
}
themeDir := filepath.Join(GetThemeDir(), path)
themeDir := filepath.Join(p.GetThemeDir(), path)
if _, err := p.fs.Source.Stat(themeDir); os.IsNotExist(err) {
return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir)
return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, p.theme, themeDir)
}
return themeDir, nil
@@ -235,7 +234,7 @@ func (p *PathSpec) GetThemesDirPath() string {
// It does so by taking either the project's static path or the theme's static
// path into consideration.
func (p *PathSpec) MakeStaticPathRelative(inPath string) (string, error) {
staticDir := GetStaticDirPath()
staticDir := p.GetStaticDirPath()
themeStaticDir := p.GetThemesDirPath()
return makePathRelative(inPath, staticDir, themeStaticDir)
@@ -360,20 +359,20 @@ func GetRelativePath(path, base string) (final string, err error) {
}
// PaginateAliasPath creates a path used to access the aliases in the paginator.
func PaginateAliasPath(base string, page int) string {
paginatePath := Config().GetString("paginatePath")
uglify := viper.GetBool("uglyURLs")
var p string
func (p *PathSpec) PaginateAliasPath(base string, page int) string {
paginatePath := p.paginatePath
uglify := p.uglyURLs
var pth string
if base != "" {
p = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
pth = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
} else {
p = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
pth = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
}
if uglify {
p += ".html"
pth += ".html"
}
return p
return pth
}
// GuessSection returns the section given a source path.

View File

@@ -34,15 +34,7 @@ import (
"github.com/spf13/viper"
)
func initCommonTestConfig() {
viper.Set("currentContentLanguage", NewLanguage("en"))
}
func TestMakePath(t *testing.T) {
viper.Reset()
defer viper.Reset()
initCommonTestConfig()
tests := []struct {
input string
expected string
@@ -64,8 +56,10 @@ func TestMakePath(t *testing.T) {
}
for _, test := range tests {
viper.Set("removePathAccents", test.removeAccents)
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v := viper.New()
l := NewDefaultLanguage(v)
v.Set("removePathAccents", test.removeAccents)
p := NewPathSpec(hugofs.NewMem(v), l)
output := p.MakePath(test.input)
if output != test.expected {
@@ -75,11 +69,9 @@ func TestMakePath(t *testing.T) {
}
func TestMakePathSanitized(t *testing.T) {
viper.Reset()
defer viper.Reset()
initCommonTestConfig()
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v := viper.New()
l := NewDefaultLanguage(v)
p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -102,12 +94,12 @@ func TestMakePathSanitized(t *testing.T) {
}
func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
viper.Reset()
defer viper.Reset()
v := viper.New()
initCommonTestConfig()
viper.Set("disablePathToLower", true)
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v.Set("disablePathToLower", true)
l := NewDefaultLanguage(v)
p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -553,9 +545,9 @@ func TestAbsPathify(t *testing.T) {
for i, d := range data {
viper.Reset()
// todo see comment in AbsPathify
viper.Set("workingDir", d.workingDir)
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
expected := AbsPathify(d.inPath)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}
@@ -563,18 +555,18 @@ 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 {
viper.Set("workingDir", d.workingDir)
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
expected := AbsPathify(d.inPath)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}
}
} else {
for i, d := range unixData {
viper.Set("workingDir", d.workingDir)
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
expected := AbsPathify(d.inPath)
expected := ps.AbsPathify(d.inPath)
if d.expected != expected {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
}

View File

@@ -16,6 +16,7 @@ package helpers
import (
"fmt"
"github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
)
@@ -26,11 +27,20 @@ type PathSpec struct {
uglyURLs bool
canonifyURLs bool
currentContentLanguage *Language
language *Language
// pagination path handling
paginatePath string
baseURL string
theme string
// Directories
themesDir string
layoutDir string
workingDir string
staticDir string
// The PathSpec looks up its config settings in both the current language
// and then in the global Viper config.
// Some settings, the settings listed below, does not make sense to be set
@@ -45,31 +55,35 @@ type PathSpec struct {
}
func (p PathSpec) String() string {
return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.currentContentLanguage.Lang, p.getLanguagePrefix(), p.multilingual)
return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.language.Lang, p.getLanguagePrefix(), p.multilingual)
}
// NewPathSpec creats a new PathSpec from the given filesystems and ConfigProvider.
func NewPathSpec(fs *hugofs.Fs, config ConfigProvider) *PathSpec {
// NewPathSpec creats a new PathSpec from the given filesystems and Language.
func NewPathSpec(fs *hugofs.Fs, cfg config.Provider) *PathSpec {
currCl, ok := config.Get("currentContentLanguage").(*Language)
if !ok {
// TODO(bep) globals
currCl = NewLanguage("en")
}
return &PathSpec{
ps := &PathSpec{
fs: fs,
disablePathToLower: config.GetBool("disablePathToLower"),
removePathAccents: config.GetBool("removePathAccents"),
uglyURLs: config.GetBool("uglyURLs"),
canonifyURLs: config.GetBool("canonifyURLs"),
multilingual: config.GetBool("multilingual"),
defaultContentLanguageInSubdir: config.GetBool("defaultContentLanguageInSubdir"),
defaultContentLanguage: config.GetString("defaultContentLanguage"),
currentContentLanguage: currCl,
paginatePath: config.GetString("paginatePath"),
disablePathToLower: cfg.GetBool("disablePathToLower"),
removePathAccents: cfg.GetBool("removePathAccents"),
uglyURLs: cfg.GetBool("uglyURLs"),
canonifyURLs: cfg.GetBool("canonifyURLs"),
multilingual: cfg.GetBool("multilingual"),
defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
defaultContentLanguage: cfg.GetString("defaultContentLanguage"),
paginatePath: cfg.GetString("paginatePath"),
baseURL: cfg.GetString("baseURL"),
themesDir: cfg.GetString("themesDir"),
layoutDir: cfg.GetString("layoutDir"),
workingDir: cfg.GetString("workingDir"),
staticDir: cfg.GetString("staticDir"),
theme: cfg.GetString("theme"),
}
if language, ok := cfg.(*Language); ok {
ps.language = language
}
return ps
}
// PaginatePath returns the configured root path used for paginator pages.

View File

@@ -23,17 +23,24 @@ import (
)
func TestNewPathSpecFromConfig(t *testing.T) {
viper.Set("disablePathToLower", true)
viper.Set("removePathAccents", true)
viper.Set("uglyURLs", true)
viper.Set("multilingual", true)
viper.Set("defaultContentLanguageInSubdir", true)
viper.Set("defaultContentLanguage", "no")
viper.Set("currentContentLanguage", NewLanguage("no"))
viper.Set("canonifyURLs", true)
viper.Set("paginatePath", "side")
v := viper.New()
l := NewLanguage("no", v)
v.Set("disablePathToLower", true)
v.Set("removePathAccents", true)
v.Set("uglyURLs", true)
v.Set("multilingual", true)
v.Set("defaultContentLanguageInSubdir", true)
v.Set("defaultContentLanguage", "no")
v.Set("canonifyURLs", true)
v.Set("paginatePath", "side")
v.Set("baseURL", "http://base.com")
v.Set("themesDir", "thethemes")
v.Set("layoutDir", "thelayouts")
v.Set("workingDir", "thework")
v.Set("staticDir", "thestatic")
v.Set("theme", "thetheme")
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
p := NewPathSpec(hugofs.NewMem(v), l)
require.True(t, p.canonifyURLs)
require.True(t, p.defaultContentLanguageInSubdir)
@@ -42,6 +49,13 @@ func TestNewPathSpecFromConfig(t *testing.T) {
require.True(t, p.removePathAccents)
require.True(t, p.uglyURLs)
require.Equal(t, "no", p.defaultContentLanguage)
require.Equal(t, "no", p.currentContentLanguage.Lang)
require.Equal(t, "no", p.language.Lang)
require.Equal(t, "side", p.paginatePath)
require.Equal(t, "http://base.com", p.baseURL)
require.Equal(t, "thethemes", p.themesDir)
require.Equal(t, "thelayouts", p.layoutDir)
require.Equal(t, "thework", p.workingDir)
require.Equal(t, "thestatic", p.staticDir)
require.Equal(t, "thetheme", p.theme)
}

View File

@@ -24,9 +24,9 @@ import (
"sort"
"strings"
"github.com/spf13/hugo/config"
"github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
const pygmentsBin = "pygmentize"
@@ -41,13 +41,13 @@ func HasPygments() bool {
}
// Highlight takes some code and returns highlighted code.
func Highlight(code, lang, optsStr string) string {
func Highlight(cfg config.Provider, code, lang, optsStr string) string {
if !HasPygments() {
jww.WARN.Println("Highlighting requires Pygments to be installed and in the path")
return code
}
options, err := parsePygmentsOpts(optsStr)
options, err := parsePygmentsOpts(cfg, optsStr)
if err != nil {
jww.ERROR.Print(err.Error())
@@ -62,8 +62,8 @@ func Highlight(code, lang, optsStr string) string {
fs := hugofs.Os
ignoreCache := viper.GetBool("ignoreCache")
cacheDir := viper.GetString("cacheDir")
ignoreCache := cfg.GetBool("ignoreCache")
cacheDir := cfg.GetString("cacheDir")
var cachefile string
if !ignoreCache && cacheDir != "" {
@@ -195,19 +195,19 @@ func createOptionsString(options map[string]string) string {
return optionsStr
}
func parseDefaultPygmentsOpts() (map[string]string, error) {
func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) {
options := make(map[string]string)
err := parseOptions(options, viper.GetString("pygmentsOptions"))
err := parseOptions(options, cfg.GetString("pygmentsOptions"))
if err != nil {
return nil, err
}
if viper.IsSet("pygmentsStyle") {
options["style"] = viper.GetString("pygmentsStyle")
if cfg.IsSet("pygmentsStyle") {
options["style"] = cfg.GetString("pygmentsStyle")
}
if viper.IsSet("pygmentsUseClasses") {
if viper.GetBool("pygmentsUseClasses") {
if cfg.IsSet("pygmentsUseClasses") {
if cfg.GetBool("pygmentsUseClasses") {
options["noclasses"] = "false"
} else {
options["noclasses"] = "true"
@@ -222,8 +222,8 @@ func parseDefaultPygmentsOpts() (map[string]string, error) {
return options, nil
}
func parsePygmentsOpts(in string) (string, error) {
options, err := parseDefaultPygmentsOpts()
func parsePygmentsOpts(cfg config.Provider, in string) (string, error) {
options, err := parseDefaultPygmentsOpts(cfg)
if err != nil {
return "", err
}

View File

@@ -34,11 +34,12 @@ func TestParsePygmentsArgs(t *testing.T) {
{"boo=invalid", "foo", false, false},
{"style", "foo", false, false},
} {
viper.Reset()
viper.Set("pygmentsStyle", this.pygmentsStyle)
viper.Set("pygmentsUseClasses", this.pygmentsUseClasses)
result1, err := parsePygmentsOpts(this.in)
v := viper.New()
v.Set("pygmentsStyle", this.pygmentsStyle)
v.Set("pygmentsUseClasses", this.pygmentsUseClasses)
result1, err := parsePygmentsOpts(v, this.in)
if b, ok := this.expect1.(bool); ok && !b {
if err == nil {
t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i)
@@ -70,19 +71,19 @@ func TestParseDefaultPygmentsArgs(t *testing.T) {
{"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"},
{"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"},
} {
viper.Reset()
v := viper.New()
viper.Set("pygmentsOptions", this.pygmentsOptions)
v.Set("pygmentsOptions", this.pygmentsOptions)
if s, ok := this.pygmentsStyle.(string); ok {
viper.Set("pygmentsStyle", s)
v.Set("pygmentsStyle", s)
}
if b, ok := this.pygmentsUseClasses.(bool); ok {
viper.Set("pygmentsUseClasses", b)
v.Set("pygmentsUseClasses", b)
}
result, err := parsePygmentsOpts(this.in)
result, err := parsePygmentsOpts(v, this.in)
if err != nil {
t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
continue

View File

@@ -0,0 +1,37 @@
package helpers
import (
"github.com/spf13/viper"
"github.com/spf13/hugo/hugofs"
)
func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *PathSpec {
l := NewDefaultLanguage(v)
return NewPathSpec(fs, l)
}
func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec {
v := viper.New()
fs := hugofs.NewMem(v)
cfg := newTestCfg(fs)
for i := 0; i < len(configKeyValues); i += 2 {
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
}
return newTestPathSpec(fs, cfg)
}
func newTestCfg(fs *hugofs.Fs) *viper.Viper {
v := viper.New()
v.SetFs(fs.Source)
return v
}
func newTestContentSpec() *ContentSpec {
v := viper.New()
return NewContentSpec(v)
}

View File

@@ -21,7 +21,6 @@ import (
"github.com/PuerkitoBio/purell"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
type pathBridge struct {
@@ -158,7 +157,7 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
return in
}
baseURL := viper.GetString("baseURL")
baseURL := p.baseURL
if strings.HasPrefix(in, "/") {
p, err := url.Parse(baseURL)
if err != nil {
@@ -200,7 +199,7 @@ func (p *PathSpec) getLanguagePrefix() string {
defaultLang := p.defaultContentLanguage
defaultInSubDir := p.defaultContentLanguageInSubdir
currentLang := p.currentContentLanguage.Lang
currentLang := p.language.Lang
if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
return ""
}
@@ -220,7 +219,7 @@ func IsAbsURL(path string) bool {
// RelURL creates a URL relative to the BaseURL root.
// Note: The result URL will not include the context root if canonifyURLs is enabled.
func (p *PathSpec) RelURL(in string, addLanguage bool) string {
baseURL := viper.GetString("baseURL")
baseURL := p.baseURL
canonifyURLs := p.canonifyURLs
if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
return in

View File

@@ -25,9 +25,10 @@ import (
)
func TestURLize(t *testing.T) {
initCommonTestConfig()
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v := viper.New()
l := NewDefaultLanguage(v)
p := NewPathSpec(hugofs.NewMem(v), l)
tests := []struct {
input string
@@ -62,11 +63,10 @@ func TestAbsURL(t *testing.T) {
}
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
viper.Reset()
viper.Set("multilingual", multilingual)
viper.Set("currentContentLanguage", NewLanguage(lang))
viper.Set("defaultContentLanguage", "en")
viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
v := viper.New()
v.Set("multilingual", multilingual)
v.Set("defaultContentLanguage", "en")
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
tests := []struct {
input string
@@ -86,10 +86,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
{"http//foo", "http://base/path", "http://base/path/MULTIhttp/foo"},
}
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
for _, test := range tests {
viper.Set("baseURL", test.baseURL)
v.Set("baseURL", test.baseURL)
l := NewLanguage(lang, v)
p := NewPathSpec(hugofs.NewMem(v), l)
output := p.AbsURL(test.input, addLanguage)
expected := test.expected
@@ -138,11 +138,10 @@ func TestRelURL(t *testing.T) {
}
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
viper.Reset()
viper.Set("multilingual", multilingual)
viper.Set("currentContentLanguage", NewLanguage(lang))
viper.Set("defaultContentLanguage", "en")
viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
v := viper.New()
v.Set("multilingual", multilingual)
v.Set("defaultContentLanguage", "en")
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
tests := []struct {
input string
@@ -165,9 +164,10 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
}
for i, test := range tests {
viper.Set("baseURL", test.baseURL)
viper.Set("canonifyURLs", test.canonify)
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v.Set("baseURL", test.baseURL)
v.Set("canonifyURLs", test.canonify)
l := NewLanguage(lang, v)
p := NewPathSpec(hugofs.NewMem(v), l)
output := p.RelURL(test.input, addLanguage)
@@ -252,8 +252,10 @@ func TestURLPrep(t *testing.T) {
}
for i, d := range data {
viper.Set("uglyURLs", d.ugly)
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
v := viper.New()
v.Set("uglyURLs", d.ugly)
l := NewDefaultLanguage(v)
p := NewPathSpec(hugofs.NewMem(v), l)
output := p.URLPrep(d.input)
if d.output != output {