blob: d896aebc9568a089cc8b55ddf237afda50e9c154 [file] [log] [blame]
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +00001// Copyright 2016 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
Ian Cottrell94b14832018-03-08 16:10:10 -05005package fastwalk_test
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +00006
7import (
8 "bytes"
9 "flag"
10 "fmt"
Ian Cottrell94b14832018-03-08 16:10:10 -050011 "io/ioutil"
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +000012 "os"
13 "path/filepath"
14 "reflect"
15 "runtime"
16 "sort"
17 "strings"
18 "sync"
19 "testing"
Ian Cottrell94b14832018-03-08 16:10:10 -050020
21 "golang.org/x/tools/internal/fastwalk"
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +000022)
23
24func formatFileModes(m map[string]os.FileMode) string {
25 var keys []string
26 for k := range m {
27 keys = append(keys, k)
28 }
29 sort.Strings(keys)
30 var buf bytes.Buffer
31 for _, k := range keys {
32 fmt.Fprintf(&buf, "%-20s: %v\n", k, m[k])
33 }
34 return buf.String()
35}
36
37func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) {
Ian Cottrell94b14832018-03-08 16:10:10 -050038 tempdir, err := ioutil.TempDir("", "test-fast-walk")
39 if err != nil {
40 t.Fatal(err)
41 }
42 defer os.RemoveAll(tempdir)
Bryan C. Mills2b542362020-05-19 00:33:09 -040043
44 symlinks := map[string]string{}
Ian Cottrell94b14832018-03-08 16:10:10 -050045 for path, contents := range files {
46 file := filepath.Join(tempdir, "/src", path)
47 if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
48 t.Fatal(err)
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +000049 }
Ian Cottrell94b14832018-03-08 16:10:10 -050050 var err error
51 if strings.HasPrefix(contents, "LINK:") {
Bryan C. Mills2b542362020-05-19 00:33:09 -040052 symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:"))
Ian Cottrell94b14832018-03-08 16:10:10 -050053 } else {
54 err = ioutil.WriteFile(file, []byte(contents), 0644)
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +000055 }
Ian Cottrell94b14832018-03-08 16:10:10 -050056 if err != nil {
57 t.Fatal(err)
58 }
59 }
Bryan C. Mills2b542362020-05-19 00:33:09 -040060
61 // Create symlinks after all other files. Otherwise, directory symlinks on
62 // Windows are unusable (see https://golang.org/issue/39183).
63 for file, dst := range symlinks {
64 err = os.Symlink(dst, file)
65 if err != nil {
66 if writeErr := ioutil.WriteFile(file, []byte(dst), 0644); writeErr == nil {
67 // Couldn't create symlink, but could write the file.
68 // Probably this filesystem doesn't support symlinks.
69 // (Perhaps we are on an older Windows and not running as administrator.)
70 t.Skipf("skipping because symlinks appear to be unsupported: %v", err)
71 }
72 }
73 }
74
Ian Cottrell94b14832018-03-08 16:10:10 -050075 got := map[string]os.FileMode{}
76 var mu sync.Mutex
Bryan C. Mills2b542362020-05-19 00:33:09 -040077 err = fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
Ian Cottrell94b14832018-03-08 16:10:10 -050078 mu.Lock()
79 defer mu.Unlock()
80 if !strings.HasPrefix(path, tempdir) {
Bryan C. Mills2b542362020-05-19 00:33:09 -040081 t.Errorf("bogus prefix on %q, expect %q", path, tempdir)
Ian Cottrell94b14832018-03-08 16:10:10 -050082 }
83 key := filepath.ToSlash(strings.TrimPrefix(path, tempdir))
84 if old, dup := got[key]; dup {
Bryan C. Mills2b542362020-05-19 00:33:09 -040085 t.Errorf("callback called twice for key %q: %v -> %v", key, old, typ)
Ian Cottrell94b14832018-03-08 16:10:10 -050086 }
87 got[key] = typ
88 return callback(path, typ)
Bryan C. Mills2b542362020-05-19 00:33:09 -040089 })
90
91 if err != nil {
Ian Cottrell94b14832018-03-08 16:10:10 -050092 t.Fatalf("callback returned: %v", err)
93 }
94 if !reflect.DeepEqual(got, want) {
95 t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want))
96 }
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +000097}
98
99func TestFastWalk_Basic(t *testing.T) {
100 testFastWalk(t, map[string]string{
101 "foo/foo.go": "one",
102 "bar/bar.go": "two",
103 "skip/skip.go": "skip",
104 },
105 func(path string, typ os.FileMode) error {
106 return nil
107 },
108 map[string]os.FileMode{
109 "": os.ModeDir,
110 "/src": os.ModeDir,
111 "/src/bar": os.ModeDir,
112 "/src/bar/bar.go": 0,
113 "/src/foo": os.ModeDir,
114 "/src/foo/foo.go": 0,
115 "/src/skip": os.ModeDir,
116 "/src/skip/skip.go": 0,
117 })
118}
119
MikoĊ‚aj Baranowski5f9a5412018-11-10 18:09:09 +0100120func TestFastWalk_LongFileName(t *testing.T) {
121 longFileName := strings.Repeat("x", 255)
122
123 testFastWalk(t, map[string]string{
124 longFileName: "one",
125 },
126 func(path string, typ os.FileMode) error {
127 return nil
128 },
129 map[string]os.FileMode{
130 "": os.ModeDir,
131 "/src": os.ModeDir,
132 "/src/" + longFileName: 0,
133 },
134 )
135}
136
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000137func TestFastWalk_Symlink(t *testing.T) {
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000138 testFastWalk(t, map[string]string{
Bryan C. Mills2b542362020-05-19 00:33:09 -0400139 "foo/foo.go": "one",
140 "bar/bar.go": "LINK:../foo/foo.go",
141 "symdir": "LINK:foo",
142 "broken/broken.go": "LINK:../nonexistent",
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000143 },
144 func(path string, typ os.FileMode) error {
145 return nil
146 },
147 map[string]os.FileMode{
Bryan C. Mills2b542362020-05-19 00:33:09 -0400148 "": os.ModeDir,
149 "/src": os.ModeDir,
150 "/src/bar": os.ModeDir,
151 "/src/bar/bar.go": os.ModeSymlink,
152 "/src/foo": os.ModeDir,
153 "/src/foo/foo.go": 0,
154 "/src/symdir": os.ModeSymlink,
155 "/src/broken": os.ModeDir,
156 "/src/broken/broken.go": os.ModeSymlink,
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000157 })
158}
159
160func TestFastWalk_SkipDir(t *testing.T) {
161 testFastWalk(t, map[string]string{
162 "foo/foo.go": "one",
163 "bar/bar.go": "two",
164 "skip/skip.go": "skip",
165 },
166 func(path string, typ os.FileMode) error {
167 if typ == os.ModeDir && strings.HasSuffix(path, "skip") {
168 return filepath.SkipDir
169 }
170 return nil
171 },
172 map[string]os.FileMode{
173 "": os.ModeDir,
174 "/src": os.ModeDir,
175 "/src/bar": os.ModeDir,
176 "/src/bar/bar.go": 0,
177 "/src/foo": os.ModeDir,
178 "/src/foo/foo.go": 0,
179 "/src/skip": os.ModeDir,
180 })
181}
182
Heschi Kreinick19e2aca2018-09-18 15:06:34 -0400183func TestFastWalk_SkipFiles(t *testing.T) {
184 // Directory iteration order is undefined, so there's no way to know
185 // which file to expect until the walk happens. Rather than mess
186 // with the test infrastructure, just mutate want.
187 var mu sync.Mutex
188 want := map[string]os.FileMode{
189 "": os.ModeDir,
190 "/src": os.ModeDir,
191 "/src/zzz": os.ModeDir,
192 "/src/zzz/c.go": 0,
193 }
194
195 testFastWalk(t, map[string]string{
196 "a_skipfiles.go": "a",
197 "b_skipfiles.go": "b",
198 "zzz/c.go": "c",
199 },
200 func(path string, typ os.FileMode) error {
201 if strings.HasSuffix(path, "_skipfiles.go") {
202 mu.Lock()
203 defer mu.Unlock()
204 want["/src/"+filepath.Base(path)] = 0
Rebecca Stambler207d3de2019-11-20 22:43:00 -0500205 return fastwalk.ErrSkipFiles
Heschi Kreinick19e2aca2018-09-18 15:06:34 -0400206 }
207 return nil
208 },
209 want)
210 if len(want) != 5 {
211 t.Errorf("saw too many files: wanted 5, got %v (%v)", len(want), want)
212 }
213}
214
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000215func TestFastWalk_TraverseSymlink(t *testing.T) {
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000216 testFastWalk(t, map[string]string{
217 "foo/foo.go": "one",
218 "bar/bar.go": "two",
219 "skip/skip.go": "skip",
220 "symdir": "LINK:foo",
221 },
222 func(path string, typ os.FileMode) error {
223 if typ == os.ModeSymlink {
Rebecca Stambler207d3de2019-11-20 22:43:00 -0500224 return fastwalk.ErrTraverseLink
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000225 }
226 return nil
227 },
228 map[string]os.FileMode{
229 "": os.ModeDir,
230 "/src": os.ModeDir,
231 "/src/bar": os.ModeDir,
232 "/src/bar/bar.go": 0,
233 "/src/foo": os.ModeDir,
234 "/src/foo/foo.go": 0,
235 "/src/skip": os.ModeDir,
236 "/src/skip/skip.go": 0,
237 "/src/symdir": os.ModeSymlink,
238 "/src/symdir/foo.go": 0,
239 })
240}
241
242var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk")
243
244func BenchmarkFastWalk(b *testing.B) {
245 b.ReportAllocs()
246 for i := 0; i < b.N; i++ {
Ian Cottrell94b14832018-03-08 16:10:10 -0500247 err := fastwalk.Walk(*benchDir, func(path string, typ os.FileMode) error { return nil })
Brad Fitzpatrickedf8e6f2016-07-18 00:47:35 +0000248 if err != nil {
249 b.Fatal(err)
250 }
251 }
252}