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

     1  package vfscache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  	"testing"
    10  	"time"
    11  
    12  	_ "github.com/rclone/rclone/backend/local" // import the local backend
    13  	"github.com/rclone/rclone/fs"
    14  	"github.com/rclone/rclone/fs/config"
    15  	"github.com/rclone/rclone/fstest"
    16  	"github.com/rclone/rclone/lib/diskusage"
    17  	"github.com/rclone/rclone/vfs/vfscommon"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  // TestMain drives the tests
    23  func TestMain(m *testing.M) {
    24  	fstest.TestMain(m)
    25  }
    26  
    27  // convert c.item to a string
    28  func itemAsString(c *Cache) []string {
    29  	c.mu.Lock()
    30  	defer c.mu.Unlock()
    31  	var out []string
    32  	for name, item := range c.item {
    33  		out = append(out, fmt.Sprintf("name=%q opens=%d size=%d", filepath.ToSlash(name), item.opens, item.info.Size))
    34  	}
    35  	sort.Strings(out)
    36  	return out
    37  }
    38  
    39  // convert c.item to a string
    40  func itemSpaceAsString(c *Cache) []string {
    41  	c.mu.Lock()
    42  	defer c.mu.Unlock()
    43  	var out []string
    44  	for name, item := range c.item {
    45  		space := item.info.Rs.Size()
    46  		out = append(out, fmt.Sprintf("name=%q opens=%d size=%d space=%d", filepath.ToSlash(name), item.opens, item.info.Size, space))
    47  	}
    48  	sort.Strings(out)
    49  	return out
    50  }
    51  
    52  // open an item and write to it
    53  func itemWrite(t *testing.T, item *Item, contents string) {
    54  	require.NoError(t, item.Open(nil))
    55  	_, err := item.WriteAt([]byte(contents), 0)
    56  	require.NoError(t, err)
    57  }
    58  
    59  func assertPathNotExist(t *testing.T, path string) {
    60  	_, err := os.Stat(path)
    61  	assert.True(t, os.IsNotExist(err))
    62  }
    63  
    64  func assertPathExist(t *testing.T, path string) os.FileInfo {
    65  	fi, err := os.Stat(path)
    66  	assert.NoError(t, err)
    67  	return fi
    68  }
    69  
    70  type avInfo struct {
    71  	Remote string
    72  	Size   int64
    73  	IsDir  bool
    74  }
    75  
    76  var avInfos []avInfo
    77  
    78  func addVirtual(remote string, size int64, isDir bool) error {
    79  	avInfos = append(avInfos, avInfo{
    80  		Remote: remote,
    81  		Size:   size,
    82  		IsDir:  isDir,
    83  	})
    84  	return nil
    85  }
    86  
    87  func newTestCacheOpt(t *testing.T, opt vfscommon.Options) (r *fstest.Run, c *Cache) {
    88  	r = fstest.NewRun(t)
    89  
    90  	ctx, cancel := context.WithCancel(context.Background())
    91  
    92  	avInfos = nil
    93  	c, err := New(ctx, r.Fremote, &opt, addVirtual)
    94  	require.NoError(t, err)
    95  
    96  	t.Cleanup(func() {
    97  		err := c.CleanUp()
    98  		require.NoError(t, err)
    99  		assertPathNotExist(t, c.root)
   100  		cancel()
   101  	})
   102  
   103  	return r, c
   104  }
   105  
   106  func newTestCache(t *testing.T) (r *fstest.Run, c *Cache) {
   107  	opt := vfscommon.DefaultOpt
   108  
   109  	// Disable the cache cleaner as it interferes with these tests
   110  	opt.CachePollInterval = 0
   111  
   112  	// Disable synchronous write
   113  	opt.WriteBack = 0
   114  
   115  	return newTestCacheOpt(t, opt)
   116  }
   117  
   118  func TestCacheNew(t *testing.T) {
   119  	r, c := newTestCache(t)
   120  
   121  	assert.Contains(t, c.root, "vfs")
   122  	assert.Contains(t, c.fcache.Root(), filepath.Base(r.Fremote.Root()))
   123  	assert.Equal(t, []string(nil), itemAsString(c))
   124  
   125  	// createItemDir
   126  	p, err := c.createItemDir("potato")
   127  	require.NoError(t, err)
   128  	assert.Equal(t, "potato", filepath.Base(p))
   129  	assert.Equal(t, []string(nil), itemAsString(c))
   130  
   131  	fi := assertPathExist(t, filepath.Dir(p))
   132  	assert.True(t, fi.IsDir())
   133  
   134  	// get
   135  	item, _ := c.get("potato")
   136  	item2, _ := c.get("potato")
   137  	assert.Equal(t, item, item2)
   138  	assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second)
   139  
   140  	// open
   141  	assert.Equal(t, []string{
   142  		`name="potato" opens=0 size=0`,
   143  	}, itemAsString(c))
   144  	potato := c.Item("/potato")
   145  	require.NoError(t, potato.Open(nil))
   146  	assert.Equal(t, []string{
   147  		`name="potato" opens=1 size=0`,
   148  	}, itemAsString(c))
   149  	assert.WithinDuration(t, time.Now(), potato.info.ATime, time.Second)
   150  	assert.Equal(t, 1, potato.opens)
   151  
   152  	// write the file
   153  	require.NoError(t, potato.Truncate(5))
   154  	atime := time.Now()
   155  	potato.info.ATime = atime
   156  
   157  	assert.Equal(t, []string{
   158  		`name="potato" opens=1 size=5`,
   159  	}, itemAsString(c))
   160  	assert.True(t, atime.Equal(potato.info.ATime), fmt.Sprintf("%v != %v", atime, potato.info.ATime))
   161  
   162  	// try purging with file open
   163  	c.purgeOld(10 * time.Second)
   164  	assertPathExist(t, p)
   165  
   166  	// close
   167  	assert.Equal(t, []string{
   168  		`name="potato" opens=1 size=5`,
   169  	}, itemAsString(c))
   170  	require.NoError(t, potato.Truncate(6))
   171  	assert.Equal(t, []string{
   172  		`name="potato" opens=1 size=6`,
   173  	}, itemAsString(c))
   174  	require.NoError(t, potato.Close(nil))
   175  	assert.Equal(t, []string{
   176  		`name="potato" opens=0 size=6`,
   177  	}, itemAsString(c))
   178  	item, _ = c.get("potato")
   179  	assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second)
   180  	assert.Equal(t, 0, item.opens)
   181  
   182  	// try purging with file closed
   183  	c.purgeOld(10 * time.Second)
   184  	assertPathExist(t, p)
   185  
   186  	//.. purge again with -ve age
   187  	c.purgeOld(-10 * time.Second)
   188  	assertPathNotExist(t, p)
   189  
   190  	// clean - have tested the internals already
   191  	c.clean(false)
   192  }
   193  
   194  func TestCacheOpens(t *testing.T) {
   195  	_, c := newTestCache(t)
   196  
   197  	assert.Equal(t, []string(nil), itemAsString(c))
   198  	potato := c.Item("potato")
   199  	require.NoError(t, potato.Open(nil))
   200  	assert.Equal(t, []string{
   201  		`name="potato" opens=1 size=0`,
   202  	}, itemAsString(c))
   203  	require.NoError(t, potato.Open(nil))
   204  	assert.Equal(t, []string{
   205  		`name="potato" opens=2 size=0`,
   206  	}, itemAsString(c))
   207  	require.NoError(t, potato.Close(nil))
   208  	assert.Equal(t, []string{
   209  		`name="potato" opens=1 size=0`,
   210  	}, itemAsString(c))
   211  	require.NoError(t, potato.Close(nil))
   212  	assert.Equal(t, []string{
   213  		`name="potato" opens=0 size=0`,
   214  	}, itemAsString(c))
   215  
   216  	require.NoError(t, potato.Open(nil))
   217  	a1 := c.Item("a//b/c/d/one")
   218  	a2 := c.Item("a/b/c/d/e/two")
   219  	a3 := c.Item("a/b/c/d/e/f/three")
   220  	require.NoError(t, a1.Open(nil))
   221  	require.NoError(t, a2.Open(nil))
   222  	require.NoError(t, a3.Open(nil))
   223  	assert.Equal(t, []string{
   224  		`name="a/b/c/d/e/f/three" opens=1 size=0`,
   225  		`name="a/b/c/d/e/two" opens=1 size=0`,
   226  		`name="a/b/c/d/one" opens=1 size=0`,
   227  		`name="potato" opens=1 size=0`,
   228  	}, itemAsString(c))
   229  	require.NoError(t, potato.Close(nil))
   230  	require.NoError(t, a1.Close(nil))
   231  	require.NoError(t, a2.Close(nil))
   232  	require.NoError(t, a3.Close(nil))
   233  	assert.Equal(t, []string{
   234  		`name="a/b/c/d/e/f/three" opens=0 size=0`,
   235  		`name="a/b/c/d/e/two" opens=0 size=0`,
   236  		`name="a/b/c/d/one" opens=0 size=0`,
   237  		`name="potato" opens=0 size=0`,
   238  	}, itemAsString(c))
   239  }
   240  
   241  // test the open, createItemDir, purge, close, purge sequence
   242  func TestCacheOpenMkdir(t *testing.T) {
   243  	_, c := newTestCache(t)
   244  
   245  	// open
   246  	potato := c.Item("sub/potato")
   247  	require.NoError(t, potato.Open(nil))
   248  
   249  	assert.Equal(t, []string{
   250  		`name="sub/potato" opens=1 size=0`,
   251  	}, itemAsString(c))
   252  
   253  	// createItemDir
   254  	p, err := c.createItemDir("sub/potato")
   255  	require.NoError(t, err)
   256  	assert.Equal(t, "potato", filepath.Base(p))
   257  	assert.Equal(t, []string{
   258  		`name="sub/potato" opens=1 size=0`,
   259  	}, itemAsString(c))
   260  
   261  	// test directory exists
   262  	fi := assertPathExist(t, filepath.Dir(p))
   263  	assert.True(t, fi.IsDir())
   264  
   265  	// clean the cache
   266  	c.purgeOld(-10 * time.Second)
   267  
   268  	// test directory still exists
   269  	fi = assertPathExist(t, filepath.Dir(p))
   270  	assert.True(t, fi.IsDir())
   271  
   272  	// close
   273  	require.NoError(t, potato.Close(nil))
   274  
   275  	assert.Equal(t, []string{
   276  		`name="sub/potato" opens=0 size=0`,
   277  	}, itemAsString(c))
   278  
   279  	// clean the cache
   280  	c.purgeOld(-10 * time.Second)
   281  	c.purgeEmptyDirs("", true)
   282  
   283  	assert.Equal(t, []string(nil), itemAsString(c))
   284  
   285  	// test directory does not exist
   286  	assertPathNotExist(t, filepath.Dir(p))
   287  }
   288  
   289  func TestCachePurgeOld(t *testing.T) {
   290  	_, c := newTestCache(t)
   291  
   292  	// Test funcs
   293  	c.purgeOld(-10 * time.Second)
   294  
   295  	potato2 := c.Item("sub/dir2/potato2")
   296  	require.NoError(t, potato2.Open(nil))
   297  	potato := c.Item("sub/dir/potato")
   298  	require.NoError(t, potato.Open(nil))
   299  	require.NoError(t, potato2.Close(nil))
   300  	require.NoError(t, potato.Open(nil))
   301  
   302  	assert.Equal(t, []string{
   303  		`name="sub/dir/potato" opens=2 size=0`,
   304  		`name="sub/dir2/potato2" opens=0 size=0`,
   305  	}, itemAsString(c))
   306  
   307  	c.purgeOld(-10 * time.Second)
   308  
   309  	assert.Equal(t, []string{
   310  		`name="sub/dir/potato" opens=2 size=0`,
   311  	}, itemAsString(c))
   312  
   313  	require.NoError(t, potato.Close(nil))
   314  
   315  	assert.Equal(t, []string{
   316  		`name="sub/dir/potato" opens=1 size=0`,
   317  	}, itemAsString(c))
   318  
   319  	c.purgeOld(-10 * time.Second)
   320  
   321  	assert.Equal(t, []string{
   322  		`name="sub/dir/potato" opens=1 size=0`,
   323  	}, itemAsString(c))
   324  
   325  	require.NoError(t, potato.Close(nil))
   326  
   327  	assert.Equal(t, []string{
   328  		`name="sub/dir/potato" opens=0 size=0`,
   329  	}, itemAsString(c))
   330  
   331  	c.purgeOld(10 * time.Second)
   332  
   333  	assert.Equal(t, []string{
   334  		`name="sub/dir/potato" opens=0 size=0`,
   335  	}, itemAsString(c))
   336  
   337  	c.purgeOld(-10 * time.Second)
   338  
   339  	assert.Equal(t, []string(nil), itemAsString(c))
   340  }
   341  
   342  func TestCachePurgeOverQuota(t *testing.T) {
   343  	_, c := newTestCache(t)
   344  
   345  	// Test funcs
   346  
   347  	// Make some test files
   348  	potato := c.Item("sub/dir/potato")
   349  	itemWrite(t, potato, "hello")
   350  
   351  	potato2 := c.Item("sub/dir2/potato2")
   352  	itemWrite(t, potato2, "hello2")
   353  
   354  	assert.Equal(t, []string{
   355  		`name="sub/dir/potato" opens=1 size=5`,
   356  		`name="sub/dir2/potato2" opens=1 size=6`,
   357  	}, itemAsString(c))
   358  
   359  	// Check nothing removed
   360  	c.opt.CacheMaxSize = 1
   361  	c.purgeOverQuota()
   362  
   363  	// Close the files
   364  	require.NoError(t, potato.Close(nil))
   365  	require.NoError(t, potato2.Close(nil))
   366  
   367  	assert.Equal(t, []string{
   368  		`name="sub/dir/potato" opens=0 size=5`,
   369  		`name="sub/dir2/potato2" opens=0 size=6`,
   370  	}, itemAsString(c))
   371  
   372  	// Update the stats to read the total size
   373  	c.updateUsed()
   374  
   375  	// make potato2 definitely after potato
   376  	t1 := time.Now().Add(10 * time.Second)
   377  	potato2.info.ATime = t1
   378  
   379  	// Check only potato removed to get below quota
   380  	c.opt.CacheMaxSize = 10
   381  	c.purgeOverQuota()
   382  	assert.Equal(t, int64(6), c.used)
   383  
   384  	assert.Equal(t, []string{
   385  		`name="sub/dir2/potato2" opens=0 size=6`,
   386  	}, itemAsString(c))
   387  
   388  	// Put potato back
   389  	potato = c.Item("sub/dir/potato")
   390  	require.NoError(t, potato.Open(nil))
   391  	require.NoError(t, potato.Truncate(5))
   392  	require.NoError(t, potato.Close(nil))
   393  
   394  	// Update the stats to read the total size
   395  	c.updateUsed()
   396  
   397  	assert.Equal(t, []string{
   398  		`name="sub/dir/potato" opens=0 size=5`,
   399  		`name="sub/dir2/potato2" opens=0 size=6`,
   400  	}, itemAsString(c))
   401  
   402  	// make potato definitely after potato2
   403  	t2 := t1.Add(20 * time.Second)
   404  	potato.info.ATime = t2
   405  
   406  	// Check only potato2 removed to get below quota
   407  	c.opt.CacheMaxSize = 10
   408  	c.purgeOverQuota()
   409  	assert.Equal(t, int64(5), c.used)
   410  	c.purgeEmptyDirs("", true)
   411  
   412  	assert.Equal(t, []string{
   413  		`name="sub/dir/potato" opens=0 size=5`,
   414  	}, itemAsString(c))
   415  
   416  	// Now purge everything
   417  	c.opt.CacheMaxSize = 1
   418  	c.purgeOverQuota()
   419  	assert.Equal(t, int64(0), c.used)
   420  	c.purgeEmptyDirs("", true)
   421  
   422  	assert.Equal(t, []string(nil), itemAsString(c))
   423  
   424  	// Check nothing left behind
   425  	c.clean(false)
   426  	assert.Equal(t, int64(0), c.used)
   427  	assert.Equal(t, []string(nil), itemAsString(c))
   428  }
   429  
   430  func TestCachePurgeMinFreeSpace(t *testing.T) {
   431  	du, err := diskusage.New(config.GetCacheDir())
   432  	if err == diskusage.ErrUnsupported {
   433  		t.Skip(err)
   434  	}
   435  	// We've tested the quota mechanism already, so just test the
   436  	// min free space quota is working.
   437  	_, c := newTestCache(t)
   438  
   439  	// First set free space quota very small and check it is OK
   440  	c.opt.CacheMinFreeSpace = 1
   441  	assert.True(t, c.minFreeSpaceQuotaOK())
   442  	assert.True(t, c.quotasOK())
   443  
   444  	// Now set it a bit larger than the current disk available and check it is BAD
   445  	c.opt.CacheMinFreeSpace = fs.SizeSuffix(du.Available) + fs.Gibi
   446  	assert.False(t, c.minFreeSpaceQuotaOK())
   447  	assert.False(t, c.quotasOK())
   448  }
   449  
   450  // test reset clean files
   451  func TestCachePurgeClean(t *testing.T) {
   452  	r, c := newItemTestCache(t)
   453  	contents, obj, potato1 := newFile(t, r, c, "existing")
   454  	_ = contents
   455  
   456  	// Open the object to create metadata for it
   457  	require.NoError(t, potato1.Open(obj))
   458  	require.NoError(t, potato1.Open(obj))
   459  
   460  	size, err := potato1.GetSize()
   461  	require.NoError(t, err)
   462  	assert.Equal(t, int64(100), size)
   463  
   464  	// Read something to instantiate the cache file
   465  	buf := make([]byte, 10)
   466  	_, err = potato1.ReadAt(buf, 10)
   467  	require.NoError(t, err)
   468  
   469  	// Test cache file present
   470  	_, err = os.Stat(potato1.c.toOSPath(potato1.name))
   471  	require.NoError(t, err)
   472  
   473  	// Add some potatoes
   474  	potato2 := c.Item("sub/dir/potato2")
   475  	require.NoError(t, potato2.Open(nil))
   476  	require.NoError(t, potato2.Truncate(5))
   477  
   478  	potato3 := c.Item("sub/dir/potato3")
   479  	require.NoError(t, potato3.Open(nil))
   480  	require.NoError(t, potato3.Truncate(6))
   481  
   482  	c.updateUsed()
   483  	c.opt.CacheMaxSize = 1
   484  	c.purgeClean()
   485  	assert.Equal(t, []string{
   486  		`name="existing" opens=2 size=100 space=0`,
   487  		`name="sub/dir/potato2" opens=1 size=5 space=5`,
   488  		`name="sub/dir/potato3" opens=1 size=6 space=6`,
   489  	}, itemSpaceAsString(c))
   490  	assert.Equal(t, int64(11), c.used)
   491  
   492  	require.NoError(t, potato2.Close(nil))
   493  	c.opt.CacheMaxSize = 1
   494  	c.purgeClean()
   495  	assert.Equal(t, []string{
   496  		`name="existing" opens=2 size=100 space=0`,
   497  		`name="sub/dir/potato3" opens=1 size=6 space=6`,
   498  	}, itemSpaceAsString(c))
   499  	assert.Equal(t, int64(6), c.used)
   500  
   501  	require.NoError(t, potato1.Close(nil))
   502  	require.NoError(t, potato1.Close(nil))
   503  	require.NoError(t, potato3.Close(nil))
   504  
   505  	// Remove all files now.  The are all not in use.
   506  	// purgeClean does not remove empty cache files. purgeOverQuota does.
   507  	// So we use purgeOverQuota here for the cleanup.
   508  	c.opt.CacheMaxSize = 1
   509  	c.purgeOverQuota()
   510  
   511  	c.purgeEmptyDirs("", true)
   512  
   513  	assert.Equal(t, []string(nil), itemAsString(c))
   514  }
   515  
   516  func TestCacheInUse(t *testing.T) {
   517  	_, c := newTestCache(t)
   518  
   519  	assert.False(t, c.InUse("potato"))
   520  
   521  	potato := c.Item("potato")
   522  
   523  	assert.False(t, c.InUse("potato"))
   524  
   525  	require.NoError(t, potato.Open(nil))
   526  
   527  	assert.True(t, c.InUse("potato"))
   528  
   529  	require.NoError(t, potato.Close(nil))
   530  
   531  	assert.False(t, c.InUse("potato"))
   532  }
   533  
   534  func TestCacheDirtyItem(t *testing.T) {
   535  	_, c := newTestCache(t)
   536  
   537  	assert.Nil(t, c.DirtyItem("potato"))
   538  
   539  	potato := c.Item("potato")
   540  
   541  	assert.Nil(t, c.DirtyItem("potato"))
   542  
   543  	require.NoError(t, potato.Open(nil))
   544  	require.NoError(t, potato.Truncate(5))
   545  
   546  	assert.Equal(t, potato, c.DirtyItem("potato"))
   547  
   548  	require.NoError(t, potato.Close(nil))
   549  
   550  	assert.Nil(t, c.DirtyItem("potato"))
   551  }
   552  
   553  func TestCacheExistsAndRemove(t *testing.T) {
   554  	_, c := newTestCache(t)
   555  
   556  	assert.False(t, c.Exists("potato"))
   557  
   558  	potato := c.Item("potato")
   559  
   560  	assert.False(t, c.Exists("potato"))
   561  
   562  	require.NoError(t, potato.Open(nil))
   563  
   564  	assert.True(t, c.Exists("potato"))
   565  
   566  	require.NoError(t, potato.Close(nil))
   567  
   568  	assert.True(t, c.Exists("potato"))
   569  
   570  	c.Remove("potato")
   571  
   572  	assert.False(t, c.Exists("potato"))
   573  
   574  }
   575  
   576  func TestCacheRename(t *testing.T) {
   577  	_, c := newTestCache(t)
   578  
   579  	// setup
   580  
   581  	assert.False(t, c.Exists("potato"))
   582  	potato := c.Item("potato")
   583  	require.NoError(t, potato.Open(nil))
   584  	require.NoError(t, potato.Close(nil))
   585  	assert.True(t, c.Exists("potato"))
   586  
   587  	osPath := c.toOSPath("potato")
   588  	osPathMeta := c.toOSPathMeta("potato")
   589  	assertPathExist(t, osPath)
   590  	assertPathExist(t, osPathMeta)
   591  
   592  	// rename potato -> newPotato
   593  
   594  	require.NoError(t, c.Rename("potato", "newPotato", nil))
   595  	assertPathNotExist(t, osPath)
   596  	assertPathNotExist(t, osPathMeta)
   597  	assert.False(t, c.Exists("potato"))
   598  
   599  	osPath = c.toOSPath("newPotato")
   600  	osPathMeta = c.toOSPathMeta("newPotato")
   601  	assertPathExist(t, osPath)
   602  	assertPathExist(t, osPathMeta)
   603  	assert.True(t, c.Exists("newPotato"))
   604  
   605  	// rename newPotato -> sub/newPotato
   606  
   607  	require.NoError(t, c.Rename("newPotato", "sub/newPotato", nil))
   608  	assertPathNotExist(t, osPath)
   609  	assertPathNotExist(t, osPathMeta)
   610  	assert.False(t, c.Exists("potato"))
   611  
   612  	osPath = c.toOSPath("sub/newPotato")
   613  	osPathMeta = c.toOSPathMeta("sub/newPotato")
   614  	assertPathExist(t, osPath)
   615  	assertPathExist(t, osPathMeta)
   616  	assert.True(t, c.Exists("sub/newPotato"))
   617  
   618  	// remove
   619  
   620  	c.Remove("sub/newPotato")
   621  	assertPathNotExist(t, osPath)
   622  	assertPathNotExist(t, osPathMeta)
   623  	assert.False(t, c.Exists("sub/newPotato"))
   624  
   625  	// nonexistent file - is ignored
   626  	assert.NoError(t, c.Rename("nonexist", "nonexist2", nil))
   627  }
   628  
   629  func TestCacheCleaner(t *testing.T) {
   630  	opt := vfscommon.DefaultOpt
   631  	opt.CachePollInterval = 10 * time.Millisecond
   632  	opt.CacheMaxAge = 20 * time.Millisecond
   633  	_, c := newTestCacheOpt(t, opt)
   634  
   635  	time.Sleep(2 * opt.CachePollInterval)
   636  
   637  	potato := c.Item("potato")
   638  	potato2, found := c.get("potato")
   639  	assert.Equal(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2))
   640  	assert.True(t, found)
   641  
   642  	for i := 0; i < 100; i++ {
   643  		time.Sleep(10 * opt.CachePollInterval)
   644  		potato2, found = c.get("potato")
   645  		if !found {
   646  			break
   647  		}
   648  	}
   649  
   650  	assert.NotEqual(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2))
   651  	assert.False(t, found)
   652  }
   653  
   654  func TestCacheSetModTime(t *testing.T) {
   655  	_, c := newTestCache(t)
   656  
   657  	t1 := time.Date(2010, 1, 2, 3, 4, 5, 9, time.UTC)
   658  
   659  	potato := c.Item("potato")
   660  	require.NoError(t, potato.Open(nil))
   661  	require.NoError(t, potato.Truncate(5))
   662  	require.NoError(t, potato.Close(nil))
   663  
   664  	c.SetModTime("potato", t1)
   665  	osPath := potato.c.toOSPath("potato")
   666  	fi, err := os.Stat(osPath)
   667  	require.NoError(t, err)
   668  
   669  	fstest.AssertTimeEqualWithPrecision(t, "potato", t1, fi.ModTime(), time.Second)
   670  }
   671  
   672  func TestCacheTotaInUse(t *testing.T) {
   673  	_, c := newTestCache(t)
   674  
   675  	assert.Equal(t, int(0), c.TotalInUse())
   676  
   677  	potato := c.Item("potato")
   678  	assert.Equal(t, int(0), c.TotalInUse())
   679  
   680  	require.NoError(t, potato.Open(nil))
   681  	assert.Equal(t, int(1), c.TotalInUse())
   682  
   683  	require.NoError(t, potato.Truncate(5))
   684  	assert.Equal(t, int(1), c.TotalInUse())
   685  
   686  	potato2 := c.Item("potato2")
   687  	assert.Equal(t, int(1), c.TotalInUse())
   688  
   689  	require.NoError(t, potato2.Open(nil))
   690  	assert.Equal(t, int(2), c.TotalInUse())
   691  
   692  	require.NoError(t, potato2.Close(nil))
   693  	assert.Equal(t, int(1), c.TotalInUse())
   694  
   695  	require.NoError(t, potato.Close(nil))
   696  	assert.Equal(t, int(0), c.TotalInUse())
   697  }
   698  
   699  func TestCacheDump(t *testing.T) {
   700  	_, c := newTestCache(t)
   701  
   702  	out := (*Cache)(nil).Dump()
   703  	assert.Equal(t, "Cache: <nil>\n", out)
   704  
   705  	out = c.Dump()
   706  	assert.Equal(t, "Cache{\n}\n", out)
   707  
   708  	c.Item("potato")
   709  
   710  	out = c.Dump()
   711  	want := "Cache{\n\t\"potato\": "
   712  	assert.Equal(t, want, out[:len(want)])
   713  
   714  	c.Remove("potato")
   715  
   716  	out = c.Dump()
   717  	assert.Equal(t, "Cache{\n}\n", out)
   718  }
   719  
   720  func TestCacheStats(t *testing.T) {
   721  	_, c := newTestCache(t)
   722  
   723  	out := c.Stats()
   724  	assert.Equal(t, int64(0), out["bytesUsed"])
   725  	assert.Equal(t, 0, out["erroredFiles"])
   726  	assert.Equal(t, 0, out["files"])
   727  	assert.Equal(t, 0, out["uploadsInProgress"])
   728  	assert.Equal(t, 0, out["uploadsQueued"])
   729  }