From 51dbd7c1a6dd1704059e9248221e78e269158cfd Mon Sep 17 00:00:00 2001 From: Brice Rising Date: Sun, 9 Dec 2018 22:04:36 -0500 Subject: [PATCH] Removed unused methods from fs package The fs package tests are going to be a lot of work to get working without the internal test package used by golang/dep, so I removed all of the unused ones by this project to make the work easier. Signed-off-by: Brice Rising --- pkg/fsutil/fs.go | 350 ----------------------------- pkg/fsutil/fs_test.go | 506 ------------------------------------------ 2 files changed, 856 deletions(-) diff --git a/pkg/fsutil/fs.go b/pkg/fsutil/fs.go index a1e44eee4..82b7d4e76 100644 --- a/pkg/fsutil/fs.go +++ b/pkg/fsutil/fs.go @@ -10,149 +10,11 @@ import ( "os" "path/filepath" "runtime" - "strings" "syscall" - "unicode" "github.com/pkg/errors" ) -// HasFilepathPrefix will determine if "path" starts with "prefix" from -// the point of view of a filesystem. -// -// Unlike filepath.HasPrefix, this function is path-aware, meaning that -// it knows that two directories /foo and /foobar are not the same -// thing, and therefore HasFilepathPrefix("/foobar", "/foo") will return -// false. -// -// This function also handles the case where the involved filesystems -// are case-insensitive, meaning /foo/bar and /Foo/Bar correspond to the -// same file. In that situation HasFilepathPrefix("/Foo/Bar", "/foo") -// will return true. The implementation is *not* OS-specific, so a FAT32 -// filesystem mounted on Linux will be handled correctly. -func HasFilepathPrefix(path, prefix string) (bool, error) { - // this function is more convoluted then ideal due to need for special - // handling of volume name/drive letter on Windows. vnPath and vnPrefix - // are first compared, and then used to initialize initial values of p and - // d which will be appended to for incremental checks using - // IsCaseSensitiveFilesystem and then equality. - - // no need to check IsCaseSensitiveFilesystem because VolumeName return - // empty string on all non-Windows machines - vnPath := strings.ToLower(filepath.VolumeName(path)) - vnPrefix := strings.ToLower(filepath.VolumeName(prefix)) - if vnPath != vnPrefix { - return false, nil - } - - // Because filepath.Join("c:","dir") returns "c:dir", we have to manually - // add path separator to drive letters. Also, we need to set the path root - // on *nix systems, since filepath.Join("", "dir") returns a relative path. - vnPath += string(os.PathSeparator) - vnPrefix += string(os.PathSeparator) - - var dn string - - if isDir, err := IsDir(path); err != nil { - return false, errors.Wrap(err, "failed to check filepath prefix") - } else if isDir { - dn = path - } else { - dn = filepath.Dir(path) - } - - dn = filepath.Clean(dn) - prefix = filepath.Clean(prefix) - - // [1:] in the lines below eliminates empty string on *nix and volume name on Windows - dirs := strings.Split(dn, string(os.PathSeparator))[1:] - prefixes := strings.Split(prefix, string(os.PathSeparator))[1:] - - if len(prefixes) > len(dirs) { - return false, nil - } - - // d,p are initialized with "/" on *nix and volume name on Windows - d := vnPath - p := vnPrefix - - for i := range prefixes { - // need to test each component of the path for - // case-sensitiveness because on Unix we could have - // something like ext4 filesystem mounted on FAT - // mountpoint, mounted on ext4 filesystem, i.e. the - // problematic filesystem is not the last one. - caseSensitive, err := IsCaseSensitiveFilesystem(filepath.Join(d, dirs[i])) - if err != nil { - return false, errors.Wrap(err, "failed to check filepath prefix") - } - if caseSensitive { - d = filepath.Join(d, dirs[i]) - p = filepath.Join(p, prefixes[i]) - } else { - d = filepath.Join(d, strings.ToLower(dirs[i])) - p = filepath.Join(p, strings.ToLower(prefixes[i])) - } - - if p != d { - return false, nil - } - } - - return true, nil -} - -// EquivalentPaths compares the paths passed to check if they are equivalent. -// It respects the case-sensitivity of the underlying filesysyems. -func EquivalentPaths(p1, p2 string) (bool, error) { - p1 = filepath.Clean(p1) - p2 = filepath.Clean(p2) - - fi1, err := os.Stat(p1) - if err != nil { - return false, errors.Wrapf(err, "could not check for path equivalence") - } - fi2, err := os.Stat(p2) - if err != nil { - return false, errors.Wrapf(err, "could not check for path equivalence") - } - - p1Filename, p2Filename := "", "" - - if !fi1.IsDir() { - p1, p1Filename = filepath.Split(p1) - } - if !fi2.IsDir() { - p2, p2Filename = filepath.Split(p2) - } - - if isPrefix1, err := HasFilepathPrefix(p1, p2); err != nil { - return false, errors.Wrap(err, "failed to check for path equivalence") - } else if isPrefix2, err := HasFilepathPrefix(p2, p1); err != nil { - return false, errors.Wrap(err, "failed to check for path equivalence") - } else if !isPrefix1 || !isPrefix2 { - return false, nil - } - - if p1Filename != "" || p2Filename != "" { - caseSensitive, err := IsCaseSensitiveFilesystem(filepath.Join(p1, p1Filename)) - if err != nil { - return false, errors.Wrap(err, "could not check for filesystem case-sensitivity") - } - if caseSensitive { - if p1Filename != p2Filename { - return false, nil - } - } else { - if strings.ToLower(p1Filename) != strings.ToLower(p2Filename) { - return false, nil - } - } - } - - return true, nil -} - // RenameWithFallback attempts to rename a file or directory, but falls back to // copying in the event of a cross-device link error. If the fallback copy // succeeds, src is still removed, emulating normal rename behavior. @@ -193,159 +55,8 @@ func renameByCopy(src, dst string) error { return errors.Wrapf(os.RemoveAll(src), "cannot delete %s", src) } -// IsCaseSensitiveFilesystem determines if the filesystem where dir -// exists is case sensitive or not. -// -// CAVEAT: this function works by taking the last component of the given -// path and flipping the case of the first letter for which case -// flipping is a reversible operation (/foo/Bar → /foo/bar), then -// testing for the existence of the new filename. There are two -// possibilities: -// -// 1. The alternate filename does not exist. We can conclude that the -// filesystem is case sensitive. -// -// 2. The filename happens to exist. We have to test if the two files -// are the same file (case insensitive file system) or different ones -// (case sensitive filesystem). -// -// If the input directory is such that the last component is composed -// exclusively of case-less codepoints (e.g. numbers), this function will -// return false. -func IsCaseSensitiveFilesystem(dir string) (bool, error) { - alt := filepath.Join(filepath.Dir(dir), genTestFilename(filepath.Base(dir))) - - dInfo, err := os.Stat(dir) - if err != nil { - return false, errors.Wrap(err, "could not determine the case-sensitivity of the filesystem") - } - - aInfo, err := os.Stat(alt) - if err != nil { - // If the file doesn't exists, assume we are on a case-sensitive filesystem. - if os.IsNotExist(err) { - return true, nil - } - - return false, errors.Wrap(err, "could not determine the case-sensitivity of the filesystem") - } - - return !os.SameFile(dInfo, aInfo), nil -} - -// genTestFilename returns a string with at most one rune case-flipped. -// -// The transformation is applied only to the first rune that can be -// reversibly case-flipped, meaning: -// -// * A lowercase rune for which it's true that lower(upper(r)) == r -// * An uppercase rune for which it's true that upper(lower(r)) == r -// -// All the other runes are left intact. -func genTestFilename(str string) string { - flip := true - return strings.Map(func(r rune) rune { - if flip { - if unicode.IsLower(r) { - u := unicode.ToUpper(r) - if unicode.ToLower(u) == r { - r = u - flip = false - } - } else if unicode.IsUpper(r) { - l := unicode.ToLower(r) - if unicode.ToUpper(l) == r { - r = l - flip = false - } - } - } - return r - }, str) -} - var errPathNotDir = errors.New("given path is not a directory") -// ReadActualFilenames is used to determine the actual file names in given directory. -// -// On case sensitive file systems like ext4, it will check if those files exist using -// `os.Stat` and return a map with key and value as filenames which exist in the folder. -// -// Otherwise, it reads the contents of the directory and returns a map which has the -// given file name as the key and actual filename as the value(if it was found). -func ReadActualFilenames(dirPath string, names []string) (map[string]string, error) { - actualFilenames := make(map[string]string, len(names)) - if len(names) == 0 { - // This isn't expected to happen for current usage. Adding edge case handling, - // as it may be useful in future. - return actualFilenames, nil - } - // First, check that the given path is valid and it is a directory - dirStat, err := os.Stat(dirPath) - if err != nil { - return nil, errors.Wrap(err, "failed to read actual filenames") - } - - if !dirStat.IsDir() { - return nil, errPathNotDir - } - - // Ideally, we would use `os.Stat` for getting the actual file names but that returns - // the name we passed in as an argument and not the actual filename. So we are forced - // to list the directory contents and check against that. Since this check is costly, - // we do it only if absolutely necessary. - caseSensitive, err := IsCaseSensitiveFilesystem(dirPath) - if err != nil { - return nil, errors.Wrap(err, "failed to read actual filenames") - } - if caseSensitive { - // There will be no difference between actual filename and given filename. So - // just check if those files exist. - for _, name := range names { - _, err := os.Stat(filepath.Join(dirPath, name)) - if err == nil { - actualFilenames[name] = name - } else if !os.IsNotExist(err) { - // Some unexpected err, wrap and return it. - return nil, errors.Wrap(err, "failed to read actual filenames") - } - } - return actualFilenames, nil - } - - dir, err := os.Open(dirPath) - if err != nil { - return nil, errors.Wrap(err, "failed to read actual filenames") - } - defer dir.Close() - - // Pass -1 to read all filenames in directory - filenames, err := dir.Readdirnames(-1) - if err != nil { - return nil, errors.Wrap(err, "failed to read actual filenames") - } - - // namesMap holds the mapping from lowercase name to search name. Using this, we can - // avoid repeatedly looping through names. - namesMap := make(map[string]string, len(names)) - for _, name := range names { - namesMap[strings.ToLower(name)] = name - } - - for _, filename := range filenames { - searchName, ok := namesMap[strings.ToLower(filename)] - if ok { - // We are interested in this file, case insensitive match successful. - actualFilenames[searchName] = filename - if len(actualFilenames) == len(names) { - // We found all that we were looking for. - return actualFilenames, nil - } - } - } - return actualFilenames, nil -} - var ( errSrcNotDir = errors.New("source is not a directory") errDstExist = errors.New("destination already exists") @@ -480,23 +191,6 @@ func cloneSymlink(sl, dst string) error { return os.Symlink(resolved, dst) } -// EnsureDir tries to ensure that a directory is present at the given path. It first -// checks if the directory already exists at the given path. If there isn't one, it tries -// to create it with the given permissions. However, it does not try to create the -// directory recursively. -func EnsureDir(path string, perm os.FileMode) error { - _, err := IsDir(path) - - if os.IsNotExist(err) { - err = os.Mkdir(path, perm) - if err != nil { - return errors.Wrapf(err, "failed to ensure directory at %q", path) - } - } - - return err -} - // IsDir determines is the path given is a directory or not. func IsDir(name string) (bool, error) { fi, err := os.Stat(name) @@ -509,50 +203,6 @@ func IsDir(name string) (bool, error) { return true, nil } -// IsNonEmptyDir determines if the path given is a non-empty directory or not. -func IsNonEmptyDir(name string) (bool, error) { - isDir, err := IsDir(name) - if err != nil && !os.IsNotExist(err) { - return false, err - } else if !isDir { - return false, nil - } - - // Get file descriptor - f, err := os.Open(name) - if err != nil { - return false, err - } - defer f.Close() - - // Query only 1 child. EOF if no children. - _, err = f.Readdirnames(1) - switch err { - case io.EOF: - return false, nil - case nil: - return true, nil - default: - return false, err - } -} - -// IsRegular determines if the path given is a regular file or not. -func IsRegular(name string) (bool, error) { - fi, err := os.Stat(name) - if os.IsNotExist(err) { - return false, nil - } - if err != nil { - return false, err - } - mode := fi.Mode() - if mode&os.ModeType != 0 { - return false, errors.Errorf("%q is a %v, expected a file", name, mode) - } - return true, nil -} - // IsSymlink determines if the given path is a symbolic link. func IsSymlink(path string) (bool, error) { l, err := os.Lstat(path) diff --git a/pkg/fsutil/fs_test.go b/pkg/fsutil/fs_test.go index 9af0c0ec9..873ab0f68 100644 --- a/pkg/fsutil/fs_test.go +++ b/pkg/fsutil/fs_test.go @@ -8,198 +8,10 @@ import ( "io/ioutil" "os" "path/filepath" - "reflect" "runtime" - "strings" "testing" - - "github.com/golang/dep/internal/test" - "github.com/pkg/errors" ) -// This function tests HadFilepathPrefix. It should test it on both case -// sensitive and insensitive situations. However, the only reliable way to test -// case-insensitive behaviour is if using case-insensitive filesystem. This -// cannot be guaranteed in an automated test. Therefore, the behaviour of the -// tests is not to test case sensitivity on *nix and to assume that Windows is -// case-insensitive. Please see link below for some background. -// -// https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem -// -// NOTE: NTFS can be made case-sensitive. However many Windows programs, -// including Windows Explorer do not handle gracefully multiple files that -// differ only in capitalization. It is possible that this can cause these tests -// to fail on some setups. -func TestHasFilepathPrefix(t *testing.T) { - dir, err := ioutil.TempDir("", "dep") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - // dir2 is the same as dir but with different capitalization on Windows to - // test case insensitivity - var dir2 string - if runtime.GOOS == "windows" { - dir = strings.ToLower(dir) - dir2 = strings.ToUpper(dir) - } else { - dir2 = dir - } - - // For testing trailing and repeated separators - sep := string(os.PathSeparator) - - cases := []struct { - path string - prefix string - want bool - }{ - {filepath.Join(dir, "a", "b"), filepath.Join(dir2), true}, - {filepath.Join(dir, "a", "b"), dir2 + sep + sep + "a", true}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a") + sep, true}, - {filepath.Join(dir, "a", "b") + sep, filepath.Join(dir2), true}, - {dir + sep + sep + filepath.Join("a", "b"), filepath.Join(dir2, "a"), true}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a"), true}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b"), true}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "c"), false}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "d", "b"), false}, - {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b2"), false}, - {filepath.Join(dir), filepath.Join(dir2, "a", "b"), false}, - {filepath.Join(dir, "ab"), filepath.Join(dir2, "a", "b"), false}, - {filepath.Join(dir, "ab"), filepath.Join(dir2, "a"), false}, - {filepath.Join(dir, "123"), filepath.Join(dir2, "123"), true}, - {filepath.Join(dir, "123"), filepath.Join(dir2, "1"), false}, - {filepath.Join(dir, "⌘"), filepath.Join(dir2, "⌘"), true}, - {filepath.Join(dir, "a"), filepath.Join(dir2, "⌘"), false}, - {filepath.Join(dir, "⌘"), filepath.Join(dir2, "a"), false}, - } - - for _, c := range cases { - if err := os.MkdirAll(c.path, 0755); err != nil { - t.Fatal(err) - } - - if err = os.MkdirAll(c.prefix, 0755); err != nil { - t.Fatal(err) - } - - got, err := HasFilepathPrefix(c.path, c.prefix) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - if c.want != got { - t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) - } - } -} - -// This function tests HadFilepathPrefix. It should test it on both case -// sensitive and insensitive situations. However, the only reliable way to test -// case-insensitive behaviour is if using case-insensitive filesystem. This -// cannot be guaranteed in an automated test. Therefore, the behaviour of the -// tests is not to test case sensitivity on *nix and to assume that Windows is -// case-insensitive. Please see link below for some background. -// -// https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem -// -// NOTE: NTFS can be made case-sensitive. However many Windows programs, -// including Windows Explorer do not handle gracefully multiple files that -// differ only in capitalization. It is possible that this can cause these tests -// to fail on some setups. -func TestHasFilepathPrefix_Files(t *testing.T) { - dir, err := ioutil.TempDir("", "dep") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - // dir2 is the same as dir but with different capitalization on Windows to - // test case insensitivity - var dir2 string - if runtime.GOOS == "windows" { - dir = strings.ToLower(dir) - dir2 = strings.ToUpper(dir) - } else { - dir2 = dir - } - - existingFile := filepath.Join(dir, "exists") - if err = os.MkdirAll(existingFile, 0755); err != nil { - t.Fatal(err) - } - - nonExistingFile := filepath.Join(dir, "does_not_exists") - - cases := []struct { - path string - prefix string - want bool - err bool - }{ - {existingFile, filepath.Join(dir2), true, false}, - {nonExistingFile, filepath.Join(dir2), false, true}, - } - - for _, c := range cases { - got, err := HasFilepathPrefix(c.path, c.prefix) - if err != nil && !c.err { - t.Fatalf("unexpected error: %s", err) - } - if c.want != got { - t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) - } - } -} - -func TestEquivalentPaths(t *testing.T) { - h := test.NewHelper(t) - h.TempDir("dir") - h.TempDir("dir2") - - h.TempFile("file", "") - h.TempFile("file2", "") - - h.TempDir("DIR") - h.TempFile("FILE", "") - - testcases := []struct { - p1, p2 string - caseSensitiveEquivalent bool - caseInensitiveEquivalent bool - err bool - }{ - {h.Path("dir"), h.Path("dir"), true, true, false}, - {h.Path("file"), h.Path("file"), true, true, false}, - {h.Path("dir"), h.Path("dir2"), false, false, false}, - {h.Path("file"), h.Path("file2"), false, false, false}, - {h.Path("dir"), h.Path("file"), false, false, false}, - {h.Path("dir"), h.Path("DIR"), false, true, false}, - {strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), false, true, true}, - } - - caseSensitive, err := IsCaseSensitiveFilesystem(h.Path("dir")) - if err != nil { - t.Fatal("unexpcted error:", err) - } - - for _, tc := range testcases { - got, err := EquivalentPaths(tc.p1, tc.p2) - if err != nil && !tc.err { - t.Error("unexpected error:", err) - } - if caseSensitive { - if tc.caseSensitiveEquivalent != got { - t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got) - } - } else { - if tc.caseInensitiveEquivalent != got { - t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got) - } - } - } -} - func TestRenameWithFallback(t *testing.T) { dir, err := ioutil.TempDir("", "dep") if err != nil { @@ -238,158 +50,6 @@ func TestRenameWithFallback(t *testing.T) { } } -func TestIsCaseSensitiveFilesystem(t *testing.T) { - isLinux := runtime.GOOS == "linux" - isWindows := runtime.GOOS == "windows" - isMacOS := runtime.GOOS == "darwin" - - if !isLinux && !isWindows && !isMacOS { - t.Skip("Run this test on Windows, Linux and macOS only") - } - - dir, err := ioutil.TempDir("", "TestCaseSensitivity") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - var want bool - if isLinux { - want = true - } else { - want = false - } - - got, err := IsCaseSensitiveFilesystem(dir) - - if err != nil { - t.Fatalf("unexpected error message: \n\t(GOT) %+v", err) - } - - if want != got { - t.Fatalf("unexpected value returned: \n\t(GOT) %t\n\t(WNT) %t", got, want) - } -} - -func TestReadActualFilenames(t *testing.T) { - // We are trying to skip this test on file systems which are case-sensiive. We could - // have used `fs.IsCaseSensitiveFilesystem` for this check. However, the code we are - // testing also relies on `fs.IsCaseSensitiveFilesystem`. So a bug in - // `fs.IsCaseSensitiveFilesystem` could prevent this test from being run. This is the - // only scenario where we prefer the OS heuristic over doing the actual work of - // validating filesystem case sensitivity via `fs.IsCaseSensitiveFilesystem`. - if runtime.GOOS != "windows" && runtime.GOOS != "darwin" { - t.Skip("skip this test on non-Windows, non-macOS") - } - - h := test.NewHelper(t) - defer h.Cleanup() - - h.TempDir("") - tmpPath := h.Path(".") - - // First, check the scenarios for which we expect an error. - _, err := ReadActualFilenames(filepath.Join(tmpPath, "does_not_exists"), []string{""}) - switch { - case err == nil: - t.Fatal("expected err for non-existing folder") - // use `errors.Cause` because the error is wrapped and returned - case !os.IsNotExist(errors.Cause(err)): - t.Fatalf("unexpected error: %+v", err) - } - h.TempFile("tmpFile", "") - _, err = ReadActualFilenames(h.Path("tmpFile"), []string{""}) - switch { - case err == nil: - t.Fatal("expected err for passing file instead of directory") - case err != errPathNotDir: - t.Fatalf("unexpected error: %+v", err) - } - - cases := []struct { - createFiles []string - names []string - want map[string]string - }{ - // If we supply no filenames to the function, it should return an empty map. - {nil, nil, map[string]string{}}, - // If the directory contains the given file with different case, it should return - // a map which has the given filename as the key and actual filename as the value. - { - []string{"test1.txt"}, - []string{"Test1.txt"}, - map[string]string{"Test1.txt": "test1.txt"}, - }, - // 1. If the given filename is same as the actual filename, map should have the - // same key and value for the file. - // 2. If the given filename is present with different case for file extension, - // it should return a map which has the given filename as the key and actual - // filename as the value. - // 3. If the given filename is not present even with a different case, the map - // returned should not have an entry for that filename. - { - []string{"test2.txt", "test3.TXT"}, - []string{"test2.txt", "Test3.txt", "Test4.txt"}, - map[string]string{ - "test2.txt": "test2.txt", - "Test3.txt": "test3.TXT", - }, - }, - } - for _, c := range cases { - for _, file := range c.createFiles { - h.TempFile(file, "") - } - got, err := ReadActualFilenames(tmpPath, c.names) - if err != nil { - t.Fatalf("unexpected error: %+v", err) - } - if !reflect.DeepEqual(c.want, got) { - t.Fatalf("returned value does not match expected: \n\t(GOT) %v\n\t(WNT) %v", - got, c.want) - } - } -} - -func TestGenTestFilename(t *testing.T) { - cases := []struct { - str string - want string - }{ - {"abc", "Abc"}, - {"ABC", "aBC"}, - {"AbC", "abC"}, - {"αβγ", "Αβγ"}, - {"123", "123"}, - {"1a2", "1A2"}, - {"12a", "12A"}, - {"⌘", "⌘"}, - } - - for _, c := range cases { - got := genTestFilename(c.str) - if c.want != got { - t.Fatalf("str: %q, expected: %q, got: %q", c.str, c.want, got) - } - } -} - -func BenchmarkGenTestFilename(b *testing.B) { - cases := []string{ - strings.Repeat("a", 128), - strings.Repeat("A", 128), - strings.Repeat("α", 128), - strings.Repeat("1", 128), - strings.Repeat("⌘", 128), - } - - for i := 0; i < b.N; i++ { - for _, str := range cases { - genTestFilename(str) - } - } -} - func TestCopyDir(t *testing.T) { dir, err := ioutil.TempDir("", "dep") if err != nil { @@ -843,117 +503,6 @@ func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { return cleanup } -func TestEnsureDir(t *testing.T) { - h := test.NewHelper(t) - defer h.Cleanup() - h.TempDir(".") - h.TempFile("file", "") - - tmpPath := h.Path(".") - - var dn string - cleanup := setupInaccessibleDir(t, func(dir string) error { - dn = filepath.Join(dir, "dir") - return os.Mkdir(dn, 0777) - }) - defer cleanup() - - tests := map[string]bool{ - // [success] A dir already exists for the given path. - tmpPath: true, - // [success] Dir does not exist but parent dir exists, so should get created. - filepath.Join(tmpPath, "testdir"): true, - // [failure] Dir and parent dir do not exist, should return an error. - filepath.Join(tmpPath, "notexist", "testdir"): false, - // [failure] Regular file present at given path. - h.Path("file"): false, - // [failure] Path inaccessible. - dn: false, - } - - if runtime.GOOS == "windows" { - // This test doesn't work on Microsoft Windows because - // of the differences in how file permissions are - // implemented. For this to work, the directory where - // the directory exists should be inaccessible. - delete(tests, dn) - } - - for path, shouldEnsure := range tests { - err := EnsureDir(path, 0777) - if shouldEnsure { - if err != nil { - t.Fatalf("unexpected error %q for %q", err, path) - } else if ok, err := IsDir(path); !ok { - t.Fatalf("expected directory to be preset at %q", path) - t.Fatal(err) - } - } else if err == nil { - t.Fatalf("expected error for path %q, got none", path) - } - } -} - -func TestIsRegular(t *testing.T) { - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - var fn string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - fn = filepath.Join(dir, "file") - fh, err := os.Create(fn) - if err != nil { - return err - } - - return fh.Close() - }) - defer cleanup() - - tests := map[string]struct { - exists bool - err bool - }{ - wd: {false, true}, - filepath.Join(wd, "testdata"): {false, true}, - filepath.Join(wd, "testdata", "test.file"): {true, false}, - filepath.Join(wd, "this_file_does_not_exist.thing"): {false, false}, - fn: {false, true}, - } - - if runtime.GOOS == "windows" { - // This test doesn't work on Microsoft Windows because - // of the differences in how file permissions are - // implemented. For this to work, the directory where - // the file exists should be inaccessible. - delete(tests, fn) - } - - for f, want := range tests { - got, err := IsRegular(f) - if err != nil { - if want.exists != got { - t.Fatalf("expected %t for %s, got %t", want.exists, f, got) - } - if !want.err { - t.Fatalf("expected no error, got %v", err) - } - } else { - if want.err { - t.Fatalf("expected error for %s, got none", f) - } - } - - if got != want.exists { - t.Fatalf("expected %t for %s, got %t", want, f, got) - } - } - -} - func TestIsDir(t *testing.T) { wd, err := os.Getwd() if err != nil { @@ -999,61 +548,6 @@ func TestIsDir(t *testing.T) { } } -func TestIsNonEmptyDir(t *testing.T) { - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - h := test.NewHelper(t) - defer h.Cleanup() - - h.TempDir("empty") - - testCases := []struct { - path string - empty bool - err bool - }{ - {wd, true, false}, - {"testdata", true, false}, - {filepath.Join(wd, "fs.go"), false, true}, - {filepath.Join(wd, "this_file_does_not_exist.thing"), false, false}, - {h.Path("empty"), false, false}, - } - - // This test case doesn't work on Microsoft Windows because of the - // differences in how file permissions are implemented. - if runtime.GOOS != "windows" { - var inaccessibleDir string - cleanup := setupInaccessibleDir(t, func(dir string) error { - inaccessibleDir = filepath.Join(dir, "empty") - return os.Mkdir(inaccessibleDir, 0777) - }) - defer cleanup() - - testCases = append(testCases, struct { - path string - empty bool - err bool - }{inaccessibleDir, false, true}) - } - - for _, want := range testCases { - got, err := IsNonEmptyDir(want.path) - if want.err && err == nil { - if got { - t.Fatalf("wanted false with error for %v, but got true", want.path) - } - t.Fatalf("wanted an error for %v, but it was nil", want.path) - } - - if got != want.empty { - t.Fatalf("wanted %t for %v, but got %t", want.empty, want.path, got) - } - } -} - func TestIsSymlink(t *testing.T) { dir, err := ioutil.TempDir("", "dep") if err != nil {