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