1
2
3
4
5 package filepath_test
6
7 import (
8 "fmt"
9 "internal/testenv"
10 "os"
11 . "path/filepath"
12 "runtime"
13 "slices"
14 "strings"
15 "testing"
16 )
17
18 type MatchTest struct {
19 pattern, s string
20 match bool
21 err error
22 }
23
24 var matchTests = []MatchTest{
25 {"abc", "abc", true, nil},
26 {"*", "abc", true, nil},
27 {"*c", "abc", true, nil},
28 {"a*", "a", true, nil},
29 {"a*", "abc", true, nil},
30 {"a*", "ab/c", false, nil},
31 {"a*/b", "abc/b", true, nil},
32 {"a*/b", "a/c/b", false, nil},
33 {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
34 {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
35 {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
36 {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
37 {"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
38 {"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
39 {"ab[c]", "abc", true, nil},
40 {"ab[b-d]", "abc", true, nil},
41 {"ab[e-g]", "abc", false, nil},
42 {"ab[^c]", "abc", false, nil},
43 {"ab[^b-d]", "abc", false, nil},
44 {"ab[^e-g]", "abc", true, nil},
45 {"a\\*b", "a*b", true, nil},
46 {"a\\*b", "ab", false, nil},
47 {"a?b", "a☺b", true, nil},
48 {"a[^a]b", "a☺b", true, nil},
49 {"a???b", "a☺b", false, nil},
50 {"a[^a][^a][^a]b", "a☺b", false, nil},
51 {"[a-ζ]*", "α", true, nil},
52 {"*[a-ζ]", "A", false, nil},
53 {"a?b", "a/b", false, nil},
54 {"a*b", "a/b", false, nil},
55 {"[\\]a]", "]", true, nil},
56 {"[\\-]", "-", true, nil},
57 {"[x\\-]", "x", true, nil},
58 {"[x\\-]", "-", true, nil},
59 {"[x\\-]", "z", false, nil},
60 {"[\\-x]", "x", true, nil},
61 {"[\\-x]", "-", true, nil},
62 {"[\\-x]", "a", false, nil},
63 {"[]a]", "]", false, ErrBadPattern},
64 {"[-]", "-", false, ErrBadPattern},
65 {"[x-]", "x", false, ErrBadPattern},
66 {"[x-]", "-", false, ErrBadPattern},
67 {"[x-]", "z", false, ErrBadPattern},
68 {"[-x]", "x", false, ErrBadPattern},
69 {"[-x]", "-", false, ErrBadPattern},
70 {"[-x]", "a", false, ErrBadPattern},
71 {"\\", "a", false, ErrBadPattern},
72 {"[a-b-c]", "a", false, ErrBadPattern},
73 {"[", "a", false, ErrBadPattern},
74 {"[^", "a", false, ErrBadPattern},
75 {"[^bc", "a", false, ErrBadPattern},
76 {"a[", "a", false, ErrBadPattern},
77 {"a[", "ab", false, ErrBadPattern},
78 {"a[", "x", false, ErrBadPattern},
79 {"a/b[", "x", 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
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 var globTests = []struct {
110 pattern, result string
111 }{
112 {"match.go", "match.go"},
113 {"mat?h.go", "match.go"},
114 {"*", "match.go"},
115 {"../*/match.go", "../filepath/match.go"},
116 }
117
118 func TestGlob(t *testing.T) {
119 for _, tt := range globTests {
120 pattern := tt.pattern
121 result := tt.result
122 if runtime.GOOS == "windows" {
123 pattern = Clean(pattern)
124 result = Clean(result)
125 }
126 matches, err := Glob(pattern)
127 if err != nil {
128 t.Errorf("Glob error for %q: %s", pattern, err)
129 continue
130 }
131 if !slices.Contains(matches, result) {
132 t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result)
133 }
134 }
135 for _, pattern := range []string{"no_match", "../*/no_match"} {
136 matches, err := Glob(pattern)
137 if err != nil {
138 t.Errorf("Glob error for %q: %s", pattern, err)
139 continue
140 }
141 if len(matches) != 0 {
142 t.Errorf("Glob(%#q) = %#v want []", pattern, matches)
143 }
144 }
145 }
146
147 func TestCVE202230632(t *testing.T) {
148
149
150
151 _, err := Glob("/*" + strings.Repeat("/", 10001))
152 if err != ErrBadPattern {
153 t.Fatalf("Glob returned err=%v, want ErrBadPattern", err)
154 }
155 }
156
157 func TestGlobError(t *testing.T) {
158 bad := []string{`[]`, `nonexist/[]`}
159 for _, pattern := range bad {
160 if _, err := Glob(pattern); err != ErrBadPattern {
161 t.Errorf("Glob(%#q) returned err=%v, want ErrBadPattern", pattern, err)
162 }
163 }
164 }
165
166 func TestGlobUNC(t *testing.T) {
167
168
169 Glob(`\\?\C:\*`)
170 }
171
172 var globSymlinkTests = []struct {
173 path, dest string
174 brokenLink bool
175 }{
176 {"test1", "link1", false},
177 {"test2", "link2", true},
178 }
179
180 func TestGlobSymlink(t *testing.T) {
181 testenv.MustHaveSymlink(t)
182
183 tmpDir := t.TempDir()
184 for _, tt := range globSymlinkTests {
185 path := Join(tmpDir, tt.path)
186 dest := Join(tmpDir, tt.dest)
187 f, err := os.Create(path)
188 if err != nil {
189 t.Fatal(err)
190 }
191 if err := f.Close(); err != nil {
192 t.Fatal(err)
193 }
194 err = os.Symlink(path, dest)
195 if err != nil {
196 t.Fatal(err)
197 }
198 if tt.brokenLink {
199
200 os.Remove(path)
201 }
202 matches, err := Glob(dest)
203 if err != nil {
204 t.Errorf("GlobSymlink error for %q: %s", dest, err)
205 }
206 if !slices.Contains(matches, dest) {
207 t.Errorf("Glob(%#q) = %#v want %v", dest, matches, dest)
208 }
209 }
210 }
211
212 type globTest struct {
213 pattern string
214 matches []string
215 }
216
217 func (test *globTest) buildWant(root string) []string {
218 want := make([]string, 0)
219 for _, m := range test.matches {
220 want = append(want, root+FromSlash(m))
221 }
222 slices.Sort(want)
223 return want
224 }
225
226 func (test *globTest) globAbs(root, rootPattern string) error {
227 p := FromSlash(rootPattern + `\` + test.pattern)
228 have, err := Glob(p)
229 if err != nil {
230 return err
231 }
232 slices.Sort(have)
233 want := test.buildWant(root + `\`)
234 if strings.Join(want, "_") == strings.Join(have, "_") {
235 return nil
236 }
237 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want)
238 }
239
240 func (test *globTest) globRel(root string) error {
241 p := root + FromSlash(test.pattern)
242 have, err := Glob(p)
243 if err != nil {
244 return err
245 }
246 slices.Sort(have)
247 want := test.buildWant(root)
248 if strings.Join(want, "_") == strings.Join(have, "_") {
249 return nil
250 }
251
252 wantWithNoRoot := test.buildWant("")
253 if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") {
254 return nil
255 }
256 return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want)
257 }
258
259 func TestWindowsGlob(t *testing.T) {
260 if runtime.GOOS != "windows" {
261 t.Skipf("skipping windows specific test")
262 }
263
264 tmpDir := tempDirCanonical(t)
265 if len(tmpDir) < 3 {
266 t.Fatalf("tmpDir path %q is too short", tmpDir)
267 }
268 if tmpDir[1] != ':' {
269 t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir)
270 }
271
272 dirs := []string{
273 "a",
274 "b",
275 "dir/d/bin",
276 }
277 files := []string{
278 "dir/d/bin/git.exe",
279 }
280 for _, dir := range dirs {
281 err := os.MkdirAll(Join(tmpDir, dir), 0777)
282 if err != nil {
283 t.Fatal(err)
284 }
285 }
286 for _, file := range files {
287 err := os.WriteFile(Join(tmpDir, file), nil, 0666)
288 if err != nil {
289 t.Fatal(err)
290 }
291 }
292
293 tests := []globTest{
294 {"a", []string{"a"}},
295 {"b", []string{"b"}},
296 {"c", []string{}},
297 {"*", []string{"a", "b", "dir"}},
298 {"d*", []string{"dir"}},
299 {"*i*", []string{"dir"}},
300 {"*r", []string{"dir"}},
301 {"?ir", []string{"dir"}},
302 {"?r", []string{}},
303 {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}},
304 }
305
306
307 for _, test := range tests {
308 var p string
309 if err := test.globAbs(tmpDir, tmpDir); err != nil {
310 t.Error(err)
311 }
312
313 p = tmpDir
314 p = strings.Replace(p, `:\`, `:\*`, 1)
315 if err := test.globAbs(tmpDir, p); err != nil {
316 t.Error(err)
317 }
318
319 p = tmpDir
320 p = strings.Replace(p, `:\`, `:`, 1)
321 p = strings.Replace(p, `\`, `*\`, 1)
322 p = strings.Replace(p, `:`, `:\`, 1)
323 if err := test.globAbs(tmpDir, p); err != nil {
324 t.Error(err)
325 }
326 }
327
328
329 t.Chdir(tmpDir)
330 for _, test := range tests {
331 err := test.globRel("")
332 if err != nil {
333 t.Error(err)
334 }
335 err = test.globRel(`.\`)
336 if err != nil {
337 t.Error(err)
338 }
339 err = test.globRel(tmpDir[:2])
340 if err != nil {
341 t.Error(err)
342 }
343 }
344 }
345
346 func TestNonWindowsGlobEscape(t *testing.T) {
347 if runtime.GOOS == "windows" {
348 t.Skipf("skipping non-windows specific test")
349 }
350 pattern := `\match.go`
351 want := []string{"match.go"}
352 matches, err := Glob(pattern)
353 if err != nil {
354 t.Fatalf("Glob error for %q: %s", pattern, err)
355 }
356 if !slices.Equal(matches, want) {
357 t.Fatalf("Glob(%#q) = %v want %v", pattern, matches, want)
358 }
359 }
360
View as plain text