github.com/haraldrudell/parl@v0.4.176/pfs/traverser_test.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pfs
     7  
     8  import (
     9  	"errors"
    10  	"io/fs"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/haraldrudell/parl/perrors"
    16  )
    17  
    18  // tests traversing single entry
    19  func TestTraverser(t *testing.T) {
    20  	var tempDir = t.TempDir()
    21  	// path is unclean providedPath
    22  	var path = filepath.Join(tempDir, ".")
    23  	var expAbs = func() (abs string) {
    24  		var e error
    25  		if abs, e = filepath.Abs(tempDir); e != nil {
    26  			panic(e)
    27  		} else if abs, e = filepath.EvalSymlinks(abs); e != nil {
    28  			panic(e)
    29  		}
    30  		return
    31  	}()
    32  	var expNo1 = uint64(1)
    33  	var expBase = filepath.Base(tempDir)
    34  
    35  	var result ResultEntry
    36  
    37  	var traverser = NewTraverser(path)
    38  
    39  	// first Next should be dir
    40  	result = traverser.Next()
    41  	if result.ProvidedPath != path {
    42  		t.Errorf("Next path %q exp %q", result.ProvidedPath, path)
    43  	}
    44  	if result.Abs != expAbs {
    45  		t.Errorf("Next Abs %q exp %q", result.Abs, expAbs)
    46  	}
    47  	if result.No != expNo1 {
    48  		t.Errorf("Next No %d exp %d", result.No, expNo1)
    49  	}
    50  	if result.Err != nil {
    51  		t.Errorf("Next Err: %s", perrors.Short(result.Err))
    52  	}
    53  	if result.SkipEntry == nil {
    54  		t.Error("Next SkipEntry nil")
    55  	}
    56  	if n := result.Name(); n != expBase {
    57  		t.Errorf("Next Name %q exp %q", n, expBase)
    58  	}
    59  
    60  	// NextNext should be end
    61  	result = traverser.Next()
    62  	if !result.IsEnd() {
    63  		t.Error("NextNext not end")
    64  	}
    65  }
    66  
    67  // tests traversing directory
    68  func TestTraverserDir(t *testing.T) {
    69  	var tempDir = t.TempDir()
    70  	var fileBase = "file"
    71  	var filename = filepath.Join(tempDir, fileBase)
    72  	if e := os.WriteFile(filename, nil, 0700); e != nil {
    73  		panic(e)
    74  	}
    75  
    76  	var result ResultEntry
    77  
    78  	var traverser = NewTraverser(tempDir)
    79  
    80  	// Next should be tempDir
    81  	result = traverser.Next()
    82  	if result.Err != nil {
    83  		t.Errorf("Next err: %s", result.Err)
    84  	}
    85  	if result.ProvidedPath != tempDir {
    86  		t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir)
    87  	}
    88  
    89  	// NextNext should be filename
    90  	result = traverser.Next()
    91  	if result.Err != nil {
    92  		t.Errorf("NextNext err: %s", result.Err)
    93  	}
    94  	if result.ProvidedPath != filename {
    95  		t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, filename)
    96  	}
    97  
    98  	// NextNextNext should be end
    99  	result = traverser.Next()
   100  	if result.Err != nil {
   101  		t.Errorf("NextNextNext err: %s", result.Err)
   102  	}
   103  	if !result.IsEnd() {
   104  		t.Error("NextNextNext not end")
   105  	}
   106  }
   107  
   108  // tests returning an error
   109  func TestTraverserError(t *testing.T) {
   110  	var path = "%"
   111  
   112  	var result ResultEntry
   113  
   114  	var traverser = NewTraverser(path)
   115  
   116  	// first Next should be err
   117  	result = traverser.Next()
   118  	if result.Err == nil {
   119  		t.Error("Next Err nil")
   120  	}
   121  }
   122  
   123  // tests skip of entry
   124  func TestTraverserSkip(t *testing.T) {
   125  	var tempDir = t.TempDir()
   126  	var fileBase = "file"
   127  	var filename = filepath.Join(tempDir, fileBase)
   128  	if e := os.WriteFile(filename, nil, 0700); e != nil {
   129  		panic(e)
   130  	}
   131  
   132  	var result ResultEntry
   133  
   134  	var traverser = NewTraverser(tempDir)
   135  
   136  	// Next should be tempDir
   137  	result = traverser.Next()
   138  	if result.Err != nil {
   139  		t.Errorf("Next err: %s", result.Err)
   140  	}
   141  	if result.ProvidedPath != tempDir {
   142  		t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir)
   143  	}
   144  	if result.SkipEntry == nil {
   145  		t.Fatalf("Next result.SkipEntry nil")
   146  	}
   147  	result.Skip()
   148  
   149  	// NextNext should be end
   150  	result = traverser.Next()
   151  	if result.Err != nil {
   152  		t.Errorf("NextNext err: %s", result.Err)
   153  	}
   154  	if !result.IsEnd() {
   155  		t.Error("NextNext not end")
   156  	}
   157  }
   158  
   159  // tests that in-root symlinks are ignored
   160  func TestTraverserInRootSymlink(t *testing.T) {
   161  	var tempDir = t.TempDir()
   162  	var symlink = filepath.Join(tempDir, "symlink")
   163  	if e := os.Symlink(tempDir, symlink); e != nil {
   164  		panic(e)
   165  	}
   166  	var cleanTempDir = func() (abs string) {
   167  		var err error
   168  		if abs, err = filepath.Abs(tempDir); err != nil {
   169  			panic(err)
   170  		} else if abs, err = filepath.EvalSymlinks(abs); err != nil {
   171  			panic(err)
   172  		}
   173  		return
   174  	}()
   175  
   176  	var result ResultEntry
   177  
   178  	var traverser = NewTraverser(tempDir)
   179  
   180  	// Next should be tempDir
   181  	result = traverser.Next()
   182  	if result.Err != nil {
   183  		t.Errorf("Next err: %s", result.Err)
   184  	}
   185  	if result.ProvidedPath != tempDir {
   186  		t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir)
   187  	}
   188  	if result.SkipEntry == nil {
   189  		t.Fatalf("Next result.SkipEntry nil")
   190  	}
   191  
   192  	// NextNext should be symlink
   193  	result = traverser.Next()
   194  	if result.Err != nil {
   195  		t.Errorf("NextNext err: %s", result.Err)
   196  	}
   197  	if result.ProvidedPath != symlink {
   198  		t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, symlink)
   199  	}
   200  	if result.Abs != cleanTempDir {
   201  		t.Errorf("NextNext Abs %q exp %q", result.Abs, cleanTempDir)
   202  	}
   203  
   204  	// NextNextNext should be end
   205  	result = traverser.Next()
   206  	if result.Err != nil {
   207  		t.Errorf("NextNextNext err: %s", result.Err)
   208  	}
   209  	if !result.IsEnd() {
   210  		t.Errorf("NextNextNext not end path: %q", result.ProvidedPath)
   211  	}
   212  }
   213  
   214  // tests that disparate symlinks are followed
   215  func TestTraverserDisparateSymlink(t *testing.T) {
   216  	var tempDir = t.TempDir()
   217  	var tempDir2 = t.TempDir()
   218  	var cleanDir2 = func() (abs string) {
   219  		var err error
   220  		if abs, err = filepath.Abs(tempDir2); err != nil {
   221  			panic(err)
   222  		} else if abs, err = filepath.EvalSymlinks(abs); err != nil {
   223  			panic(err)
   224  		}
   225  		return
   226  	}()
   227  	var symlink = filepath.Join(tempDir, "symlink")
   228  	if e := os.Symlink(tempDir2, symlink); e != nil {
   229  		panic(e)
   230  	}
   231  
   232  	var result ResultEntry
   233  
   234  	var traverser = NewTraverser(tempDir)
   235  
   236  	// Next should be tempDir
   237  	result = traverser.Next()
   238  	if result.Err != nil {
   239  		t.Errorf("Next err: %s", result.Err)
   240  	}
   241  	if result.ProvidedPath != tempDir {
   242  		t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir)
   243  	}
   244  
   245  	// NextNext should be symlink
   246  	result = traverser.Next()
   247  	if result.Err != nil {
   248  		t.Errorf("NextNext err: %s", result.Err)
   249  	}
   250  	if result.ProvidedPath != symlink {
   251  		t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, symlink)
   252  	}
   253  	if result.Abs != cleanDir2 {
   254  		t.Errorf("NextNext Abs %q exp %q", result.Abs, cleanDir2)
   255  	}
   256  
   257  	// Next3 should be cleanDir2
   258  	result = traverser.Next()
   259  	if result.Err != nil {
   260  		t.Errorf("Next3 err: %s", result.Err)
   261  	}
   262  	if result.IsEnd() {
   263  		t.Error("Next3 IsEnd")
   264  	}
   265  	if result.ProvidedPath != cleanDir2 {
   266  		t.Errorf("Next3 ProvidedPath %q exp %q", result.ProvidedPath, cleanDir2)
   267  	}
   268  
   269  	// Next4 should be end
   270  	result = traverser.Next()
   271  	if result.Err != nil {
   272  		t.Errorf("Next4 err: %s", result.Err)
   273  	}
   274  	if !result.IsEnd() {
   275  		t.Error("Next4 not end")
   276  	}
   277  }
   278  
   279  func TestTraverserBrokenSymlink(t *testing.T) {
   280  	//t.Fail()
   281  	var tempDir = t.TempDir()
   282  	var brokenLink = filepath.Join(tempDir, "brokenLink")
   283  	if e := os.Symlink(filepath.Join(tempDir, "nonExistent"), brokenLink); e != nil {
   284  		panic(e)
   285  	}
   286  
   287  	var result ResultEntry
   288  
   289  	var traverser = NewTraverser(brokenLink)
   290  
   291  	// Next should be err
   292  	result = traverser.Next()
   293  
   294  	// result.Err: pfs.Load filepath.EvalSymlinks
   295  	// lstat /private/var/folders/sq/0x1_9fyn1bv907s7ypfryt1c0000gn/T/TestTraverserBrokenSymlink814588820/001/nonExistent:
   296  	// no such file or directory at pfs.(*Root2).Load()-root2.go:51
   297  	t.Logf("result.Err: %s", perrors.Short(result.Err))
   298  
   299  	if result.Err == nil {
   300  		t.Error("Next missing err")
   301  	} else if !errors.Is(result.Err, fs.ErrNotExist) {
   302  		t.Errorf("Next err not fs.ErrNotExist: %s", perrors.Short(result.Err))
   303  	}
   304  }
   305  
   306  func TestTraverserObsoletingSymlink(t *testing.T) {
   307  	var tempDir = t.TempDir()
   308  	var dir = filepath.Join(tempDir, "dir")
   309  	if e := os.Mkdir(dir, 0700); e != nil {
   310  		panic(e)
   311  	}
   312  	var obsoletingLink = filepath.Join(dir, "obsoletingLink")
   313  	if e := os.Symlink(tempDir, obsoletingLink); e != nil {
   314  		panic(e)
   315  	}
   316  	var cleanTempDir = func() (abs string) {
   317  		var err error
   318  		if abs, err = filepath.Abs(tempDir); err != nil {
   319  			panic(err)
   320  		} else if abs, err = filepath.EvalSymlinks(abs); err != nil {
   321  			panic(err)
   322  		}
   323  		return
   324  	}()
   325  
   326  	var result ResultEntry
   327  
   328  	var traverser = NewTraverser(dir)
   329  
   330  	// Next should be dir
   331  	result = traverser.Next()
   332  	if result.Err != nil {
   333  		t.Errorf("Next err: %s", result.Err)
   334  	}
   335  	if result.ProvidedPath != dir {
   336  		t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, dir)
   337  	}
   338  
   339  	// Next2 should be obsoletingLink
   340  	result = traverser.Next()
   341  	if result.Err != nil {
   342  		t.Errorf("Next2 err: %s", result.Err)
   343  	}
   344  	if result.IsEnd() {
   345  		t.Error("Next2 end")
   346  	}
   347  	if result.ProvidedPath != obsoletingLink {
   348  		t.Errorf("Next2 ProvidedPath %q exp %q", result.ProvidedPath, obsoletingLink)
   349  	}
   350  	if result.Abs != cleanTempDir {
   351  		t.Errorf("Next2 Abs %q exp %q", result.Abs, cleanTempDir)
   352  	}
   353  
   354  	// NextNext should be cleanTempDir
   355  	//	- dir/obsoletingLink links to parent tempDir
   356  	//	- entries from dir ends
   357  	//	- the new root is traversed
   358  	result = traverser.Next()
   359  	if result.Err != nil {
   360  		t.Errorf("NextNext err: %s", result.Err)
   361  	}
   362  	if result.IsEnd() {
   363  		t.Error("NextNext end")
   364  	}
   365  	if result.ProvidedPath != cleanTempDir {
   366  		t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, cleanTempDir)
   367  	}
   368  
   369  	// NextNextNext should end
   370  	result = traverser.Next()
   371  	if result.Err != nil {
   372  		t.Errorf("NextNextNext err: %s", result.Err)
   373  	}
   374  	if !result.IsEnd() {
   375  		t.Error("NextNextNext not end")
   376  	}
   377  }