github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/vfs_test.go (about)

     1  // Test suite for vfs
     2  
     3  package vfs
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"testing"
    12  	"time"
    13  
    14  	_ "github.com/rclone/rclone/backend/all" // import all the backends
    15  	"github.com/rclone/rclone/fs"
    16  	"github.com/rclone/rclone/fstest"
    17  	"github.com/rclone/rclone/vfs/vfscommon"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  // Some times used in the tests
    23  var (
    24  	t1 = fstest.Time("2001-02-03T04:05:06.499999999Z")
    25  	t2 = fstest.Time("2011-12-25T12:59:59.123456789Z")
    26  	t3 = fstest.Time("2011-12-30T12:59:59.000000000Z")
    27  )
    28  
    29  // Constants uses in the tests
    30  const (
    31  	writeBackDelay      = 100 * time.Millisecond // A short writeback delay for testing
    32  	waitForWritersDelay = 30 * time.Second       // time to wait for existing writers
    33  )
    34  
    35  // TestMain drives the tests
    36  func TestMain(m *testing.M) {
    37  	fstest.TestMain(m)
    38  }
    39  
    40  // Clean up a test VFS
    41  func cleanupVFS(t *testing.T, vfs *VFS) {
    42  	vfs.WaitForWriters(waitForWritersDelay)
    43  	err := vfs.CleanUp()
    44  	require.NoError(t, err)
    45  	vfs.Shutdown()
    46  }
    47  
    48  // Create a new VFS
    49  func newTestVFSOpt(t *testing.T, opt *vfscommon.Options) (r *fstest.Run, vfs *VFS) {
    50  	r = fstest.NewRun(t)
    51  	vfs = New(r.Fremote, opt)
    52  	t.Cleanup(func() {
    53  		cleanupVFS(t, vfs)
    54  	})
    55  	return r, vfs
    56  }
    57  
    58  // Create a new VFS with default options
    59  func newTestVFS(t *testing.T) (r *fstest.Run, vfs *VFS) {
    60  	return newTestVFSOpt(t, nil)
    61  }
    62  
    63  // Check baseHandle performs as advertised
    64  func TestVFSbaseHandle(t *testing.T) {
    65  	fh := baseHandle{}
    66  
    67  	err := fh.Chdir()
    68  	assert.Equal(t, ENOSYS, err)
    69  
    70  	err = fh.Chmod(0)
    71  	assert.Equal(t, ENOSYS, err)
    72  
    73  	err = fh.Chown(0, 0)
    74  	assert.Equal(t, ENOSYS, err)
    75  
    76  	err = fh.Close()
    77  	assert.Equal(t, ENOSYS, err)
    78  
    79  	fd := fh.Fd()
    80  	assert.Equal(t, uintptr(0), fd)
    81  
    82  	name := fh.Name()
    83  	assert.Equal(t, "", name)
    84  
    85  	_, err = fh.Read(nil)
    86  	assert.Equal(t, ENOSYS, err)
    87  
    88  	_, err = fh.ReadAt(nil, 0)
    89  	assert.Equal(t, ENOSYS, err)
    90  
    91  	_, err = fh.Readdir(0)
    92  	assert.Equal(t, ENOSYS, err)
    93  
    94  	_, err = fh.Readdirnames(0)
    95  	assert.Equal(t, ENOSYS, err)
    96  
    97  	_, err = fh.Seek(0, io.SeekStart)
    98  	assert.Equal(t, ENOSYS, err)
    99  
   100  	_, err = fh.Stat()
   101  	assert.Equal(t, ENOSYS, err)
   102  
   103  	err = fh.Sync()
   104  	assert.Equal(t, nil, err)
   105  
   106  	err = fh.Truncate(0)
   107  	assert.Equal(t, ENOSYS, err)
   108  
   109  	_, err = fh.Write(nil)
   110  	assert.Equal(t, ENOSYS, err)
   111  
   112  	_, err = fh.WriteAt(nil, 0)
   113  	assert.Equal(t, ENOSYS, err)
   114  
   115  	_, err = fh.WriteString("")
   116  	assert.Equal(t, ENOSYS, err)
   117  
   118  	err = fh.Flush()
   119  	assert.Equal(t, ENOSYS, err)
   120  
   121  	err = fh.Release()
   122  	assert.Equal(t, ENOSYS, err)
   123  
   124  	node := fh.Node()
   125  	assert.Nil(t, node)
   126  }
   127  
   128  // TestVFSNew sees if the New command works properly
   129  func TestVFSNew(t *testing.T) {
   130  	// Check active cache has this many entries
   131  	checkActiveCacheEntries := func(i int) {
   132  		_, count := activeCacheEntries()
   133  		assert.Equal(t, i, count)
   134  	}
   135  
   136  	checkActiveCacheEntries(0)
   137  
   138  	r, vfs := newTestVFS(t)
   139  
   140  	// Check making a VFS with nil options
   141  	var defaultOpt = vfscommon.DefaultOpt
   142  	defaultOpt.DirPerms |= os.ModeDir
   143  	assert.Equal(t, vfs.Opt, defaultOpt)
   144  	assert.Equal(t, vfs.f, r.Fremote)
   145  
   146  	checkActiveCacheEntries(1)
   147  
   148  	// Check that we get the same VFS if we ask for it again with
   149  	// the same options
   150  	vfs2 := New(r.Fremote, nil)
   151  	assert.Equal(t, fmt.Sprintf("%p", vfs), fmt.Sprintf("%p", vfs2))
   152  
   153  	checkActiveCacheEntries(1)
   154  
   155  	// Shut the new VFS down and check the cache still has stuff in
   156  	vfs2.Shutdown()
   157  
   158  	checkActiveCacheEntries(1)
   159  
   160  	cleanupVFS(t, vfs)
   161  
   162  	checkActiveCacheEntries(0)
   163  }
   164  
   165  // TestVFSNewWithOpts sees if the New command works properly
   166  func TestVFSNewWithOpts(t *testing.T) {
   167  	var opt = vfscommon.DefaultOpt
   168  	opt.DirPerms = 0777
   169  	opt.FilePerms = 0666
   170  	opt.Umask = 0002
   171  	_, vfs := newTestVFSOpt(t, &opt)
   172  
   173  	assert.Equal(t, os.FileMode(0775)|os.ModeDir, vfs.Opt.DirPerms)
   174  	assert.Equal(t, os.FileMode(0664), vfs.Opt.FilePerms)
   175  }
   176  
   177  // TestVFSRoot checks root directory is present and correct
   178  func TestVFSRoot(t *testing.T) {
   179  	_, vfs := newTestVFS(t)
   180  
   181  	root, err := vfs.Root()
   182  	require.NoError(t, err)
   183  	assert.Equal(t, vfs.root, root)
   184  	assert.True(t, root.IsDir())
   185  	assert.Equal(t, vfs.Opt.DirPerms.Perm(), root.Mode().Perm())
   186  }
   187  
   188  func TestVFSStat(t *testing.T) {
   189  	r, vfs := newTestVFS(t)
   190  
   191  	file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
   192  	file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
   193  	r.CheckRemoteItems(t, file1, file2)
   194  
   195  	node, err := vfs.Stat("file1")
   196  	require.NoError(t, err)
   197  	assert.True(t, node.IsFile())
   198  	assert.Equal(t, "file1", node.Name())
   199  
   200  	node, err = vfs.Stat("dir")
   201  	require.NoError(t, err)
   202  	assert.True(t, node.IsDir())
   203  	assert.Equal(t, "dir", node.Name())
   204  
   205  	node, err = vfs.Stat("dir/file2")
   206  	require.NoError(t, err)
   207  	assert.True(t, node.IsFile())
   208  	assert.Equal(t, "file2", node.Name())
   209  
   210  	_, err = vfs.Stat("not found")
   211  	assert.Equal(t, os.ErrNotExist, err)
   212  
   213  	_, err = vfs.Stat("dir/not found")
   214  	assert.Equal(t, os.ErrNotExist, err)
   215  
   216  	_, err = vfs.Stat("not found/not found")
   217  	assert.Equal(t, os.ErrNotExist, err)
   218  
   219  	_, err = vfs.Stat("file1/under a file")
   220  	assert.Equal(t, os.ErrNotExist, err)
   221  }
   222  
   223  func TestVFSStatParent(t *testing.T) {
   224  	r, vfs := newTestVFS(t)
   225  
   226  	file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
   227  	file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
   228  	r.CheckRemoteItems(t, file1, file2)
   229  
   230  	node, leaf, err := vfs.StatParent("file1")
   231  	require.NoError(t, err)
   232  	assert.True(t, node.IsDir())
   233  	assert.Equal(t, "/", node.Name())
   234  	assert.Equal(t, "file1", leaf)
   235  
   236  	node, leaf, err = vfs.StatParent("dir/file2")
   237  	require.NoError(t, err)
   238  	assert.True(t, node.IsDir())
   239  	assert.Equal(t, "dir", node.Name())
   240  	assert.Equal(t, "file2", leaf)
   241  
   242  	node, leaf, err = vfs.StatParent("not found")
   243  	require.NoError(t, err)
   244  	assert.True(t, node.IsDir())
   245  	assert.Equal(t, "/", node.Name())
   246  	assert.Equal(t, "not found", leaf)
   247  
   248  	_, _, err = vfs.StatParent("not found dir/not found")
   249  	assert.Equal(t, os.ErrNotExist, err)
   250  
   251  	_, _, err = vfs.StatParent("file1/under a file")
   252  	assert.Equal(t, os.ErrExist, err)
   253  }
   254  
   255  func TestVFSOpenFile(t *testing.T) {
   256  	r, vfs := newTestVFS(t)
   257  
   258  	file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
   259  	file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
   260  	r.CheckRemoteItems(t, file1, file2)
   261  
   262  	fd, err := vfs.OpenFile("file1", os.O_RDONLY, 0777)
   263  	require.NoError(t, err)
   264  	assert.NotNil(t, fd)
   265  	require.NoError(t, fd.Close())
   266  
   267  	fd, err = vfs.OpenFile("dir", os.O_RDONLY, 0777)
   268  	require.NoError(t, err)
   269  	assert.NotNil(t, fd)
   270  	require.NoError(t, fd.Close())
   271  
   272  	fd, err = vfs.OpenFile("dir/new_file.txt", os.O_RDONLY, 0777)
   273  	assert.Equal(t, os.ErrNotExist, err)
   274  	assert.Nil(t, fd)
   275  
   276  	fd, err = vfs.OpenFile("dir/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777)
   277  	require.NoError(t, err)
   278  	assert.NotNil(t, fd)
   279  	err = fd.Close()
   280  	if !errors.Is(err, fs.ErrorCantUploadEmptyFiles) {
   281  		require.NoError(t, err)
   282  	}
   283  
   284  	fd, err = vfs.OpenFile("not found/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777)
   285  	assert.Equal(t, os.ErrNotExist, err)
   286  	assert.Nil(t, fd)
   287  }
   288  
   289  func TestVFSRename(t *testing.T) {
   290  	r, vfs := newTestVFS(t)
   291  
   292  	features := r.Fremote.Features()
   293  	if features.Move == nil && features.Copy == nil {
   294  		t.Skip("skip as can't rename files")
   295  	}
   296  
   297  	file1 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
   298  	r.CheckRemoteItems(t, file1)
   299  
   300  	err := vfs.Rename("dir/file2", "dir/file1")
   301  	require.NoError(t, err)
   302  	file1.Path = "dir/file1"
   303  	r.CheckRemoteItems(t, file1)
   304  
   305  	err = vfs.Rename("dir/file1", "file0")
   306  	require.NoError(t, err)
   307  	file1.Path = "file0"
   308  	r.CheckRemoteItems(t, file1)
   309  
   310  	err = vfs.Rename("not found/file0", "file0")
   311  	assert.Equal(t, os.ErrNotExist, err)
   312  
   313  	err = vfs.Rename("file0", "not found/file0")
   314  	assert.Equal(t, os.ErrNotExist, err)
   315  }
   316  
   317  func TestVFSStatfs(t *testing.T) {
   318  	r, vfs := newTestVFS(t)
   319  
   320  	// pre-conditions
   321  	assert.Nil(t, vfs.usage)
   322  	assert.True(t, vfs.usageTime.IsZero())
   323  
   324  	aboutSupported := r.Fremote.Features().About != nil
   325  
   326  	// read
   327  	total, used, free := vfs.Statfs()
   328  	if !aboutSupported {
   329  		assert.Equal(t, int64(unknownFreeBytes), total)
   330  		assert.Equal(t, int64(unknownFreeBytes), free)
   331  		assert.Equal(t, int64(0), used)
   332  		return // can't test anything else if About not supported
   333  	}
   334  	require.NotNil(t, vfs.usage)
   335  	assert.False(t, vfs.usageTime.IsZero())
   336  	if vfs.usage.Total != nil {
   337  		assert.Equal(t, *vfs.usage.Total, total)
   338  	} else {
   339  		assert.True(t, total >= int64(unknownFreeBytes))
   340  	}
   341  	if vfs.usage.Free != nil {
   342  		assert.Equal(t, *vfs.usage.Free, free)
   343  	} else {
   344  		if vfs.usage.Total != nil && vfs.usage.Used != nil {
   345  			assert.Equal(t, free, total-used)
   346  		} else {
   347  			assert.True(t, free >= int64(unknownFreeBytes))
   348  		}
   349  	}
   350  	if vfs.usage.Used != nil {
   351  		assert.Equal(t, *vfs.usage.Used, used)
   352  	} else {
   353  		assert.Equal(t, int64(0), used)
   354  	}
   355  
   356  	// read cached
   357  	oldUsage := vfs.usage
   358  	oldTime := vfs.usageTime
   359  	total2, used2, free2 := vfs.Statfs()
   360  	assert.Equal(t, oldUsage, vfs.usage)
   361  	assert.Equal(t, total, total2)
   362  	assert.Equal(t, used, used2)
   363  	assert.Equal(t, free, free2)
   364  	assert.Equal(t, oldTime, vfs.usageTime)
   365  }
   366  
   367  func TestVFSMkdir(t *testing.T) {
   368  	r, vfs := newTestVFS(t)
   369  
   370  	if !r.Fremote.Features().CanHaveEmptyDirectories {
   371  		return // can't test if can't have empty directories
   372  	}
   373  
   374  	r.CheckRemoteListing(t, nil, []string{})
   375  
   376  	// Try making the root
   377  	err := vfs.Mkdir("", 0777)
   378  	require.NoError(t, err)
   379  	r.CheckRemoteListing(t, nil, []string{})
   380  
   381  	// Try making a sub directory
   382  	err = vfs.Mkdir("a", 0777)
   383  	require.NoError(t, err)
   384  
   385  	r.CheckRemoteListing(t, nil, []string{"a"})
   386  
   387  	// Try making an existing directory
   388  	err = vfs.Mkdir("a", 0777)
   389  	require.NoError(t, err)
   390  
   391  	r.CheckRemoteListing(t, nil, []string{"a"})
   392  
   393  	// Try making a new directory
   394  	err = vfs.Mkdir("b/", 0777)
   395  	require.NoError(t, err)
   396  
   397  	r.CheckRemoteListing(t, nil, []string{"a", "b"})
   398  
   399  	// Try making a new directory
   400  	err = vfs.Mkdir("/c", 0777)
   401  	require.NoError(t, err)
   402  
   403  	r.CheckRemoteListing(t, nil, []string{"a", "b", "c"})
   404  
   405  	// Try making a new directory
   406  	err = vfs.Mkdir("/d/", 0777)
   407  	require.NoError(t, err)
   408  
   409  	r.CheckRemoteListing(t, nil, []string{"a", "b", "c", "d"})
   410  }
   411  
   412  func TestVFSMkdirAll(t *testing.T) {
   413  	r, vfs := newTestVFS(t)
   414  
   415  	if !r.Fremote.Features().CanHaveEmptyDirectories {
   416  		return // can't test if can't have empty directories
   417  	}
   418  
   419  	r.CheckRemoteListing(t, nil, []string{})
   420  
   421  	// Try making the root
   422  	err := vfs.MkdirAll("", 0777)
   423  	require.NoError(t, err)
   424  	r.CheckRemoteListing(t, nil, []string{})
   425  
   426  	// Try making a sub directory
   427  	err = vfs.MkdirAll("a/b/c/d", 0777)
   428  	require.NoError(t, err)
   429  
   430  	r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
   431  
   432  	// Try making an existing directory
   433  	err = vfs.MkdirAll("a/b/c", 0777)
   434  	require.NoError(t, err)
   435  
   436  	r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
   437  
   438  	// Try making an existing directory
   439  	err = vfs.MkdirAll("/a/b/c/", 0777)
   440  	require.NoError(t, err)
   441  
   442  	r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
   443  }
   444  
   445  func TestFillInMissingSizes(t *testing.T) {
   446  	const unknownFree = 10
   447  	for _, test := range []struct {
   448  		total, free, used             int64
   449  		wantTotal, wantUsed, wantFree int64
   450  	}{
   451  		{
   452  			total: 20, free: 5, used: 15,
   453  			wantTotal: 20, wantFree: 5, wantUsed: 15,
   454  		},
   455  		{
   456  			total: 20, free: 5, used: -1,
   457  			wantTotal: 20, wantFree: 5, wantUsed: 15,
   458  		},
   459  		{
   460  			total: 20, free: -1, used: 15,
   461  			wantTotal: 20, wantFree: 5, wantUsed: 15,
   462  		},
   463  		{
   464  			total: 20, free: -1, used: -1,
   465  			wantTotal: 20, wantFree: 20, wantUsed: 0,
   466  		},
   467  		{
   468  			total: -1, free: 5, used: 15,
   469  			wantTotal: 20, wantFree: 5, wantUsed: 15,
   470  		},
   471  		{
   472  			total: -1, free: 15, used: -1,
   473  			wantTotal: 15, wantFree: 15, wantUsed: 0,
   474  		},
   475  		{
   476  			total: -1, free: -1, used: 15,
   477  			wantTotal: 25, wantFree: 10, wantUsed: 15,
   478  		},
   479  		{
   480  			total: -1, free: -1, used: -1,
   481  			wantTotal: 10, wantFree: 10, wantUsed: 0,
   482  		},
   483  	} {
   484  		t.Run(fmt.Sprintf("total=%d,free=%d,used=%d", test.total, test.free, test.used), func(t *testing.T) {
   485  			gotTotal, gotUsed, gotFree := fillInMissingSizes(test.total, test.used, test.free, unknownFree)
   486  			assert.Equal(t, test.wantTotal, gotTotal, "total")
   487  			assert.Equal(t, test.wantUsed, gotUsed, "used")
   488  			assert.Equal(t, test.wantFree, gotFree, "free")
   489  		})
   490  	}
   491  }