github.com/v2fly/tools@v0.100.0/internal/fastwalk/fastwalk_test.go (about) 1 // 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 5 package fastwalk_test 6 7 import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "reflect" 15 "runtime" 16 "sort" 17 "strings" 18 "sync" 19 "testing" 20 21 "github.com/v2fly/tools/internal/fastwalk" 22 ) 23 24 func 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 37 func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) { 38 tempdir, err := ioutil.TempDir("", "test-fast-walk") 39 if err != nil { 40 t.Fatal(err) 41 } 42 defer os.RemoveAll(tempdir) 43 44 symlinks := map[string]string{} 45 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) 49 } 50 var err error 51 if strings.HasPrefix(contents, "LINK:") { 52 symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:")) 53 } else { 54 err = ioutil.WriteFile(file, []byte(contents), 0644) 55 } 56 if err != nil { 57 t.Fatal(err) 58 } 59 } 60 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 75 got := map[string]os.FileMode{} 76 var mu sync.Mutex 77 err = fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error { 78 mu.Lock() 79 defer mu.Unlock() 80 if !strings.HasPrefix(path, tempdir) { 81 t.Errorf("bogus prefix on %q, expect %q", path, tempdir) 82 } 83 key := filepath.ToSlash(strings.TrimPrefix(path, tempdir)) 84 if old, dup := got[key]; dup { 85 t.Errorf("callback called twice for key %q: %v -> %v", key, old, typ) 86 } 87 got[key] = typ 88 return callback(path, typ) 89 }) 90 91 if err != nil { 92 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 } 97 } 98 99 func 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 120 func 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 137 func TestFastWalk_Symlink(t *testing.T) { 138 testFastWalk(t, map[string]string{ 139 "foo/foo.go": "one", 140 "bar/bar.go": "LINK:../foo/foo.go", 141 "symdir": "LINK:foo", 142 "broken/broken.go": "LINK:../nonexistent", 143 }, 144 func(path string, typ os.FileMode) error { 145 return nil 146 }, 147 map[string]os.FileMode{ 148 "": 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, 157 }) 158 } 159 160 func 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 183 func 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 205 return fastwalk.ErrSkipFiles 206 } 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 215 func TestFastWalk_TraverseSymlink(t *testing.T) { 216 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 { 224 return fastwalk.ErrTraverseLink 225 } 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 242 var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk") 243 244 func BenchmarkFastWalk(b *testing.B) { 245 b.ReportAllocs() 246 for i := 0; i < b.N; i++ { 247 err := fastwalk.Walk(*benchDir, func(path string, typ os.FileMode) error { return nil }) 248 if err != nil { 249 b.Fatal(err) 250 } 251 } 252 }