github.com/MRtecno98/afero@v1.9.3/iofs_test.go (about)

     1  //go:build go1.16
     2  // +build go1.16
     3  
     4  package afero
     5  
     6  import (
     7  	"bytes"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"io/fs"
    12  	"math/rand"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  	"testing"
    17  	"testing/fstest"
    18  	"time"
    19  
    20  	"github.com/MRtecno98/afero/internal/common"
    21  )
    22  
    23  func TestIOFS(t *testing.T) {
    24  	if runtime.GOOS == "windows" {
    25  		// TODO(bep): some of the "bad path" tests in fstest.TestFS fail on Windows
    26  		t.Skip("Skipping on Windows")
    27  	}
    28  	t.Parallel()
    29  
    30  	t.Run("use MemMapFs", func(t *testing.T) {
    31  		mmfs := NewMemMapFs()
    32  
    33  		err := mmfs.MkdirAll("dir1/dir2", os.ModePerm)
    34  		if err != nil {
    35  			t.Fatal("MkdirAll failed:", err)
    36  		}
    37  
    38  		f, err := mmfs.OpenFile("dir1/dir2/test.txt", os.O_RDWR|os.O_CREATE, os.ModePerm)
    39  		if err != nil {
    40  			t.Fatal("OpenFile (O_CREATE) failed:", err)
    41  		}
    42  
    43  		f.Close()
    44  
    45  		if err := fstest.TestFS(NewIOFS(mmfs), "dir1/dir2/test.txt"); err != nil {
    46  			t.Error(err)
    47  		}
    48  	})
    49  
    50  	t.Run("use OsFs", func(t *testing.T) {
    51  		osfs := NewBasePathFs(NewOsFs(), t.TempDir())
    52  
    53  		err := osfs.MkdirAll("dir1/dir2", os.ModePerm)
    54  		if err != nil {
    55  			t.Fatal("MkdirAll failed:", err)
    56  		}
    57  
    58  		f, err := osfs.OpenFile("dir1/dir2/test.txt", os.O_RDWR|os.O_CREATE, os.ModePerm)
    59  		if err != nil {
    60  			t.Fatal("OpenFile (O_CREATE) failed:", err)
    61  		}
    62  
    63  		f.Close()
    64  
    65  		if err := fstest.TestFS(NewIOFS(osfs), "dir1/dir2/test.txt"); err != nil {
    66  			t.Error(err)
    67  		}
    68  	})
    69  
    70  }
    71  
    72  func TestIOFSNativeDirEntryWhenPossible(t *testing.T) {
    73  	t.Parallel()
    74  
    75  	osfs := NewBasePathFs(NewOsFs(), t.TempDir())
    76  
    77  	err := osfs.MkdirAll("dir1/dir2", os.ModePerm)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	const numFiles = 10
    83  
    84  	var fileNumbers []int
    85  	for i := 0; i < numFiles; i++ {
    86  		fileNumbers = append(fileNumbers, i)
    87  	}
    88  	rand.Shuffle(len(fileNumbers), func(i, j int) {
    89  		fileNumbers[i], fileNumbers[j] = fileNumbers[j], fileNumbers[i]
    90  	})
    91  
    92  	for _, i := range fileNumbers {
    93  		f, err := osfs.Create(fmt.Sprintf("dir1/dir2/test%d.txt", i))
    94  		if err != nil {
    95  			t.Fatal(err)
    96  		}
    97  		f.Close()
    98  	}
    99  
   100  	dir2, err := osfs.Open("dir1/dir2")
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  
   105  	assertDirEntries := func(entries []fs.DirEntry, ordered bool) {
   106  		if len(entries) != numFiles {
   107  			t.Fatalf("expected %d, got %d", numFiles, len(entries))
   108  		}
   109  		for i, entry := range entries {
   110  			if _, ok := entry.(common.FileInfoDirEntry); ok {
   111  				t.Fatal("DirEntry not native")
   112  			}
   113  			if ordered && entry.Name() != fmt.Sprintf("test%d.txt", i) {
   114  				t.Fatalf("expected %s, got %s", fmt.Sprintf("test%d.txt", i), entry.Name())
   115  			}
   116  		}
   117  	}
   118  
   119  	dirEntries, err := dir2.(fs.ReadDirFile).ReadDir(-1)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	assertDirEntries(dirEntries, false)
   124  
   125  	iofs := NewIOFS(osfs)
   126  
   127  	dirEntries, err = iofs.ReadDir("dir1/dir2")
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	assertDirEntries(dirEntries, true)
   132  
   133  	fileCount := 0
   134  	err = fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error {
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		if !d.IsDir() {
   140  			fileCount++
   141  		}
   142  
   143  		if _, ok := d.(common.FileInfoDirEntry); ok {
   144  			t.Fatal("DirEntry not native")
   145  		}
   146  
   147  		return nil
   148  
   149  	})
   150  
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	if fileCount != numFiles {
   156  		t.Fatalf("expected %d, got %d", numFiles, fileCount)
   157  	}
   158  
   159  }
   160  
   161  func TestFromIOFS(t *testing.T) {
   162  	t.Parallel()
   163  
   164  	fsys := fstest.MapFS{
   165  		"test.txt": {
   166  			Data:    []byte("File in root"),
   167  			Mode:    fs.ModePerm,
   168  			ModTime: time.Now(),
   169  		},
   170  		"dir1": {
   171  			Mode:    fs.ModeDir | fs.ModePerm,
   172  			ModTime: time.Now(),
   173  		},
   174  		"dir1/dir2": {
   175  			Mode:    fs.ModeDir | fs.ModePerm,
   176  			ModTime: time.Now(),
   177  		},
   178  		"dir1/dir2/hello.txt": {
   179  			Data:    []byte("Hello world"),
   180  			Mode:    fs.ModePerm,
   181  			ModTime: time.Now(),
   182  		},
   183  	}
   184  
   185  	fromIOFS := FromIOFS{fsys}
   186  
   187  	t.Run("Create", func(t *testing.T) {
   188  		_, err := fromIOFS.Create("test")
   189  		assertPermissionError(t, err)
   190  	})
   191  
   192  	t.Run("Mkdir", func(t *testing.T) {
   193  		err := fromIOFS.Mkdir("test", 0)
   194  		assertPermissionError(t, err)
   195  	})
   196  
   197  	t.Run("MkdirAll", func(t *testing.T) {
   198  		err := fromIOFS.Mkdir("test", 0)
   199  		assertPermissionError(t, err)
   200  	})
   201  
   202  	t.Run("Open", func(t *testing.T) {
   203  		t.Run("non existing file", func(t *testing.T) {
   204  			_, err := fromIOFS.Open("nonexisting")
   205  			if !errors.Is(err, fs.ErrNotExist) {
   206  				t.Errorf("Expected error to be fs.ErrNotExist, got %[1]T (%[1]v)", err)
   207  			}
   208  		})
   209  
   210  		t.Run("directory", func(t *testing.T) {
   211  			dirFile, err := fromIOFS.Open("dir1")
   212  			if err != nil {
   213  				t.Errorf("dir1 open failed: %v", err)
   214  				return
   215  			}
   216  
   217  			defer dirFile.Close()
   218  
   219  			dirStat, err := dirFile.Stat()
   220  			if err != nil {
   221  				t.Errorf("dir1 stat failed: %v", err)
   222  				return
   223  			}
   224  
   225  			if !dirStat.IsDir() {
   226  				t.Errorf("dir1 stat told that it is not a directory")
   227  				return
   228  			}
   229  		})
   230  
   231  		t.Run("simple file", func(t *testing.T) {
   232  			file, err := fromIOFS.Open("test.txt")
   233  			if err != nil {
   234  				t.Errorf("test.txt open failed: %v", err)
   235  				return
   236  			}
   237  
   238  			defer file.Close()
   239  
   240  			fileStat, err := file.Stat()
   241  			if err != nil {
   242  				t.Errorf("test.txt stat failed: %v", err)
   243  				return
   244  			}
   245  
   246  			if fileStat.IsDir() {
   247  				t.Errorf("test.txt stat told that it is a directory")
   248  				return
   249  			}
   250  		})
   251  	})
   252  
   253  	t.Run("Remove", func(t *testing.T) {
   254  		err := fromIOFS.Remove("test")
   255  		assertPermissionError(t, err)
   256  	})
   257  
   258  	t.Run("Rename", func(t *testing.T) {
   259  		err := fromIOFS.Rename("test", "test2")
   260  		assertPermissionError(t, err)
   261  	})
   262  
   263  	t.Run("Stat", func(t *testing.T) {
   264  		t.Run("non existing file", func(t *testing.T) {
   265  			_, err := fromIOFS.Stat("nonexisting")
   266  			if !errors.Is(err, fs.ErrNotExist) {
   267  				t.Errorf("Expected error to be fs.ErrNotExist, got %[1]T (%[1]v)", err)
   268  			}
   269  		})
   270  
   271  		t.Run("directory", func(t *testing.T) {
   272  			stat, err := fromIOFS.Stat("dir1/dir2")
   273  			if err != nil {
   274  				t.Errorf("dir1/dir2 stat failed: %v", err)
   275  				return
   276  			}
   277  
   278  			if !stat.IsDir() {
   279  				t.Errorf("dir1/dir2 stat told that it is not a directory")
   280  				return
   281  			}
   282  		})
   283  
   284  		t.Run("file", func(t *testing.T) {
   285  			stat, err := fromIOFS.Stat("dir1/dir2/hello.txt")
   286  			if err != nil {
   287  				t.Errorf("dir1/dir2 stat failed: %v", err)
   288  				return
   289  			}
   290  
   291  			if stat.IsDir() {
   292  				t.Errorf("dir1/dir2/hello.txt stat told that it is a directory")
   293  				return
   294  			}
   295  
   296  			if lenFile := len(fsys["dir1/dir2/hello.txt"].Data); int64(lenFile) != stat.Size() {
   297  				t.Errorf("dir1/dir2/hello.txt stat told invalid size: expected %d, got %d", lenFile, stat.Size())
   298  				return
   299  			}
   300  		})
   301  	})
   302  
   303  	t.Run("Chmod", func(t *testing.T) {
   304  		err := fromIOFS.Chmod("test", os.ModePerm)
   305  		assertPermissionError(t, err)
   306  	})
   307  
   308  	t.Run("Chown", func(t *testing.T) {
   309  		err := fromIOFS.Chown("test", 0, 0)
   310  		assertPermissionError(t, err)
   311  	})
   312  
   313  	t.Run("Chtimes", func(t *testing.T) {
   314  		err := fromIOFS.Chtimes("test", time.Now(), time.Now())
   315  		assertPermissionError(t, err)
   316  	})
   317  }
   318  
   319  func TestFromIOFS_File(t *testing.T) {
   320  	t.Parallel()
   321  
   322  	fsys := fstest.MapFS{
   323  		"test.txt": {
   324  			Data:    []byte("File in root"),
   325  			Mode:    fs.ModePerm,
   326  			ModTime: time.Now(),
   327  		},
   328  		"dir1": {
   329  			Mode:    fs.ModeDir | fs.ModePerm,
   330  			ModTime: time.Now(),
   331  		},
   332  		"dir2": {
   333  			Mode:    fs.ModeDir | fs.ModePerm,
   334  			ModTime: time.Now(),
   335  		},
   336  	}
   337  
   338  	fromIOFS := FromIOFS{fsys}
   339  
   340  	file, err := fromIOFS.Open("test.txt")
   341  	if err != nil {
   342  		t.Errorf("test.txt open failed: %v", err)
   343  		return
   344  	}
   345  
   346  	defer file.Close()
   347  
   348  	fileStat, err := file.Stat()
   349  	if err != nil {
   350  		t.Errorf("test.txt stat failed: %v", err)
   351  		return
   352  	}
   353  
   354  	if fileStat.IsDir() {
   355  		t.Errorf("test.txt stat told that it is a directory")
   356  		return
   357  	}
   358  
   359  	t.Run("ReadAt", func(t *testing.T) {
   360  		// MapFS files implements io.ReaderAt
   361  		b := make([]byte, 2)
   362  		_, err := file.ReadAt(b, 2)
   363  
   364  		if err != nil {
   365  			t.Errorf("ReadAt failed: %v", err)
   366  			return
   367  		}
   368  
   369  		if expectedData := fsys["test.txt"].Data[2:4]; !bytes.Equal(b, expectedData) {
   370  			t.Errorf("Unexpected content read: %s, expected %s", b, expectedData)
   371  		}
   372  	})
   373  
   374  	t.Run("Seek", func(t *testing.T) {
   375  		n, err := file.Seek(2, io.SeekStart)
   376  		if err != nil {
   377  			t.Errorf("Seek failed: %v", err)
   378  			return
   379  		}
   380  
   381  		if n != 2 {
   382  			t.Errorf("Seek returned unexpected value: %d, expected 2", n)
   383  		}
   384  	})
   385  
   386  	t.Run("Write", func(t *testing.T) {
   387  		_, err := file.Write(nil)
   388  		assertPermissionError(t, err)
   389  	})
   390  
   391  	t.Run("WriteAt", func(t *testing.T) {
   392  		_, err := file.WriteAt(nil, 0)
   393  		assertPermissionError(t, err)
   394  	})
   395  
   396  	t.Run("Name", func(t *testing.T) {
   397  		if name := file.Name(); name != "test.txt" {
   398  			t.Errorf("expected file.Name() == test.txt, got %s", name)
   399  		}
   400  	})
   401  
   402  	t.Run("Readdir", func(t *testing.T) {
   403  		t.Run("not directory", func(t *testing.T) {
   404  			_, err := file.Readdir(-1)
   405  			assertPermissionError(t, err)
   406  		})
   407  
   408  		t.Run("root directory", func(t *testing.T) {
   409  			root, err := fromIOFS.Open(".")
   410  			if err != nil {
   411  				t.Errorf("root open failed: %v", err)
   412  				return
   413  			}
   414  
   415  			defer root.Close()
   416  
   417  			items, err := root.Readdir(-1)
   418  			if err != nil {
   419  				t.Errorf("Readdir error: %v", err)
   420  				return
   421  			}
   422  
   423  			var expectedItems = []struct {
   424  				Name  string
   425  				IsDir bool
   426  				Size  int64
   427  			}{
   428  				{Name: "dir1", IsDir: true, Size: 0},
   429  				{Name: "dir2", IsDir: true, Size: 0},
   430  				{Name: "test.txt", IsDir: false, Size: int64(len(fsys["test.txt"].Data))},
   431  			}
   432  
   433  			if len(expectedItems) != len(items) {
   434  				t.Errorf("Items count mismatch, expected %d, got %d", len(expectedItems), len(items))
   435  				return
   436  			}
   437  
   438  			for i, item := range items {
   439  				if item.Name() != expectedItems[i].Name {
   440  					t.Errorf("Item %d: expected name %s, got %s", i, expectedItems[i].Name, item.Name())
   441  				}
   442  
   443  				if item.IsDir() != expectedItems[i].IsDir {
   444  					t.Errorf("Item %d: expected IsDir %t, got %t", i, expectedItems[i].IsDir, item.IsDir())
   445  				}
   446  
   447  				if item.Size() != expectedItems[i].Size {
   448  					t.Errorf("Item %d: expected IsDir %d, got %d", i, expectedItems[i].Size, item.Size())
   449  				}
   450  			}
   451  		})
   452  	})
   453  
   454  	t.Run("Readdirnames", func(t *testing.T) {
   455  		t.Run("not directory", func(t *testing.T) {
   456  			_, err := file.Readdirnames(-1)
   457  			assertPermissionError(t, err)
   458  		})
   459  
   460  		t.Run("root directory", func(t *testing.T) {
   461  			root, err := fromIOFS.Open(".")
   462  			if err != nil {
   463  				t.Errorf("root open failed: %v", err)
   464  				return
   465  			}
   466  
   467  			defer root.Close()
   468  
   469  			items, err := root.Readdirnames(-1)
   470  			if err != nil {
   471  				t.Errorf("Readdirnames error: %v", err)
   472  				return
   473  			}
   474  
   475  			var expectedItems = []string{"dir1", "dir2", "test.txt"}
   476  
   477  			if len(expectedItems) != len(items) {
   478  				t.Errorf("Items count mismatch, expected %d, got %d", len(expectedItems), len(items))
   479  				return
   480  			}
   481  
   482  			for i, item := range items {
   483  				if item != expectedItems[i] {
   484  					t.Errorf("Item %d: expected name %s, got %s", i, expectedItems[i], item)
   485  				}
   486  			}
   487  		})
   488  	})
   489  
   490  	t.Run("Truncate", func(t *testing.T) {
   491  		err := file.Truncate(1)
   492  		assertPermissionError(t, err)
   493  	})
   494  
   495  	t.Run("WriteString", func(t *testing.T) {
   496  		_, err := file.WriteString("a")
   497  		assertPermissionError(t, err)
   498  	})
   499  }
   500  
   501  func assertPermissionError(t *testing.T, err error) {
   502  	t.Helper()
   503  
   504  	var perr *fs.PathError
   505  	if !errors.As(err, &perr) {
   506  		t.Errorf("Expected *fs.PathError, got %[1]T (%[1]v)", err)
   507  		return
   508  	}
   509  
   510  	if perr.Err != fs.ErrPermission {
   511  		t.Errorf("Expected (*fs.PathError).Err == fs.ErrPermisson, got %[1]T (%[1]v)", err)
   512  	}
   513  }
   514  
   515  func BenchmarkWalkDir(b *testing.B) {
   516  	osfs := NewBasePathFs(NewOsFs(), b.TempDir())
   517  
   518  	createSomeFiles := func(dirname string) {
   519  		for i := 0; i < 10; i++ {
   520  			f, err := osfs.Create(filepath.Join(dirname, fmt.Sprintf("test%d.txt", i)))
   521  			if err != nil {
   522  				b.Fatal(err)
   523  			}
   524  			f.Close()
   525  		}
   526  	}
   527  
   528  	depth := 10
   529  	for level := depth; level > 0; level-- {
   530  		dirname := ""
   531  		for i := 0; i < level; i++ {
   532  			dirname = filepath.Join(dirname, fmt.Sprintf("dir%d", i))
   533  			err := osfs.MkdirAll(dirname, 0755)
   534  			if err != nil && !os.IsExist(err) {
   535  				b.Fatal(err)
   536  			}
   537  		}
   538  		createSomeFiles(dirname)
   539  	}
   540  
   541  	iofs := NewIOFS(osfs)
   542  
   543  	b.ResetTimer()
   544  	for i := 0; i < b.N; i++ {
   545  		err := fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error {
   546  			if err != nil {
   547  				return err
   548  			}
   549  			return nil
   550  
   551  		})
   552  
   553  		if err != nil {
   554  			b.Fatal(err)
   555  		}
   556  	}
   557  
   558  }