github.com/philippgille/go@v0.0.0-20180418191957-236c567ba9b7/src/path/filepath/match_test.go (about) 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 5 package filepath_test 6 7 import ( 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 20 type MatchTest struct { 21 pattern, s string 22 match bool 23 err error 24 } 25 26 var 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 83 func errp(e error) string { 84 if e == nil { 85 return "<nil>" 86 } 87 return e.Error() 88 } 89 90 func 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 returns true if vector contains the string s. 110 func 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 119 var globTests = []struct { 120 pattern, result string 121 }{ 122 {"match.go", "match.go"}, 123 {"mat?h.go", "match.go"}, 124 {"*", "match.go"}, 125 {"../*/match.go", "../filepath/match.go"}, 126 } 127 128 func TestGlob(t *testing.T) { 129 for _, tt := range globTests { 130 pattern := tt.pattern 131 result := tt.result 132 if runtime.GOOS == "windows" { 133 pattern = Clean(pattern) 134 result = Clean(result) 135 } 136 matches, err := Glob(pattern) 137 if err != nil { 138 t.Errorf("Glob error for %q: %s", pattern, err) 139 continue 140 } 141 if !contains(matches, result) { 142 t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result) 143 } 144 } 145 for _, pattern := range []string{"no_match", "../*/no_match"} { 146 matches, err := Glob(pattern) 147 if err != nil { 148 t.Errorf("Glob error for %q: %s", pattern, err) 149 continue 150 } 151 if len(matches) != 0 { 152 t.Errorf("Glob(%#q) = %#v want []", pattern, matches) 153 } 154 } 155 } 156 157 func TestGlobError(t *testing.T) { 158 _, err := Glob("[]") 159 if err == nil { 160 t.Error("expected error for bad pattern; got none") 161 } 162 } 163 164 func TestGlobUNC(t *testing.T) { 165 // Just make sure this runs without crashing for now. 166 // See issue 15879. 167 Glob(`\\?\C:\*`) 168 } 169 170 var globSymlinkTests = []struct { 171 path, dest string 172 brokenLink bool 173 }{ 174 {"test1", "link1", false}, 175 {"test2", "link2", true}, 176 } 177 178 func TestGlobSymlink(t *testing.T) { 179 testenv.MustHaveSymlink(t) 180 181 tmpDir, err := ioutil.TempDir("", "globsymlink") 182 if err != nil { 183 t.Fatal("creating temp dir:", err) 184 } 185 defer os.RemoveAll(tmpDir) 186 187 for _, tt := range globSymlinkTests { 188 path := Join(tmpDir, tt.path) 189 dest := Join(tmpDir, tt.dest) 190 f, err := os.Create(path) 191 if err != nil { 192 t.Fatal(err) 193 } 194 if err := f.Close(); err != nil { 195 t.Fatal(err) 196 } 197 err = os.Symlink(path, dest) 198 if err != nil { 199 t.Fatal(err) 200 } 201 if tt.brokenLink { 202 // Break the symlink. 203 os.Remove(path) 204 } 205 matches, err := Glob(dest) 206 if err != nil { 207 t.Errorf("GlobSymlink error for %q: %s", dest, err) 208 } 209 if !contains(matches, dest) { 210 t.Errorf("Glob(%#q) = %#v want %v", dest, matches, dest) 211 } 212 } 213 } 214 215 type globTest struct { 216 pattern string 217 matches []string 218 } 219 220 func (test *globTest) buildWant(root string) []string { 221 want := make([]string, 0) 222 for _, m := range test.matches { 223 want = append(want, root+FromSlash(m)) 224 } 225 sort.Strings(want) 226 return want 227 } 228 229 func (test *globTest) globAbs(root, rootPattern string) error { 230 p := FromSlash(rootPattern + `\` + test.pattern) 231 have, err := Glob(p) 232 if err != nil { 233 return err 234 } 235 sort.Strings(have) 236 want := test.buildWant(root + `\`) 237 if strings.Join(want, "_") == strings.Join(have, "_") { 238 return nil 239 } 240 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) 241 } 242 243 func (test *globTest) globRel(root string) error { 244 p := root + FromSlash(test.pattern) 245 have, err := Glob(p) 246 if err != nil { 247 return err 248 } 249 sort.Strings(have) 250 want := test.buildWant(root) 251 if strings.Join(want, "_") == strings.Join(have, "_") { 252 return nil 253 } 254 // try also matching version without root prefix 255 wantWithNoRoot := test.buildWant("") 256 if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") { 257 return nil 258 } 259 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) 260 } 261 262 func TestWindowsGlob(t *testing.T) { 263 if runtime.GOOS != "windows" { 264 t.Skipf("skipping windows specific test") 265 } 266 267 tmpDir, err := ioutil.TempDir("", "TestWindowsGlob") 268 if err != nil { 269 t.Fatal(err) 270 } 271 defer os.RemoveAll(tmpDir) 272 273 // /tmp may itself be a symlink 274 tmpDir, err = EvalSymlinks(tmpDir) 275 if err != nil { 276 t.Fatal("eval symlink for tmp dir:", err) 277 } 278 279 if len(tmpDir) < 3 { 280 t.Fatalf("tmpDir path %q is too short", tmpDir) 281 } 282 if tmpDir[1] != ':' { 283 t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir) 284 } 285 286 dirs := []string{ 287 "a", 288 "b", 289 "dir/d/bin", 290 } 291 files := []string{ 292 "dir/d/bin/git.exe", 293 } 294 for _, dir := range dirs { 295 err := os.MkdirAll(Join(tmpDir, dir), 0777) 296 if err != nil { 297 t.Fatal(err) 298 } 299 } 300 for _, file := range files { 301 err := ioutil.WriteFile(Join(tmpDir, file), nil, 0666) 302 if err != nil { 303 t.Fatal(err) 304 } 305 } 306 307 tests := []globTest{ 308 {"a", []string{"a"}}, 309 {"b", []string{"b"}}, 310 {"c", []string{}}, 311 {"*", []string{"a", "b", "dir"}}, 312 {"d*", []string{"dir"}}, 313 {"*i*", []string{"dir"}}, 314 {"*r", []string{"dir"}}, 315 {"?ir", []string{"dir"}}, 316 {"?r", []string{}}, 317 {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}}, 318 } 319 320 // test absolute paths 321 for _, test := range tests { 322 var p string 323 err = test.globAbs(tmpDir, tmpDir) 324 if err != nil { 325 t.Error(err) 326 } 327 // test C:\*Documents and Settings\... 328 p = tmpDir 329 p = strings.Replace(p, `:\`, `:\*`, 1) 330 err = test.globAbs(tmpDir, p) 331 if err != nil { 332 t.Error(err) 333 } 334 // test C:\Documents and Settings*\... 335 p = tmpDir 336 p = strings.Replace(p, `:\`, `:`, 1) 337 p = strings.Replace(p, `\`, `*\`, 1) 338 p = strings.Replace(p, `:`, `:\`, 1) 339 err = test.globAbs(tmpDir, p) 340 if err != nil { 341 t.Error(err) 342 } 343 } 344 345 // test relative paths 346 wd, err := os.Getwd() 347 if err != nil { 348 t.Fatal(err) 349 } 350 err = os.Chdir(tmpDir) 351 if err != nil { 352 t.Fatal(err) 353 } 354 defer func() { 355 err := os.Chdir(wd) 356 if err != nil { 357 t.Fatal(err) 358 } 359 }() 360 for _, test := range tests { 361 err := test.globRel("") 362 if err != nil { 363 t.Error(err) 364 } 365 err = test.globRel(`.\`) 366 if err != nil { 367 t.Error(err) 368 } 369 err = test.globRel(tmpDir[:2]) // C: 370 if err != nil { 371 t.Error(err) 372 } 373 } 374 } 375 376 func TestNonWindowsGlobEscape(t *testing.T) { 377 if runtime.GOOS == "windows" { 378 t.Skipf("skipping non-windows specific test") 379 } 380 pattern := `\match.go` 381 want := []string{"match.go"} 382 matches, err := Glob(pattern) 383 if err != nil { 384 t.Fatalf("Glob error for %q: %s", pattern, err) 385 } 386 if !reflect.DeepEqual(matches, want) { 387 t.Fatalf("Glob(%#q) = %v want %v", pattern, matches, want) 388 } 389 }