1// Copyright 2009 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 5package filepath_test 6 7import ( 8 "fmt" 9 "internal/testenv" 10 "io/ioutil" 11 "os" 12 . "path/filepath" 13 "reflect" 14 "runtime" 15 "sort" 16 "strings" 17 "testing" 18) 19 20type MatchTest struct { 21 pattern, s string 22 match bool 23 err error 24} 25 26var matchTests = []MatchTest{ 27 {"abc", "abc", true, nil}, 28 {"*", "abc", true, nil}, 29 {"*c", "abc", true, nil}, 30 {"a*", "a", true, nil}, 31 {"a*", "abc", true, nil}, 32 {"a*", "ab/c", false, nil}, 33 {"a*/b", "abc/b", true, nil}, 34 {"a*/b", "a/c/b", false, nil}, 35 {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil}, 36 {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil}, 37 {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil}, 38 {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil}, 39 {"a*b?c*x", "abxbbxdbxebxczzx", true, nil}, 40 {"a*b?c*x", "abxbbxdbxebxczzy", false, nil}, 41 {"ab[c]", "abc", true, nil}, 42 {"ab[b-d]", "abc", true, nil}, 43 {"ab[e-g]", "abc", false, nil}, 44 {"ab[^c]", "abc", false, nil}, 45 {"ab[^b-d]", "abc", false, nil}, 46 {"ab[^e-g]", "abc", true, nil}, 47 {"a\\*b", "a*b", true, nil}, 48 {"a\\*b", "ab", false, nil}, 49 {"a?b", "a☺b", true, nil}, 50 {"a[^a]b", "a☺b", true, nil}, 51 {"a???b", "a☺b", false, nil}, 52 {"a[^a][^a][^a]b", "a☺b", false, nil}, 53 {"[a-ζ]*", "α", true, nil}, 54 {"*[a-ζ]", "A", false, nil}, 55 {"a?b", "a/b", false, nil}, 56 {"a*b", "a/b", false, nil}, 57 {"[\\]a]", "]", true, nil}, 58 {"[\\-]", "-", true, nil}, 59 {"[x\\-]", "x", true, nil}, 60 {"[x\\-]", "-", true, nil}, 61 {"[x\\-]", "z", false, nil}, 62 {"[\\-x]", "x", true, nil}, 63 {"[\\-x]", "-", true, nil}, 64 {"[\\-x]", "a", false, nil}, 65 {"[]a]", "]", false, ErrBadPattern}, 66 {"[-]", "-", false, ErrBadPattern}, 67 {"[x-]", "x", false, ErrBadPattern}, 68 {"[x-]", "-", false, ErrBadPattern}, 69 {"[x-]", "z", false, ErrBadPattern}, 70 {"[-x]", "x", false, ErrBadPattern}, 71 {"[-x]", "-", false, ErrBadPattern}, 72 {"[-x]", "a", false, ErrBadPattern}, 73 {"\\", "a", false, ErrBadPattern}, 74 {"[a-b-c]", "a", false, ErrBadPattern}, 75 {"[", "a", false, ErrBadPattern}, 76 {"[^", "a", false, ErrBadPattern}, 77 {"[^bc", "a", false, ErrBadPattern}, 78 {"a[", "a", false, nil}, 79 {"a[", "ab", false, ErrBadPattern}, 80 {"*x", "xxx", true, nil}, 81} 82 83func errp(e error) string { 84 if e == nil { 85 return "<nil>" 86 } 87 return e.Error() 88} 89 90func TestMatch(t *testing.T) { 91 for _, tt := range matchTests { 92 pattern := tt.pattern 93 s := tt.s 94 if runtime.GOOS == "windows" { 95 if strings.Contains(pattern, "\\") { 96 // no escape allowed on windows. 97 continue 98 } 99 pattern = Clean(pattern) 100 s = Clean(s) 101 } 102 ok, err := Match(pattern, s) 103 if ok != tt.match || err != tt.err { 104 t.Errorf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err)) 105 } 106 } 107} 108 109// contains reports whether vector contains the string s. 110func contains(vector []string, s string) bool { 111 for _, elem := range vector { 112 if elem == s { 113 return true 114 } 115 } 116 return false 117} 118 119var globTests = []struct { 120 pattern, result string 121}{ 122 {"match.go", "match.go"}, 123 {"mat?h.go", "match.go"}, 124 {"*", "match.go"}, 125 // Does not work in gccgo test environment. 126 // {"../*/match.go", "../filepath/match.go"}, 127} 128 129func TestGlob(t *testing.T) { 130 for _, tt := range globTests { 131 pattern := tt.pattern 132 result := tt.result 133 if runtime.GOOS == "windows" { 134 pattern = Clean(pattern) 135 result = Clean(result) 136 } 137 matches, err := Glob(pattern) 138 if err != nil { 139 t.Errorf("Glob error for %q: %s", pattern, err) 140 continue 141 } 142 if !contains(matches, result) { 143 t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result) 144 } 145 } 146 for _, pattern := range []string{"no_match", "../*/no_match"} { 147 matches, err := Glob(pattern) 148 if err != nil { 149 t.Errorf("Glob error for %q: %s", pattern, err) 150 continue 151 } 152 if len(matches) != 0 { 153 t.Errorf("Glob(%#q) = %#v want []", pattern, matches) 154 } 155 } 156} 157 158func TestGlobError(t *testing.T) { 159 _, err := Glob("[]") 160 if err == nil { 161 t.Error("expected error for bad pattern; got none") 162 } 163} 164 165func TestGlobUNC(t *testing.T) { 166 // Just make sure this runs without crashing for now. 167 // See issue 15879. 168 Glob(`\\?\C:\*`) 169} 170 171var globSymlinkTests = []struct { 172 path, dest string 173 brokenLink bool 174}{ 175 {"test1", "link1", false}, 176 {"test2", "link2", true}, 177} 178 179func TestGlobSymlink(t *testing.T) { 180 testenv.MustHaveSymlink(t) 181 182 tmpDir, err := ioutil.TempDir("", "globsymlink") 183 if err != nil { 184 t.Fatal("creating temp dir:", err) 185 } 186 defer os.RemoveAll(tmpDir) 187 188 for _, tt := range globSymlinkTests { 189 path := Join(tmpDir, tt.path) 190 dest := Join(tmpDir, tt.dest) 191 f, err := os.Create(path) 192 if err != nil { 193 t.Fatal(err) 194 } 195 if err := f.Close(); err != nil { 196 t.Fatal(err) 197 } 198 err = os.Symlink(path, dest) 199 if err != nil { 200 t.Fatal(err) 201 } 202 if tt.brokenLink { 203 // Break the symlink. 204 os.Remove(path) 205 } 206 matches, err := Glob(dest) 207 if err != nil { 208 t.Errorf("GlobSymlink error for %q: %s", dest, err) 209 } 210 if !contains(matches, dest) { 211 t.Errorf("Glob(%#q) = %#v want %v", dest, matches, dest) 212 } 213 } 214} 215 216type globTest struct { 217 pattern string 218 matches []string 219} 220 221func (test *globTest) buildWant(root string) []string { 222 want := make([]string, 0) 223 for _, m := range test.matches { 224 want = append(want, root+FromSlash(m)) 225 } 226 sort.Strings(want) 227 return want 228} 229 230func (test *globTest) globAbs(root, rootPattern string) error { 231 p := FromSlash(rootPattern + `\` + test.pattern) 232 have, err := Glob(p) 233 if err != nil { 234 return err 235 } 236 sort.Strings(have) 237 want := test.buildWant(root + `\`) 238 if strings.Join(want, "_") == strings.Join(have, "_") { 239 return nil 240 } 241 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) 242} 243 244func (test *globTest) globRel(root string) error { 245 p := root + FromSlash(test.pattern) 246 have, err := Glob(p) 247 if err != nil { 248 return err 249 } 250 sort.Strings(have) 251 want := test.buildWant(root) 252 if strings.Join(want, "_") == strings.Join(have, "_") { 253 return nil 254 } 255 // try also matching version without root prefix 256 wantWithNoRoot := test.buildWant("") 257 if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") { 258 return nil 259 } 260 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) 261} 262 263func TestWindowsGlob(t *testing.T) { 264 if runtime.GOOS != "windows" { 265 t.Skipf("skipping windows specific test") 266 } 267 268 tmpDir, err := ioutil.TempDir("", "TestWindowsGlob") 269 if err != nil { 270 t.Fatal(err) 271 } 272 defer os.RemoveAll(tmpDir) 273 274 // /tmp may itself be a symlink 275 tmpDir, err = EvalSymlinks(tmpDir) 276 if err != nil { 277 t.Fatal("eval symlink for tmp dir:", err) 278 } 279 280 if len(tmpDir) < 3 { 281 t.Fatalf("tmpDir path %q is too short", tmpDir) 282 } 283 if tmpDir[1] != ':' { 284 t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir) 285 } 286 287 dirs := []string{ 288 "a", 289 "b", 290 "dir/d/bin", 291 } 292 files := []string{ 293 "dir/d/bin/git.exe", 294 } 295 for _, dir := range dirs { 296 err := os.MkdirAll(Join(tmpDir, dir), 0777) 297 if err != nil { 298 t.Fatal(err) 299 } 300 } 301 for _, file := range files { 302 err := ioutil.WriteFile(Join(tmpDir, file), nil, 0666) 303 if err != nil { 304 t.Fatal(err) 305 } 306 } 307 308 tests := []globTest{ 309 {"a", []string{"a"}}, 310 {"b", []string{"b"}}, 311 {"c", []string{}}, 312 {"*", []string{"a", "b", "dir"}}, 313 {"d*", []string{"dir"}}, 314 {"*i*", []string{"dir"}}, 315 {"*r", []string{"dir"}}, 316 {"?ir", []string{"dir"}}, 317 {"?r", []string{}}, 318 {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}}, 319 } 320 321 // test absolute paths 322 for _, test := range tests { 323 var p string 324 err = test.globAbs(tmpDir, tmpDir) 325 if err != nil { 326 t.Error(err) 327 } 328 // test C:\*Documents and Settings\... 329 p = tmpDir 330 p = strings.Replace(p, `:\`, `:\*`, 1) 331 err = test.globAbs(tmpDir, p) 332 if err != nil { 333 t.Error(err) 334 } 335 // test C:\Documents and Settings*\... 336 p = tmpDir 337 p = strings.Replace(p, `:\`, `:`, 1) 338 p = strings.Replace(p, `\`, `*\`, 1) 339 p = strings.Replace(p, `:`, `:\`, 1) 340 err = test.globAbs(tmpDir, p) 341 if err != nil { 342 t.Error(err) 343 } 344 } 345 346 // test relative paths 347 wd, err := os.Getwd() 348 if err != nil { 349 t.Fatal(err) 350 } 351 err = os.Chdir(tmpDir) 352 if err != nil { 353 t.Fatal(err) 354 } 355 defer func() { 356 err := os.Chdir(wd) 357 if err != nil { 358 t.Fatal(err) 359 } 360 }() 361 for _, test := range tests { 362 err := test.globRel("") 363 if err != nil { 364 t.Error(err) 365 } 366 err = test.globRel(`.\`) 367 if err != nil { 368 t.Error(err) 369 } 370 err = test.globRel(tmpDir[:2]) // C: 371 if err != nil { 372 t.Error(err) 373 } 374 } 375} 376 377func TestNonWindowsGlobEscape(t *testing.T) { 378 if runtime.GOOS == "windows" { 379 t.Skipf("skipping non-windows specific test") 380 } 381 pattern := `\match.go` 382 want := []string{"match.go"} 383 matches, err := Glob(pattern) 384 if err != nil { 385 t.Fatalf("Glob error for %q: %s", pattern, err) 386 } 387 if !reflect.DeepEqual(matches, want) { 388 t.Fatalf("Glob(%#q) = %v want %v", pattern, matches, want) 389 } 390} 391