rootmapping_fs_test.go (18029B)
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 hugofs 15 16 import ( 17 "fmt" 18 "io/ioutil" 19 "path/filepath" 20 "sort" 21 "testing" 22 23 "github.com/gohugoio/hugo/config" 24 "github.com/gohugoio/hugo/hugofs/glob" 25 26 qt "github.com/frankban/quicktest" 27 "github.com/gohugoio/hugo/htesting" 28 "github.com/spf13/afero" 29 ) 30 31 func TestLanguageRootMapping(t *testing.T) { 32 c := qt.New(t) 33 v := config.NewWithTestDefaults() 34 v.Set("contentDir", "content") 35 36 fs := NewBaseFileDecorator(afero.NewMemMapFs()) 37 38 c.Assert(afero.WriteFile(fs, filepath.Join("content/sv/svdir", "main.txt"), []byte("main sv"), 0755), qt.IsNil) 39 40 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mysvblogcontent", "sv-f.txt"), []byte("some sv blog content"), 0755), qt.IsNil) 41 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/myenblogcontent", "en-f.txt"), []byte("some en blog content in a"), 0755), qt.IsNil) 42 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mysvblogcontent/d1", "sv-d1-f.txt"), []byte("some sv blog content"), 0755), qt.IsNil) 43 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/myenblogcontent/d1", "en-d1-f.txt"), []byte("some en blog content in a"), 0755), qt.IsNil) 44 45 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/myotherenblogcontent", "en-f2.txt"), []byte("some en content"), 0755), qt.IsNil) 46 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mysvdocs", "sv-docs.txt"), []byte("some sv docs content"), 0755), qt.IsNil) 47 c.Assert(afero.WriteFile(fs, filepath.Join("themes/b/myenblogcontent", "en-b-f.txt"), []byte("some en content"), 0755), qt.IsNil) 48 49 rfs, err := NewRootMappingFs(fs, 50 RootMapping{ 51 From: "content/blog", // Virtual path, first element is one of content, static, layouts etc. 52 To: "themes/a/mysvblogcontent", // Real path 53 Meta: &FileMeta{Lang: "sv"}, 54 }, 55 RootMapping{ 56 From: "content/blog", 57 To: "themes/a/myenblogcontent", 58 Meta: &FileMeta{Lang: "en"}, 59 }, 60 RootMapping{ 61 From: "content/blog", 62 To: "content/sv", 63 Meta: &FileMeta{Lang: "sv"}, 64 }, 65 RootMapping{ 66 From: "content/blog", 67 To: "themes/a/myotherenblogcontent", 68 Meta: &FileMeta{Lang: "en"}, 69 }, 70 RootMapping{ 71 From: "content/docs", 72 To: "themes/a/mysvdocs", 73 Meta: &FileMeta{Lang: "sv"}, 74 }, 75 ) 76 77 c.Assert(err, qt.IsNil) 78 79 collected, err := collectFilenames(rfs, "content", "content") 80 c.Assert(err, qt.IsNil) 81 c.Assert(collected, qt.DeepEquals, 82 []string{"blog/d1/en-d1-f.txt", "blog/d1/sv-d1-f.txt", "blog/en-f.txt", "blog/en-f2.txt", "blog/sv-f.txt", "blog/svdir/main.txt", "docs/sv-docs.txt"}, qt.Commentf("%#v", collected)) 83 84 dirs, err := rfs.Dirs(filepath.FromSlash("content/blog")) 85 c.Assert(err, qt.IsNil) 86 c.Assert(len(dirs), qt.Equals, 4) 87 for _, dir := range dirs { 88 f, err := dir.Meta().Open() 89 c.Assert(err, qt.IsNil) 90 f.Close() 91 } 92 93 blog, err := rfs.Open(filepath.FromSlash("content/blog")) 94 c.Assert(err, qt.IsNil) 95 fis, err := blog.Readdir(-1) 96 for _, fi := range fis { 97 f, err := fi.(FileMetaInfo).Meta().Open() 98 c.Assert(err, qt.IsNil) 99 f.Close() 100 } 101 blog.Close() 102 103 getDirnames := func(name string, rfs *RootMappingFs) []string { 104 c.Helper() 105 filename := filepath.FromSlash(name) 106 f, err := rfs.Open(filename) 107 c.Assert(err, qt.IsNil) 108 names, err := f.Readdirnames(-1) 109 110 f.Close() 111 c.Assert(err, qt.IsNil) 112 113 info, err := rfs.Stat(filename) 114 c.Assert(err, qt.IsNil) 115 f2, err := info.(FileMetaInfo).Meta().Open() 116 c.Assert(err, qt.IsNil) 117 names2, err := f2.Readdirnames(-1) 118 c.Assert(err, qt.IsNil) 119 c.Assert(names2, qt.DeepEquals, names) 120 f2.Close() 121 122 return names 123 } 124 125 rfsEn := rfs.Filter(func(rm RootMapping) bool { 126 return rm.Meta.Lang == "en" 127 }) 128 129 c.Assert(getDirnames("content/blog", rfsEn), qt.DeepEquals, []string{"d1", "en-f.txt", "en-f2.txt"}) 130 131 rfsSv := rfs.Filter(func(rm RootMapping) bool { 132 return rm.Meta.Lang == "sv" 133 }) 134 135 c.Assert(getDirnames("content/blog", rfsSv), qt.DeepEquals, []string{"d1", "sv-f.txt", "svdir"}) 136 137 // Make sure we have not messed with the original 138 c.Assert(getDirnames("content/blog", rfs), qt.DeepEquals, []string{"d1", "sv-f.txt", "en-f.txt", "svdir", "en-f2.txt"}) 139 140 c.Assert(getDirnames("content", rfsSv), qt.DeepEquals, []string{"blog", "docs"}) 141 c.Assert(getDirnames("content", rfs), qt.DeepEquals, []string{"blog", "docs"}) 142 } 143 144 func TestRootMappingFsDirnames(t *testing.T) { 145 c := qt.New(t) 146 fs := NewBaseFileDecorator(afero.NewMemMapFs()) 147 148 testfile := "myfile.txt" 149 c.Assert(fs.Mkdir("f1t", 0755), qt.IsNil) 150 c.Assert(fs.Mkdir("f2t", 0755), qt.IsNil) 151 c.Assert(fs.Mkdir("f3t", 0755), qt.IsNil) 152 c.Assert(afero.WriteFile(fs, filepath.Join("f2t", testfile), []byte("some content"), 0755), qt.IsNil) 153 154 rfs, err := newRootMappingFsFromFromTo("", fs, "static/bf1", "f1t", "static/cf2", "f2t", "static/af3", "f3t") 155 c.Assert(err, qt.IsNil) 156 157 fif, err := rfs.Stat(filepath.Join("static/cf2", testfile)) 158 c.Assert(err, qt.IsNil) 159 c.Assert(fif.Name(), qt.Equals, "myfile.txt") 160 fifm := fif.(FileMetaInfo).Meta() 161 c.Assert(fifm.Filename, qt.Equals, filepath.FromSlash("f2t/myfile.txt")) 162 163 root, err := rfs.Open("static") 164 c.Assert(err, qt.IsNil) 165 166 dirnames, err := root.Readdirnames(-1) 167 c.Assert(err, qt.IsNil) 168 c.Assert(dirnames, qt.DeepEquals, []string{"af3", "bf1", "cf2"}) 169 } 170 171 func TestRootMappingFsFilename(t *testing.T) { 172 c := qt.New(t) 173 workDir, clean, err := htesting.CreateTempDir(Os, "hugo-root-filename") 174 c.Assert(err, qt.IsNil) 175 defer clean() 176 fs := NewBaseFileDecorator(Os) 177 178 testfilename := filepath.Join(workDir, "f1t/foo/file.txt") 179 180 c.Assert(fs.MkdirAll(filepath.Join(workDir, "f1t/foo"), 0777), qt.IsNil) 181 c.Assert(afero.WriteFile(fs, testfilename, []byte("content"), 0666), qt.IsNil) 182 183 rfs, err := newRootMappingFsFromFromTo(workDir, fs, "static/f1", filepath.Join(workDir, "f1t"), "static/f2", filepath.Join(workDir, "f2t")) 184 c.Assert(err, qt.IsNil) 185 186 fi, err := rfs.Stat(filepath.FromSlash("static/f1/foo/file.txt")) 187 c.Assert(err, qt.IsNil) 188 fim := fi.(FileMetaInfo) 189 c.Assert(fim.Meta().Filename, qt.Equals, testfilename) 190 _, err = rfs.Stat(filepath.FromSlash("static/f1")) 191 c.Assert(err, qt.IsNil) 192 } 193 194 func TestRootMappingFsMount(t *testing.T) { 195 c := qt.New(t) 196 fs := NewBaseFileDecorator(afero.NewMemMapFs()) 197 198 testfile := "test.txt" 199 200 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mynoblogcontent", testfile), []byte("some no content"), 0755), qt.IsNil) 201 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/myenblogcontent", testfile), []byte("some en content"), 0755), qt.IsNil) 202 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mysvblogcontent", testfile), []byte("some sv content"), 0755), qt.IsNil) 203 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/mysvblogcontent", "other.txt"), []byte("some sv content"), 0755), qt.IsNil) 204 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/singlefiles", "no.txt"), []byte("no text"), 0755), qt.IsNil) 205 c.Assert(afero.WriteFile(fs, filepath.Join("themes/a/singlefiles", "sv.txt"), []byte("sv text"), 0755), qt.IsNil) 206 207 bfs := afero.NewBasePathFs(fs, "themes/a").(*afero.BasePathFs) 208 rm := []RootMapping{ 209 // Directories 210 { 211 From: "content/blog", 212 To: "mynoblogcontent", 213 Meta: &FileMeta{Lang: "no"}, 214 }, 215 { 216 From: "content/blog", 217 To: "myenblogcontent", 218 Meta: &FileMeta{Lang: "en"}, 219 }, 220 { 221 From: "content/blog", 222 To: "mysvblogcontent", 223 Meta: &FileMeta{Lang: "sv"}, 224 }, 225 // Files 226 { 227 From: "content/singles/p1.md", 228 To: "singlefiles/no.txt", 229 ToBasedir: "singlefiles", 230 Meta: &FileMeta{Lang: "no"}, 231 }, 232 { 233 From: "content/singles/p1.md", 234 To: "singlefiles/sv.txt", 235 ToBasedir: "singlefiles", 236 Meta: &FileMeta{Lang: "sv"}, 237 }, 238 } 239 240 rfs, err := NewRootMappingFs(bfs, rm...) 241 c.Assert(err, qt.IsNil) 242 243 blog, err := rfs.Stat(filepath.FromSlash("content/blog")) 244 c.Assert(err, qt.IsNil) 245 c.Assert(blog.IsDir(), qt.Equals, true) 246 blogm := blog.(FileMetaInfo).Meta() 247 c.Assert(blogm.Lang, qt.Equals, "no") // First match 248 249 f, err := blogm.Open() 250 c.Assert(err, qt.IsNil) 251 defer f.Close() 252 dirs1, err := f.Readdirnames(-1) 253 c.Assert(err, qt.IsNil) 254 // Union with duplicate dir names filtered. 255 c.Assert(dirs1, qt.DeepEquals, []string{"test.txt", "test.txt", "other.txt", "test.txt"}) 256 257 files, err := afero.ReadDir(rfs, filepath.FromSlash("content/blog")) 258 c.Assert(err, qt.IsNil) 259 c.Assert(len(files), qt.Equals, 4) 260 261 testfilefi := files[1] 262 c.Assert(testfilefi.Name(), qt.Equals, testfile) 263 264 testfilem := testfilefi.(FileMetaInfo).Meta() 265 c.Assert(testfilem.Filename, qt.Equals, filepath.FromSlash("themes/a/mynoblogcontent/test.txt")) 266 267 tf, err := testfilem.Open() 268 c.Assert(err, qt.IsNil) 269 defer tf.Close() 270 b, err := ioutil.ReadAll(tf) 271 c.Assert(err, qt.IsNil) 272 c.Assert(string(b), qt.Equals, "some no content") 273 274 // Ambiguous 275 _, err = rfs.Stat(filepath.FromSlash("content/singles/p1.md")) 276 c.Assert(err, qt.Not(qt.IsNil)) 277 278 singlesDir, err := rfs.Open(filepath.FromSlash("content/singles")) 279 c.Assert(err, qt.IsNil) 280 defer singlesDir.Close() 281 singles, err := singlesDir.Readdir(-1) 282 c.Assert(err, qt.IsNil) 283 c.Assert(singles, qt.HasLen, 2) 284 for i, lang := range []string{"no", "sv"} { 285 fi := singles[i].(FileMetaInfo) 286 c.Assert(fi.Meta().PathFile(), qt.Equals, filepath.FromSlash("themes/a/singlefiles/"+lang+".txt")) 287 c.Assert(fi.Meta().Lang, qt.Equals, lang) 288 c.Assert(fi.Name(), qt.Equals, "p1.md") 289 } 290 } 291 292 func TestRootMappingFsMountOverlap(t *testing.T) { 293 c := qt.New(t) 294 fs := NewBaseFileDecorator(afero.NewMemMapFs()) 295 296 c.Assert(afero.WriteFile(fs, filepath.FromSlash("da/a.txt"), []byte("some no content"), 0755), qt.IsNil) 297 c.Assert(afero.WriteFile(fs, filepath.FromSlash("db/b.txt"), []byte("some no content"), 0755), qt.IsNil) 298 c.Assert(afero.WriteFile(fs, filepath.FromSlash("dc/c.txt"), []byte("some no content"), 0755), qt.IsNil) 299 c.Assert(afero.WriteFile(fs, filepath.FromSlash("de/e.txt"), []byte("some no content"), 0755), qt.IsNil) 300 301 rm := []RootMapping{ 302 { 303 From: "static", 304 To: "da", 305 }, 306 { 307 From: "static/b", 308 To: "db", 309 }, 310 { 311 From: "static/b/c", 312 To: "dc", 313 }, 314 { 315 From: "/static/e/", 316 To: "de", 317 }, 318 } 319 320 rfs, err := NewRootMappingFs(fs, rm...) 321 c.Assert(err, qt.IsNil) 322 323 checkDirnames := func(name string, expect []string) { 324 c.Helper() 325 name = filepath.FromSlash(name) 326 f, err := rfs.Open(name) 327 c.Assert(err, qt.IsNil) 328 defer f.Close() 329 names, err := f.Readdirnames(-1) 330 c.Assert(err, qt.IsNil) 331 c.Assert(names, qt.DeepEquals, expect, qt.Commentf(fmt.Sprintf("%#v", names))) 332 } 333 334 checkDirnames("static", []string{"a.txt", "b", "e"}) 335 checkDirnames("static/b", []string{"b.txt", "c"}) 336 checkDirnames("static/b/c", []string{"c.txt"}) 337 338 fi, err := rfs.Stat(filepath.FromSlash("static/b/b.txt")) 339 c.Assert(err, qt.IsNil) 340 c.Assert(fi.Name(), qt.Equals, "b.txt") 341 } 342 343 func TestRootMappingFsOs(t *testing.T) { 344 c := qt.New(t) 345 fs := NewBaseFileDecorator(afero.NewOsFs()) 346 347 d, clean, err := htesting.CreateTempDir(fs, "hugo-root-mapping-os") 348 c.Assert(err, qt.IsNil) 349 defer clean() 350 351 testfile := "myfile.txt" 352 c.Assert(fs.Mkdir(filepath.Join(d, "f1t"), 0755), qt.IsNil) 353 c.Assert(fs.Mkdir(filepath.Join(d, "f2t"), 0755), qt.IsNil) 354 c.Assert(fs.Mkdir(filepath.Join(d, "f3t"), 0755), qt.IsNil) 355 356 // Deep structure 357 deepDir := filepath.Join(d, "d1", "d2", "d3", "d4", "d5") 358 c.Assert(fs.MkdirAll(deepDir, 0755), qt.IsNil) 359 for i := 1; i <= 3; i++ { 360 c.Assert(fs.MkdirAll(filepath.Join(d, "d1", "d2", "d3", "d4", fmt.Sprintf("d4-%d", i)), 0755), qt.IsNil) 361 c.Assert(afero.WriteFile(fs, filepath.Join(d, "d1", "d2", "d3", fmt.Sprintf("f-%d.txt", i)), []byte("some content"), 0755), qt.IsNil) 362 } 363 364 c.Assert(afero.WriteFile(fs, filepath.Join(d, "f2t", testfile), []byte("some content"), 0755), qt.IsNil) 365 366 // https://github.com/gohugoio/hugo/issues/6854 367 mystaticDir := filepath.Join(d, "mystatic", "a", "b", "c") 368 c.Assert(fs.MkdirAll(mystaticDir, 0755), qt.IsNil) 369 c.Assert(afero.WriteFile(fs, filepath.Join(mystaticDir, "ms-1.txt"), []byte("some content"), 0755), qt.IsNil) 370 371 rfs, err := newRootMappingFsFromFromTo( 372 d, 373 fs, 374 "static/bf1", filepath.Join(d, "f1t"), 375 "static/cf2", filepath.Join(d, "f2t"), 376 "static/af3", filepath.Join(d, "f3t"), 377 "static", filepath.Join(d, "mystatic"), 378 "static/a/b/c", filepath.Join(d, "d1", "d2", "d3"), 379 "layouts", filepath.Join(d, "d1"), 380 ) 381 382 c.Assert(err, qt.IsNil) 383 384 fif, err := rfs.Stat(filepath.Join("static/cf2", testfile)) 385 c.Assert(err, qt.IsNil) 386 c.Assert(fif.Name(), qt.Equals, "myfile.txt") 387 388 root, err := rfs.Open("static") 389 c.Assert(err, qt.IsNil) 390 391 dirnames, err := root.Readdirnames(-1) 392 c.Assert(err, qt.IsNil) 393 c.Assert(dirnames, qt.DeepEquals, []string{"a", "af3", "bf1", "cf2"}, qt.Commentf(fmt.Sprintf("%#v", dirnames))) 394 395 getDirnames := func(dirname string) []string { 396 dirname = filepath.FromSlash(dirname) 397 f, err := rfs.Open(dirname) 398 c.Assert(err, qt.IsNil) 399 defer f.Close() 400 dirnames, err := f.Readdirnames(-1) 401 c.Assert(err, qt.IsNil) 402 sort.Strings(dirnames) 403 return dirnames 404 } 405 406 c.Assert(getDirnames("static/a/b"), qt.DeepEquals, []string{"c"}) 407 c.Assert(getDirnames("static/a/b/c"), qt.DeepEquals, []string{"d4", "f-1.txt", "f-2.txt", "f-3.txt", "ms-1.txt"}) 408 c.Assert(getDirnames("static/a/b/c/d4"), qt.DeepEquals, []string{"d4-1", "d4-2", "d4-3", "d5"}) 409 410 all, err := collectFilenames(rfs, "static", "static") 411 c.Assert(err, qt.IsNil) 412 413 c.Assert(all, qt.DeepEquals, []string{"a/b/c/f-1.txt", "a/b/c/f-2.txt", "a/b/c/f-3.txt", "a/b/c/ms-1.txt", "cf2/myfile.txt"}) 414 415 fis, err := collectFileinfos(rfs, "static", "static") 416 c.Assert(err, qt.IsNil) 417 418 c.Assert(fis[9].Meta().PathFile(), qt.Equals, filepath.FromSlash("d1/d2/d3/f-1.txt")) 419 420 dirc := fis[3].Meta() 421 422 f, err := dirc.Open() 423 c.Assert(err, qt.IsNil) 424 defer f.Close() 425 fileInfos, err := f.Readdir(-1) 426 c.Assert(err, qt.IsNil) 427 sortFileInfos(fileInfos) 428 i := 0 429 for _, fi := range fileInfos { 430 if fi.IsDir() || fi.Name() == "ms-1.txt" { 431 continue 432 } 433 i++ 434 meta := fi.(FileMetaInfo).Meta() 435 c.Assert(meta.Filename, qt.Equals, filepath.Join(d, fmt.Sprintf("/d1/d2/d3/f-%d.txt", i))) 436 c.Assert(meta.PathFile(), qt.Equals, filepath.FromSlash(fmt.Sprintf("d1/d2/d3/f-%d.txt", i))) 437 } 438 439 _, err = rfs.Stat(filepath.FromSlash("layouts/d2/d3/f-1.txt")) 440 c.Assert(err, qt.IsNil) 441 _, err = rfs.Stat(filepath.FromSlash("layouts/d2/d3")) 442 c.Assert(err, qt.IsNil) 443 } 444 445 func TestRootMappingFsOsBase(t *testing.T) { 446 c := qt.New(t) 447 fs := NewBaseFileDecorator(afero.NewOsFs()) 448 449 d, clean, err := htesting.CreateTempDir(fs, "hugo-root-mapping-os-base") 450 c.Assert(err, qt.IsNil) 451 defer clean() 452 453 // Deep structure 454 deepDir := filepath.Join(d, "d1", "d2", "d3", "d4", "d5") 455 c.Assert(fs.MkdirAll(deepDir, 0755), qt.IsNil) 456 for i := 1; i <= 3; i++ { 457 c.Assert(fs.MkdirAll(filepath.Join(d, "d1", "d2", "d3", "d4", fmt.Sprintf("d4-%d", i)), 0755), qt.IsNil) 458 c.Assert(afero.WriteFile(fs, filepath.Join(d, "d1", "d2", "d3", fmt.Sprintf("f-%d.txt", i)), []byte("some content"), 0755), qt.IsNil) 459 } 460 461 mystaticDir := filepath.Join(d, "mystatic", "a", "b", "c") 462 c.Assert(fs.MkdirAll(mystaticDir, 0755), qt.IsNil) 463 c.Assert(afero.WriteFile(fs, filepath.Join(mystaticDir, "ms-1.txt"), []byte("some content"), 0755), qt.IsNil) 464 465 bfs := afero.NewBasePathFs(fs, d) 466 467 rfs, err := newRootMappingFsFromFromTo( 468 "", 469 bfs, 470 "static", "mystatic", 471 "static/a/b/c", filepath.Join("d1", "d2", "d3"), 472 ) 473 474 getDirnames := func(dirname string) []string { 475 dirname = filepath.FromSlash(dirname) 476 f, err := rfs.Open(dirname) 477 c.Assert(err, qt.IsNil) 478 defer f.Close() 479 dirnames, err := f.Readdirnames(-1) 480 c.Assert(err, qt.IsNil) 481 sort.Strings(dirnames) 482 return dirnames 483 } 484 485 c.Assert(getDirnames("static/a/b/c"), qt.DeepEquals, []string{"d4", "f-1.txt", "f-2.txt", "f-3.txt", "ms-1.txt"}) 486 } 487 488 func TestRootMappingFileFilter(t *testing.T) { 489 c := qt.New(t) 490 fs := NewBaseFileDecorator(afero.NewMemMapFs()) 491 492 for _, lang := range []string{"no", "en", "fr"} { 493 for i := 1; i <= 3; i++ { 494 c.Assert(afero.WriteFile(fs, filepath.Join(lang, fmt.Sprintf("my%s%d.txt", lang, i)), []byte("some text file for"+lang), 0755), qt.IsNil) 495 } 496 } 497 498 for _, lang := range []string{"no", "en", "fr"} { 499 for i := 1; i <= 3; i++ { 500 c.Assert(afero.WriteFile(fs, filepath.Join(lang, "sub", fmt.Sprintf("mysub%s%d.txt", lang, i)), []byte("some text file for"+lang), 0755), qt.IsNil) 501 } 502 } 503 504 rm := []RootMapping{ 505 { 506 From: "content", 507 To: "no", 508 Meta: &FileMeta{Lang: "no", InclusionFilter: glob.MustNewFilenameFilter(nil, []string{"**.txt"})}, 509 }, 510 { 511 From: "content", 512 To: "en", 513 Meta: &FileMeta{Lang: "en"}, 514 }, 515 { 516 From: "content", 517 To: "fr", 518 Meta: &FileMeta{Lang: "fr", InclusionFilter: glob.MustNewFilenameFilter(nil, []string{"**.txt"})}, 519 }, 520 } 521 522 rfs, err := NewRootMappingFs(fs, rm...) 523 c.Assert(err, qt.IsNil) 524 525 assertExists := func(filename string, shouldExist bool) { 526 c.Helper() 527 filename = filepath.Clean(filename) 528 _, err1 := rfs.Stat(filename) 529 f, err2 := rfs.Open(filename) 530 if shouldExist { 531 c.Assert(err1, qt.IsNil) 532 c.Assert(err2, qt.IsNil) 533 c.Assert(f.Close(), qt.IsNil) 534 } else { 535 c.Assert(err1, qt.Not(qt.IsNil)) 536 c.Assert(err2, qt.Not(qt.IsNil)) 537 } 538 } 539 540 assertExists("content/myno1.txt", false) 541 assertExists("content/myen1.txt", true) 542 assertExists("content/myfr1.txt", false) 543 544 dirEntriesSub, err := afero.ReadDir(rfs, filepath.Join("content", "sub")) 545 c.Assert(err, qt.IsNil) 546 c.Assert(len(dirEntriesSub), qt.Equals, 3) 547 548 dirEntries, err := afero.ReadDir(rfs, "content") 549 550 c.Assert(err, qt.IsNil) 551 c.Assert(len(dirEntries), qt.Equals, 4) 552 553 }