github.com/charlievieth/fastwalk@v1.0.3/dirent_unix_test.go (about) 1 //go:build (aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris) && !appengine 2 // +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris 3 // +build !appengine 4 5 package fastwalk 6 7 import ( 8 "io/fs" 9 "os" 10 "path/filepath" 11 "runtime" 12 "sync" 13 "sync/atomic" 14 "testing" 15 "unsafe" 16 ) 17 18 func testUnixDirentParallel(t *testing.T, ent *unixDirent, want fs.FileInfo, 19 fn func(*unixDirent) (fs.FileInfo, error)) { 20 21 sameFile := func(fi1, fi2 fs.FileInfo) bool { 22 return fi1.Name() == fi2.Name() && 23 fi1.Size() == fi2.Size() && 24 fi1.Mode() == fi2.Mode() && 25 fi1.ModTime() == fi2.ModTime() && 26 fi1.IsDir() == fi2.IsDir() && 27 os.SameFile(fi1, fi2) 28 } 29 30 numCPU := runtime.NumCPU() 31 if numCPU < 4 { 32 numCPU = 4 33 } 34 if numCPU > 16 { 35 numCPU = 16 36 } 37 38 var wg sync.WaitGroup 39 start := make(chan struct{}) 40 var mu sync.Mutex 41 infos := make(map[*fileInfo]int) 42 stats := make(map[*fileInfo]int) 43 44 for i := 0; i < numCPU; i++ { 45 wg.Add(1) 46 go func() { 47 defer wg.Done() 48 <-start 49 for i := 0; i < 16; i++ { 50 fi, err := fn(ent) 51 if err != nil { 52 t.Error(err) 53 return 54 } 55 info := (*fileInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&ent.info)))) 56 stat := (*fileInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&ent.stat)))) 57 mu.Lock() 58 infos[info]++ 59 stats[stat]++ 60 mu.Unlock() 61 if !sameFile(fi, want) { 62 t.Errorf("FileInfo not equal:\nwant: %s\ngot: %s\n", 63 FormatFileInfo(want), FormatFileInfo(fi)) 64 return 65 } 66 } 67 }() 68 } 69 70 close(start) 71 wg.Wait() 72 73 t.Logf("Infos: %d Stats: %d\n", len(infos), len(stats)) 74 } 75 76 func TestUnixDirent(t *testing.T) { 77 tempdir := t.TempDir() 78 79 fileName := filepath.Join(tempdir, "file.txt") 80 if err := os.WriteFile(fileName, []byte("file.txt"), 0644); err != nil { 81 t.Fatal(err) 82 } 83 t.Run("File", func(t *testing.T) { 84 fileInfo, err := os.Lstat(fileName) 85 if err != nil { 86 t.Fatal(err) 87 } 88 t.Run("Stat", func(t *testing.T) { 89 ent := newUnixDirent(tempdir, filepath.Base(fileName), fileInfo.Mode().Type()) 90 testUnixDirentParallel(t, ent, fileInfo, (*unixDirent).Stat) 91 }) 92 t.Run("Info", func(t *testing.T) { 93 ent := newUnixDirent(tempdir, filepath.Base(fileName), fileInfo.Mode().Type()) 94 testUnixDirentParallel(t, ent, fileInfo, (*unixDirent).Info) 95 }) 96 }) 97 98 t.Run("Link", func(t *testing.T) { 99 linkName := filepath.Join(tempdir, "link.link") 100 if err := os.Symlink(filepath.Base(fileName), linkName); err != nil { 101 t.Fatal(err) 102 } 103 fileInfo, err := os.Lstat(linkName) 104 if err != nil { 105 t.Fatal(err) 106 } 107 t.Run("Stat", func(t *testing.T) { 108 want, err := os.Stat(linkName) 109 if err != nil { 110 t.Fatal(err) 111 } 112 ent := newUnixDirent(tempdir, filepath.Base(linkName), fileInfo.Mode().Type()) 113 testUnixDirentParallel(t, ent, want, (*unixDirent).Stat) 114 }) 115 t.Run("Info", func(t *testing.T) { 116 ent := newUnixDirent(tempdir, filepath.Base(linkName), fileInfo.Mode().Type()) 117 testUnixDirentParallel(t, ent, fileInfo, (*unixDirent).Info) 118 }) 119 }) 120 } 121 122 func BenchmarkUnixDirentLoadFileInfo(b *testing.B) { 123 wd, err := os.Getwd() 124 if err != nil { 125 b.Fatal(err) 126 } 127 fi, err := os.Lstat(wd) 128 if err != nil { 129 b.Fatal(err) 130 } 131 parent, name := filepath.Split(wd) 132 d := newUnixDirent(parent, name, fi.Mode().Type()) 133 134 for i := 0; i < b.N; i++ { 135 loadFileInfo(&d.info) 136 d.info = nil 137 } 138 } 139 140 func BenchmarkUnixDirentInfo(b *testing.B) { 141 wd, err := os.Getwd() 142 if err != nil { 143 b.Fatal(err) 144 } 145 fi, err := os.Lstat(wd) 146 if err != nil { 147 b.Fatal(err) 148 } 149 parent, name := filepath.Split(wd) 150 d := newUnixDirent(parent, name, fi.Mode().Type()) 151 152 for i := 0; i < b.N; i++ { 153 fi, err := d.Info() 154 if err != nil { 155 b.Fatal(err) 156 } 157 if fi == nil { 158 b.Fatal("Nil FileInfo") 159 } 160 } 161 } 162 163 func BenchmarkUnixDirentStat(b *testing.B) { 164 wd, err := os.Getwd() 165 if err != nil { 166 b.Fatal(err) 167 } 168 fi, err := os.Lstat(wd) 169 if err != nil { 170 b.Fatal(err) 171 } 172 parent, name := filepath.Split(wd) 173 d := newUnixDirent(parent, name, fi.Mode().Type()) 174 175 for i := 0; i < b.N; i++ { 176 fi, err := d.Stat() 177 if err != nil { 178 b.Fatal(err) 179 } 180 if fi == nil { 181 b.Fatal("Nil FileInfo") 182 } 183 } 184 }