github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/hugolib/filesystems/basefs_test.go (about)

     1  // Copyright 2023 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 filesystems_test
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/gohugoio/hugo/config"
    25  	"github.com/gohugoio/hugo/config/testconfig"
    26  
    27  	"github.com/spf13/afero"
    28  
    29  	qt "github.com/frankban/quicktest"
    30  	"github.com/gohugoio/hugo/hugofs"
    31  	"github.com/gohugoio/hugo/hugolib/filesystems"
    32  	"github.com/gohugoio/hugo/hugolib/paths"
    33  )
    34  
    35  func TestNewBaseFs(t *testing.T) {
    36  	c := qt.New(t)
    37  	v := config.New()
    38  
    39  	themes := []string{"btheme", "atheme"}
    40  
    41  	workingDir := filepath.FromSlash("/my/work")
    42  	v.Set("workingDir", workingDir)
    43  	v.Set("contentDir", "content")
    44  	v.Set("themesDir", "themes")
    45  	v.Set("defaultContentLanguage", "en")
    46  	v.Set("theme", themes[:1])
    47  	v.Set("publishDir", "public")
    48  
    49  	afs := afero.NewMemMapFs()
    50  
    51  	// Write some data to the themes
    52  	for _, theme := range themes {
    53  		for _, dir := range []string{"i18n", "data", "archetypes", "layouts"} {
    54  			base := filepath.Join(workingDir, "themes", theme, dir)
    55  			filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme))
    56  			filenameOverlap := filepath.Join(base, "f3.txt")
    57  			afs.Mkdir(base, 0755)
    58  			content := []byte(fmt.Sprintf("content:%s:%s", theme, dir))
    59  			afero.WriteFile(afs, filenameTheme, content, 0755)
    60  			afero.WriteFile(afs, filenameOverlap, content, 0755)
    61  		}
    62  		// Write some files to the root of the theme
    63  		base := filepath.Join(workingDir, "themes", theme)
    64  		afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0755)
    65  		afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0755)
    66  	}
    67  
    68  	afero.WriteFile(afs, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0755)
    69  
    70  	afero.WriteFile(afs, filepath.Join(workingDir, "themes", "btheme", "config.toml"), []byte(`
    71  theme = ["atheme"]
    72  `), 0755)
    73  
    74  	setConfigAndWriteSomeFilesTo(afs, v, "contentDir", "mycontent", 3)
    75  	setConfigAndWriteSomeFilesTo(afs, v, "i18nDir", "myi18n", 4)
    76  	setConfigAndWriteSomeFilesTo(afs, v, "layoutDir", "mylayouts", 5)
    77  	setConfigAndWriteSomeFilesTo(afs, v, "staticDir", "mystatic", 6)
    78  	setConfigAndWriteSomeFilesTo(afs, v, "dataDir", "mydata", 7)
    79  	setConfigAndWriteSomeFilesTo(afs, v, "archetypeDir", "myarchetypes", 8)
    80  	setConfigAndWriteSomeFilesTo(afs, v, "assetDir", "myassets", 9)
    81  	setConfigAndWriteSomeFilesTo(afs, v, "resourceDir", "myrsesource", 10)
    82  
    83  	conf := testconfig.GetTestConfig(afs, v)
    84  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
    85  
    86  	p, err := paths.New(fs, conf)
    87  	c.Assert(err, qt.IsNil)
    88  
    89  	bfs, err := filesystems.NewBase(p, nil)
    90  	c.Assert(err, qt.IsNil)
    91  	c.Assert(bfs, qt.Not(qt.IsNil))
    92  
    93  	root, err := bfs.I18n.Fs.Open("")
    94  	c.Assert(err, qt.IsNil)
    95  	dirnames, err := root.Readdirnames(-1)
    96  	c.Assert(err, qt.IsNil)
    97  	c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"})
    98  
    99  	root, err = bfs.Data.Fs.Open("")
   100  	c.Assert(err, qt.IsNil)
   101  	dirnames, err = root.Readdirnames(-1)
   102  	c.Assert(err, qt.IsNil)
   103  	c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f5.txt", "f6.txt", "f7.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"})
   104  
   105  	checkFileCount(bfs.Layouts.Fs, "", c, 7)
   106  
   107  	checkFileCount(bfs.Content.Fs, "", c, 3)
   108  	checkFileCount(bfs.I18n.Fs, "", c, 8) // 4 + 4 themes
   109  
   110  	checkFileCount(bfs.Static[""].Fs, "", c, 6)
   111  	checkFileCount(bfs.Data.Fs, "", c, 11)       // 7 + 4 themes
   112  	checkFileCount(bfs.Archetypes.Fs, "", c, 10) // 8 + 2 themes
   113  	checkFileCount(bfs.Assets.Fs, "", c, 9)
   114  	checkFileCount(bfs.Work, "", c, 90)
   115  
   116  	c.Assert(bfs.IsData(filepath.Join(workingDir, "mydata", "file1.txt")), qt.Equals, true)
   117  	c.Assert(bfs.IsI18n(filepath.Join(workingDir, "myi18n", "file1.txt")), qt.Equals, true)
   118  	c.Assert(bfs.IsLayout(filepath.Join(workingDir, "mylayouts", "file1.txt")), qt.Equals, true)
   119  	c.Assert(bfs.IsStatic(filepath.Join(workingDir, "mystatic", "file1.txt")), qt.Equals, true)
   120  	c.Assert(bfs.IsAsset(filepath.Join(workingDir, "myassets", "file1.txt")), qt.Equals, true)
   121  
   122  	contentFilename := filepath.Join(workingDir, "mycontent", "file1.txt")
   123  	c.Assert(bfs.IsContent(contentFilename), qt.Equals, true)
   124  	rel := bfs.RelContentDir(contentFilename)
   125  	c.Assert(rel, qt.Equals, "file1.txt")
   126  
   127  	// Check Work fs vs theme
   128  	checkFileContent(bfs.Work, "file-root.txt", c, "content-project")
   129  	checkFileContent(bfs.Work, "theme-root-atheme.txt", c, "content:atheme")
   130  
   131  	// https://github.com/gohugoio/hugo/issues/5318
   132  	// Check both project and theme.
   133  	for _, fs := range []afero.Fs{bfs.Archetypes.Fs, bfs.Layouts.Fs} {
   134  		for _, filename := range []string{"/f1.txt", "/theme-file-atheme.txt"} {
   135  			filename = filepath.FromSlash(filename)
   136  			f, err := fs.Open(filename)
   137  			c.Assert(err, qt.IsNil)
   138  			f.Close()
   139  		}
   140  	}
   141  }
   142  
   143  func TestNewBaseFsEmpty(t *testing.T) {
   144  	c := qt.New(t)
   145  	afs := afero.NewMemMapFs()
   146  	conf := testconfig.GetTestConfig(afs, nil)
   147  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   148  	p, err := paths.New(fs, conf)
   149  	c.Assert(err, qt.IsNil)
   150  	bfs, err := filesystems.NewBase(p, nil)
   151  	c.Assert(err, qt.IsNil)
   152  	c.Assert(bfs, qt.Not(qt.IsNil))
   153  	c.Assert(bfs.Archetypes.Fs, qt.Not(qt.IsNil))
   154  	c.Assert(bfs.Layouts.Fs, qt.Not(qt.IsNil))
   155  	c.Assert(bfs.Data.Fs, qt.Not(qt.IsNil))
   156  	c.Assert(bfs.I18n.Fs, qt.Not(qt.IsNil))
   157  	c.Assert(bfs.Work, qt.Not(qt.IsNil))
   158  	c.Assert(bfs.Content.Fs, qt.Not(qt.IsNil))
   159  	c.Assert(bfs.Static, qt.Not(qt.IsNil))
   160  }
   161  
   162  func TestRealDirs(t *testing.T) {
   163  	c := qt.New(t)
   164  	v := config.New()
   165  	root, themesDir := t.TempDir(), t.TempDir()
   166  	v.Set("workingDir", root)
   167  	v.Set("themesDir", themesDir)
   168  	v.Set("assetDir", "myassets")
   169  	v.Set("theme", "mytheme")
   170  
   171  	afs := hugofs.Os
   172  
   173  	defer func() {
   174  		os.RemoveAll(root)
   175  		os.RemoveAll(themesDir)
   176  	}()
   177  
   178  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf1"), 0755), qt.IsNil)
   179  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf2"), 0755), qt.IsNil)
   180  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2"), 0755), qt.IsNil)
   181  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3"), 0755), qt.IsNil)
   182  	c.Assert(afs.MkdirAll(filepath.Join(root, "resources"), 0755), qt.IsNil)
   183  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "resources"), 0755), qt.IsNil)
   184  
   185  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "js", "f2"), 0755), qt.IsNil)
   186  
   187  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf1", "a1.scss")), []byte("content"), 0755)
   188  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf2", "a3.scss")), []byte("content"), 0755)
   189  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "a2.scss")), []byte("content"), 0755)
   190  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2", "a3.scss")), []byte("content"), 0755)
   191  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3", "a4.scss")), []byte("content"), 0755)
   192  
   193  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "resources", "t1.txt")), []byte("content"), 0755)
   194  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p1.txt")), []byte("content"), 0755)
   195  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p2.txt")), []byte("content"), 0755)
   196  
   197  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "f2", "a1.js")), []byte("content"), 0755)
   198  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "a2.js")), []byte("content"), 0755)
   199  
   200  	conf := testconfig.GetTestConfig(afs, v)
   201  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   202  	p, err := paths.New(fs, conf)
   203  	c.Assert(err, qt.IsNil)
   204  	bfs, err := filesystems.NewBase(p, nil)
   205  	c.Assert(err, qt.IsNil)
   206  	c.Assert(bfs, qt.Not(qt.IsNil))
   207  
   208  	checkFileCount(bfs.Assets.Fs, "", c, 6)
   209  
   210  	realDirs := bfs.Assets.RealDirs("scss")
   211  	c.Assert(len(realDirs), qt.Equals, 2)
   212  	c.Assert(realDirs[0], qt.Equals, filepath.Join(root, "myassets/scss"))
   213  	c.Assert(realDirs[len(realDirs)-1], qt.Equals, filepath.Join(themesDir, "mytheme/assets/scss"))
   214  
   215  }
   216  
   217  func TestStaticFs(t *testing.T) {
   218  	c := qt.New(t)
   219  	v := config.New()
   220  	workDir := "mywork"
   221  	v.Set("workingDir", workDir)
   222  	v.Set("themesDir", "themes")
   223  	v.Set("staticDir", "mystatic")
   224  	v.Set("theme", []string{"t1", "t2"})
   225  
   226  	afs := afero.NewMemMapFs()
   227  
   228  	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
   229  	themeStaticDir2 := filepath.Join(workDir, "themes", "t2", "static")
   230  
   231  	afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0755)
   232  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0755)
   233  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0755)
   234  	afero.WriteFile(afs, filepath.Join(themeStaticDir2, "f2.txt"), []byte("Hugo Themes Rocks in t2!"), 0755)
   235  
   236  	conf := testconfig.GetTestConfig(afs, v)
   237  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   238  	p, err := paths.New(fs, conf)
   239  
   240  	c.Assert(err, qt.IsNil)
   241  	bfs, err := filesystems.NewBase(p, nil)
   242  	c.Assert(err, qt.IsNil)
   243  
   244  	sfs := bfs.StaticFs("en")
   245  
   246  	checkFileContent(sfs, "f1.txt", c, "Hugo Rocks!")
   247  	checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
   248  }
   249  
   250  func TestStaticFsMultiHost(t *testing.T) {
   251  	c := qt.New(t)
   252  	v := config.New()
   253  	workDir := "mywork"
   254  	v.Set("workingDir", workDir)
   255  	v.Set("themesDir", "themes")
   256  	v.Set("staticDir", "mystatic")
   257  	v.Set("theme", "t1")
   258  	v.Set("defaultContentLanguage", "en")
   259  
   260  	langConfig := map[string]any{
   261  		"no": map[string]any{
   262  			"staticDir": "static_no",
   263  			"baseURL":   "https://example.org/no/",
   264  		},
   265  		"en": map[string]any{
   266  			"baseURL": "https://example.org/en/",
   267  		},
   268  	}
   269  
   270  	v.Set("languages", langConfig)
   271  
   272  	afs := afero.NewMemMapFs()
   273  
   274  	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
   275  
   276  	afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0755)
   277  	afero.WriteFile(afs, filepath.Join(workDir, "static_no", "f1.txt"), []byte("Hugo Rocks in Norway!"), 0755)
   278  
   279  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0755)
   280  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0755)
   281  
   282  	conf := testconfig.GetTestConfig(afs, v)
   283  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   284  
   285  	fmt.Println("IS", conf.IsMultihost())
   286  
   287  	p, err := paths.New(fs, conf)
   288  	c.Assert(err, qt.IsNil)
   289  	bfs, err := filesystems.NewBase(p, nil)
   290  	c.Assert(err, qt.IsNil)
   291  	enFs := bfs.StaticFs("en")
   292  	checkFileContent(enFs, "f1.txt", c, "Hugo Rocks!")
   293  	checkFileContent(enFs, "f2.txt", c, "Hugo Themes Still Rocks!")
   294  
   295  	noFs := bfs.StaticFs("no")
   296  	checkFileContent(noFs, "f1.txt", c, "Hugo Rocks in Norway!")
   297  	checkFileContent(noFs, "f2.txt", c, "Hugo Themes Still Rocks!")
   298  }
   299  
   300  func TestMakePathRelative(t *testing.T) {
   301  	c := qt.New(t)
   302  	v := config.New()
   303  	afs := afero.NewMemMapFs()
   304  	workDir := "mywork"
   305  	v.Set("workingDir", workDir)
   306  
   307  	c.Assert(afs.MkdirAll(filepath.Join(workDir, "dist", "d1"), 0777), qt.IsNil)
   308  	c.Assert(afs.MkdirAll(filepath.Join(workDir, "static", "d2"), 0777), qt.IsNil)
   309  	c.Assert(afs.MkdirAll(filepath.Join(workDir, "dust", "d2"), 0777), qt.IsNil)
   310  
   311  	moduleCfg := map[string]any{
   312  		"mounts": []any{
   313  			map[string]any{
   314  				"source": "dist",
   315  				"target": "static/mydist",
   316  			},
   317  			map[string]any{
   318  				"source": "dust",
   319  				"target": "static/foo/bar",
   320  			},
   321  			map[string]any{
   322  				"source": "static",
   323  				"target": "static",
   324  			},
   325  		},
   326  	}
   327  
   328  	v.Set("module", moduleCfg)
   329  
   330  	conf := testconfig.GetTestConfig(afs, v)
   331  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   332  
   333  	p, err := paths.New(fs, conf)
   334  	c.Assert(err, qt.IsNil)
   335  	bfs, err := filesystems.NewBase(p, nil)
   336  	c.Assert(err, qt.IsNil)
   337  
   338  	sfs := bfs.Static[""]
   339  	c.Assert(sfs, qt.Not(qt.IsNil))
   340  
   341  	makeRel := func(s string) string {
   342  		r, _ := sfs.MakePathRelative(s)
   343  		return r
   344  	}
   345  
   346  	c.Assert(makeRel(filepath.Join(workDir, "dist", "d1", "foo.txt")), qt.Equals, filepath.FromSlash("mydist/d1/foo.txt"))
   347  	c.Assert(makeRel(filepath.Join(workDir, "static", "d2", "foo.txt")), qt.Equals, filepath.FromSlash("d2/foo.txt"))
   348  	c.Assert(makeRel(filepath.Join(workDir, "dust", "d3", "foo.txt")), qt.Equals, filepath.FromSlash("foo/bar/d3/foo.txt"))
   349  }
   350  
   351  func checkFileCount(fs afero.Fs, dirname string, c *qt.C, expected int) {
   352  	c.Helper()
   353  	count, _, err := countFilesAndGetFilenames(fs, dirname)
   354  	c.Assert(err, qt.IsNil)
   355  	c.Assert(count, qt.Equals, expected)
   356  }
   357  
   358  func checkFileContent(fs afero.Fs, filename string, c *qt.C, expected ...string) {
   359  	b, err := afero.ReadFile(fs, filename)
   360  	c.Assert(err, qt.IsNil)
   361  
   362  	content := string(b)
   363  
   364  	for _, e := range expected {
   365  		c.Assert(content, qt.Contains, e)
   366  	}
   367  }
   368  
   369  func countFilesAndGetFilenames(fs afero.Fs, dirname string) (int, []string, error) {
   370  	if fs == nil {
   371  		return 0, nil, errors.New("no fs")
   372  	}
   373  
   374  	counter := 0
   375  	var filenames []string
   376  
   377  	wf := func(path string, info hugofs.FileMetaInfo, err error) error {
   378  		if err != nil {
   379  			return err
   380  		}
   381  		if !info.IsDir() {
   382  			counter++
   383  		}
   384  
   385  		if info.Name() != "." {
   386  			name := info.Name()
   387  			name = strings.Replace(name, filepath.FromSlash("/my/work"), "WORK_DIR", 1)
   388  			filenames = append(filenames, name)
   389  		}
   390  
   391  		return nil
   392  	}
   393  
   394  	w := hugofs.NewWalkway(hugofs.WalkwayConfig{Fs: fs, Root: dirname, WalkFn: wf})
   395  
   396  	if err := w.Walk(); err != nil {
   397  		return -1, nil, err
   398  	}
   399  
   400  	return counter, filenames, nil
   401  }
   402  
   403  func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val string, num int) {
   404  	workingDir := v.GetString("workingDir")
   405  	v.Set(key, val)
   406  	fs.Mkdir(val, 0755)
   407  	for i := 0; i < num; i++ {
   408  		filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1))
   409  		afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0755)
   410  	}
   411  }