NewConsoleFile = newConsoleFile
CommandLineToArgv = commandLineToArgv
AllowReadDirFileID = &allowReadDirFileID
+ SplitPath = splitPath
)
t.Error(err)
}
}
+
+func TestSplitPath(t *testing.T) {
+ t.Parallel()
+ for _, tt := range []struct{ path, wantDir, wantBase string }{
+ {`a`, `.`, `a`},
+ {`a\`, `.`, `a`},
+ {`a\\`, `.`, `a`},
+ {`a\b`, `a`, `b`},
+ {`a\\b`, `a`, `b`},
+ {`a\b\`, `a`, `b`},
+ {`a\b\c`, `a\b`, `c`},
+ {`\a`, `\`, `a`},
+ {`\a\`, `\`, `a`},
+ {`\a\b`, `\a`, `b`},
+ {`\a\b\`, `\a`, `b`},
+ {`\a\b\c`, `\a\b`, `c`},
+ {`\\a`, `\\a`, `.`},
+ {`\\a\`, `\\a\`, `.`},
+ {`\\\a`, `\\\a`, `.`},
+ {`\\\a\`, `\\\a`, `.`},
+ {`\\a\b\c`, `\\a\b`, `c`},
+ {`c:`, `c:`, `.`},
+ {`c:\`, `c:\`, `.`},
+ {`c:\a`, `c:\`, `a`},
+ {`c:a`, `c:`, `a`},
+ {`c:a\b\`, `c:a`, `b`},
+ {`c:base`, `c:`, `base`},
+ {`a/b/c`, `a/b`, `c`},
+ {`a/b/c/`, `a/b`, `c`},
+ {`\\?\c:\a`, `\\?\c:\`, `a`},
+ } {
+ if dir, base := os.SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase {
+ t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase)
+ }
+ }
+}
// splitPath returns the base name and parent directory.
func splitPath(path string) (string, string) {
- dirname, basename := filepathlite.Split(path)
- volnamelen := filepathlite.VolumeNameLen(dirname)
- for len(dirname) > volnamelen && IsPathSeparator(dirname[len(dirname)-1]) {
- dirname = dirname[:len(dirname)-1]
+ if path == "" {
+ return ".", "."
}
+
+ // The first prefixlen bytes are part of the parent directory.
+ // The prefix consists of the volume name (if any) and the first \ (if significant).
+ prefixlen := filepathlite.VolumeNameLen(path)
+ if len(path) > prefixlen && IsPathSeparator(path[prefixlen]) {
+ if prefixlen == 0 {
+ // This is a path relative to the current volume, like \foo.
+ // Include the initial \ in the prefix.
+ prefixlen = 1
+ } else if path[prefixlen-1] == ':' {
+ // This is an absolute path on a named drive, like c:\foo.
+ // Include the initial \ in the prefix.
+ prefixlen++
+ }
+ }
+
+ i := len(path) - 1
+
+ // Remove trailing slashes.
+ for i >= prefixlen && IsPathSeparator(path[i]) {
+ i--
+ }
+ path = path[:i+1]
+
+ // Find the last path separator. The basename is what follows.
+ for i >= prefixlen && !IsPathSeparator(path[i]) {
+ i--
+ }
+ basename := path[i+1:]
+ if basename == "" {
+ basename = "."
+ }
+
+ // Remove trailing slashes. The remainder is dirname.
+ for i >= prefixlen && IsPathSeparator(path[i]) {
+ i--
+ }
+ dirname := path[:i+1]
+ if dirname == "" {
+ dirname = "."
+ }
+
return dirname, basename
}
}
}
+func TestRemoveAllTrailingSlash(t *testing.T) {
+ slashes := []string{"/"}
+ if runtime.GOOS == "windows" {
+ slashes = append(slashes, `\`)
+ }
+ for _, slash := range slashes {
+ dir := makefs(t, []string{
+ "dir/a/file1",
+ "dir/a/file2",
+ "dir/file3",
+ })
+ path := dir + "/dir"
+ if err := RemoveAll(path + slash); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := Stat(path); !IsNotExist(err) {
+ t.Errorf("after RemoveAll(%q), directory still exists", path+slash)
+ }
+ }
+}
+
func BenchmarkRemoveAll(b *testing.B) {
tmpDir := filepath.Join(b.TempDir(), "target")
b.ReportAllocs()