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  }