github.com/gohugoio/hugo@v0.88.1/tpl/tplimpl/template_funcs_test.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package tplimpl
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"path/filepath"
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/gohugoio/hugo/modules"
    25  
    26  	"github.com/gohugoio/hugo/resources/page"
    27  
    28  	qt "github.com/frankban/quicktest"
    29  	"github.com/gohugoio/hugo/common/hugo"
    30  	"github.com/gohugoio/hugo/common/loggers"
    31  	"github.com/gohugoio/hugo/config"
    32  	"github.com/gohugoio/hugo/deps"
    33  	"github.com/gohugoio/hugo/hugofs"
    34  	"github.com/gohugoio/hugo/langs"
    35  	"github.com/gohugoio/hugo/langs/i18n"
    36  	"github.com/gohugoio/hugo/tpl"
    37  	"github.com/gohugoio/hugo/tpl/internal"
    38  	"github.com/gohugoio/hugo/tpl/partials"
    39  	"github.com/spf13/afero"
    40  	
    41  )
    42  
    43  var logger = loggers.NewErrorLogger()
    44  
    45  func newTestConfig() config.Provider {
    46  	v := config.New()
    47  	v.Set("contentDir", "content")
    48  	v.Set("dataDir", "data")
    49  	v.Set("i18nDir", "i18n")
    50  	v.Set("layoutDir", "layouts")
    51  	v.Set("archetypeDir", "archetypes")
    52  	v.Set("assetDir", "assets")
    53  	v.Set("resourceDir", "resources")
    54  	v.Set("publishDir", "public")
    55  
    56  	langs.LoadLanguageSettings(v, nil)
    57  	mod, err := modules.CreateProjectModule(v)
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  	v.Set("allModules", modules.Modules{mod})
    62  
    63  	return v
    64  }
    65  
    66  func newDepsConfig(cfg config.Provider) deps.DepsCfg {
    67  	l := langs.NewLanguage("en", cfg)
    68  	return deps.DepsCfg{
    69  		Language:            l,
    70  		Site:                page.NewDummyHugoSite(cfg),
    71  		Cfg:                 cfg,
    72  		Fs:                  hugofs.NewMem(l),
    73  		Logger:              logger,
    74  		TemplateProvider:    DefaultTemplateProvider,
    75  		TranslationProvider: i18n.NewTranslationProvider(),
    76  	}
    77  }
    78  
    79  func TestTemplateFuncsExamples(t *testing.T) {
    80  	t.Parallel()
    81  	c := qt.New(t)
    82  
    83  	workingDir := "/home/hugo"
    84  
    85  	v := newTestConfig()
    86  
    87  	v.Set("workingDir", workingDir)
    88  	v.Set("multilingual", true)
    89  	v.Set("contentDir", "content")
    90  	v.Set("assetDir", "assets")
    91  	v.Set("baseURL", "http://mysite.com/hugo/")
    92  	v.Set("CurrentContentLanguage", langs.NewLanguage("en", v))
    93  
    94  	fs := hugofs.NewMem(v)
    95  
    96  	afero.WriteFile(fs.Source, filepath.Join(workingDir, "files", "README.txt"), []byte("Hugo Rocks!"), 0755)
    97  
    98  	depsCfg := newDepsConfig(v)
    99  	depsCfg.Fs = fs
   100  	d, err := deps.New(depsCfg)
   101  	defer d.Close()
   102  	c.Assert(err, qt.IsNil)
   103  
   104  	var data struct {
   105  		Title   string
   106  		Section string
   107  		Hugo    map[string]interface{}
   108  		Params  map[string]interface{}
   109  	}
   110  
   111  	data.Title = "**BatMan**"
   112  	data.Section = "blog"
   113  	data.Params = map[string]interface{}{"langCode": "en"}
   114  	data.Hugo = map[string]interface{}{"Version": hugo.MustParseVersion("0.36.1").Version()}
   115  
   116  	for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
   117  		ns := nsf(d)
   118  		for _, mm := range ns.MethodMappings {
   119  			for i, example := range mm.Examples {
   120  				in, expected := example[0], example[1]
   121  				d.WithTemplate = func(templ tpl.TemplateManager) error {
   122  					c.Assert(templ.AddTemplate("test", in), qt.IsNil)
   123  					c.Assert(templ.AddTemplate("partials/header.html", "<title>Hugo Rocks!</title>"), qt.IsNil)
   124  					return nil
   125  				}
   126  				c.Assert(d.LoadResources(), qt.IsNil)
   127  
   128  				var b bytes.Buffer
   129  				templ, _ := d.Tmpl().Lookup("test")
   130  				c.Assert(d.Tmpl().Execute(templ, &b, &data), qt.IsNil)
   131  				if b.String() != expected {
   132  					t.Fatalf("%s[%d]: got %q expected %q", ns.Name, i, b.String(), expected)
   133  				}
   134  			}
   135  		}
   136  	}
   137  }
   138  
   139  // TODO(bep) it would be dandy to put this one into the partials package, but
   140  // we have some package cycle issues to solve first.
   141  func TestPartialCached(t *testing.T) {
   142  	t.Parallel()
   143  
   144  	c := qt.New(t)
   145  
   146  	partial := `Now: {{ now.UnixNano }}`
   147  	name := "testing"
   148  
   149  	var data struct {
   150  	}
   151  
   152  	v := newTestConfig()
   153  
   154  	config := newDepsConfig(v)
   155  
   156  	config.WithTemplate = func(templ tpl.TemplateManager) error {
   157  		err := templ.AddTemplate("partials/"+name, partial)
   158  		if err != nil {
   159  			return err
   160  		}
   161  
   162  		return nil
   163  	}
   164  
   165  	de, err := deps.New(config)
   166  	c.Assert(err, qt.IsNil)
   167  	defer de.Close()
   168  	c.Assert(de.LoadResources(), qt.IsNil)
   169  
   170  	ns := partials.New(de)
   171  
   172  	res1, err := ns.IncludeCached(name, &data)
   173  	c.Assert(err, qt.IsNil)
   174  
   175  	for j := 0; j < 10; j++ {
   176  		time.Sleep(2 * time.Nanosecond)
   177  		res2, err := ns.IncludeCached(name, &data)
   178  		c.Assert(err, qt.IsNil)
   179  
   180  		if !reflect.DeepEqual(res1, res2) {
   181  			t.Fatalf("cache mismatch")
   182  		}
   183  
   184  		res3, err := ns.IncludeCached(name, &data, fmt.Sprintf("variant%d", j))
   185  		c.Assert(err, qt.IsNil)
   186  
   187  		if reflect.DeepEqual(res1, res3) {
   188  			t.Fatalf("cache mismatch")
   189  		}
   190  	}
   191  }
   192  
   193  func BenchmarkPartial(b *testing.B) {
   194  	doBenchmarkPartial(b, func(ns *partials.Namespace) error {
   195  		_, err := ns.Include("bench1")
   196  		return err
   197  	})
   198  }
   199  
   200  func BenchmarkPartialCached(b *testing.B) {
   201  	doBenchmarkPartial(b, func(ns *partials.Namespace) error {
   202  		_, err := ns.IncludeCached("bench1", nil)
   203  		return err
   204  	})
   205  }
   206  
   207  func doBenchmarkPartial(b *testing.B, f func(ns *partials.Namespace) error) {
   208  	c := qt.New(b)
   209  	config := newDepsConfig(config.New())
   210  	config.WithTemplate = func(templ tpl.TemplateManager) error {
   211  		err := templ.AddTemplate("partials/bench1", `{{ shuffle (seq 1 10) }}`)
   212  		if err != nil {
   213  			return err
   214  		}
   215  
   216  		return nil
   217  	}
   218  
   219  	de, err := deps.New(config)
   220  	c.Assert(err, qt.IsNil)
   221  	defer de.Close()
   222  	c.Assert(de.LoadResources(), qt.IsNil)
   223  
   224  	ns := partials.New(de)
   225  
   226  	b.ResetTimer()
   227  	b.RunParallel(func(pb *testing.PB) {
   228  		for pb.Next() {
   229  			if err := f(ns); err != nil {
   230  				b.Fatalf("error executing template: %s", err)
   231  			}
   232  		}
   233  	})
   234  }