• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package pathtools
16
17import (
18	"os"
19	"path/filepath"
20	"reflect"
21	"strings"
22	"testing"
23)
24
25var pwd, _ = os.Getwd()
26
27type globTestCase struct {
28	pattern  string
29	matches  []string
30	excludes []string
31	deps     []string
32	err      error
33}
34
35var globTestCases = []globTestCase{
36	// Current directory tests
37	{
38		pattern: "*",
39		matches: []string{"a/", "b/", "c/", "d.ext", "e.ext"},
40		deps:    []string{"."},
41	},
42	{
43		pattern: "*.ext",
44		matches: []string{"d.ext", "e.ext"},
45		deps:    []string{"."},
46	},
47	{
48		pattern: "*/a",
49		matches: []string{"a/a/", "b/a"},
50		deps:    []string{".", "a", "b", "c"},
51	},
52	{
53		pattern: "*/*/a",
54		matches: []string{"a/a/a"},
55		deps:    []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"},
56	},
57	{
58		pattern: "*/a/a",
59		matches: []string{"a/a/a"},
60		deps:    []string{".", "a", "b", "c", "a/a"},
61	},
62	{
63		pattern: "c/*/?",
64		matches: []string{"c/h/h"},
65		deps:    []string{"c", "c/f", "c/g", "c/h"},
66	},
67	{
68		pattern: "c/*/[gh]*",
69		matches: []string{"c/g/g.ext", "c/h/h"},
70		deps:    []string{"c", "c/f", "c/g", "c/h"},
71	},
72	{
73		pattern: "c/*/[fgh]*",
74		matches: []string{"c/f/f.ext", "c/g/g.ext", "c/h/h"},
75		deps:    []string{"c", "c/f", "c/g", "c/h"},
76	},
77	{
78		pattern: "c/*/[f-h]*",
79		matches: []string{"c/f/f.ext", "c/g/g.ext", "c/h/h"},
80		deps:    []string{"c", "c/f", "c/g", "c/h"},
81	},
82	// ./ directory tests
83	{
84		pattern: "./*",
85		matches: []string{"a/", "b/", "c/", "d.ext", "e.ext"},
86		deps:    []string{"."},
87	},
88	{
89		pattern: "./*.ext",
90		matches: []string{"d.ext", "e.ext"},
91		deps:    []string{"."},
92	},
93	{
94		pattern: "./*/a",
95		matches: []string{"a/a/", "b/a"},
96		deps:    []string{".", "a", "b", "c"},
97	},
98	{
99		pattern: "./[ac]/a",
100		matches: []string{"a/a/"},
101		deps:    []string{".", "a", "c"},
102	},
103
104	// subdirectory tests
105	{
106		pattern: "c/*/*.ext",
107		matches: []string{"c/f/f.ext", "c/g/g.ext"},
108		deps:    []string{"c", "c/f", "c/g", "c/h"},
109	},
110	{
111		pattern: "a/*/a",
112		matches: []string{"a/a/a"},
113		deps:    []string{"a", "a/a", "a/b"},
114	},
115
116	// absolute tests
117	{
118		pattern: filepath.Join(pwd, "testdata/glob/c/*/*.ext"),
119		matches: []string{
120			filepath.Join(pwd, "testdata/glob/c/f/f.ext"),
121			filepath.Join(pwd, "testdata/glob/c/g/g.ext"),
122		},
123		deps: []string{
124			filepath.Join(pwd, "testdata/glob/c"),
125			filepath.Join(pwd, "testdata/glob/c/f"),
126			filepath.Join(pwd, "testdata/glob/c/g"),
127			filepath.Join(pwd, "testdata/glob/c/h"),
128		},
129	},
130
131	// no-wild tests
132	{
133		pattern: "a",
134		matches: []string{"a/"},
135		deps:    []string{"a"},
136	},
137	{
138		pattern: "a/a",
139		matches: []string{"a/a/"},
140		deps:    []string{"a/a"},
141	},
142
143	// clean tests
144	{
145		pattern: "./c/*/*.ext",
146		matches: []string{"c/f/f.ext", "c/g/g.ext"},
147		deps:    []string{"c", "c/f", "c/g", "c/h"},
148	},
149	{
150		pattern: "c/../c/*/*.ext",
151		matches: []string{"c/f/f.ext", "c/g/g.ext"},
152		deps:    []string{"c", "c/f", "c/g", "c/h"},
153	},
154
155	// recursive tests
156	{
157		pattern: "**/a",
158		matches: []string{"a/", "a/a/", "a/a/a", "b/a"},
159		deps:    []string{".", "a", "a/a", "a/b", "b", "c", "c/f", "c/g", "c/h"},
160	},
161	{
162		pattern: "a/**/a",
163		matches: []string{"a/a/", "a/a/a"},
164		deps:    []string{"a", "a/a", "a/b"},
165	},
166	{
167		pattern: "a/**/*",
168		matches: []string{"a/a/", "a/b/", "a/a/a", "a/b/b"},
169		deps:    []string{"a", "a/a", "a/b"},
170	},
171
172	// absolute recursive tests
173	{
174		pattern: filepath.Join(pwd, "testdata/glob/**/*.ext"),
175		matches: []string{
176			filepath.Join(pwd, "testdata/glob/d.ext"),
177			filepath.Join(pwd, "testdata/glob/e.ext"),
178			filepath.Join(pwd, "testdata/glob/c/f/f.ext"),
179			filepath.Join(pwd, "testdata/glob/c/g/g.ext"),
180		},
181		deps: []string{
182			filepath.Join(pwd, "testdata/glob"),
183			filepath.Join(pwd, "testdata/glob/a"),
184			filepath.Join(pwd, "testdata/glob/a/a"),
185			filepath.Join(pwd, "testdata/glob/a/b"),
186			filepath.Join(pwd, "testdata/glob/b"),
187			filepath.Join(pwd, "testdata/glob/c"),
188			filepath.Join(pwd, "testdata/glob/c/f"),
189			filepath.Join(pwd, "testdata/glob/c/g"),
190			filepath.Join(pwd, "testdata/glob/c/h"),
191		},
192	},
193
194	// recursive error tests
195	{
196		pattern: "**/**/*",
197		err:     GlobMultipleRecursiveErr,
198	},
199	{
200		pattern: "a/**/**/*",
201		err:     GlobMultipleRecursiveErr,
202	},
203	{
204		pattern: "**/a/**/*",
205		err:     GlobMultipleRecursiveErr,
206	},
207	{
208		pattern: "**/**/a/*",
209		err:     GlobMultipleRecursiveErr,
210	},
211	{
212		pattern: "a/**",
213		err:     GlobLastRecursiveErr,
214	},
215	{
216		pattern: "**/**",
217		err:     GlobLastRecursiveErr,
218	},
219	{
220		pattern: "a**/",
221		err:     GlobInvalidRecursiveErr,
222	},
223	{
224		pattern: "**a/",
225		err:     GlobInvalidRecursiveErr,
226	},
227
228	// exclude tests
229	{
230		pattern:  "*.ext",
231		excludes: []string{"d.ext"},
232		matches:  []string{"e.ext"},
233		deps:     []string{"."},
234	},
235	{
236		pattern:  "*/*",
237		excludes: []string{"a/b"},
238		matches:  []string{"a/a/", "b/a", "c/c", "c/f/", "c/g/", "c/h/"},
239		deps:     []string{".", "a", "b", "c"},
240	},
241	{
242		pattern:  "*/*",
243		excludes: []string{"a/b", "c/c"},
244		matches:  []string{"a/a/", "b/a", "c/f/", "c/g/", "c/h/"},
245		deps:     []string{".", "a", "b", "c"},
246	},
247	{
248		pattern:  "*/*",
249		excludes: []string{"c/*", "*/a"},
250		matches:  []string{"a/b/"},
251		deps:     []string{".", "a", "b", "c"},
252	},
253	{
254		pattern:  "*/*",
255		excludes: []string{"*/*"},
256		matches:  nil,
257		deps:     []string{".", "a", "b", "c"},
258	},
259
260	// absolute exclude tests
261	{
262		pattern:  filepath.Join(pwd, "testdata/glob/c/*/*.ext"),
263		excludes: []string{filepath.Join(pwd, "testdata/glob/c/*/f.ext")},
264		matches: []string{
265			filepath.Join(pwd, "testdata/glob/c/g/g.ext"),
266		},
267		deps: []string{
268			filepath.Join(pwd, "testdata/glob/c"),
269			filepath.Join(pwd, "testdata/glob/c/f"),
270			filepath.Join(pwd, "testdata/glob/c/g"),
271			filepath.Join(pwd, "testdata/glob/c/h"),
272		},
273	},
274	{
275		pattern:  filepath.Join(pwd, "testdata/glob/c/*/*.ext"),
276		excludes: []string{filepath.Join(pwd, "testdata/glob/c/f/*.ext")},
277		matches: []string{
278			filepath.Join(pwd, "testdata/glob/c/g/g.ext"),
279		},
280		deps: []string{
281			filepath.Join(pwd, "testdata/glob/c"),
282			filepath.Join(pwd, "testdata/glob/c/f"),
283			filepath.Join(pwd, "testdata/glob/c/g"),
284			filepath.Join(pwd, "testdata/glob/c/h"),
285		},
286	},
287
288	// recursive exclude tests
289	{
290		pattern:  "*.ext",
291		excludes: []string{"**/*.ext"},
292		matches:  nil,
293		deps:     []string{"."},
294	},
295	{
296		pattern:  "*/*",
297		excludes: []string{"**/b"},
298		matches:  []string{"a/a/", "b/a", "c/c", "c/f/", "c/g/", "c/h/"},
299		deps:     []string{".", "a", "b", "c"},
300	},
301	{
302		pattern:  "*/*",
303		excludes: []string{"a/**/*"},
304		matches:  []string{"b/a", "c/c", "c/f/", "c/g/", "c/h/"},
305		deps:     []string{".", "a", "b", "c"},
306	},
307	{
308		pattern:  "**/*",
309		excludes: []string{"**/*"},
310		matches:  nil,
311		deps:     []string{".", "a", "a/a", "a/b", "b", "c", "c/f", "c/g", "c/h"},
312	},
313	{
314		pattern:  "*/*/*",
315		excludes: []string{"a/**/a"},
316		matches:  []string{"a/b/b", "c/f/f.ext", "c/g/g.ext", "c/h/h"},
317		deps:     []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"},
318	},
319	{
320		pattern:  "*/*/*",
321		excludes: []string{"**/a"},
322		matches:  []string{"a/b/b", "c/f/f.ext", "c/g/g.ext", "c/h/h"},
323		deps:     []string{".", "a", "b", "c", "a/a", "a/b", "c/f", "c/g", "c/h"},
324	},
325	{
326		pattern:  "c/*/*.ext",
327		excludes: []string{"c/**/f.ext"},
328		matches:  []string{"c/g/g.ext"},
329		deps:     []string{"c", "c/f", "c/g", "c/h"},
330	},
331
332	// absoulte recursive exclude tests
333	{
334		pattern:  filepath.Join(pwd, "testdata/glob/c/*/*.ext"),
335		excludes: []string{filepath.Join(pwd, "testdata/glob/**/f.ext")},
336		matches: []string{
337			filepath.Join(pwd, "testdata/glob/c/g/g.ext"),
338		},
339		deps: []string{
340			filepath.Join(pwd, "testdata/glob/c"),
341			filepath.Join(pwd, "testdata/glob/c/f"),
342			filepath.Join(pwd, "testdata/glob/c/g"),
343			filepath.Join(pwd, "testdata/glob/c/h"),
344		},
345	},
346
347	// clean exclude tests
348	{
349		pattern:  "./c/*/*.ext",
350		excludes: []string{"./c/*/f.ext"},
351		matches:  []string{"c/g/g.ext"},
352		deps:     []string{"c", "c/f", "c/g", "c/h"},
353	},
354	{
355		pattern:  "c/*/*.ext",
356		excludes: []string{"./c/*/f.ext"},
357		matches:  []string{"c/g/g.ext"},
358		deps:     []string{"c", "c/f", "c/g", "c/h"},
359	},
360	{
361		pattern:  "./c/*/*.ext",
362		excludes: []string{"c/*/f.ext"},
363		matches:  []string{"c/g/g.ext"},
364		deps:     []string{"c", "c/f", "c/g", "c/h"},
365	},
366
367	// non-existant non-wild path tests
368	{
369		pattern: "d/*",
370		matches: nil,
371		deps:    []string{"."},
372	},
373	{
374		pattern: "d",
375		matches: nil,
376		deps:    []string{"."},
377	},
378	{
379		pattern: "a/d/*",
380		matches: nil,
381		deps:    []string{"a"},
382	},
383	{
384		pattern: "a/d",
385		matches: nil,
386		deps:    []string{"a"},
387	},
388	{
389		pattern: "a/a/d/*",
390		matches: nil,
391		deps:    []string{"a/a"},
392	},
393	{
394		pattern: "a/a/d",
395		matches: nil,
396		deps:    []string{"a/a"},
397	},
398	{
399		pattern: "a/d/a/*",
400		matches: nil,
401		deps:    []string{"a"},
402	},
403	{
404		pattern: "a/d/a",
405		matches: nil,
406		deps:    []string{"a"},
407	},
408	{
409		pattern: "a/d/a/*/a",
410		matches: nil,
411		deps:    []string{"a"},
412	},
413	{
414		pattern: "a/d/a/**/a",
415		matches: nil,
416		deps:    []string{"a"},
417	},
418
419	// recursive exclude error tests
420	{
421		pattern:  "**/*",
422		excludes: []string{"**/**/*"},
423		err:      GlobMultipleRecursiveErr,
424	},
425	{
426		pattern:  "**/*",
427		excludes: []string{"a/**/**/*"},
428		err:      GlobMultipleRecursiveErr,
429	},
430	{
431		pattern:  "**/*",
432		excludes: []string{"**/a/**/*"},
433		err:      GlobMultipleRecursiveErr,
434	},
435	{
436		pattern:  "**/*",
437		excludes: []string{"**/**/a/*"},
438		err:      GlobMultipleRecursiveErr,
439	},
440	{
441		pattern:  "**/*",
442		excludes: []string{"a/**"},
443		err:      GlobLastRecursiveErr,
444	},
445	{
446		pattern:  "**/*",
447		excludes: []string{"**/**"},
448		err:      GlobLastRecursiveErr,
449	},
450
451	// If names are excluded by default, but referenced explicitly, they should return results
452	{
453		pattern: ".test/*",
454		matches: []string{".test/a"},
455		deps:    []string{".test"},
456	},
457	{
458		pattern: ".t*/a",
459		matches: []string{".test/a"},
460		deps:    []string{".", ".test"},
461	},
462	{
463		pattern: ".*/.*",
464		matches: []string{".test/.ing"},
465		deps:    []string{".", ".test"},
466	},
467	{
468		pattern: ".t*",
469		matches: []string{".test/", ".testing"},
470		deps:    []string{"."},
471	},
472}
473
474func TestMockGlob(t *testing.T) {
475	files := []string{
476		"a/a/a",
477		"a/b/b",
478		"b/a",
479		"c/c",
480		"c/f/f.ext",
481		"c/g/g.ext",
482		"c/h/h",
483		"d.ext",
484		"e.ext",
485		".test/a",
486		".testing",
487		".test/.ing",
488	}
489
490	mockFiles := make(map[string][]byte)
491
492	for _, f := range files {
493		mockFiles[f] = nil
494		mockFiles[filepath.Join(pwd, "testdata/glob", f)] = nil
495	}
496
497	mock := MockFs(mockFiles)
498
499	for _, testCase := range globTestCases {
500		t.Run(testCase.pattern, func(t *testing.T) {
501			testGlob(t, mock, testCase, FollowSymlinks)
502		})
503	}
504}
505
506func TestGlob(t *testing.T) {
507	os.Chdir("testdata/glob")
508	defer os.Chdir("../..")
509	for _, testCase := range globTestCases {
510		t.Run(testCase.pattern, func(t *testing.T) {
511			testGlob(t, OsFs, testCase, FollowSymlinks)
512		})
513	}
514}
515
516var globEscapeTestCases = []globTestCase{
517	{
518		pattern: `**/*`,
519		matches: []string{`*`, `**/`, `?`, `a/`, `b`, `**/*`, `**/a`, `**/b/`, `**/b/b`, `a/a`},
520		deps:    []string{`.`, `**`, `**/b`, `a`},
521	},
522	{
523		pattern: `**/\*`,
524		matches: []string{`*`, `**/*`},
525		deps:    []string{`.`, `**`, `**/b`, `a`},
526	},
527	{
528		pattern: `\*\*/*`,
529		matches: []string{`**/*`, `**/a`, `**/b/`},
530		deps:    []string{`.`, `**`},
531	},
532	{
533		pattern: `\*\*/**/*`,
534		matches: []string{`**/*`, `**/a`, `**/b/`, `**/b/b`},
535		deps:    []string{`.`, `**`, `**/b`},
536	},
537}
538
539func TestMockGlobEscapes(t *testing.T) {
540	files := []string{
541		`*`,
542		`**/*`,
543		`**/a`,
544		`**/b/b`,
545		`?`,
546		`a/a`,
547		`b`,
548	}
549
550	mockFiles := make(map[string][]byte)
551
552	for _, f := range files {
553		mockFiles[f] = nil
554	}
555
556	mock := MockFs(mockFiles)
557
558	for _, testCase := range globEscapeTestCases {
559		t.Run(testCase.pattern, func(t *testing.T) {
560			testGlob(t, mock, testCase, FollowSymlinks)
561		})
562	}
563
564}
565
566func TestGlobEscapes(t *testing.T) {
567	os.Chdir("testdata/escapes")
568	defer os.Chdir("../..")
569	for _, testCase := range globEscapeTestCases {
570		t.Run(testCase.pattern, func(t *testing.T) {
571			testGlob(t, OsFs, testCase, FollowSymlinks)
572		})
573	}
574
575}
576
577var globSymlinkTestCases = []globTestCase{
578	{
579		pattern: `**/*`,
580		matches: []string{"a/", "b/", "c/", "d/", "e", "a/a/", "a/a/a", "b/a/", "b/a/a", "c/a", "d/a"},
581		deps:    []string{".", "a", "a/a", "b", "b/a", "c", "d"},
582	},
583	{
584		pattern: `b/**/*`,
585		matches: []string{"b/a/", "b/a/a"},
586		deps:    []string{"b", "b/a"},
587	},
588}
589
590func TestMockGlobSymlinks(t *testing.T) {
591	files := []string{
592		"a/a/a",
593		"b -> a",
594		"c -> a/a",
595		"d -> c",
596		"e -> a/a/a",
597	}
598
599	mockFiles := make(map[string][]byte)
600
601	for _, f := range files {
602		mockFiles[f] = nil
603	}
604
605	mock := MockFs(mockFiles)
606
607	for _, testCase := range globSymlinkTestCases {
608		t.Run(testCase.pattern, func(t *testing.T) {
609			testGlob(t, mock, testCase, FollowSymlinks)
610		})
611	}
612}
613
614func TestGlobSymlinks(t *testing.T) {
615	os.Chdir("testdata/symlinks")
616	defer os.Chdir("../..")
617
618	for _, testCase := range globSymlinkTestCases {
619		t.Run(testCase.pattern, func(t *testing.T) {
620			testGlob(t, OsFs, testCase, FollowSymlinks)
621		})
622	}
623}
624
625var globDontFollowSymlinkTestCases = []globTestCase{
626	{
627		pattern: `**/*`,
628		matches: []string{"a/", "b", "c", "d", "e", "a/a/", "a/a/a"},
629		deps:    []string{".", "a", "a/a"},
630	},
631	{
632		pattern: `b/**/*`,
633		matches: []string{"b/a/", "b/a/a"},
634		deps:    []string{"b", "b/a"},
635	},
636}
637
638func TestMockGlobDontFollowSymlinks(t *testing.T) {
639	files := []string{
640		"a/a/a",
641		"b -> a",
642		"c -> a/a",
643		"d -> c",
644		"e -> a/a/a",
645	}
646
647	mockFiles := make(map[string][]byte)
648
649	for _, f := range files {
650		mockFiles[f] = nil
651	}
652
653	mock := MockFs(mockFiles)
654
655	for _, testCase := range globDontFollowSymlinkTestCases {
656		t.Run(testCase.pattern, func(t *testing.T) {
657			testGlob(t, mock, testCase, DontFollowSymlinks)
658		})
659	}
660}
661
662func TestGlobDontFollowSymlinks(t *testing.T) {
663	os.Chdir("testdata/symlinks")
664	defer os.Chdir("../..")
665
666	for _, testCase := range globDontFollowSymlinkTestCases {
667		t.Run(testCase.pattern, func(t *testing.T) {
668			testGlob(t, OsFs, testCase, DontFollowSymlinks)
669		})
670	}
671}
672
673var globDontFollowDanglingSymlinkTestCases = []globTestCase{
674	{
675		pattern: `**/*`,
676		matches: []string{"a/", "b", "c", "d", "dangling", "e", "f", "a/a/", "a/a/a", "a/a/f"},
677		deps:    []string{".", "a", "a/a"},
678	},
679	{
680		pattern: `dangling`,
681		matches: []string{"dangling"},
682		deps:    []string{"dangling"},
683	},
684}
685
686func TestMockGlobDontFollowDanglingSymlinks(t *testing.T) {
687	files := []string{
688		"a/a/a",
689		"a/a/f -> ../../f",
690		"b -> a",
691		"c -> a/a",
692		"d -> c",
693		"e -> a/a/a",
694		"f",
695		"dangling -> missing",
696	}
697
698	mockFiles := make(map[string][]byte)
699
700	for _, f := range files {
701		mockFiles[f] = nil
702	}
703
704	mock := MockFs(mockFiles)
705
706	for _, testCase := range globDontFollowDanglingSymlinkTestCases {
707		t.Run(testCase.pattern, func(t *testing.T) {
708			testGlob(t, mock, testCase, DontFollowSymlinks)
709		})
710	}
711}
712
713func TestGlobDontFollowDanglingSymlinks(t *testing.T) {
714	os.Chdir("testdata/dangling")
715	defer os.Chdir("../..")
716
717	for _, testCase := range globDontFollowDanglingSymlinkTestCases {
718		t.Run(testCase.pattern, func(t *testing.T) {
719			testGlob(t, OsFs, testCase, DontFollowSymlinks)
720		})
721	}
722}
723
724var globFollowDanglingSymlinkTestCases = []globTestCase{
725	{
726		pattern: `**/*`,
727		matches: []string{"a/", "b/", "c/", "d/", "dangling", "e", "f", "a/a/", "a/a/a", "a/a/f", "b/a/", "b/a/a", "b/a/f", "c/a", "c/f", "d/a", "d/f"},
728		deps:    []string{".", "a", "a/a", "b", "b/a", "c", "d"},
729	},
730	{
731		pattern: `dangling`,
732		matches: []string{"dangling"},
733		deps:    []string{"dangling"},
734	},
735}
736
737func TestMockGlobFollowDanglingSymlinks(t *testing.T) {
738	files := []string{
739		"a/a/a",
740		"a/a/f -> ../../f",
741		"b -> a",
742		"c -> a/a",
743		"d -> c",
744		"e -> a/a/a",
745		"f",
746		"dangling -> missing",
747	}
748
749	mockFiles := make(map[string][]byte)
750
751	for _, f := range files {
752		mockFiles[f] = nil
753	}
754
755	mock := MockFs(mockFiles)
756
757	for _, testCase := range globFollowDanglingSymlinkTestCases {
758		t.Run(testCase.pattern, func(t *testing.T) {
759			testGlob(t, mock, testCase, FollowSymlinks)
760		})
761	}
762}
763
764func TestGlobFollowDanglingSymlinks(t *testing.T) {
765	os.Chdir("testdata/dangling")
766	defer os.Chdir("../..")
767
768	for _, testCase := range globFollowDanglingSymlinkTestCases {
769		t.Run(testCase.pattern, func(t *testing.T) {
770			testGlob(t, OsFs, testCase, FollowSymlinks)
771		})
772	}
773}
774
775func testGlob(t *testing.T, fs FileSystem, testCase globTestCase, follow ShouldFollowSymlinks) {
776	t.Helper()
777	result, err := fs.Glob(testCase.pattern, testCase.excludes, follow)
778	if err != testCase.err {
779		if err == nil {
780			t.Fatalf("missing error: %s", testCase.err)
781		} else {
782			t.Fatalf("error: %s", err)
783		}
784		return
785	}
786
787	if !reflect.DeepEqual(result.Matches, testCase.matches) {
788		t.Errorf("incorrect matches list:")
789		t.Errorf(" pattern: %q", testCase.pattern)
790		if testCase.excludes != nil {
791			t.Errorf("excludes: %q", testCase.excludes)
792		}
793		t.Errorf("     got: %#v", result.Matches)
794		t.Errorf("expected: %#v", testCase.matches)
795	}
796	if !reflect.DeepEqual(result.Deps, testCase.deps) {
797		t.Errorf("incorrect deps list:")
798		t.Errorf(" pattern: %q", testCase.pattern)
799		if testCase.excludes != nil {
800			t.Errorf("excludes: %q", testCase.excludes)
801		}
802		t.Errorf("     got: %#v", result.Deps)
803		t.Errorf("expected: %#v", testCase.deps)
804	}
805}
806
807func TestMatch(t *testing.T) {
808	testCases := []struct {
809		pattern, name string
810		match         bool
811	}{
812		{"a/*", "b/", false},
813		{"a/*", "b/a", false},
814		{"a/*", "b/b/", false},
815		{"a/*", "b/b/c", false},
816		{"a/**/*", "b/", false},
817		{"a/**/*", "b/a", false},
818		{"a/**/*", "b/b/", false},
819		{"a/**/*", "b/b/c", false},
820
821		{"a/*", "a/", false},
822		{"a/*", "a/a", true},
823		{"a/*", "a/b/", false},
824		{"a/*", "a/b/c", false},
825
826		{"a/*/", "a/", false},
827		{"a/*/", "a/a", false},
828		{"a/*/", "a/b/", true},
829		{"a/*/", "a/b/c", false},
830
831		{"a/**/*", "a/", false},
832		{"a/**/*", "a/a", true},
833		{"a/**/*", "a/b/", false},
834		{"a/**/*", "a/b/c", true},
835
836		{"a/**/*/", "a/", false},
837		{"a/**/*/", "a/a", false},
838		{"a/**/*/", "a/b/", true},
839		{"a/**/*/", "a/b/c", false},
840
841		{"**/*", "a/", false},
842		{"**/*", "a/a", true},
843		{"**/*", "a/b/", false},
844		{"**/*", "a/b/c", true},
845
846		{"**/*/", "a/", true},
847		{"**/*/", "a/a", false},
848		{"**/*/", "a/b/", true},
849		{"**/*/", "a/b/c", false},
850
851		{`a/\*\*/\*`, `a/**/*`, true},
852		{`a/\*\*/\*`, `a/a/*`, false},
853		{`a/\*\*/\*`, `a/**/a`, false},
854		{`a/\*\*/\*`, `a/a/a`, false},
855
856		{`a/**/\*`, `a/**/*`, true},
857		{`a/**/\*`, `a/a/*`, true},
858		{`a/**/\*`, `a/**/a`, false},
859		{`a/**/\*`, `a/a/a`, false},
860
861		{`a/\*\*/*`, `a/**/*`, true},
862		{`a/\*\*/*`, `a/a/*`, false},
863		{`a/\*\*/*`, `a/**/a`, true},
864		{`a/\*\*/*`, `a/a/a`, false},
865
866		{`*/**/a`, `a/a/a`, true},
867		{`*/**/a`, `*/a/a`, true},
868		{`*/**/a`, `a/**/a`, true},
869		{`*/**/a`, `*/**/a`, true},
870
871		{`\*/\*\*/a`, `a/a/a`, false},
872		{`\*/\*\*/a`, `*/a/a`, false},
873		{`\*/\*\*/a`, `a/**/a`, false},
874		{`\*/\*\*/a`, `*/**/a`, true},
875
876		{`a/?`, `a/?`, true},
877		{`a/?`, `a/a`, true},
878		{`a/\?`, `a/?`, true},
879		{`a/\?`, `a/a`, false},
880
881		{`a/?`, `a/?`, true},
882		{`a/?`, `a/a`, true},
883		{`a/\?`, `a/?`, true},
884		{`a/\?`, `a/a`, false},
885
886		{`a/[a-c]`, `a/b`, true},
887		{`a/[abc]`, `a/b`, true},
888
889		{`a/\[abc]`, `a/b`, false},
890		{`a/\[abc]`, `a/[abc]`, true},
891
892		{`a/\[abc\]`, `a/b`, false},
893		{`a/\[abc\]`, `a/[abc]`, true},
894
895		{`a/?`, `a/?`, true},
896		{`a/?`, `a/a`, true},
897		{`a/\?`, `a/?`, true},
898		{`a/\?`, `a/a`, false},
899
900		{"/a/*", "/a/", false},
901		{"/a/*", "/a/a", true},
902		{"/a/*", "/a/b/", false},
903		{"/a/*", "/a/b/c", false},
904
905		{"/a/*/", "/a/", false},
906		{"/a/*/", "/a/a", false},
907		{"/a/*/", "/a/b/", true},
908		{"/a/*/", "/a/b/c", false},
909
910		{"/a/**/*", "/a/", false},
911		{"/a/**/*", "/a/a", true},
912		{"/a/**/*", "/a/b/", false},
913		{"/a/**/*", "/a/b/c", true},
914
915		{"/**/*", "/a/", false},
916		{"/**/*", "/a/a", true},
917		{"/**/*", "/a/b/", false},
918		{"/**/*", "/a/b/c", true},
919
920		{"/**/*/", "/a/", true},
921		{"/**/*/", "/a/a", false},
922		{"/**/*/", "/a/b/", true},
923		{"/**/*/", "/a/b/c", false},
924
925		{`a`, `/a`, false},
926		{`/a`, `a`, false},
927		{`*`, `/a`, false},
928		{`/*`, `a`, false},
929		{`**/*`, `/a`, false},
930		{`/**/*`, `a`, false},
931	}
932
933	for _, test := range testCases {
934		t.Run(test.pattern+","+test.name, func(t *testing.T) {
935			match, err := Match(test.pattern, test.name)
936			if err != nil {
937				t.Fatal(err)
938			}
939			if match != test.match {
940				t.Errorf("want: %v, got %v", test.match, match)
941			}
942		})
943	}
944
945	// Run the same test cases through Glob
946	for _, test := range testCases {
947		// Glob and Match disagree on matching directories
948		if strings.HasSuffix(test.name, "/") || strings.HasSuffix(test.pattern, "/") {
949			continue
950		}
951		t.Run("glob:"+test.pattern+","+test.name, func(t *testing.T) {
952			mockFiles := map[string][]byte{
953				test.name: nil,
954			}
955
956			mock := MockFs(mockFiles)
957
958			result, err := mock.Glob(test.pattern, nil, DontFollowSymlinks)
959			t.Log(test.name, test.pattern, result.Matches)
960			if err != nil {
961				t.Fatal(err)
962			}
963
964			match := false
965			for _, x := range result.Matches {
966				if x == test.name {
967					match = true
968				}
969			}
970
971			if match != test.match {
972				t.Errorf("want: %v, got %v", test.match, match)
973			}
974		})
975	}
976}
977