github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/file/mkdir_windows_test.go (about) 1 //go:build windows 2 3 package file 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 // Basic test from golang's os/path_test.go 16 func TestMkdirAll(t *testing.T) { 17 tmpDir := t.TempDir() 18 19 path := tmpDir + "/dir/./dir2" 20 err := MkdirAll(path, 0777) 21 if err != nil { 22 t.Fatalf("MkdirAll %q: %s", path, err) 23 } 24 25 // Already exists, should succeed. 26 err = MkdirAll(path, 0777) 27 if err != nil { 28 t.Fatalf("MkdirAll %q (second time): %s", path, err) 29 } 30 31 // Make file. 32 fpath := path + "/file" 33 f, err := Create(fpath) 34 if err != nil { 35 t.Fatalf("create %q: %s", fpath, err) 36 } 37 defer func() { 38 if err := f.Close(); err != nil { 39 t.Fatalf("Close %q: %s", fpath, err) 40 } 41 }() 42 43 // Can't make directory named after file. 44 err = MkdirAll(fpath, 0777) 45 if err == nil { 46 t.Fatalf("MkdirAll %q: no error", fpath) 47 } 48 perr, ok := err.(*os.PathError) 49 if !ok { 50 t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err) 51 } 52 if filepath.Clean(perr.Path) != filepath.Clean(fpath) { 53 t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) 54 } 55 56 // Can't make subdirectory of file. 57 ffpath := fpath + "/subdir" 58 err = MkdirAll(ffpath, 0777) 59 if err == nil { 60 t.Fatalf("MkdirAll %q: no error", ffpath) 61 } 62 perr, ok = err.(*os.PathError) 63 if !ok { 64 t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err) 65 } 66 if filepath.Clean(perr.Path) != filepath.Clean(fpath) { 67 t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) 68 } 69 70 path = tmpDir + `\dir\.\dir2\` 71 err = MkdirAll(path, 0777) 72 if err != nil { 73 t.Fatalf("MkdirAll %q: %s", path, err) 74 } 75 } 76 77 func unusedDrive(t *testing.T) string { 78 letter := FindUnusedDriveLetter() 79 require.NotEqual(t, letter, 0) 80 return string(letter) + ":" 81 } 82 83 func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs ...string) { 84 if valid { 85 assert.NoError(t, MkdirAll(path, 0777)) 86 } else { 87 err := MkdirAll(path, 0777) 88 assert.Error(t, err) 89 ok := false 90 for _, msg := range errormsgs { 91 if err.Error() == msg { 92 ok = true 93 } 94 } 95 assert.True(t, ok, fmt.Sprintf("Error message '%v' didn't match any of %v", err, errormsgs)) 96 } 97 } 98 99 func checkMkdirAllSubdirs(t *testing.T, path string, valid bool, errormsgs ...string) { 100 checkMkdirAll(t, path, valid, errormsgs...) 101 checkMkdirAll(t, path+`\`, valid, errormsgs...) 102 checkMkdirAll(t, path+`\parent`, valid, errormsgs...) 103 checkMkdirAll(t, path+`\parent\`, valid, errormsgs...) 104 checkMkdirAll(t, path+`\parent\child`, valid, errormsgs...) 105 checkMkdirAll(t, path+`\parent\child\`, valid, errormsgs...) 106 } 107 108 // Testing paths on existing drive 109 func TestMkdirAllOnDrive(t *testing.T) { 110 path := t.TempDir() 111 112 dir, err := os.Stat(path) 113 require.NoError(t, err) 114 require.True(t, dir.IsDir()) 115 116 drive := filepath.VolumeName(path) 117 118 checkMkdirAll(t, drive, true, "") 119 checkMkdirAll(t, drive+`\`, true, "") 120 // checkMkdirAll(t, `\\?\`+drive, true, "") - this isn't actually a Valid Windows path - this test used to work under go1.21.3 but fails under go1.21.4 121 checkMkdirAll(t, `\\?\`+drive+`\`, true, "") 122 checkMkdirAllSubdirs(t, path, true, "") 123 checkMkdirAllSubdirs(t, `\\?\`+path, true, "") 124 } 125 126 // Testing paths on unused drive 127 // This is where there is a difference from golang's os.MkdirAll. It would 128 // recurse extended-length paths down to the "\\?" prefix and return the 129 // noninformative error: 130 // "mkdir \\?: The filename, directory name, or volume label syntax is incorrect." 131 // Our version stops the recursion at drive's root directory, and reports: 132 // "mkdir \\?\A:\: The system cannot find the path specified." 133 func TestMkdirAllOnUnusedDrive(t *testing.T) { 134 path := unusedDrive(t) 135 errormsg := fmt.Sprintf(`mkdir %s\: The system cannot find the path specified.`, path) 136 checkMkdirAllSubdirs(t, path, false, errormsg) 137 errormsg1 := fmt.Sprintf(`mkdir \\?\%s\: The system cannot find the path specified.`, path) // pre go1.21.4 138 errormsg2 := fmt.Sprintf(`mkdir \\?\%s: The system cannot find the file specified.`, path) // go1.21.4 and after 139 checkMkdirAllSubdirs(t, `\\?\`+path, false, errormsg1, errormsg2) 140 } 141 142 // Testing paths on unknown network host 143 // This is an additional difference from golang's os.MkdirAll. With our 144 // first fix, stopping it from recursing extended-length paths down to 145 // the "\\?" prefix, it would now stop at `\\?\UNC`, because that is what 146 // filepath.VolumeName returns (which is wrong, that is not a volume name!), 147 // and still return a nonifnromative error: 148 // "mkdir \\?\UNC\\: The filename, directory name, or volume label syntax is incorrect." 149 // Our version stops the recursion at level before this, and reports: 150 // "mkdir \\?\UNC\0.0.0.0: The specified path is invalid." 151 func TestMkdirAllOnUnusedNetworkHost(t *testing.T) { 152 path := `\\0.0.0.0\share` 153 errormsg := fmt.Sprintf("mkdir %s\\: The format of the specified network name is invalid.", path) 154 checkMkdirAllSubdirs(t, path, false, errormsg) 155 path = `\\?\UNC\0.0.0.0\share` 156 checkMkdirAllSubdirs(t, path, false, 157 `mkdir \\?\UNC\0.0.0.0: The specified path is invalid.`, // pre go1.20 158 `mkdir \\?\UNC\0.0.0.0\share\: The format of the specified network name is invalid.`, 159 ) 160 161 }