github.com/charlievieth/fastwalk@v1.0.3/dirent_test.go (about)

     1  //go:build (linux || darwin || freebsd || openbsd || netbsd) && !appengine
     2  // +build linux darwin freebsd openbsd netbsd
     3  // +build !appengine
     4  
     5  package fastwalk_test
     6  
     7  import (
     8  	"io/fs"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/charlievieth/fastwalk"
    16  )
    17  
    18  func TestDirent(t *testing.T) {
    19  	tempdir := t.TempDir()
    20  
    21  	fileName := filepath.Join(tempdir, "file.txt")
    22  	if err := writeFile(fileName, "file.txt", 0644); err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	linkName := filepath.Join(tempdir, "link.link")
    26  	if err := symlink(t, filepath.Base(fileName), linkName); err != nil {
    27  		t.Fatal(err)
    28  	}
    29  
    30  	// Use fastwalk.Walk to create the dir entries
    31  	getDirEnts := func(t *testing.T) (linkEnt, fileEnt fs.DirEntry) {
    32  		err := fastwalk.Walk(nil, tempdir, func(path string, d fs.DirEntry, err error) error {
    33  			switch path {
    34  			case linkName:
    35  				linkEnt = d
    36  			case fileName:
    37  				fileEnt = d
    38  			}
    39  			return nil
    40  		})
    41  		if err != nil {
    42  			t.Fatal(err)
    43  		}
    44  		if fileEnt == nil || linkEnt == nil {
    45  			t.Fatal("error walking directory")
    46  		}
    47  		return linkEnt, fileEnt
    48  	}
    49  
    50  	t.Run("Lstat", func(t *testing.T) {
    51  		linkEnt, _ := getDirEnts(t)
    52  		want, err := os.Lstat(linkName)
    53  		if err != nil {
    54  			t.Fatal(err)
    55  		}
    56  		got, err := linkEnt.Info()
    57  		if err != nil {
    58  			t.Fatal(err)
    59  		}
    60  		if !os.SameFile(want, got) {
    61  			t.Errorf("lstat mismatch\n got:\n%s\nwant:\n%s",
    62  				fastwalk.FormatFileInfo(got), fastwalk.FormatFileInfo(want))
    63  		}
    64  	})
    65  
    66  	t.Run("Stat", func(t *testing.T) {
    67  		_, fileEnt := getDirEnts(t)
    68  		want, err := os.Stat(fileName)
    69  		if err != nil {
    70  			t.Fatal(err)
    71  		}
    72  		got, err := fastwalk.StatDirEntry(linkName, fileEnt)
    73  		if err != nil {
    74  			t.Fatal(err)
    75  		}
    76  		if !os.SameFile(want, got) {
    77  			t.Errorf("lstat mismatch\n got:\n%s\nwant:\n%s",
    78  				fastwalk.FormatFileInfo(got), fastwalk.FormatFileInfo(want))
    79  		}
    80  		fi, err := fileEnt.Info()
    81  		if err != nil {
    82  			t.Fatal(err)
    83  		}
    84  		if fi != got {
    85  			t.Error("failed to return or cache FileInfo")
    86  		}
    87  		de := fileEnt.(fastwalk.DirEntry)
    88  		fi, err = de.Stat()
    89  		if err != nil {
    90  			t.Fatal(err)
    91  		}
    92  		if fi != got {
    93  			t.Error("failed to use cached Info result for non-symlink")
    94  		}
    95  	})
    96  
    97  	t.Run("Parallel", func(t *testing.T) {
    98  		testParallel := func(t *testing.T, de fs.DirEntry, fn func() (fs.FileInfo, error)) {
    99  			numCPU := runtime.NumCPU()
   100  
   101  			infos := make([][]fs.FileInfo, numCPU)
   102  			for i := range infos {
   103  				infos[i] = make([]fs.FileInfo, 100)
   104  			}
   105  
   106  			// Start all the goroutines at the same time to
   107  			// maximise the chance of a race
   108  			start := make(chan struct{})
   109  			var wg, ready sync.WaitGroup
   110  			ready.Add(numCPU)
   111  			wg.Add(numCPU)
   112  			for i := 0; i < numCPU; i++ {
   113  				go func(fis []fs.FileInfo, de fs.DirEntry) {
   114  					defer wg.Done()
   115  					ready.Done()
   116  					<-start
   117  					for i := range fis {
   118  						fis[i], _ = de.Info()
   119  					}
   120  				}(infos[i], de)
   121  			}
   122  
   123  			ready.Wait()
   124  			close(start) // start all goroutines at once
   125  			wg.Wait()
   126  
   127  			first := infos[0][0]
   128  			if first == nil {
   129  				t.Fatal("failed to stat file:", de.Name())
   130  			}
   131  			for _, fis := range infos {
   132  				for _, fi := range fis {
   133  					if fi != first {
   134  						t.Errorf("Expected the same fs.FileInfo to always "+
   135  							"be returned got: %#v want: %#v", fi, first)
   136  					}
   137  				}
   138  			}
   139  		}
   140  
   141  		t.Run("File", func(t *testing.T) {
   142  			t.Run("Stat", func(t *testing.T) {
   143  				_, fileEnt := getDirEnts(t)
   144  				de := fileEnt.(fastwalk.DirEntry)
   145  				testParallel(t, de, de.Stat)
   146  			})
   147  			t.Run("Lstat", func(t *testing.T) {
   148  				_, fileEnt := getDirEnts(t)
   149  				de := fileEnt.(fastwalk.DirEntry)
   150  				testParallel(t, de, de.Info)
   151  			})
   152  		})
   153  
   154  		t.Run("Link", func(t *testing.T) {
   155  			t.Run("Stat", func(t *testing.T) {
   156  				linkEnt, _ := getDirEnts(t)
   157  				de := linkEnt.(fastwalk.DirEntry)
   158  				testParallel(t, de, de.Stat)
   159  			})
   160  			t.Run("Lstat", func(t *testing.T) {
   161  				linkEnt, _ := getDirEnts(t)
   162  				de := linkEnt.(fastwalk.DirEntry)
   163  				testParallel(t, de, de.Info)
   164  			})
   165  		})
   166  	})
   167  }