github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/test/test_utils.go (about) 1 // 2 // Copyright 2021 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package test 18 19 import ( 20 "bytes" 21 "crypto/sha512" 22 "fmt" 23 "io/fs" 24 "path/filepath" 25 "sort" 26 "strconv" 27 "strings" 28 "testing" 29 30 "github.com/avfs/avfs" 31 "github.com/avfs/avfs/vfs/memfs" 32 ) 33 34 type pathTest struct { 35 path, result string 36 } 37 38 func (ts *Suite) TestUtils(t *testing.T) { 39 ts.RunTests(t, UsrTest, 40 ts.TestCopyFile, 41 ts.TestDirExists, 42 ts.TestExists, 43 ts.TestHashFile, 44 ts.TestIsDir, 45 ts.TestIsEmpty, 46 ts.TestIsPathSeparator, 47 ts.TestRndTree, 48 ts.TestUMask) 49 } 50 51 // TestAbs test Abs function. 52 func (ts *Suite) TestAbs(t *testing.T, testDir string) { 53 vfs := ts.vfsSetup 54 55 t.Run("Abs", func(t *testing.T) { 56 // Test directories relative to temporary directory. 57 // The tests are run in absTestDirs[0]. 58 absTestDirs := []string{ 59 "a", 60 "a/b", 61 "a/b/c", 62 } 63 64 // Test paths relative to temporary directory. $ expands to the directory. 65 // The tests are run in absTestDirs[0]. 66 // We create absTestDirs first. 67 absTests := []string{ 68 ".", 69 "b", 70 "b/", 71 "../a", 72 "../a/b", 73 "../a/b/./c/../../.././a", 74 "../a/b/./c/../../.././a/", 75 "$", 76 "$/.", 77 "$/a/../a/b", 78 "$/a/b/c/../../.././a", 79 "$/a/b/c/../../.././a/", 80 } 81 82 wd, err := vfs.Getwd() 83 if err != nil { 84 t.Fatal("getwd failed: ", err) 85 } 86 87 err = vfs.Chdir(testDir) 88 if err != nil { 89 t.Fatal("chdir failed: ", err) 90 } 91 92 defer vfs.Chdir(wd) //nolint:errcheck // Ignore errors. 93 94 for _, dir := range absTestDirs { 95 err = vfs.Mkdir(dir, 0o777) 96 if err != nil { 97 t.Fatal("Mkdir failed: ", err) 98 } 99 } 100 101 err = vfs.Chdir(absTestDirs[0]) 102 if err != nil { 103 t.Fatal("chdir failed: ", err) 104 } 105 106 vfs = ts.vfsTest 107 108 for _, path := range absTests { 109 path = strings.ReplaceAll(path, "$", testDir) 110 111 info, err := vfs.Stat(path) 112 if err != nil { 113 t.Errorf("%s: %s", path, err) 114 115 continue 116 } 117 118 abspath, err := vfs.Abs(path) 119 if err != nil { 120 t.Errorf("Abs(%q) error: %v", path, err) 121 122 continue 123 } 124 125 absinfo, err := vfs.Stat(abspath) 126 if err != nil || !vfs.SameFile(absinfo, info) { 127 t.Errorf("Abs(%q)=%q, not the same file", path, abspath) 128 } 129 130 if !vfs.IsAbs(abspath) { 131 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath) 132 } 133 134 if vfs.IsAbs(abspath) && abspath != vfs.Clean(abspath) { 135 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath) 136 } 137 } 138 }) 139 140 // AbsEmptyString tests Abs functions with an empty string input. 141 // Empty path needs to be special-cased on Windows. See golang.org/issue/24441. 142 // We test it separately from all other absTests because the empty string is not 143 // a valid path, so it can't be used with os.Stat. 144 t.Run("AbsEmptyString", func(t *testing.T) { 145 wd, err := vfs.Getwd() 146 if err != nil { 147 t.Fatal("getwd failed: ", err) 148 } 149 150 err = vfs.Chdir(testDir) 151 if err != nil { 152 t.Fatal("chdir failed: ", err) 153 } 154 155 defer vfs.Chdir(wd) //nolint:errcheck // Ignore errors. 156 157 info, err := vfs.Stat(testDir) 158 if err != nil { 159 t.Fatalf("%s: %s", testDir, err) 160 } 161 162 abspath, err := vfs.Abs("") 163 if err != nil { 164 t.Fatalf(`Abs("") error: %v`, err) 165 } 166 167 absinfo, err := vfs.Stat(abspath) 168 if err != nil || !vfs.SameFile(absinfo, info) { 169 t.Errorf(`Abs("")=%q, not the same file`, abspath) 170 } 171 172 if !vfs.IsAbs(abspath) { 173 t.Errorf(`Abs("")=%q, not an absolute path`, abspath) 174 } 175 176 if vfs.IsAbs(abspath) && abspath != vfs.Clean(abspath) { 177 t.Errorf(`Abs("")=%q, isn't clean`, abspath) 178 } 179 }) 180 } 181 182 // TestBase tests Base function. 183 func (ts *Suite) TestBase(t *testing.T, _ string) { 184 vfs := ts.vfsTest 185 186 var baseTests []*pathTest 187 188 switch vfs.OSType() { 189 case avfs.OsWindows: 190 baseTests = []*pathTest{ 191 {`c:\`, `\`}, 192 {`c:.`, `.`}, 193 {`c:\a\b`, `b`}, 194 {`c:a\b`, `b`}, 195 {`c:a\b\c`, `c`}, 196 {`\\host\share\`, `\`}, 197 {`\\host\share\a`, `a`}, 198 {`\\host\share\a\b`, `b`}, 199 } 200 default: 201 baseTests = []*pathTest{ 202 {"", "."}, 203 {".", "."}, 204 {"/.", "."}, 205 {"/", "/"}, 206 {"////", "/"}, 207 {"x/", "x"}, 208 {"abc", "abc"}, 209 {"abc/def", "def"}, 210 {"a/b/.x", ".x"}, 211 {"a/b/c.", "c."}, 212 {"a/b/c.x", "c.x"}, 213 } 214 } 215 216 for _, test := range baseTests { 217 s := vfs.Base(test.path) 218 if s != test.result { 219 t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result) 220 } 221 } 222 } 223 224 // TestClean tests Clean function. 225 func (ts *Suite) TestClean(t *testing.T, _ string) { 226 vfs := ts.vfsTest 227 228 cleanTests := []*pathTest{ 229 // Already clean 230 {"abc", "abc"}, 231 {"abc/def", "abc/def"}, 232 {"a/b/c", "a/b/c"}, 233 {".", "."}, 234 {"..", ".."}, 235 {"../..", "../.."}, 236 {"../../abc", "../../abc"}, 237 {"/abc", "/abc"}, 238 {"/", "/"}, 239 240 // Empty is current dir 241 {"", "."}, 242 243 // Remove trailing slash 244 {"abc/", "abc"}, 245 {"abc/def/", "abc/def"}, 246 {"a/b/c/", "a/b/c"}, 247 {"./", "."}, 248 {"../", ".."}, 249 {"../../", "../.."}, 250 {"/abc/", "/abc"}, 251 252 // Remove doubled slash 253 {"abc//def//ghi", "abc/def/ghi"}, 254 {"abc//", "abc"}, 255 256 // Remove . elements 257 {"abc/./def", "abc/def"}, 258 {"/./abc/def", "/abc/def"}, 259 {"abc/.", "abc"}, 260 261 // Remove .. elements 262 {"abc/def/ghi/../jkl", "abc/def/jkl"}, 263 {"abc/def/../ghi/../jkl", "abc/jkl"}, 264 {"abc/def/..", "abc"}, 265 {"abc/def/../..", "."}, 266 {"/abc/def/../..", "/"}, 267 {"abc/def/../../..", ".."}, 268 {"/abc/def/../../..", "/"}, 269 {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, 270 {"/../abc", "/abc"}, 271 272 // Combinations 273 {"abc/./../def", "def"}, 274 {"abc//./../def", "def"}, 275 {"abc/../../././../def", "../../def"}, 276 } 277 278 nonWinCleanTests := []*pathTest{ 279 // Remove leading doubled slash 280 {"//abc", "/abc"}, 281 {"///abc", "/abc"}, 282 {"//abc//", "/abc"}, 283 } 284 285 winCleanTests := []*pathTest{ 286 {`c:`, `c:.`}, 287 {`c:\`, `c:\`}, 288 {`c:\abc`, `c:\abc`}, 289 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, 290 {`c:\abc\def\..\..`, `c:\`}, 291 {`c:\..\abc`, `c:\abc`}, 292 {`c:..\abc`, `c:..\abc`}, 293 {`\`, `\`}, 294 {`/`, `\`}, 295 {`\\i\..\c$`, `\\i\..\c$`}, 296 {`\\i\..\i\c$`, `\\i\..\i\c$`}, 297 {`\\i\..\I\c$`, `\\i\..\I\c$`}, 298 {`\\host\share\foo\..\bar`, `\\host\share\bar`}, 299 {`//host/share/foo/../baz`, `\\host\share\baz`}, 300 {`\\host\share\foo\..\..\..\..\bar`, `\\host\share\bar`}, 301 {`\\.\C:\a\..\..\..\..\bar`, `\\.\C:\bar`}, 302 {`\\.\C:\\\\a`, `\\.\C:\a`}, 303 {`\\a\b\..\c`, `\\a\b\c`}, 304 {`\\a\b`, `\\a\b`}, 305 {`.\c:`, `.\c:`}, 306 {`.\c:\foo`, `.\c:\foo`}, 307 {`.\c:foo`, `.\c:foo`}, 308 {`//abc`, `\\abc`}, 309 {`///abc`, `\\\abc`}, 310 {`//abc//`, `\\abc\\`}, 311 312 // Don't allow cleaning to move an element with a colon to the start of the path. 313 {`a/../c:`, `.\c:`}, 314 {`a\..\c:`, `.\c:`}, 315 {`a/../c:/a`, `.\c:\a`}, 316 {`a/../../c:`, `..\c:`}, 317 {`foo:bar`, `foo:bar`}, 318 } 319 320 tests := cleanTests 321 if vfs.OSType() == avfs.OsWindows { 322 for i := range tests { 323 tests[i].result = filepath.FromSlash(tests[i].result) 324 } 325 326 tests = append(tests, winCleanTests...) 327 } else { 328 tests = append(tests, nonWinCleanTests...) 329 } 330 331 for _, test := range tests { 332 if s := filepath.Clean(test.path); s != test.result { 333 t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) 334 } 335 336 if s := filepath.Clean(test.result); s != test.result { 337 t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) 338 } 339 } 340 } 341 342 // TestCopyFile tests avfs.CopyFile function. 343 func (ts *Suite) TestCopyFile(t *testing.T, testDir string) { 344 const copyFile = "CopyFile" 345 346 srcFS := ts.vfsSetup 347 dstFS := memfs.New() 348 349 rt := avfs.NewRndTree(srcFS, &avfs.RndTreeOpts{NbFiles: 32, MaxFileSize: 100 * 1024}) 350 351 err := rt.CreateTree(testDir) 352 RequireNoError(t, err, "CreateTree %s") 353 354 h := sha512.New() 355 356 t.Run("CopyFile_WithHashSum", func(t *testing.T) { 357 dstDir, err := dstFS.MkdirTemp("", copyFile) 358 RequireNoError(t, err, "MkdirTemp") 359 360 defer dstFS.RemoveAll(dstDir) //nolint:errcheck // Ignore errors. 361 362 for _, srcFile := range rt.Files() { 363 srcPath := srcFS.Join(testDir, srcFile.Name) 364 fileName := srcFS.Base(srcFile.Name) 365 dstPath := dstFS.Join(dstDir, fileName) 366 367 wantSum, err := avfs.CopyFileHash(dstFS, srcFS, dstPath, srcPath, h) 368 RequireNoError(t, err, "CopyFile (%s)%s, (%s)%s", 369 dstFS.Type(), dstPath, srcFS.Type(), srcFile.Name) 370 371 gotSum, err := avfs.HashFile(dstFS, dstPath, h) 372 RequireNoError(t, err, "HashFile (%s)%s", dstFS.Type(), dstPath) 373 374 if !bytes.Equal(wantSum, gotSum) { 375 t.Errorf("HashFile %s : \nwant : %x\ngot : %x", fileName, wantSum, gotSum) 376 } 377 } 378 }) 379 380 t.Run("CopyFile", func(t *testing.T) { 381 dstDir, err := dstFS.MkdirTemp("", copyFile) 382 RequireNoError(t, err, "MkdirTemp %s", copyFile) 383 384 for _, srcFile := range rt.Files() { 385 srcPath := srcFS.Join(testDir, srcFile.Name) 386 fileName := srcFS.Base(srcFile.Name) 387 dstPath := dstFS.Join(dstDir, fileName) 388 389 err = avfs.CopyFile(dstFS, srcFS, dstPath, srcPath) 390 RequireNoError(t, err, "CopyFile (%s)%s, (%s)%s", 391 dstFS.Type(), dstPath, srcFS.Type(), srcFile.Name) 392 393 wantSum, err := avfs.HashFile(srcFS, srcPath, h) 394 RequireNoError(t, err, "HashFile (%s)%s", srcFS.Type(), srcFile.Name) 395 396 gotSum, err := avfs.HashFile(dstFS, dstPath, h) 397 RequireNoError(t, err, "HashFile (%s)%s", dstFS.Type(), dstPath) 398 399 if !bytes.Equal(wantSum, gotSum) { 400 t.Errorf("HashFile %s : \nwant : %x\ngot : %x", fileName, wantSum, gotSum) 401 } 402 } 403 }) 404 } 405 406 // TestMkSystemDirs tests CreateSystemDirs function. 407 func (ts *Suite) TestMkSystemDirs(t *testing.T, testDir string) { 408 vfs := ts.vfsSetup 409 dirs := avfs.SystemDirs(vfs, testDir) 410 411 err := avfs.MkSystemDirs(vfs, dirs) 412 RequireNoError(t, err, "MkSystemDirs %s", testDir) 413 414 for _, dir := range avfs.SystemDirs(vfs, testDir) { 415 info, err := vfs.Stat(dir.Path) 416 if !AssertNoError(t, err, "Stat %s", dir.Path) { 417 continue 418 } 419 420 gotMode := info.Mode() & fs.ModePerm 421 if gotMode != dir.Perm { 422 t.Errorf("MkSystemDirs %s : want mode to be %o, got %o", dir.Path, dir.Perm, gotMode) 423 } 424 } 425 } 426 427 // TestCreateHomeDir tests that the user home directory exists and has the correct permissions. 428 func (ts *Suite) TestCreateHomeDir(t *testing.T, _ string) { 429 if !ts.canTestPerm { 430 return 431 } 432 433 vfs := ts.vfsSetup 434 435 for _, ui := range UserInfos() { 436 u, err := vfs.Idm().LookupUser(ui.Name) 437 RequireNoError(t, err, "Can't find user %s", ui.Name) 438 439 homeDir, err := avfs.MkHomeDir(vfs, "", u) 440 if !AssertNoError(t, err, "CreateHomeDir %s", ui.Name) { 441 continue 442 } 443 444 fst, err := vfs.Stat(homeDir) 445 if !AssertNoError(t, err, "Stat %s", homeDir) { 446 continue 447 } 448 449 err = vfs.Remove(homeDir) 450 if !AssertNoError(t, err, "Remove %s", homeDir) { 451 continue 452 } 453 454 if vfs.OSType() == avfs.OsWindows { 455 return 456 } 457 458 wantMode := fs.ModeDir | avfs.HomeDirPerm()&^vfs.UMask() 459 if fst.Mode() != wantMode { 460 t.Errorf("Stat %s : want mode to be %o, got %o", homeDir, wantMode, fst.Mode()) 461 } 462 463 sst := vfs.ToSysStat(fst) 464 465 uid, gid := sst.Uid(), sst.Gid() 466 if uid != u.Uid() || gid != u.Gid() { 467 t.Errorf("Stat %s : want uid=%d, gid=%d, got uid=%d, gid=%d", homeDir, u.Uid(), u.Gid(), uid, gid) 468 } 469 } 470 } 471 472 // TestDir tests Dir function. 473 func (ts *Suite) TestDir(t *testing.T, _ string) { 474 vfs := ts.vfsTest 475 476 var dirTests []*pathTest 477 478 switch vfs.OSType() { 479 case avfs.OsWindows: 480 dirTests = []*pathTest{ 481 {`c:\`, `c:\`}, 482 {`c:.`, `c:.`}, 483 {`c:\a\b`, `c:\a`}, 484 {`c:a\b`, `c:a`}, 485 {`c:a\b\c`, `c:a\b`}, 486 {`\\host\share`, `\\host\share`}, 487 {`\\host\share\`, `\\host\share\`}, 488 {`\\host\share\a`, `\\host\share\`}, 489 {`\\host\share\a\b`, `\\host\share\a`}, 490 } 491 default: 492 dirTests = []*pathTest{ 493 {"", "."}, 494 {".", "."}, 495 {"/.", "/"}, 496 {"/", "/"}, 497 {"////", "/"}, 498 {"/foo", "/"}, 499 {"x/", "x"}, 500 {"abc", "."}, 501 {"abc/def", "abc"}, 502 {"a/b/.x", "a/b"}, 503 {"a/b/c.", "a/b"}, 504 {"a/b/c.x", "a/b"}, 505 } 506 } 507 508 for _, test := range dirTests { 509 s := vfs.Dir(test.path) 510 if s != test.result { 511 t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result) 512 } 513 } 514 } 515 516 // TestDirExists tests avfs.DirExists function. 517 func (ts *Suite) TestDirExists(t *testing.T, testDir string) { 518 vfs := ts.vfsTest 519 520 t.Run("DirExistsDir", func(t *testing.T) { 521 ok, err := avfs.DirExists(vfs, testDir) 522 RequireNoError(t, err, "DirExists %s", testDir) 523 524 if !ok { 525 t.Error("DirExists : want DirExists to be true, got false") 526 } 527 }) 528 529 t.Run("DirExistsFile", func(t *testing.T) { 530 existingFile := ts.emptyFile(t, testDir) 531 532 ok, err := avfs.DirExists(vfs, existingFile) 533 RequireNoError(t, err, "DirExists %s", testDir) 534 535 if ok { 536 t.Error("DirExists : want DirExists to be false, got true") 537 } 538 }) 539 540 t.Run("DirExistsNotExisting", func(t *testing.T) { 541 nonExistingFile := ts.nonExistingFile(t, testDir) 542 543 ok, err := avfs.DirExists(vfs, nonExistingFile) 544 RequireNoError(t, err, "DirExists %s", nonExistingFile) 545 546 if ok { 547 t.Error("DirExists : want DirExists to be false, got true") 548 } 549 }) 550 } 551 552 // TestExists tests avfs.Exists function. 553 func (ts *Suite) TestExists(t *testing.T, testDir string) { 554 vfs := ts.vfsTest 555 556 t.Run("ExistsDir", func(t *testing.T) { 557 ok, err := avfs.Exists(vfs, testDir) 558 RequireNoError(t, err, "Exists %s", testDir) 559 560 if !ok { 561 t.Error("Exists : want DirExists to be true, got false") 562 } 563 }) 564 565 t.Run("ExistsFile", func(t *testing.T) { 566 existingFile := ts.emptyFile(t, testDir) 567 568 ok, err := avfs.Exists(vfs, existingFile) 569 RequireNoError(t, err, "DirExists %s", existingFile) 570 571 if !ok { 572 t.Error("Exists : want DirExists to be true, got false") 573 } 574 }) 575 576 t.Run("ExistsNotExisting", func(t *testing.T) { 577 nonExistingFile := ts.nonExistingFile(t, testDir) 578 579 ok, err := avfs.Exists(vfs, nonExistingFile) 580 RequireNoError(t, err, "Exists %s", nonExistingFile) 581 582 if ok { 583 t.Error("Exists : want Exists to be false, got true") 584 } 585 }) 586 587 t.Run("ExistsInvalidPath", func(t *testing.T) { 588 existingFile := ts.emptyFile(t, testDir) 589 invalidPath := vfs.Join(existingFile, defaultFile) 590 591 ok, err := avfs.Exists(vfs, invalidPath) 592 593 AssertPathError(t, err). 594 OSType(avfs.OsLinux).OpStat().Path(invalidPath).Err(avfs.ErrNotADirectory).Test(). 595 OSType(avfs.OsWindows).NoError().Test() 596 597 if ok { 598 t.Error("Exists : want Exists to be false, got true") 599 } 600 }) 601 } 602 603 // TestFromToSlash tests FromSlash and ToSlash functions. 604 func (ts *Suite) TestFromToSlash(t *testing.T, _ string) { 605 vfs := ts.vfsTest 606 607 sep := byte('/') 608 if vfs.OSType() == avfs.OsWindows { 609 sep = '\\' 610 } 611 612 slashTests := []*pathTest{ 613 {"", ""}, 614 {"/", string(sep)}, 615 {"/a/b", string([]byte{sep, 'a', sep, 'b'})}, 616 {"a//b", string([]byte{'a', sep, sep, 'b'})}, 617 } 618 619 for _, test := range slashTests { 620 if s := vfs.FromSlash(test.path); s != test.result { 621 t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result) 622 } 623 624 if s := vfs.ToSlash(test.result); s != test.path { 625 t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path) 626 } 627 } 628 } 629 630 // TestGlob tests Glob function. 631 func (ts *Suite) TestGlob(t *testing.T, testDir string) { 632 _ = ts.createSampleDirs(t, testDir) 633 _ = ts.createSampleFiles(t, testDir) 634 sl := len(ts.createSampleSymlinks(t, testDir)) 635 636 vfs := ts.vfsTest 637 638 t.Run("GlobNormal", func(t *testing.T) { 639 pattern := testDir + "/*/*/[A-Z0-9]" 640 641 dirNames, err := vfs.Glob(pattern) 642 RequireNoError(t, err, "Glob %s", pattern) 643 644 wantDirs := 3 645 if sl > 0 { 646 wantDirs += 5 647 } 648 649 if len(dirNames) != wantDirs { 650 t.Errorf("Glob %s : want dirs to be %d, got %d", pattern, wantDirs, len(dirNames)) 651 652 for _, dirName := range dirNames { 653 t.Log(dirName) 654 } 655 } 656 }) 657 658 t.Run("GlobWithoutMeta", func(t *testing.T) { 659 pattern := testDir 660 dirNames, err := vfs.Glob(pattern) 661 RequireNoError(t, err, "Glob %s", pattern) 662 663 if len(dirNames) != 1 { 664 t.Errorf("Glob %s : want dirs to be %d, got %d", pattern, 1, len(dirNames)) 665 666 for _, dirName := range dirNames { 667 t.Log(dirName) 668 } 669 } 670 }) 671 672 t.Run("GlobWithoutMetaNonExisting", func(t *testing.T) { 673 pattern := vfs.Join(testDir, "/NonExisting") 674 675 dirNames, err := vfs.Glob(pattern) 676 if dirNames != nil || err != nil { 677 t.Errorf("Glob %s : want error and result to be nil, got %s, %v", pattern, dirNames, err) 678 } 679 }) 680 681 t.Run("GlobError", func(t *testing.T) { 682 patterns := []string{ 683 "[]", 684 testDir + "/[A-Z", 685 } 686 687 for _, pattern := range patterns { 688 _, err := vfs.Glob(pattern) 689 if err != filepath.ErrBadPattern { 690 t.Errorf("Glob %s : want error to be %v, got %v", pattern, filepath.ErrBadPattern, err) 691 } 692 } 693 }) 694 } 695 696 // TestHashFile tests avfs.HashFile function. 697 func (ts *Suite) TestHashFile(t *testing.T, testDir string) { 698 vfs := ts.vfsSetup 699 rt := avfs.NewRndTree(vfs, &avfs.RndTreeOpts{NbFiles: 100, MaxFileSize: 100 * 1024}) 700 701 err := rt.CreateTree(testDir) 702 RequireNoError(t, err, "CreateTree %s", testDir) 703 704 defer vfs.RemoveAll(testDir) //nolint:errcheck // Ignore errors. 705 706 h := sha512.New() 707 708 for _, file := range rt.Files() { 709 path := vfs.Join(testDir, file.Name) 710 711 content, err := vfs.ReadFile(path) 712 if !AssertNoError(t, err, "ReadFile %s", path) { 713 continue 714 } 715 716 h.Reset() 717 718 _, err = h.Write(content) 719 RequireNoError(t, err, "Write %s", path) 720 721 wantSum := h.Sum(nil) 722 723 gotSum, err := avfs.HashFile(vfs, path, h) 724 RequireNoError(t, err, "HashFile %s", path) 725 726 if !bytes.Equal(wantSum, gotSum) { 727 t.Errorf("HashFile %s : \nwant : %x\ngot : %x", file.Name, wantSum, gotSum) 728 } 729 } 730 } 731 732 // TestIsAbs tests IsAbs function. 733 func (ts *Suite) TestIsAbs(t *testing.T, _ string) { 734 vfs := ts.vfsTest 735 736 type IsAbsTest struct { 737 path string 738 isAbs bool 739 } 740 741 var isAbsTests []IsAbsTest 742 743 switch vfs.OSType() { 744 case avfs.OsWindows: 745 isAbsTests = []IsAbsTest{ 746 {`C:\`, true}, 747 {`c\`, false}, 748 {`c::`, false}, 749 {`c:`, false}, 750 {`/`, false}, 751 {`\`, false}, 752 {`\Windows`, false}, 753 {`c:a\b`, false}, 754 {`c:\a\b`, true}, 755 {`c:/a/b`, true}, 756 {`\\host\share\foo`, true}, 757 {`//host/share/foo/bar`, true}, 758 } 759 760 default: 761 isAbsTests = []IsAbsTest{ 762 {"", false}, 763 {"/", true}, 764 {"/usr/bin/gcc", true}, 765 {"..", false}, 766 {"/a/../bb", true}, 767 {".", false}, 768 {"./", false}, 769 {"lala", false}, 770 } 771 } 772 773 for _, test := range isAbsTests { 774 r := vfs.IsAbs(test.path) 775 if r != test.isAbs { 776 t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs) 777 } 778 } 779 } 780 781 // TestIsDir tests avfs.IsDir function. 782 func (ts *Suite) TestIsDir(t *testing.T, testDir string) { 783 vfs := ts.vfsTest 784 785 t.Run("IsDir", func(t *testing.T) { 786 existingDir := ts.existingDir(t, testDir) 787 788 ok, err := avfs.IsDir(vfs, existingDir) 789 RequireNoError(t, err, "IsDir %s", existingDir) 790 791 if !ok { 792 t.Error("IsDir : want IsDir to be true, got false") 793 } 794 }) 795 796 t.Run("IsDirFile", func(t *testing.T) { 797 existingFile := ts.emptyFile(t, testDir) 798 799 ok, err := avfs.IsDir(vfs, existingFile) 800 RequireNoError(t, err, "IsDir %s", existingFile) 801 802 if ok { 803 t.Error("IsDirFile : want DirExists to be false, got true") 804 } 805 }) 806 807 t.Run("IsDirNonExisting", func(t *testing.T) { 808 nonExistingFile := ts.nonExistingFile(t, testDir) 809 810 ok, err := avfs.IsDir(vfs, nonExistingFile) 811 AssertPathError(t, err).OpStat().Path(nonExistingFile). 812 OSType(avfs.OsLinux).Err(avfs.ErrNoSuchFileOrDir).Test(). 813 OSType(avfs.OsWindows).Err(avfs.ErrWinFileNotFound).Test() 814 815 if ok { 816 t.Error("IsDirNonExisting : want DirExists to be false, got true") 817 } 818 }) 819 } 820 821 // TestIsEmpty tests avfs.IsEmpty function. 822 func (ts *Suite) TestIsEmpty(t *testing.T, testDir string) { 823 vfs := ts.vfsTest 824 825 t.Run("IsEmptyFile", func(t *testing.T) { 826 existingFile := ts.emptyFile(t, testDir) 827 828 ok, err := avfs.IsEmpty(vfs, existingFile) 829 RequireNoError(t, err, "IsEmpty %s", existingFile) 830 831 if !ok { 832 t.Error("IsEmpty : want IsEmpty to be true, got false") 833 } 834 }) 835 836 t.Run("IsEmptyDirEmpty", func(t *testing.T) { 837 emptyDir := ts.existingDir(t, testDir) 838 839 ok, err := avfs.IsEmpty(vfs, emptyDir) 840 RequireNoError(t, err, "IsEmpty %s", emptyDir) 841 842 if !ok { 843 t.Error("IsEmpty : want IsEmpty to be true, got false") 844 } 845 }) 846 847 t.Run("IsEmptyDir", func(t *testing.T) { 848 ts.existingDir(t, testDir) 849 850 ok, err := avfs.IsEmpty(vfs, testDir) 851 RequireNoError(t, err, "IsEmpty %s", testDir) 852 853 if ok { 854 t.Error("IsEmpty : want IsEmpty to be false, got true") 855 } 856 }) 857 858 t.Run("IsEmptyNonExisting", func(t *testing.T) { 859 nonExistingFile := ts.nonExistingFile(t, testDir) 860 861 wantErr := fmt.Errorf("%q path does not exist", nonExistingFile) 862 863 ok, err := avfs.IsEmpty(vfs, nonExistingFile) 864 if err.Error() != wantErr.Error() { 865 t.Errorf("IsEmpty : want error to be %v, got %v", wantErr, err) 866 } 867 868 if ok { 869 t.Error("IsEmpty : want IsEmpty to be false, got true") 870 } 871 }) 872 } 873 874 // TestIsPathSeparator tests IsPathSeparator function. 875 func (ts *Suite) TestIsPathSeparator(t *testing.T, _ string) { 876 vfs := ts.vfsTest 877 878 isPathSepTests := []struct { 879 sep uint8 880 isAbs bool 881 }{ 882 {sep: '/', isAbs: true}, 883 {sep: '\\', isAbs: vfs.OSType() == avfs.OsWindows}, 884 {sep: '.', isAbs: false}, 885 {sep: 'a', isAbs: false}, 886 } 887 888 for _, test := range isPathSepTests { 889 r := vfs.IsPathSeparator(test.sep) 890 if r != test.isAbs { 891 t.Errorf("IsPathSeparator(%q) = %v, want %v", test.sep, r, test.isAbs) 892 } 893 } 894 } 895 896 // TestJoin tests Join function. 897 func (ts *Suite) TestJoin(t *testing.T, _ string) { 898 vfs := ts.vfsTest 899 900 type joinTest struct { 901 elem []string 902 path string 903 } 904 905 joinTests := []*joinTest{ 906 // zero parameters 907 {[]string{}, ""}, 908 909 // one parameter 910 {[]string{""}, ""}, 911 {[]string{"/"}, "/"}, 912 {[]string{"a"}, "a"}, 913 914 // two parameters 915 {[]string{"a", "b"}, "a/b"}, 916 {[]string{"a", ""}, "a"}, 917 {[]string{"", "b"}, "b"}, 918 {[]string{"/", "a"}, "/a"}, 919 {[]string{"/", "a/b"}, "/a/b"}, 920 {[]string{"/", ""}, "/"}, 921 {[]string{"/a", "b"}, "/a/b"}, 922 {[]string{"a", "/b"}, "a/b"}, 923 {[]string{"/a", "/b"}, "/a/b"}, 924 {[]string{"a/", "b"}, "a/b"}, 925 {[]string{"a/", ""}, "a"}, 926 {[]string{"", ""}, ""}, 927 928 // three parameters 929 {[]string{"/", "a", "b"}, "/a/b"}, 930 } 931 932 nonWinJoinTests := []*joinTest{ 933 {[]string{"//", "a"}, "/a"}, 934 } 935 936 winJoinTests := []*joinTest{ 937 {[]string{`directory`, `file`}, `directory\file`}, 938 {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, 939 {[]string{`C:\Windows\`, ``}, `C:\Windows`}, 940 {[]string{`C:\`, `Windows`}, `C:\Windows`}, 941 {[]string{`C:`, `a`}, `C:a`}, 942 {[]string{`C:`, `a\b`}, `C:a\b`}, 943 {[]string{`C:`, `a`, `b`}, `C:a\b`}, 944 {[]string{`C:`, ``, `b`}, `C:b`}, 945 {[]string{`C:`, ``, ``, `b`}, `C:b`}, 946 {[]string{`C:`, ``}, `C:.`}, 947 {[]string{`C:`, ``, ``}, `C:.`}, 948 {[]string{`C:`, `\a`}, `C:\a`}, 949 {[]string{`C:`, ``, `\a`}, `C:\a`}, 950 {[]string{`C:.`, `a`}, `C:a`}, 951 {[]string{`C:a`, `b`}, `C:a\b`}, 952 {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, 953 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, 954 {[]string{`\\host\share\foo`}, `\\host\share\foo`}, 955 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, 956 {[]string{`\`}, `\`}, 957 {[]string{`\`, ``}, `\`}, 958 {[]string{`\`, `a`}, `\a`}, 959 {[]string{`\\`, `a`}, `\\a`}, 960 {[]string{`\`, `a`, `b`}, `\a\b`}, 961 {[]string{`\\`, `a`, `b`}, `\\a\b`}, 962 {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, 963 {[]string{`\\a`, `b`, `c`}, `\\a\b\c`}, 964 {[]string{`\\a\`, `b`, `c`}, `\\a\b\c`}, 965 {[]string{`//`, `a`}, `\\a`}, 966 } 967 968 if vfs.OSType() == avfs.OsWindows { 969 joinTests = append(joinTests, winJoinTests...) 970 } else { 971 joinTests = append(joinTests, nonWinJoinTests...) 972 } 973 974 for _, test := range joinTests { 975 expected := filepath.FromSlash(test.path) 976 if p := filepath.Join(test.elem...); p != expected { 977 t.Errorf("join(%q) = %q, want %q", test.elem, p, expected) 978 } 979 } 980 } 981 982 func errp(e error) string { 983 if e == nil { 984 return "<nil>" 985 } 986 987 return e.Error() 988 } 989 990 func (ts *Suite) TestMatch(t *testing.T, _ string) { 991 vfs := ts.vfsTest 992 993 matchTests := []struct { 994 pattern, s string 995 match bool 996 err error 997 }{ 998 {"abc", "abc", true, nil}, 999 {"*", "abc", true, nil}, 1000 {"*c", "abc", true, nil}, 1001 {"a*", "a", true, nil}, 1002 {"a*", "abc", true, nil}, 1003 {"a*", "ab/c", false, nil}, 1004 {"a*/b", "abc/b", true, nil}, 1005 {"a*/b", "a/c/b", false, nil}, 1006 {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil}, 1007 {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil}, 1008 {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil}, 1009 {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil}, 1010 {"a*b?c*x", "abxbbxdbxebxczzx", true, nil}, 1011 {"a*b?c*x", "abxbbxdbxebxczzy", false, nil}, 1012 {"ab[c]", "abc", true, nil}, 1013 {"ab[b-d]", "abc", true, nil}, 1014 {"ab[e-g]", "abc", false, nil}, 1015 {"ab[^c]", "abc", false, nil}, 1016 {"ab[^b-d]", "abc", false, nil}, 1017 {"ab[^e-g]", "abc", true, nil}, 1018 {"a\\*b", "a*b", true, nil}, 1019 {"a\\*b", "ab", false, nil}, 1020 {"a?b", "a☺b", true, nil}, 1021 {"a[^a]b", "a☺b", true, nil}, 1022 {"a???b", "a☺b", false, nil}, 1023 {"a[^a][^a][^a]b", "a☺b", false, nil}, 1024 {"[a-ζ]*", "α", true, nil}, 1025 {"*[a-ζ]", "A", false, nil}, 1026 {"a?b", "a/b", false, nil}, 1027 {"a*b", "a/b", false, nil}, 1028 {"[\\]a]", "]", true, nil}, 1029 {"[\\-]", "-", true, nil}, 1030 {"[x\\-]", "x", true, nil}, 1031 {"[x\\-]", "-", true, nil}, 1032 {"[x\\-]", "z", false, nil}, 1033 {"[\\-x]", "x", true, nil}, 1034 {"[\\-x]", "-", true, nil}, 1035 {"[\\-x]", "a", false, nil}, 1036 {"[]a]", "]", false, filepath.ErrBadPattern}, 1037 {"[-]", "-", false, filepath.ErrBadPattern}, 1038 {"[x-]", "x", false, filepath.ErrBadPattern}, 1039 {"[x-]", "-", false, filepath.ErrBadPattern}, 1040 {"[x-]", "z", false, filepath.ErrBadPattern}, 1041 {"[-x]", "x", false, filepath.ErrBadPattern}, 1042 {"[-x]", "-", false, filepath.ErrBadPattern}, 1043 {"[-x]", "a", false, filepath.ErrBadPattern}, 1044 {"\\", "a", false, filepath.ErrBadPattern}, 1045 {"[a-b-c]", "a", false, filepath.ErrBadPattern}, 1046 {"[", "a", false, filepath.ErrBadPattern}, 1047 {"[^", "a", false, filepath.ErrBadPattern}, 1048 {"[^bc", "a", false, filepath.ErrBadPattern}, 1049 {"a[", "a", false, filepath.ErrBadPattern}, 1050 {"a[", "ab", false, filepath.ErrBadPattern}, 1051 {"a[", "x", false, filepath.ErrBadPattern}, 1052 {"a/b[", "x", false, filepath.ErrBadPattern}, 1053 {"*x", "xxx", true, nil}, 1054 } 1055 1056 for _, tt := range matchTests { 1057 pattern := tt.pattern 1058 s := tt.s 1059 1060 if vfs.OSType() == avfs.OsWindows { 1061 if strings.Contains(pattern, "\\") { 1062 // no escape allowed on Windows. 1063 continue 1064 } 1065 1066 pattern = vfs.Clean(pattern) 1067 s = vfs.Clean(s) 1068 } 1069 1070 ok, err := vfs.Match(pattern, s) 1071 if ok != tt.match || err != tt.err { 1072 t.Errorf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err)) 1073 } 1074 } 1075 } 1076 1077 // TestRel tests Rel function. 1078 func (ts *Suite) TestRel(t *testing.T, _ string) { 1079 vfs := ts.vfsTest 1080 1081 type relTest struct { 1082 root, path, want string 1083 } 1084 1085 relTests := []*relTest{ 1086 {root: "a/b", path: "a/b", want: "."}, 1087 {root: "a/b/.", path: "a/b", want: "."}, 1088 {root: "a/b", path: "a/b/.", want: "."}, 1089 {root: "./a/b", path: "a/b", want: "."}, 1090 {root: "a/b", path: "./a/b", want: "."}, 1091 {root: "ab/cd", path: "ab/cde", want: "../cde"}, 1092 {root: "ab/cd", path: "ab/c", want: "../c"}, 1093 {root: "a/b", path: "a/b/c/d", want: "c/d"}, 1094 {root: "a/b", path: "a/b/../c", want: "../c"}, 1095 {root: "a/b/../c", path: "a/b", want: "../b"}, 1096 {root: "a/b/c", path: "a/c/d", want: "../../c/d"}, 1097 {root: "a/b", path: "c/d", want: "../../c/d"}, 1098 {root: "a/b/c/d", path: "a/b", want: "../.."}, 1099 {root: "a/b/c/d", path: "a/b/", want: "../.."}, 1100 {root: "a/b/c/d/", path: "a/b", want: "../.."}, 1101 {root: "a/b/c/d/", path: "a/b/", want: "../.."}, 1102 {root: "../../a/b", path: "../../a/b/c/d", want: "c/d"}, 1103 {root: "/a/b", path: "/a/b", want: "."}, 1104 {root: "/a/b/.", path: "/a/b", want: "."}, 1105 {root: "/a/b", path: "/a/b/.", want: "."}, 1106 {root: "/ab/cd", path: "/ab/cde", want: "../cde"}, 1107 {root: "/ab/cd", path: "/ab/c", want: "../c"}, 1108 {root: "/a/b", path: "/a/b/c/d", want: "c/d"}, 1109 {root: "/a/b", path: "/a/b/../c", want: "../c"}, 1110 {root: "/a/b/../c", path: "/a/b", want: "../b"}, 1111 {root: "/a/b/c", path: "/a/c/d", want: "../../c/d"}, 1112 {root: "/a/b", path: "/c/d", want: "../../c/d"}, 1113 {root: "/a/b/c/d", path: "/a/b", want: "../.."}, 1114 {root: "/a/b/c/d", path: "/a/b/", want: "../.."}, 1115 {root: "/a/b/c/d/", path: "/a/b", want: "../.."}, 1116 {root: "/a/b/c/d/", path: "/a/b/", want: "../.."}, 1117 {root: "/../../a/b", path: "/../../a/b/c/d", want: "c/d"}, 1118 {root: ".", path: "a/b", want: "a/b"}, 1119 {root: ".", path: "..", want: ".."}, 1120 1121 // can't do purely lexically 1122 {root: "..", path: ".", want: "err"}, 1123 {root: "..", path: "a", want: "err"}, 1124 {root: "../..", path: "..", want: "err"}, 1125 {root: "a", path: "/a", want: "err"}, 1126 {root: "/a", path: "a", want: "err"}, 1127 } 1128 1129 relTestsWin := []*relTest{ 1130 {root: `C:a\b\c`, path: `C:a/b/d`, want: `..\d`}, 1131 {root: `C:\`, path: `D:\`, want: `err`}, 1132 {root: `C:`, path: `D:`, want: `err`}, 1133 {root: `C:\Projects`, path: `c:\projects\src`, want: `src`}, 1134 {root: `C:\Projects`, path: `c:\projects`, want: `.`}, 1135 {root: `C:\Projects\a\..`, path: `c:\projects`, want: `.`}, 1136 } 1137 1138 if vfs.OSType() == avfs.OsWindows { 1139 relTests = append(relTests, relTestsWin...) 1140 for i := range relTests { 1141 relTests[i].want = filepath.FromSlash(relTests[i].want) 1142 } 1143 } 1144 1145 for _, test := range relTests { 1146 got, err := vfs.Rel(test.root, test.path) 1147 if test.want == "err" { 1148 if err == nil { 1149 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got) 1150 } 1151 1152 continue 1153 } 1154 1155 if err != nil { 1156 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err) 1157 } 1158 1159 if got != test.want { 1160 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want) 1161 } 1162 } 1163 } 1164 1165 // TestRndTree tests RndTree methods. 1166 func (ts *Suite) TestRndTree(t *testing.T, testDir string) { 1167 vfs := ts.vfsSetup 1168 1169 rtOpts := []*avfs.RndTreeOpts{ 1170 {NbDirs: -1, NbFiles: -1, NbSymlinks: -1, MaxFileSize: -1, MaxDepth: -1}, 1171 {NbDirs: 0, NbFiles: 0, NbSymlinks: 0, MaxFileSize: 0, MaxDepth: 0}, 1172 {NbDirs: 0, NbFiles: 3, NbSymlinks: 3, MaxFileSize: 3, MaxDepth: 0}, 1173 {NbDirs: 3, NbFiles: 0, NbSymlinks: 3, MaxFileSize: 0, MaxDepth: 0}, 1174 {NbDirs: 3, NbFiles: 3, NbSymlinks: 0, MaxFileSize: 3, MaxDepth: 0}, 1175 {NbDirs: 3, NbFiles: 3, NbSymlinks: 3, MaxFileSize: 0, MaxDepth: 0}, 1176 {NbDirs: 3, NbFiles: 11, NbSymlinks: 4, MaxFileSize: 0, MaxDepth: 0}, 1177 {NbDirs: 20, NbFiles: 30, NbSymlinks: 10, MaxFileSize: 2048, MaxDepth: 3}, 1178 } 1179 1180 t.Run("RndTreeGenerate", func(t *testing.T) { 1181 for i, rtOpt := range rtOpts { 1182 rt := avfs.NewRndTree(vfs, rtOpt) 1183 1184 rt.GenTree() 1185 1186 nbDirs := len(rt.Dirs()) 1187 if nbDirs != rtOpt.NbDirs { 1188 t.Errorf("Dirs %d : want nb Dirs to be %d, got %d", i, rtOpt.NbDirs, nbDirs) 1189 } 1190 1191 maxDepth := 0 1192 1193 for _, file := range rt.Files() { 1194 depth := strings.Count(file.Name, "/") - 1 1195 if depth > maxDepth { 1196 maxDepth = depth 1197 } 1198 } 1199 1200 if maxDepth > rt.MaxDepth { 1201 t.Errorf("Dirs MaxDepth %d : want MaxDepth to be <= %d, got %d", i, rt.MaxDepth, maxDepth) 1202 } 1203 1204 nbFiles := len(rt.Files()) 1205 if nbFiles != rtOpt.NbFiles { 1206 t.Errorf("Files %d : want NbFiles to be %d, got %d", i, rtOpt.NbFiles, nbFiles) 1207 } 1208 1209 maxDepth = 0 1210 1211 for _, file := range rt.Files() { 1212 depth := strings.Count(file.Name, "/") - 1 1213 if depth > maxDepth { 1214 maxDepth = depth 1215 } 1216 } 1217 1218 if maxDepth > rt.MaxDepth { 1219 t.Errorf("Files MaxDepth %d : want MaxDepth to be <= %d, got %d", i, rt.MaxDepth, maxDepth) 1220 } 1221 1222 maxSize := 0 1223 1224 for _, file := range rt.Files() { 1225 size := file.Size 1226 if size > maxSize { 1227 maxSize = size 1228 } 1229 } 1230 1231 if maxSize > rt.MaxFileSize { 1232 t.Errorf("MaxFileSize %d : want MaxFileSize to be <= %d, got %d", i, rt.MaxFileSize, maxSize) 1233 } 1234 1235 nbSymlinks := len(rt.SymLinks()) 1236 wantSymLinks := rtOpt.NbSymlinks 1237 1238 if rt.NbFiles == 0 || !vfs.HasFeature(avfs.FeatSymlink) { 1239 wantSymLinks = 0 1240 } 1241 1242 if nbSymlinks != wantSymLinks { 1243 t.Errorf("Symlinks %d : want NbSymlinks to be %d, got %d", i, wantSymLinks, nbSymlinks) 1244 } 1245 1246 maxDepth = 0 1247 1248 for _, symlink := range rt.SymLinks() { 1249 depth := strings.Count(symlink.NewName, "/") - 1 1250 if depth > maxDepth { 1251 maxDepth = depth 1252 } 1253 } 1254 1255 if maxDepth > rt.MaxDepth { 1256 t.Errorf("Symlinks MaxDepth %d : want MaxDepth to be <= %d, got %d", i, rt.MaxDepth, maxDepth) 1257 } 1258 } 1259 }) 1260 1261 t.Run("RndTreeCreate", func(t *testing.T) { 1262 for i, rtOpt := range rtOpts { 1263 path := vfs.Join(testDir, strconv.Itoa(i)) 1264 1265 ts.createDir(t, path, avfs.DefaultDirPerm) 1266 1267 rt := avfs.NewRndTree(vfs, rtOpt) 1268 1269 err := rt.CreateDirs(testDir) 1270 RequireNoError(t, err, "CreateDirs %s", path) 1271 1272 err = rt.CreateFiles(testDir) 1273 RequireNoError(t, err, "CreateFiles %s", path) 1274 1275 if !vfs.HasFeature(avfs.FeatSymlink) { 1276 continue 1277 } 1278 1279 err = rt.CreateSymlinks(testDir) 1280 RequireNoError(t, err, "CreateSymlinks %s", path) 1281 } 1282 }) 1283 } 1284 1285 // TestSplit tests Split function. 1286 func (ts *Suite) TestSplit(t *testing.T, _ string) { 1287 vfs := ts.vfsTest 1288 1289 type splitTest struct { 1290 path, dir, file string 1291 } 1292 1293 var splitTests []*splitTest 1294 1295 switch vfs.OSType() { 1296 case avfs.OsWindows: 1297 splitTests = []*splitTest{ 1298 {path: `c:`, dir: `c:`}, 1299 {path: `c:/`, dir: `c:/`}, 1300 {path: `c:/foo`, dir: `c:/`, file: `foo`}, 1301 {path: `c:/foo/bar`, dir: `c:/foo/`, file: `bar`}, 1302 {path: `//host/share`, dir: `//host/share`}, 1303 {path: `//host/share/`, dir: `//host/share/`}, 1304 {path: `//host/share/foo`, dir: `//host/share/`, file: `foo`}, 1305 {path: `\\host\share`, dir: `\\host\share`}, 1306 {path: `\\host\share\`, dir: `\\host\share\`}, 1307 {path: `\\host\share\foo`, dir: `\\host\share\`, file: `foo`}, 1308 } 1309 default: 1310 splitTests = []*splitTest{ 1311 {path: "a/b", dir: "a/", file: "b"}, 1312 {path: "a/b/", dir: "a/b/"}, 1313 {path: "a/", dir: "a/"}, 1314 {path: "a", file: "a"}, 1315 {path: "/", dir: "/"}, 1316 } 1317 } 1318 1319 for _, test := range splitTests { 1320 d, f := vfs.Split(test.path) 1321 if d != test.dir || f != test.file { 1322 t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) 1323 } 1324 } 1325 } 1326 1327 // TestSplitAbs tests SplitAbs function. 1328 func (ts *Suite) TestSplitAbs(t *testing.T, _ string) { 1329 vfs := ts.vfsTest 1330 1331 cases := []struct { 1332 path string 1333 dir string 1334 file string 1335 osType avfs.OSType 1336 }{ 1337 {osType: avfs.OsLinux, path: "/", dir: "", file: ""}, 1338 {osType: avfs.OsLinux, path: "/home", dir: "", file: "home"}, 1339 {osType: avfs.OsLinux, path: "/home/user", dir: "/home", file: "user"}, 1340 {osType: avfs.OsLinux, path: "/usr/lib/xorg", dir: "/usr/lib", file: "xorg"}, 1341 {osType: avfs.OsWindows, path: `C:\`, dir: `C:`, file: ""}, 1342 {osType: avfs.OsWindows, path: `C:\Users`, dir: `C:`, file: "Users"}, 1343 {osType: avfs.OsWindows, path: `C:\Users\Default`, dir: `C:\Users`, file: "Default"}, 1344 } 1345 1346 for _, c := range cases { 1347 if c.osType != vfs.OSType() { 1348 continue 1349 } 1350 1351 dir, file := avfs.SplitAbs(vfs, c.path) 1352 if c.dir != dir { 1353 t.Errorf("splitPath %s : want dir to be %s, got %s", c.path, c.dir, dir) 1354 } 1355 1356 if c.file != file { 1357 t.Errorf("splitPath %s : want file to be %s, got %s", c.path, c.file, file) 1358 } 1359 } 1360 } 1361 1362 // TestWalkDir tests WalkDir function. 1363 func (ts *Suite) TestWalkDir(t *testing.T, testDir string) { 1364 dirs := ts.createSampleDirs(t, testDir) 1365 files := ts.createSampleFiles(t, testDir) 1366 symlinks := ts.createSampleSymlinks(t, testDir) 1367 1368 vfs := ts.vfsTest 1369 lNames := len(dirs) + len(files) + len(symlinks) 1370 wantNames := make([]string, 0, lNames) 1371 1372 wantNames = append(wantNames, testDir) 1373 for _, dir := range dirs { 1374 wantNames = append(wantNames, dir.Path) 1375 } 1376 1377 for _, file := range files { 1378 wantNames = append(wantNames, file.Path) 1379 } 1380 1381 if vfs.HasFeature(avfs.FeatSymlink) { 1382 for _, sl := range symlinks { 1383 wantNames = append(wantNames, sl.NewPath) 1384 } 1385 } 1386 1387 sort.Strings(wantNames) 1388 1389 t.Run("WalkDir", func(t *testing.T) { 1390 gotNames := make(map[string]int) 1391 err := vfs.WalkDir(testDir, func(path string, info fs.DirEntry, err error) error { 1392 gotNames[path]++ 1393 1394 return nil 1395 }) 1396 RequireNoError(t, err, "WalkDir %s", testDir) 1397 1398 if len(wantNames) != len(gotNames) { 1399 t.Errorf("Walk %s : want %d files or dirs, got %d", testDir, len(wantNames), len(gotNames)) 1400 } 1401 1402 for _, wantName := range wantNames { 1403 n, ok := gotNames[wantName] 1404 if !ok || n != 1 { 1405 t.Errorf("Walk %s : path %s not found", testDir, wantName) 1406 } 1407 } 1408 }) 1409 1410 t.Run("WalkNonExistingFile", func(t *testing.T) { 1411 nonExistingFile := ts.nonExistingFile(t, testDir) 1412 1413 err := vfs.WalkDir(nonExistingFile, func(path string, info fs.DirEntry, err error) error { 1414 return nil 1415 }) 1416 1417 RequireNoError(t, err, "WalkDir %s", nonExistingFile) 1418 }) 1419 }