2020-02-29 19:08:22 +01:00
|
|
|
package vfscache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
_ "github.com/rclone/rclone/backend/local" // import the local backend
|
2023-09-05 13:38:19 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fs/config"
|
2020-02-29 19:08:22 +01:00
|
|
|
"github.com/rclone/rclone/fstest"
|
2023-09-05 13:38:19 +02:00
|
|
|
"github.com/rclone/rclone/lib/diskusage"
|
2020-02-29 19:08:22 +01:00
|
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestMain drives the tests
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
fstest.TestMain(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert c.item to a string
|
|
|
|
func itemAsString(c *Cache) []string {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
var out []string
|
|
|
|
for name, item := range c.item {
|
|
|
|
out = append(out, fmt.Sprintf("name=%q opens=%d size=%d", filepath.ToSlash(name), item.opens, item.info.Size))
|
|
|
|
}
|
|
|
|
sort.Strings(out)
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
// convert c.item to a string
|
|
|
|
func itemSpaceAsString(c *Cache) []string {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
var out []string
|
|
|
|
for name, item := range c.item {
|
|
|
|
space := item.info.Rs.Size()
|
|
|
|
out = append(out, fmt.Sprintf("name=%q opens=%d size=%d space=%d", filepath.ToSlash(name), item.opens, item.info.Size, space))
|
|
|
|
}
|
|
|
|
sort.Strings(out)
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-04-17 12:18:58 +02:00
|
|
|
// open an item and write to it
|
|
|
|
func itemWrite(t *testing.T, item *Item, contents string) {
|
|
|
|
require.NoError(t, item.Open(nil))
|
|
|
|
_, err := item.WriteAt([]byte(contents), 0)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertPathNotExist(t *testing.T, path string) {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
assert.True(t, os.IsNotExist(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertPathExist(t *testing.T, path string) os.FileInfo {
|
|
|
|
fi, err := os.Stat(path)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
return fi
|
|
|
|
}
|
|
|
|
|
2020-06-23 16:18:58 +02:00
|
|
|
type avInfo struct {
|
|
|
|
Remote string
|
|
|
|
Size int64
|
|
|
|
IsDir bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var avInfos []avInfo
|
|
|
|
|
|
|
|
func addVirtual(remote string, size int64, isDir bool) error {
|
|
|
|
avInfos = append(avInfos, avInfo{
|
|
|
|
Remote: remote,
|
|
|
|
Size: size,
|
|
|
|
IsDir: isDir,
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-08 13:43:53 +01:00
|
|
|
func newTestCacheOpt(t *testing.T, opt vfscommon.Options) (r *fstest.Run, c *Cache) {
|
2020-04-17 12:18:58 +02:00
|
|
|
r = fstest.NewRun(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
2020-06-23 16:18:58 +02:00
|
|
|
avInfos = nil
|
|
|
|
c, err := New(ctx, r.Fremote, &opt, addVirtual)
|
2020-04-17 12:18:58 +02:00
|
|
|
require.NoError(t, err)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
2022-12-08 13:43:53 +01:00
|
|
|
t.Cleanup(func() {
|
2020-04-17 12:18:58 +02:00
|
|
|
err := c.CleanUp()
|
|
|
|
require.NoError(t, err)
|
|
|
|
assertPathNotExist(t, c.root)
|
|
|
|
cancel()
|
2022-12-08 13:43:53 +01:00
|
|
|
})
|
2020-04-17 12:18:58 +02:00
|
|
|
|
2022-12-08 13:43:53 +01:00
|
|
|
return r, c
|
2020-04-17 12:18:58 +02:00
|
|
|
}
|
|
|
|
|
2022-12-08 13:43:53 +01:00
|
|
|
func newTestCache(t *testing.T) (r *fstest.Run, c *Cache) {
|
2024-07-03 12:34:29 +02:00
|
|
|
opt := vfscommon.Opt
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
// Disable the cache cleaner as it interferes with these tests
|
2020-02-29 19:08:22 +01:00
|
|
|
opt.CachePollInterval = 0
|
2020-04-17 12:18:58 +02:00
|
|
|
|
2020-06-03 16:49:41 +02:00
|
|
|
// Disable synchronous write
|
2020-04-17 12:18:58 +02:00
|
|
|
opt.WriteBack = 0
|
|
|
|
|
|
|
|
return newTestCacheOpt(t, opt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheNew(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, c := newTestCache(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Contains(t, c.root, "vfs")
|
|
|
|
assert.Contains(t, c.fcache.Root(), filepath.Base(r.Fremote.Root()))
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
|
2021-05-28 15:11:19 +02:00
|
|
|
// createItemDir
|
|
|
|
p, err := c.createItemDir("potato")
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "potato", filepath.Base(p))
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
|
2020-04-17 12:18:58 +02:00
|
|
|
fi := assertPathExist(t, filepath.Dir(p))
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.True(t, fi.IsDir())
|
|
|
|
|
|
|
|
// get
|
|
|
|
item, _ := c.get("potato")
|
|
|
|
item2, _ := c.get("potato")
|
|
|
|
assert.Equal(t, item, item2)
|
|
|
|
assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second)
|
|
|
|
|
|
|
|
// open
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
potato := c.Item("/potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
assert.WithinDuration(t, time.Now(), potato.info.ATime, time.Second)
|
|
|
|
assert.Equal(t, 1, potato.opens)
|
|
|
|
|
|
|
|
// write the file
|
|
|
|
require.NoError(t, potato.Truncate(5))
|
|
|
|
atime := time.Now()
|
|
|
|
potato.info.ATime = atime
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=5`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
assert.True(t, atime.Equal(potato.info.ATime), fmt.Sprintf("%v != %v", atime, potato.info.ATime))
|
|
|
|
|
|
|
|
// try purging with file open
|
|
|
|
c.purgeOld(10 * time.Second)
|
2020-04-17 12:18:58 +02:00
|
|
|
assertPathExist(t, p)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// close
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=5`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Truncate(6))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=0 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
item, _ = c.get("potato")
|
|
|
|
assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second)
|
|
|
|
assert.Equal(t, 0, item.opens)
|
|
|
|
|
|
|
|
// try purging with file closed
|
|
|
|
c.purgeOld(10 * time.Second)
|
2020-04-17 12:18:58 +02:00
|
|
|
assertPathExist(t, p)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
//.. purge again with -ve age
|
|
|
|
c.purgeOld(-10 * time.Second)
|
2020-04-17 12:18:58 +02:00
|
|
|
assertPathNotExist(t, p)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// clean - have tested the internals already
|
2020-08-25 17:20:29 +02:00
|
|
|
c.clean(false)
|
2020-02-29 19:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheOpens(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
potato := c.Item("potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=2 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
a1 := c.Item("a//b/c/d/one")
|
|
|
|
a2 := c.Item("a/b/c/d/e/two")
|
|
|
|
a3 := c.Item("a/b/c/d/e/f/three")
|
|
|
|
require.NoError(t, a1.Open(nil))
|
|
|
|
require.NoError(t, a2.Open(nil))
|
|
|
|
require.NoError(t, a3.Open(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="a/b/c/d/e/f/three" opens=1 size=0`,
|
|
|
|
`name="a/b/c/d/e/two" opens=1 size=0`,
|
|
|
|
`name="a/b/c/d/one" opens=1 size=0`,
|
|
|
|
`name="potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
require.NoError(t, a1.Close(nil))
|
|
|
|
require.NoError(t, a2.Close(nil))
|
|
|
|
require.NoError(t, a3.Close(nil))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="a/b/c/d/e/f/three" opens=0 size=0`,
|
|
|
|
`name="a/b/c/d/e/two" opens=0 size=0`,
|
|
|
|
`name="a/b/c/d/one" opens=0 size=0`,
|
|
|
|
`name="potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
}
|
|
|
|
|
2021-05-28 15:11:19 +02:00
|
|
|
// test the open, createItemDir, purge, close, purge sequence
|
2020-02-29 19:08:22 +01:00
|
|
|
func TestCacheOpenMkdir(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// open
|
|
|
|
potato := c.Item("sub/potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
2021-05-28 15:11:19 +02:00
|
|
|
// createItemDir
|
|
|
|
p, err := c.createItemDir("sub/potato")
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "potato", filepath.Base(p))
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// test directory exists
|
2020-04-17 12:18:58 +02:00
|
|
|
fi := assertPathExist(t, filepath.Dir(p))
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.True(t, fi.IsDir())
|
|
|
|
|
|
|
|
// clean the cache
|
|
|
|
c.purgeOld(-10 * time.Second)
|
|
|
|
|
|
|
|
// test directory still exists
|
2020-04-17 12:18:58 +02:00
|
|
|
fi = assertPathExist(t, filepath.Dir(p))
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.True(t, fi.IsDir())
|
|
|
|
|
|
|
|
// close
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// clean the cache
|
|
|
|
c.purgeOld(-10 * time.Second)
|
2021-03-17 10:48:25 +01:00
|
|
|
c.purgeEmptyDirs("", true)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
|
2020-04-17 12:18:58 +02:00
|
|
|
// test directory does not exist
|
|
|
|
assertPathNotExist(t, filepath.Dir(p))
|
2020-02-29 19:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestCachePurgeOld(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// Test funcs
|
2020-08-25 17:20:29 +02:00
|
|
|
c.purgeOld(-10 * time.Second)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
potato2 := c.Item("sub/dir2/potato2")
|
|
|
|
require.NoError(t, potato2.Open(nil))
|
|
|
|
potato := c.Item("sub/dir/potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
require.NoError(t, potato2.Close(nil))
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=2 size=0`,
|
|
|
|
`name="sub/dir2/potato2" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
c.purgeOld(-10 * time.Second)
|
|
|
|
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.Equal(t, []string{
|
2020-08-25 17:20:29 +02:00
|
|
|
`name="sub/dir/potato" opens=2 size=0`,
|
|
|
|
}, itemAsString(c))
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
c.purgeOld(-10 * time.Second)
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=1 size=0`,
|
|
|
|
}, itemAsString(c))
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
c.purgeOld(10 * time.Second)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=0 size=0`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
c.purgeOld(-10 * time.Second)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCachePurgeOverQuota(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// Test funcs
|
|
|
|
|
|
|
|
// Make some test files
|
|
|
|
potato := c.Item("sub/dir/potato")
|
2020-04-17 12:18:58 +02:00
|
|
|
itemWrite(t, potato, "hello")
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
potato2 := c.Item("sub/dir2/potato2")
|
2020-04-17 12:18:58 +02:00
|
|
|
itemWrite(t, potato2, "hello2")
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=1 size=5`,
|
|
|
|
`name="sub/dir2/potato2" opens=1 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// Check nothing removed
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 1
|
|
|
|
c.purgeOverQuota()
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
// Close the files
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
require.NoError(t, potato2.Close(nil))
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=0 size=5`,
|
|
|
|
`name="sub/dir2/potato2" opens=0 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// Update the stats to read the total size
|
|
|
|
c.updateUsed()
|
|
|
|
|
|
|
|
// make potato2 definitely after potato
|
|
|
|
t1 := time.Now().Add(10 * time.Second)
|
|
|
|
potato2.info.ATime = t1
|
|
|
|
|
|
|
|
// Check only potato removed to get below quota
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 10
|
|
|
|
c.purgeOverQuota()
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.Equal(t, int64(6), c.used)
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir2/potato2" opens=0 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// Put potato back
|
|
|
|
potato = c.Item("sub/dir/potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
require.NoError(t, potato.Truncate(5))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
// Update the stats to read the total size
|
|
|
|
c.updateUsed()
|
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=0 size=5`,
|
|
|
|
`name="sub/dir2/potato2" opens=0 size=6`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// make potato definitely after potato2
|
|
|
|
t2 := t1.Add(20 * time.Second)
|
|
|
|
potato.info.ATime = t2
|
|
|
|
|
|
|
|
// Check only potato2 removed to get below quota
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 10
|
|
|
|
c.purgeOverQuota()
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.Equal(t, int64(5), c.used)
|
2021-03-17 10:48:25 +01:00
|
|
|
c.purgeEmptyDirs("", true)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="sub/dir/potato" opens=0 size=5`,
|
|
|
|
}, itemAsString(c))
|
|
|
|
|
|
|
|
// Now purge everything
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 1
|
|
|
|
c.purgeOverQuota()
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.Equal(t, int64(0), c.used)
|
2021-03-17 10:48:25 +01:00
|
|
|
c.purgeEmptyDirs("", true)
|
2020-02-29 19:08:22 +01:00
|
|
|
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
|
|
|
|
// Check nothing left behind
|
2020-08-25 17:20:29 +02:00
|
|
|
c.clean(false)
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.Equal(t, int64(0), c.used)
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
}
|
2020-04-17 12:18:58 +02:00
|
|
|
|
2023-09-05 13:38:19 +02:00
|
|
|
func TestCachePurgeMinFreeSpace(t *testing.T) {
|
|
|
|
du, err := diskusage.New(config.GetCacheDir())
|
|
|
|
if err == diskusage.ErrUnsupported {
|
|
|
|
t.Skip(err)
|
|
|
|
}
|
|
|
|
// We've tested the quota mechanism already, so just test the
|
|
|
|
// min free space quota is working.
|
|
|
|
_, c := newTestCache(t)
|
|
|
|
|
|
|
|
// First set free space quota very small and check it is OK
|
|
|
|
c.opt.CacheMinFreeSpace = 1
|
|
|
|
assert.True(t, c.minFreeSpaceQuotaOK())
|
|
|
|
assert.True(t, c.quotasOK())
|
|
|
|
|
|
|
|
// Now set it a bit larger than the current disk available and check it is BAD
|
|
|
|
c.opt.CacheMinFreeSpace = fs.SizeSuffix(du.Available) + fs.Gibi
|
|
|
|
assert.False(t, c.minFreeSpaceQuotaOK())
|
|
|
|
assert.False(t, c.quotasOK())
|
|
|
|
}
|
|
|
|
|
2020-08-25 17:20:29 +02:00
|
|
|
// test reset clean files
|
|
|
|
func TestCachePurgeClean(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, c := newItemTestCache(t)
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
contents, obj, potato1 := newFile(t, r, c, "existing")
|
2020-08-25 17:20:29 +02:00
|
|
|
_ = contents
|
|
|
|
|
|
|
|
// Open the object to create metadata for it
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
require.NoError(t, potato1.Open(obj))
|
|
|
|
require.NoError(t, potato1.Open(obj))
|
2020-08-25 17:20:29 +02:00
|
|
|
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
size, err := potato1.GetSize()
|
2020-08-25 17:20:29 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(100), size)
|
|
|
|
|
|
|
|
// Read something to instantiate the cache file
|
|
|
|
buf := make([]byte, 10)
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
_, err = potato1.ReadAt(buf, 10)
|
2020-08-25 17:20:29 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Test cache file present
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
_, err = os.Stat(potato1.c.toOSPath(potato1.name))
|
2020-08-25 17:20:29 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-08-14 04:56:32 +02:00
|
|
|
// Add some potatoes
|
2020-08-25 17:20:29 +02:00
|
|
|
potato2 := c.Item("sub/dir/potato2")
|
|
|
|
require.NoError(t, potato2.Open(nil))
|
|
|
|
require.NoError(t, potato2.Truncate(5))
|
|
|
|
|
|
|
|
potato3 := c.Item("sub/dir/potato3")
|
|
|
|
require.NoError(t, potato3.Open(nil))
|
|
|
|
require.NoError(t, potato3.Truncate(6))
|
|
|
|
|
|
|
|
c.updateUsed()
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 1
|
|
|
|
c.purgeClean()
|
2020-08-25 17:20:29 +02:00
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="existing" opens=2 size=100 space=0`,
|
|
|
|
`name="sub/dir/potato2" opens=1 size=5 space=5`,
|
|
|
|
`name="sub/dir/potato3" opens=1 size=6 space=6`,
|
|
|
|
}, itemSpaceAsString(c))
|
|
|
|
assert.Equal(t, int64(11), c.used)
|
|
|
|
|
|
|
|
require.NoError(t, potato2.Close(nil))
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 1
|
|
|
|
c.purgeClean()
|
2020-08-25 17:20:29 +02:00
|
|
|
assert.Equal(t, []string{
|
|
|
|
`name="existing" opens=2 size=100 space=0`,
|
|
|
|
`name="sub/dir/potato3" opens=1 size=6 space=6`,
|
|
|
|
}, itemSpaceAsString(c))
|
|
|
|
assert.Equal(t, int64(6), c.used)
|
|
|
|
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
require.NoError(t, potato1.Close(nil))
|
|
|
|
require.NoError(t, potato1.Close(nil))
|
2020-08-25 17:20:29 +02:00
|
|
|
require.NoError(t, potato3.Close(nil))
|
|
|
|
|
|
|
|
// Remove all files now. The are all not in use.
|
|
|
|
// purgeClean does not remove empty cache files. purgeOverQuota does.
|
|
|
|
// So we use purgeOverQuota here for the cleanup.
|
2023-09-05 13:38:19 +02:00
|
|
|
c.opt.CacheMaxSize = 1
|
|
|
|
c.purgeOverQuota()
|
2020-08-25 17:20:29 +02:00
|
|
|
|
2021-03-17 10:48:25 +01:00
|
|
|
c.purgeEmptyDirs("", true)
|
2020-08-25 17:20:29 +02:00
|
|
|
|
|
|
|
assert.Equal(t, []string(nil), itemAsString(c))
|
|
|
|
}
|
|
|
|
|
2020-04-17 12:18:58 +02:00
|
|
|
func TestCacheInUse(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
assert.False(t, c.InUse("potato"))
|
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
|
|
|
|
assert.False(t, c.InUse("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
|
|
|
|
assert.True(t, c.InUse("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
assert.False(t, c.InUse("potato"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheDirtyItem(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
assert.Nil(t, c.DirtyItem("potato"))
|
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
|
|
|
|
assert.Nil(t, c.DirtyItem("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
require.NoError(t, potato.Truncate(5))
|
|
|
|
|
|
|
|
assert.Equal(t, potato, c.DirtyItem("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
assert.Nil(t, c.DirtyItem("potato"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheExistsAndRemove(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
|
|
|
|
assert.True(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
assert.True(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
c.Remove("potato")
|
|
|
|
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheRename(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
// setup
|
|
|
|
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
potato := c.Item("potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
assert.True(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
osPath := c.toOSPath("potato")
|
|
|
|
osPathMeta := c.toOSPathMeta("potato")
|
|
|
|
assertPathExist(t, osPath)
|
|
|
|
assertPathExist(t, osPathMeta)
|
|
|
|
|
|
|
|
// rename potato -> newPotato
|
|
|
|
|
|
|
|
require.NoError(t, c.Rename("potato", "newPotato", nil))
|
|
|
|
assertPathNotExist(t, osPath)
|
|
|
|
assertPathNotExist(t, osPathMeta)
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
osPath = c.toOSPath("newPotato")
|
|
|
|
osPathMeta = c.toOSPathMeta("newPotato")
|
|
|
|
assertPathExist(t, osPath)
|
|
|
|
assertPathExist(t, osPathMeta)
|
|
|
|
assert.True(t, c.Exists("newPotato"))
|
|
|
|
|
|
|
|
// rename newPotato -> sub/newPotato
|
|
|
|
|
|
|
|
require.NoError(t, c.Rename("newPotato", "sub/newPotato", nil))
|
|
|
|
assertPathNotExist(t, osPath)
|
|
|
|
assertPathNotExist(t, osPathMeta)
|
|
|
|
assert.False(t, c.Exists("potato"))
|
|
|
|
|
|
|
|
osPath = c.toOSPath("sub/newPotato")
|
|
|
|
osPathMeta = c.toOSPathMeta("sub/newPotato")
|
|
|
|
assertPathExist(t, osPath)
|
|
|
|
assertPathExist(t, osPathMeta)
|
|
|
|
assert.True(t, c.Exists("sub/newPotato"))
|
|
|
|
|
|
|
|
// remove
|
|
|
|
|
|
|
|
c.Remove("sub/newPotato")
|
|
|
|
assertPathNotExist(t, osPath)
|
|
|
|
assertPathNotExist(t, osPathMeta)
|
|
|
|
assert.False(t, c.Exists("sub/newPotato"))
|
|
|
|
|
2022-08-14 04:56:32 +02:00
|
|
|
// nonexistent file - is ignored
|
2020-04-17 12:18:58 +02:00
|
|
|
assert.NoError(t, c.Rename("nonexist", "nonexist2", nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheCleaner(t *testing.T) {
|
2024-07-03 12:34:29 +02:00
|
|
|
opt := vfscommon.Opt
|
2024-07-02 18:31:51 +02:00
|
|
|
opt.CachePollInterval = fs.Duration(10 * time.Millisecond)
|
|
|
|
opt.CacheMaxAge = fs.Duration(20 * time.Millisecond)
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCacheOpt(t, opt)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
2024-07-02 18:31:51 +02:00
|
|
|
time.Sleep(time.Duration(2 * opt.CachePollInterval))
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
potato2, found := c.get("potato")
|
2020-06-03 16:49:41 +02:00
|
|
|
assert.Equal(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2))
|
2020-04-17 12:18:58 +02:00
|
|
|
assert.True(t, found)
|
|
|
|
|
2020-06-23 15:41:59 +02:00
|
|
|
for i := 0; i < 100; i++ {
|
2024-07-02 18:31:51 +02:00
|
|
|
time.Sleep(time.Duration(10 * opt.CachePollInterval))
|
2020-06-23 15:41:59 +02:00
|
|
|
potato2, found = c.get("potato")
|
|
|
|
if !found {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 12:18:58 +02:00
|
|
|
|
2020-06-03 16:49:41 +02:00
|
|
|
assert.NotEqual(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2))
|
2020-04-17 12:18:58 +02:00
|
|
|
assert.False(t, found)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheSetModTime(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
t1 := time.Date(2010, 1, 2, 3, 4, 5, 9, time.UTC)
|
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
require.NoError(t, potato.Truncate(5))
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
|
|
|
|
c.SetModTime("potato", t1)
|
|
|
|
osPath := potato.c.toOSPath("potato")
|
|
|
|
fi, err := os.Stat(osPath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
fstest.AssertTimeEqualWithPrecision(t, "potato", t1, fi.ModTime(), time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheTotaInUse(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
assert.Equal(t, int(0), c.TotalInUse())
|
|
|
|
|
|
|
|
potato := c.Item("potato")
|
|
|
|
assert.Equal(t, int(0), c.TotalInUse())
|
|
|
|
|
|
|
|
require.NoError(t, potato.Open(nil))
|
|
|
|
assert.Equal(t, int(1), c.TotalInUse())
|
|
|
|
|
|
|
|
require.NoError(t, potato.Truncate(5))
|
|
|
|
assert.Equal(t, int(1), c.TotalInUse())
|
|
|
|
|
|
|
|
potato2 := c.Item("potato2")
|
|
|
|
assert.Equal(t, int(1), c.TotalInUse())
|
|
|
|
|
|
|
|
require.NoError(t, potato2.Open(nil))
|
|
|
|
assert.Equal(t, int(2), c.TotalInUse())
|
|
|
|
|
|
|
|
require.NoError(t, potato2.Close(nil))
|
|
|
|
assert.Equal(t, int(1), c.TotalInUse())
|
|
|
|
|
|
|
|
require.NoError(t, potato.Close(nil))
|
|
|
|
assert.Equal(t, int(0), c.TotalInUse())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCacheDump(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2020-04-17 12:18:58 +02:00
|
|
|
|
|
|
|
out := (*Cache)(nil).Dump()
|
|
|
|
assert.Equal(t, "Cache: <nil>\n", out)
|
|
|
|
|
|
|
|
out = c.Dump()
|
|
|
|
assert.Equal(t, "Cache{\n}\n", out)
|
|
|
|
|
|
|
|
c.Item("potato")
|
|
|
|
|
|
|
|
out = c.Dump()
|
|
|
|
want := "Cache{\n\t\"potato\": "
|
|
|
|
assert.Equal(t, want, out[:len(want)])
|
|
|
|
|
|
|
|
c.Remove("potato")
|
|
|
|
|
|
|
|
out = c.Dump()
|
|
|
|
assert.Equal(t, "Cache{\n}\n", out)
|
|
|
|
}
|
2021-11-17 17:11:08 +01:00
|
|
|
|
|
|
|
func TestCacheStats(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, c := newTestCache(t)
|
2021-11-17 17:11:08 +01:00
|
|
|
|
|
|
|
out := c.Stats()
|
|
|
|
assert.Equal(t, int64(0), out["bytesUsed"])
|
|
|
|
assert.Equal(t, 0, out["erroredFiles"])
|
|
|
|
assert.Equal(t, 0, out["files"])
|
|
|
|
assert.Equal(t, 0, out["uploadsInProgress"])
|
|
|
|
assert.Equal(t, 0, out["uploadsQueued"])
|
|
|
|
}
|