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 }