1
2
3
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
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
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
160
161
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
179
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
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
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
318 for _, test := range tests {
319 var p string
320 if err := test.globAbs(tmpDir, tmpDir); err != nil {
321 t.Error(err)
322 }
323
324 p = tmpDir
325 p = strings.Replace(p, `:\`, `:\*`, 1)
326 if err := test.globAbs(tmpDir, p); err != nil {
327 t.Error(err)
328 }
329
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
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])
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 }
384
View as plain text