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  }