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