github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/path/filepath/path_windows_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package filepath_test 6 7 import ( 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "syscall" 17 "testing" 18 ) 19 20 func init() { 21 tmpdir, err := ioutil.TempDir("", "symtest") 22 if err != nil { 23 panic("failed to create temp directory: " + err.Error()) 24 } 25 defer os.RemoveAll(tmpdir) 26 27 err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) 28 if err == nil { 29 return 30 } 31 32 err = err.(*os.LinkError).Err 33 switch err { 34 case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: 35 supportsSymlinks = false 36 } 37 } 38 39 func TestWinSplitListTestsAreValid(t *testing.T) { 40 comspec := os.Getenv("ComSpec") 41 if comspec == "" { 42 t.Fatal("%ComSpec% must be set") 43 } 44 45 for ti, tt := range winsplitlisttests { 46 testWinSplitListTestIsValid(t, ti, tt, comspec) 47 } 48 } 49 50 func testWinSplitListTestIsValid(t *testing.T, ti int, tt SplitListTest, 51 comspec string) { 52 53 const ( 54 cmdfile = `printdir.cmd` 55 perm os.FileMode = 0700 56 ) 57 58 tmp, err := ioutil.TempDir("", "testWinSplitListTestIsValid") 59 if err != nil { 60 t.Fatalf("TempDir failed: %v", err) 61 } 62 defer os.RemoveAll(tmp) 63 64 for i, d := range tt.result { 65 if d == "" { 66 continue 67 } 68 if cd := filepath.Clean(d); filepath.VolumeName(cd) != "" || 69 cd[0] == '\\' || cd == ".." || (len(cd) >= 3 && cd[0:3] == `..\`) { 70 t.Errorf("%d,%d: %#q refers outside working directory", ti, i, d) 71 return 72 } 73 dd := filepath.Join(tmp, d) 74 if _, err := os.Stat(dd); err == nil { 75 t.Errorf("%d,%d: %#q already exists", ti, i, d) 76 return 77 } 78 if err = os.MkdirAll(dd, perm); err != nil { 79 t.Errorf("%d,%d: MkdirAll(%#q) failed: %v", ti, i, dd, err) 80 return 81 } 82 fn, data := filepath.Join(dd, cmdfile), []byte("@echo "+d+"\r\n") 83 if err = ioutil.WriteFile(fn, data, perm); err != nil { 84 t.Errorf("%d,%d: WriteFile(%#q) failed: %v", ti, i, fn, err) 85 return 86 } 87 } 88 89 for i, d := range tt.result { 90 if d == "" { 91 continue 92 } 93 exp := []byte(d + "\r\n") 94 cmd := &exec.Cmd{ 95 Path: comspec, 96 Args: []string{`/c`, cmdfile}, 97 Env: []string{`Path=` + tt.list}, 98 Dir: tmp, 99 } 100 out, err := cmd.CombinedOutput() 101 switch { 102 case err != nil: 103 t.Errorf("%d,%d: execution error %v\n%q", ti, i, err, out) 104 return 105 case !reflect.DeepEqual(out, exp): 106 t.Errorf("%d,%d: expected %#q, got %#q", ti, i, exp, out) 107 return 108 default: 109 // unshadow cmdfile in next directory 110 err = os.Remove(filepath.Join(tmp, d, cmdfile)) 111 if err != nil { 112 t.Fatalf("Remove test command failed: %v", err) 113 } 114 } 115 } 116 } 117 118 // TestEvalSymlinksCanonicalNames verify that EvalSymlinks 119 // returns "canonical" path names on windows. 120 func TestEvalSymlinksCanonicalNames(t *testing.T) { 121 tmp, err := ioutil.TempDir("", "evalsymlinkcanonical") 122 if err != nil { 123 t.Fatal("creating temp dir:", err) 124 } 125 defer os.RemoveAll(tmp) 126 127 // ioutil.TempDir might return "non-canonical" name. 128 cTmpName, err := filepath.EvalSymlinks(tmp) 129 if err != nil { 130 t.Errorf("EvalSymlinks(%q) error: %v", tmp, err) 131 } 132 133 dirs := []string{ 134 "test", 135 "test/dir", 136 "testing_long_dir", 137 "TEST2", 138 } 139 140 for _, d := range dirs { 141 dir := filepath.Join(cTmpName, d) 142 err := os.Mkdir(dir, 0755) 143 if err != nil { 144 t.Fatal(err) 145 } 146 cname, err := filepath.EvalSymlinks(dir) 147 if err != nil { 148 t.Errorf("EvalSymlinks(%q) error: %v", dir, err) 149 continue 150 } 151 if dir != cname { 152 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", dir, cname, dir) 153 continue 154 } 155 // test non-canonical names 156 test := strings.ToUpper(dir) 157 p, err := filepath.EvalSymlinks(test) 158 if err != nil { 159 t.Errorf("EvalSymlinks(%q) error: %v", test, err) 160 continue 161 } 162 if p != cname { 163 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname) 164 continue 165 } 166 // another test 167 test = strings.ToLower(dir) 168 p, err = filepath.EvalSymlinks(test) 169 if err != nil { 170 t.Errorf("EvalSymlinks(%q) error: %v", test, err) 171 continue 172 } 173 if p != cname { 174 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname) 175 continue 176 } 177 } 178 } 179 180 // checkVolume8dot3Setting runs "fsutil 8dot3name query c:" command 181 // (where c: is vol parameter) to discover "8dot3 name creation state". 182 // The state is combination of 2 flags. The global flag controls if it 183 // is per volume or global setting: 184 // 0 - Enable 8dot3 name creation on all volumes on the system 185 // 1 - Disable 8dot3 name creation on all volumes on the system 186 // 2 - Set 8dot3 name creation on a per volume basis 187 // 3 - Disable 8dot3 name creation on all volumes except the system volume 188 // If global flag is set to 2, then per-volume flag needs to be examined: 189 // 0 - Enable 8dot3 name creation on this volume 190 // 1 - Disable 8dot3 name creation on this volume 191 // checkVolume8dot3Setting verifies that "8dot3 name creation" flags 192 // are set to 2 and 0, if enabled parameter is true, or 2 and 1, if enabled 193 // is false. Otherwise checkVolume8dot3Setting returns error. 194 func checkVolume8dot3Setting(vol string, enabled bool) error { 195 // It appears, on some systems "fsutil 8dot3name query ..." command always 196 // exits with error. Ignore exit code, and look at fsutil output instead. 197 out, _ := exec.Command("fsutil", "8dot3name", "query", vol).CombinedOutput() 198 // Check that system has "Volume level setting" set. 199 expected := "The registry state of NtfsDisable8dot3NameCreation is 2, the default (Volume level setting)" 200 if !strings.Contains(string(out), expected) { 201 // Windows 10 version of fsutil has different output message. 202 expectedWindow10 := "The registry state is: 2 (Per volume setting - the default)" 203 if !strings.Contains(string(out), expectedWindow10) { 204 return fmt.Errorf("fsutil output should contain %q, but is %q", expected, string(out)) 205 } 206 } 207 // Now check the volume setting. 208 expected = "Based on the above two settings, 8dot3 name creation is %s on %s" 209 if enabled { 210 expected = fmt.Sprintf(expected, "enabled", vol) 211 } else { 212 expected = fmt.Sprintf(expected, "disabled", vol) 213 } 214 if !strings.Contains(string(out), expected) { 215 return fmt.Errorf("unexpected fsutil output: %q", string(out)) 216 } 217 return nil 218 } 219 220 func setVolume8dot3Setting(vol string, enabled bool) error { 221 cmd := []string{"fsutil", "8dot3name", "set", vol} 222 if enabled { 223 cmd = append(cmd, "0") 224 } else { 225 cmd = append(cmd, "1") 226 } 227 // It appears, on some systems "fsutil 8dot3name set ..." command always 228 // exits with error. Ignore exit code, and look at fsutil output instead. 229 out, _ := exec.Command(cmd[0], cmd[1:]...).CombinedOutput() 230 if string(out) != "\r\nSuccessfully set 8dot3name behavior.\r\n" { 231 // Windows 10 version of fsutil has different output message. 232 expectedWindow10 := "Successfully %s 8dot3name generation on %s\r\n" 233 if enabled { 234 expectedWindow10 = fmt.Sprintf(expectedWindow10, "enabled", vol) 235 } else { 236 expectedWindow10 = fmt.Sprintf(expectedWindow10, "disabled", vol) 237 } 238 if string(out) != expectedWindow10 { 239 return fmt.Errorf("%v command failed: %q", cmd, string(out)) 240 } 241 } 242 return nil 243 } 244 245 var runFSModifyTests = flag.Bool("run_fs_modify_tests", false, "run tests which modify filesystem parameters") 246 247 // This test assumes registry state of NtfsDisable8dot3NameCreation is 2, 248 // the default (Volume level setting). 249 func TestEvalSymlinksCanonicalNamesWith8dot3Disabled(t *testing.T) { 250 if !*runFSModifyTests { 251 t.Skip("skipping test that modifies file system setting; enable with -run_fs_modify_tests") 252 } 253 tempVol := filepath.VolumeName(os.TempDir()) 254 if len(tempVol) != 2 { 255 t.Fatalf("unexpected temp volume name %q", tempVol) 256 } 257 258 err := checkVolume8dot3Setting(tempVol, true) 259 if err != nil { 260 t.Fatal(err) 261 } 262 err = setVolume8dot3Setting(tempVol, false) 263 if err != nil { 264 t.Fatal(err) 265 } 266 defer func() { 267 err := setVolume8dot3Setting(tempVol, true) 268 if err != nil { 269 t.Fatal(err) 270 } 271 err = checkVolume8dot3Setting(tempVol, true) 272 if err != nil { 273 t.Fatal(err) 274 } 275 }() 276 err = checkVolume8dot3Setting(tempVol, false) 277 if err != nil { 278 t.Fatal(err) 279 } 280 TestEvalSymlinksCanonicalNames(t) 281 } 282 283 func TestToNorm(t *testing.T) { 284 stubBase := func(path string) (string, error) { 285 vol := filepath.VolumeName(path) 286 path = path[len(vol):] 287 288 if strings.Contains(path, "/") { 289 return "", fmt.Errorf("invalid path is given to base: %s", vol+path) 290 } 291 292 if path == "" || path == "." || path == `\` { 293 return "", fmt.Errorf("invalid path is given to base: %s", vol+path) 294 } 295 296 i := strings.LastIndexByte(path, filepath.Separator) 297 if i == len(path)-1 { // trailing '\' is invalid 298 return "", fmt.Errorf("invalid path is given to base: %s", vol+path) 299 } 300 if i == -1 { 301 return strings.ToUpper(path), nil 302 } 303 304 return strings.ToUpper(path[i+1:]), nil 305 } 306 307 // On this test, toNorm should be same as string.ToUpper(filepath.Clean(path)) except empty string. 308 tests := []struct { 309 arg string 310 want string 311 }{ 312 {"", ""}, 313 {".", "."}, 314 {"./foo/bar", `FOO\BAR`}, 315 {"/", `\`}, 316 {"/foo/bar", `\FOO\BAR`}, 317 {"/foo/bar/baz/qux", `\FOO\BAR\BAZ\QUX`}, 318 {"foo/bar", `FOO\BAR`}, 319 {"C:/foo/bar", `C:\FOO\BAR`}, 320 {"C:foo/bar", `C:FOO\BAR`}, 321 {"c:/foo/bar", `C:\FOO\BAR`}, 322 {"C:/foo/bar", `C:\FOO\BAR`}, 323 {"C:/foo/bar/", `C:\FOO\BAR`}, 324 {`C:\foo\bar`, `C:\FOO\BAR`}, 325 {`C:\foo/bar\`, `C:\FOO\BAR`}, 326 {"C:/ふー/バー", `C:\ふー\バー`}, 327 } 328 329 for _, test := range tests { 330 got, err := filepath.ToNorm(test.arg, stubBase) 331 if err != nil { 332 t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err) 333 } else if got != test.want { 334 t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got) 335 } 336 } 337 }