github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/vfs/vfscache/vfscache_test.go (about)

     1  package vfscache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/djherbis/times"
    14  	_ "github.com/rclone/rclone/backend/local" // import the local backend
    15  	"github.com/rclone/rclone/fstest"
    16  	"github.com/rclone/rclone/vfs/vfscommon"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  // TestMain drives the tests
    22  func TestMain(m *testing.M) {
    23  	fstest.TestMain(m)
    24  }
    25  
    26  // convert c.item to a string
    27  func itemAsString(c *Cache) []string {
    28  	c.itemMu.Lock()
    29  	defer c.itemMu.Unlock()
    30  	var out []string
    31  	for name, item := range c.item {
    32  		out = append(out, fmt.Sprintf("name=%q isFile=%v opens=%d size=%d", filepath.ToSlash(name), item.isFile, item.opens, item.size))
    33  	}
    34  	sort.Strings(out)
    35  	return out
    36  }
    37  
    38  func TestCacheNew(t *testing.T) {
    39  	r := fstest.NewRun(t)
    40  	defer r.Finalise()
    41  
    42  	ctx, cancel := context.WithCancel(context.Background())
    43  	defer cancel()
    44  
    45  	// Disable the cache cleaner as it interferes with these tests
    46  	opt := vfscommon.DefaultOpt
    47  	opt.CachePollInterval = 0
    48  	c, err := New(ctx, r.Fremote, &opt)
    49  	require.NoError(t, err)
    50  
    51  	assert.Contains(t, c.root, "vfs")
    52  	assert.Contains(t, c.fcache.Root(), filepath.Base(r.Fremote.Root()))
    53  	assert.Equal(t, []string(nil), itemAsString(c))
    54  
    55  	// mkdir
    56  	p, err := c.Mkdir("potato")
    57  	require.NoError(t, err)
    58  	assert.Equal(t, "potato", filepath.Base(p))
    59  	assert.Equal(t, []string{
    60  		`name="" isFile=false opens=0 size=0`,
    61  	}, itemAsString(c))
    62  
    63  	fi, err := os.Stat(filepath.Dir(p))
    64  	require.NoError(t, err)
    65  	assert.True(t, fi.IsDir())
    66  
    67  	// get
    68  	item := c.get("potato")
    69  	item2 := c.get("potato")
    70  	assert.Equal(t, item, item2)
    71  	assert.WithinDuration(t, time.Now(), item.atime, time.Second)
    72  
    73  	// updateTime
    74  	//.. before
    75  	t1 := time.Now().Add(-60 * time.Minute)
    76  	c.updateStat("potato", t1, 0)
    77  	item = c.get("potato")
    78  	assert.NotEqual(t, t1, item.atime)
    79  	assert.Equal(t, 0, item.opens)
    80  	//..after
    81  	t2 := time.Now().Add(60 * time.Minute)
    82  	c.updateStat("potato", t2, 0)
    83  	item = c.get("potato")
    84  	assert.Equal(t, t2, item.atime)
    85  	assert.Equal(t, 0, item.opens)
    86  
    87  	// open
    88  	assert.Equal(t, []string{
    89  		`name="" isFile=false opens=0 size=0`,
    90  		`name="potato" isFile=true opens=0 size=0`,
    91  	}, itemAsString(c))
    92  	c.Open("/potato")
    93  	assert.Equal(t, []string{
    94  		`name="" isFile=false opens=1 size=0`,
    95  		`name="potato" isFile=true opens=1 size=0`,
    96  	}, itemAsString(c))
    97  	item = c.get("potato")
    98  	assert.WithinDuration(t, time.Now(), item.atime, time.Second)
    99  	assert.Equal(t, 1, item.opens)
   100  
   101  	// write the file
   102  	err = ioutil.WriteFile(p, []byte("hello"), 0600)
   103  	require.NoError(t, err)
   104  
   105  	// read its atime
   106  	fi, err = os.Stat(p)
   107  	assert.NoError(t, err)
   108  	atime := times.Get(fi).AccessTime()
   109  
   110  	// updateAtimes
   111  	item = c.get("potato")
   112  	item.atime = time.Now().Add(-24 * time.Hour)
   113  	err = c.updateStats()
   114  	require.NoError(t, err)
   115  	assert.Equal(t, []string{
   116  		`name="" isFile=false opens=1 size=0`,
   117  		`name="potato" isFile=true opens=1 size=5`,
   118  	}, itemAsString(c))
   119  	item = c.get("potato")
   120  	assert.Equal(t, atime, item.atime)
   121  
   122  	// updateAtimes - not in the cache
   123  	oldItem := item
   124  	c.itemMu.Lock()
   125  	delete(c.item, "potato") // remove from cache
   126  	c.itemMu.Unlock()
   127  	err = c.updateStats()
   128  	require.NoError(t, err)
   129  	assert.Equal(t, []string{
   130  		`name="" isFile=false opens=1 size=0`,
   131  		`name="potato" isFile=true opens=0 size=5`,
   132  	}, itemAsString(c))
   133  	item = c.get("potato")
   134  	assert.Equal(t, atime, item.atime)
   135  	c.itemMu.Lock()
   136  	c.item["potato"] = oldItem // restore to cache
   137  	c.itemMu.Unlock()
   138  
   139  	// try purging with file open
   140  	c.purgeOld(10 * time.Second)
   141  	_, err = os.Stat(p)
   142  	assert.NoError(t, err)
   143  
   144  	// close
   145  	assert.Equal(t, []string{
   146  		`name="" isFile=false opens=1 size=0`,
   147  		`name="potato" isFile=true opens=1 size=5`,
   148  	}, itemAsString(c))
   149  	c.updateStat("potato", t2, 6)
   150  	assert.Equal(t, []string{
   151  		`name="" isFile=false opens=1 size=0`,
   152  		`name="potato" isFile=true opens=1 size=6`,
   153  	}, itemAsString(c))
   154  	c.Close("potato/")
   155  	assert.Equal(t, []string{
   156  		`name="" isFile=false opens=0 size=0`,
   157  		`name="potato" isFile=true opens=0 size=5`,
   158  	}, itemAsString(c))
   159  	item = c.get("potato")
   160  	assert.WithinDuration(t, time.Now(), item.atime, time.Second)
   161  	assert.Equal(t, 0, item.opens)
   162  
   163  	// try purging with file closed
   164  	c.purgeOld(10 * time.Second)
   165  	// ...nothing should happen
   166  	_, err = os.Stat(p)
   167  	assert.NoError(t, err)
   168  
   169  	//.. purge again with -ve age
   170  	c.purgeOld(-10 * time.Second)
   171  	_, err = os.Stat(p)
   172  	assert.True(t, os.IsNotExist(err))
   173  
   174  	// clean - have tested the internals already
   175  	c.clean()
   176  
   177  	// cleanup
   178  	err = c.CleanUp()
   179  	require.NoError(t, err)
   180  	_, err = os.Stat(c.root)
   181  	assert.True(t, os.IsNotExist(err))
   182  }
   183  
   184  func TestCacheOpens(t *testing.T) {
   185  	r := fstest.NewRun(t)
   186  	defer r.Finalise()
   187  
   188  	ctx, cancel := context.WithCancel(context.Background())
   189  	defer cancel()
   190  
   191  	c, err := New(ctx, r.Fremote, &vfscommon.DefaultOpt)
   192  	require.NoError(t, err)
   193  
   194  	assert.Equal(t, []string(nil), itemAsString(c))
   195  	c.Open("potato")
   196  	assert.Equal(t, []string{
   197  		`name="" isFile=false opens=1 size=0`,
   198  		`name="potato" isFile=true opens=1 size=0`,
   199  	}, itemAsString(c))
   200  	c.Open("potato")
   201  	assert.Equal(t, []string{
   202  		`name="" isFile=false opens=2 size=0`,
   203  		`name="potato" isFile=true opens=2 size=0`,
   204  	}, itemAsString(c))
   205  	c.Close("potato")
   206  	assert.Equal(t, []string{
   207  		`name="" isFile=false opens=1 size=0`,
   208  		`name="potato" isFile=true opens=1 size=0`,
   209  	}, itemAsString(c))
   210  	c.Close("potato")
   211  	assert.Equal(t, []string{
   212  		`name="" isFile=false opens=0 size=0`,
   213  		`name="potato" isFile=true opens=0 size=0`,
   214  	}, itemAsString(c))
   215  
   216  	c.Open("potato")
   217  	c.Open("a//b/c/d/one")
   218  	c.Open("a/b/c/d/e/two")
   219  	c.Open("a/b/c/d/e/f/three")
   220  	assert.Equal(t, []string{
   221  		`name="" isFile=false opens=4 size=0`,
   222  		`name="a" isFile=false opens=3 size=0`,
   223  		`name="a/b" isFile=false opens=3 size=0`,
   224  		`name="a/b/c" isFile=false opens=3 size=0`,
   225  		`name="a/b/c/d" isFile=false opens=3 size=0`,
   226  		`name="a/b/c/d/e" isFile=false opens=2 size=0`,
   227  		`name="a/b/c/d/e/f" isFile=false opens=1 size=0`,
   228  		`name="a/b/c/d/e/f/three" isFile=true opens=1 size=0`,
   229  		`name="a/b/c/d/e/two" isFile=true opens=1 size=0`,
   230  		`name="a/b/c/d/one" isFile=true opens=1 size=0`,
   231  		`name="potato" isFile=true opens=1 size=0`,
   232  	}, itemAsString(c))
   233  	c.Close("potato")
   234  	c.Close("a/b/c/d/one")
   235  	c.Close("a/b/c/d/e/two")
   236  	c.Close("a/b/c//d/e/f/three")
   237  	assert.Equal(t, []string{
   238  		`name="" isFile=false opens=0 size=0`,
   239  		`name="a" isFile=false opens=0 size=0`,
   240  		`name="a/b" isFile=false opens=0 size=0`,
   241  		`name="a/b/c" isFile=false opens=0 size=0`,
   242  		`name="a/b/c/d" isFile=false opens=0 size=0`,
   243  		`name="a/b/c/d/e" isFile=false opens=0 size=0`,
   244  		`name="a/b/c/d/e/f" isFile=false opens=0 size=0`,
   245  		`name="a/b/c/d/e/f/three" isFile=true opens=0 size=0`,
   246  		`name="a/b/c/d/e/two" isFile=true opens=0 size=0`,
   247  		`name="a/b/c/d/one" isFile=true opens=0 size=0`,
   248  		`name="potato" isFile=true opens=0 size=0`,
   249  	}, itemAsString(c))
   250  }
   251  
   252  // test the open, mkdir, purge, close, purge sequence
   253  func TestCacheOpenMkdir(t *testing.T) {
   254  	r := fstest.NewRun(t)
   255  	defer r.Finalise()
   256  
   257  	ctx, cancel := context.WithCancel(context.Background())
   258  	defer cancel()
   259  
   260  	// Disable the cache cleaner as it interferes with these tests
   261  	opt := vfscommon.DefaultOpt
   262  	opt.CachePollInterval = 0
   263  	c, err := New(ctx, r.Fremote, &opt)
   264  	require.NoError(t, err)
   265  
   266  	// open
   267  	c.Open("sub/potato")
   268  
   269  	assert.Equal(t, []string{
   270  		`name="" isFile=false opens=1 size=0`,
   271  		`name="sub" isFile=false opens=1 size=0`,
   272  		`name="sub/potato" isFile=true opens=1 size=0`,
   273  	}, itemAsString(c))
   274  
   275  	// mkdir
   276  	p, err := c.Mkdir("sub/potato")
   277  	require.NoError(t, err)
   278  	assert.Equal(t, "potato", filepath.Base(p))
   279  	assert.Equal(t, []string{
   280  		`name="" isFile=false opens=1 size=0`,
   281  		`name="sub" isFile=false opens=1 size=0`,
   282  		`name="sub/potato" isFile=true opens=1 size=0`,
   283  	}, itemAsString(c))
   284  
   285  	// test directory exists
   286  	fi, err := os.Stat(filepath.Dir(p))
   287  	require.NoError(t, err)
   288  	assert.True(t, fi.IsDir())
   289  
   290  	// clean the cache
   291  	c.purgeOld(-10 * time.Second)
   292  
   293  	// test directory still exists
   294  	fi, err = os.Stat(filepath.Dir(p))
   295  	require.NoError(t, err)
   296  	assert.True(t, fi.IsDir())
   297  
   298  	// close
   299  	c.Close("sub/potato")
   300  
   301  	assert.Equal(t, []string{
   302  		`name="" isFile=false opens=0 size=0`,
   303  		`name="sub" isFile=false opens=0 size=0`,
   304  		`name="sub/potato" isFile=true opens=0 size=0`,
   305  	}, itemAsString(c))
   306  
   307  	// clean the cache
   308  	c.purgeOld(-10 * time.Second)
   309  	c.purgeEmptyDirs()
   310  
   311  	assert.Equal(t, []string(nil), itemAsString(c))
   312  
   313  	// test directory does not exist
   314  	_, err = os.Stat(filepath.Dir(p))
   315  	require.True(t, os.IsNotExist(err))
   316  }
   317  
   318  func TestCacheCacheDir(t *testing.T) {
   319  	r := fstest.NewRun(t)
   320  	defer r.Finalise()
   321  
   322  	ctx, cancel := context.WithCancel(context.Background())
   323  	defer cancel()
   324  
   325  	c, err := New(ctx, r.Fremote, &vfscommon.DefaultOpt)
   326  	require.NoError(t, err)
   327  
   328  	assert.Equal(t, []string(nil), itemAsString(c))
   329  
   330  	c.cacheDir("dir")
   331  	assert.Equal(t, []string{
   332  		`name="" isFile=false opens=0 size=0`,
   333  		`name="dir" isFile=false opens=0 size=0`,
   334  	}, itemAsString(c))
   335  
   336  	c.cacheDir("dir/sub")
   337  	assert.Equal(t, []string{
   338  		`name="" isFile=false opens=0 size=0`,
   339  		`name="dir" isFile=false opens=0 size=0`,
   340  		`name="dir/sub" isFile=false opens=0 size=0`,
   341  	}, itemAsString(c))
   342  
   343  	c.cacheDir("dir/sub2/subsub2")
   344  	assert.Equal(t, []string{
   345  		`name="" isFile=false opens=0 size=0`,
   346  		`name="dir" isFile=false opens=0 size=0`,
   347  		`name="dir/sub" isFile=false opens=0 size=0`,
   348  		`name="dir/sub2" isFile=false opens=0 size=0`,
   349  		`name="dir/sub2/subsub2" isFile=false opens=0 size=0`,
   350  	}, itemAsString(c))
   351  }
   352  
   353  func TestCachePurgeOld(t *testing.T) {
   354  	r := fstest.NewRun(t)
   355  	defer r.Finalise()
   356  
   357  	ctx, cancel := context.WithCancel(context.Background())
   358  	defer cancel()
   359  
   360  	c, err := New(ctx, r.Fremote, &vfscommon.DefaultOpt)
   361  	require.NoError(t, err)
   362  
   363  	// Test funcs
   364  	var removed []string
   365  	removedDir := true
   366  	removeFile := func(name string) {
   367  		removed = append(removed, filepath.ToSlash(name))
   368  	}
   369  	removeDir := func(name string) bool {
   370  		if removedDir {
   371  			removed = append(removed, filepath.ToSlash(name)+"/")
   372  		}
   373  		return removedDir
   374  	}
   375  
   376  	removed = nil
   377  	c._purgeOld(-10*time.Second, removeFile)
   378  	c._purgeEmptyDirs(removeDir)
   379  	assert.Equal(t, []string(nil), removed)
   380  
   381  	c.Open("sub/dir2/potato2")
   382  	c.Open("sub/dir/potato")
   383  	c.Close("sub/dir2/potato2")
   384  	c.Open("sub/dir/potato")
   385  
   386  	assert.Equal(t, []string{
   387  		`name="" isFile=false opens=2 size=0`,
   388  		`name="sub" isFile=false opens=2 size=0`,
   389  		`name="sub/dir" isFile=false opens=2 size=0`,
   390  		`name="sub/dir/potato" isFile=true opens=2 size=0`,
   391  		`name="sub/dir2" isFile=false opens=0 size=0`,
   392  		`name="sub/dir2/potato2" isFile=true opens=0 size=0`,
   393  	}, itemAsString(c))
   394  
   395  	removed = nil
   396  	removedDir = true
   397  	c._purgeOld(-10*time.Second, removeFile)
   398  	c._purgeEmptyDirs(removeDir)
   399  	assert.Equal(t, []string{
   400  		"sub/dir2/potato2",
   401  		"sub/dir2/",
   402  	}, removed)
   403  
   404  	c.Close("sub/dir/potato")
   405  
   406  	removed = nil
   407  	removedDir = true
   408  	c._purgeOld(-10*time.Second, removeFile)
   409  	c._purgeEmptyDirs(removeDir)
   410  	assert.Equal(t, []string(nil), removed)
   411  
   412  	c.Close("sub/dir/potato")
   413  
   414  	assert.Equal(t, []string{
   415  		`name="" isFile=false opens=0 size=0`,
   416  		`name="sub" isFile=false opens=0 size=0`,
   417  		`name="sub/dir" isFile=false opens=0 size=0`,
   418  		`name="sub/dir/potato" isFile=true opens=0 size=0`,
   419  	}, itemAsString(c))
   420  
   421  	removed = nil
   422  	removedDir = false
   423  	c._purgeOld(10*time.Second, removeFile)
   424  	c._purgeEmptyDirs(removeDir)
   425  	assert.Equal(t, []string(nil), removed)
   426  
   427  	assert.Equal(t, []string{
   428  		`name="" isFile=false opens=0 size=0`,
   429  		`name="sub" isFile=false opens=0 size=0`,
   430  		`name="sub/dir" isFile=false opens=0 size=0`,
   431  		`name="sub/dir/potato" isFile=true opens=0 size=0`,
   432  	}, itemAsString(c))
   433  
   434  	removed = nil
   435  	removedDir = true
   436  	c._purgeOld(-10*time.Second, removeFile)
   437  	c._purgeEmptyDirs(removeDir)
   438  	assert.Equal(t, []string{
   439  		"sub/dir/potato",
   440  		"sub/dir/",
   441  		"sub/",
   442  		"/",
   443  	}, removed)
   444  
   445  	assert.Equal(t, []string(nil), itemAsString(c))
   446  }
   447  
   448  func TestCachePurgeOverQuota(t *testing.T) {
   449  	r := fstest.NewRun(t)
   450  	defer r.Finalise()
   451  
   452  	ctx, cancel := context.WithCancel(context.Background())
   453  	defer cancel()
   454  
   455  	// Disable the cache cleaner as it interferes with these tests
   456  	opt := vfscommon.DefaultOpt
   457  	opt.CachePollInterval = 0
   458  	c, err := New(ctx, r.Fremote, &opt)
   459  	require.NoError(t, err)
   460  
   461  	// Test funcs
   462  	var removed []string
   463  	remove := func(name string) {
   464  		removed = append(removed, filepath.ToSlash(name))
   465  		c.Remove(name)
   466  	}
   467  
   468  	removed = nil
   469  	c._purgeOverQuota(-1, remove)
   470  	assert.Equal(t, []string(nil), removed)
   471  
   472  	removed = nil
   473  	c._purgeOverQuota(0, remove)
   474  	assert.Equal(t, []string(nil), removed)
   475  
   476  	removed = nil
   477  	c._purgeOverQuota(1, remove)
   478  	assert.Equal(t, []string(nil), removed)
   479  
   480  	// Make some test files
   481  	c.Open("sub/dir/potato")
   482  	p, err := c.Mkdir("sub/dir/potato")
   483  	require.NoError(t, err)
   484  	err = ioutil.WriteFile(p, []byte("hello"), 0600)
   485  	require.NoError(t, err)
   486  
   487  	p, err = c.Mkdir("sub/dir2/potato2")
   488  	c.Open("sub/dir2/potato2")
   489  	require.NoError(t, err)
   490  	err = ioutil.WriteFile(p, []byte("hello2"), 0600)
   491  	require.NoError(t, err)
   492  
   493  	assert.Equal(t, []string{
   494  		`name="" isFile=false opens=2 size=0`,
   495  		`name="sub" isFile=false opens=2 size=0`,
   496  		`name="sub/dir" isFile=false opens=1 size=0`,
   497  		`name="sub/dir/potato" isFile=true opens=1 size=0`,
   498  		`name="sub/dir2" isFile=false opens=1 size=0`,
   499  		`name="sub/dir2/potato2" isFile=true opens=1 size=0`,
   500  	}, itemAsString(c))
   501  
   502  	// Check nothing removed
   503  	removed = nil
   504  	c._purgeOverQuota(1, remove)
   505  	assert.Equal(t, []string(nil), removed)
   506  
   507  	// Close the files
   508  	c.Close("sub/dir/potato")
   509  	c.Close("sub/dir2/potato2")
   510  
   511  	assert.Equal(t, []string{
   512  		`name="" isFile=false opens=0 size=0`,
   513  		`name="sub" isFile=false opens=0 size=0`,
   514  		`name="sub/dir" isFile=false opens=0 size=0`,
   515  		`name="sub/dir/potato" isFile=true opens=0 size=5`,
   516  		`name="sub/dir2" isFile=false opens=0 size=0`,
   517  		`name="sub/dir2/potato2" isFile=true opens=0 size=6`,
   518  	}, itemAsString(c))
   519  
   520  	// Update the stats to read the total size
   521  	err = c.updateStats()
   522  	require.NoError(t, err)
   523  	assert.Equal(t, int64(11), c.used)
   524  
   525  	// make potato2 definitely after potato
   526  	t1 := time.Now().Add(10 * time.Second)
   527  	c.updateStat("sub/dir2/potato2", t1, 6)
   528  
   529  	// Check only potato removed to get below quota
   530  	removed = nil
   531  	c._purgeOverQuota(10, remove)
   532  	assert.Equal(t, []string{
   533  		"sub/dir/potato",
   534  	}, removed)
   535  	assert.Equal(t, int64(6), c.used)
   536  
   537  	assert.Equal(t, []string{
   538  		`name="" isFile=false opens=0 size=0`,
   539  		`name="sub" isFile=false opens=0 size=0`,
   540  		`name="sub/dir" isFile=false opens=0 size=0`,
   541  		`name="sub/dir2" isFile=false opens=0 size=0`,
   542  		`name="sub/dir2/potato2" isFile=true opens=0 size=6`,
   543  	}, itemAsString(c))
   544  
   545  	// Put potato back
   546  	c.Open("sub/dir/potato")
   547  	p, err = c.Mkdir("sub/dir/potato")
   548  	require.NoError(t, err)
   549  	err = ioutil.WriteFile(p, []byte("hello"), 0600)
   550  	require.NoError(t, err)
   551  	c.Close("sub/dir/potato")
   552  
   553  	// Update the stats to read the total size
   554  	err = c.updateStats()
   555  	require.NoError(t, err)
   556  	assert.Equal(t, int64(11), c.used)
   557  
   558  	assert.Equal(t, []string{
   559  		`name="" isFile=false opens=0 size=0`,
   560  		`name="sub" isFile=false opens=0 size=0`,
   561  		`name="sub/dir" isFile=false opens=0 size=0`,
   562  		`name="sub/dir/potato" isFile=true opens=0 size=5`,
   563  		`name="sub/dir2" isFile=false opens=0 size=0`,
   564  		`name="sub/dir2/potato2" isFile=true opens=0 size=6`,
   565  	}, itemAsString(c))
   566  
   567  	// make potato definitely after potato2
   568  	t2 := t1.Add(20 * time.Second)
   569  	c.updateStat("sub/dir/potato", t2, 5)
   570  
   571  	// Check only potato2 removed to get below quota
   572  	removed = nil
   573  	c._purgeOverQuota(10, remove)
   574  	assert.Equal(t, []string{
   575  		"sub/dir2/potato2",
   576  	}, removed)
   577  	assert.Equal(t, int64(5), c.used)
   578  	c.purgeEmptyDirs()
   579  
   580  	assert.Equal(t, []string{
   581  		`name="" isFile=false opens=0 size=0`,
   582  		`name="sub" isFile=false opens=0 size=0`,
   583  		`name="sub/dir" isFile=false opens=0 size=0`,
   584  		`name="sub/dir/potato" isFile=true opens=0 size=5`,
   585  	}, itemAsString(c))
   586  
   587  	// Now purge everything
   588  	removed = nil
   589  	c._purgeOverQuota(1, remove)
   590  	assert.Equal(t, []string{
   591  		"sub/dir/potato",
   592  	}, removed)
   593  	assert.Equal(t, int64(0), c.used)
   594  	c.purgeEmptyDirs()
   595  
   596  	assert.Equal(t, []string(nil), itemAsString(c))
   597  
   598  	// Check nothing left behind
   599  	c.clean()
   600  	assert.Equal(t, int64(0), c.used)
   601  	assert.Equal(t, []string(nil), itemAsString(c))
   602  }