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  }