github.com/charlievieth/fastwalk@v1.0.3/entry_filter_test.go (about) 1 package fastwalk_test 2 3 import ( 4 "fmt" 5 "io/fs" 6 "math/rand" 7 "os" 8 "path/filepath" 9 "runtime" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/charlievieth/fastwalk" 15 ) 16 17 func TestEntryFilter(t *testing.T) { 18 tempdir := t.TempDir() 19 files := map[string]string{ 20 "foo/foo.go": "one", 21 "bar/bar.go": "LINK:../foo/foo.go", 22 "bar/baz.go": "two", 23 "bar/loop": "LINK:../bar/", // symlink loop 24 "file.go": "three", 25 26 // Use multiple symdirs to increase the chance that one 27 // of these and not "foo" is followed first. 28 "symdir1": "LINK:foo", 29 "symdir2": "LINK:foo", 30 "symdir3": "LINK:foo", 31 "symdir4": "LINK:foo", 32 } 33 testCreateFiles(t, tempdir, files) 34 35 var mu sync.Mutex 36 var seen []os.FileInfo 37 filter := fastwalk.NewEntryFilter() 38 walkFn := fastwalk.IgnoreDuplicateFiles(func(path string, de fs.DirEntry, err error) error { 39 requireNoError(t, err) 40 fi1, err := fastwalk.StatDirEntry(path, de) 41 if err != nil { 42 t.Error(err) 43 return err 44 } 45 mu.Lock() 46 defer mu.Unlock() 47 if !filter.Entry(path, de) { 48 for _, fi2 := range seen { 49 if os.SameFile(fi1, fi2) { 50 t.Errorf("Visited file twice: %q (%s) and %q (%s)", 51 path, fi1.Mode(), fi2.Name(), fi2.Mode()) 52 } 53 } 54 } 55 seen = append(seen, fi1) 56 return nil 57 }) 58 if err := fastwalk.Walk(nil, tempdir, walkFn); err != nil { 59 t.Fatal(err) 60 } 61 62 // Test that true is returned for a non-existent directory 63 // On Windows the Info field of the returned DirEntry 64 // is already populated so this will succeed. 65 if runtime.GOOS != "windows" { 66 path := filepath.Join(tempdir, "src", "foo/foo.go") 67 fi, err := os.Lstat(path) 68 if err != nil { 69 t.Fatal(err) 70 } 71 if err := os.Remove(path); err != nil { 72 t.Fatal(err) 73 } 74 if !filter.Entry(path, fs.FileInfoToDirEntry(fi)) { 75 t.Error("EntryFilter should return true when the file does not exist") 76 } 77 } 78 } 79 80 func BenchmarkEntryFilter(b *testing.B) { 81 tempdir := b.TempDir() 82 83 names := make([]string, 0, 2048) 84 for i := 0; i < 1024; i++ { 85 name := filepath.Join(tempdir, fmt.Sprintf("dir_%04d", i)) 86 if err := os.Mkdir(name, 0755); err != nil { 87 b.Fatal(err) 88 } 89 names = append(names, name) 90 } 91 for i := 0; i < 1024; i++ { 92 name := filepath.Join(tempdir, fmt.Sprintf("file_%04d", i)) 93 if err := writeFile(name, filepath.Base(name), 0644); err != nil { 94 b.Fatal(err) 95 } 96 names = append(names, name) 97 } 98 rr := rand.New(rand.NewSource(time.Now().UnixNano())) 99 rr.Shuffle(len(names), func(i, j int) { 100 names[i], names[j] = names[j], names[i] 101 }) 102 103 type fileInfo struct { 104 Name string 105 Info fs.DirEntry 106 } 107 infos := make([]fileInfo, len(names)) 108 for i, name := range names { 109 fi, err := os.Lstat(name) 110 if err != nil { 111 b.Fatal(err) 112 } 113 infos[i] = fileInfo{name, fs.FileInfoToDirEntry(fi)} 114 } 115 116 b.ResetTimer() 117 118 b.Run("MostlyHits", func(b *testing.B) { 119 filter := fastwalk.NewEntryFilter() 120 for i := 0; i < b.N; i++ { 121 x := infos[i%len(infos)] 122 filter.Entry(x.Name, x.Info) 123 } 124 }) 125 126 b.Run("MostlyHitsParallel", func(b *testing.B) { 127 filter := fastwalk.NewEntryFilter() 128 b.RunParallel(func(pb *testing.PB) { 129 i := 0 130 for pb.Next() { 131 x := infos[i%len(infos)] 132 filter.Entry(x.Name, x.Info) 133 i++ 134 } 135 }) 136 }) 137 138 b.Run("HalfMisses", func(b *testing.B) { 139 filter := fastwalk.NewEntryFilter() 140 n := len(infos) 141 for i := 0; i < b.N; i++ { 142 x := infos[i%len(infos)] 143 filter.Entry(x.Name, x.Info) 144 if i != 0 && i%n == 0 { 145 b.StopTimer() 146 filter = fastwalk.NewEntryFilter() 147 b.StartTimer() 148 } 149 } 150 }) 151 }