mirror of
https://github.com/rclone/rclone.git
synced 2025-04-22 12:04:57 +08:00

Some checks failed
build / linux_386 (push) Has been cancelled
Build & Push Docker Images / Build Docker Image for linux/386 (push) Has been cancelled
Build & Push Docker Images / Build Docker Image for linux/amd64 (push) Has been cancelled
Build & Push Docker Images / Build Docker Image for linux/arm/v6 (push) Has been cancelled
Build & Push Docker Images / Build Docker Image for linux/arm/v7 (push) Has been cancelled
build / windows (push) Has been cancelled
build / other_os (push) Has been cancelled
build / mac_amd64 (push) Has been cancelled
build / mac_arm64 (push) Has been cancelled
build / linux (push) Has been cancelled
build / go1.23 (push) Has been cancelled
build / lint (push) Has been cancelled
build / android-all (push) Has been cancelled
Build & Push Docker Images / Build Docker Image for linux/arm64 (push) Has been cancelled
Build & Push Docker Images / Merge & Push Final Docker Image (push) Has been cancelled
Metadata files have the file handle of their source file with 0x00000001 suffixed in big endian so we can look them up directly from their file handles.
179 lines
4.7 KiB
Go
179 lines
4.7 KiB
Go
//go:build unix
|
|
|
|
package nfs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/object"
|
|
"github.com/rclone/rclone/vfs"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// NB to test the symlink cache, running with elevated permissions is needed
|
|
const testSymlinkCache = "go test -c && sudo setcap cap_dac_read_search+ep ./nfs.test && ./nfs.test -test.v -test.run TestCache/symlink"
|
|
|
|
// Check basic CRUD operations
|
|
func testCacheCRUD(t *testing.T, h *Handler, c Cache, fileName string) {
|
|
isMetadata := strings.HasSuffix(fileName, ".metadata")
|
|
|
|
// Check reading a non existent handle returns an error
|
|
_, _, err := c.FromHandle([]byte{10})
|
|
assert.Error(t, err)
|
|
|
|
// Write a handle
|
|
splitPath := []string{"dir", fileName}
|
|
fh := c.ToHandle(h.billyFS, splitPath)
|
|
assert.True(t, len(fh) > 0)
|
|
if isMetadata {
|
|
assert.Equal(t, metadataSuffix, fh[len(fh)-len(metadataSuffix):])
|
|
} else {
|
|
assert.NotEqual(t, metadataSuffix, fh[len(fh)-len(metadataSuffix):])
|
|
}
|
|
|
|
// Read the handle back
|
|
newFs, newSplitPath, err := c.FromHandle(fh)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, h.billyFS, newFs)
|
|
assert.Equal(t, splitPath, newSplitPath)
|
|
|
|
// Invalidate the handle
|
|
err = c.InvalidateHandle(h.billyFS, fh)
|
|
require.NoError(t, err)
|
|
|
|
// Invalidate the handle twice
|
|
err = c.InvalidateHandle(h.billyFS, fh)
|
|
require.NoError(t, err)
|
|
|
|
// Check the handle is gone and returning stale handle error
|
|
_, _, err = c.FromHandle(fh)
|
|
if !isMetadata {
|
|
require.Error(t, err)
|
|
assert.Equal(t, errStaleHandle, err)
|
|
} else {
|
|
// Can't invalidate metadata handles
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
// Thrash the cache operations in parallel on different files
|
|
func testCacheThrashDifferent(t *testing.T, h *Handler, c Cache) {
|
|
var wg sync.WaitGroup
|
|
for i := range 100 {
|
|
i := i
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
testCacheCRUD(t, h, c, fmt.Sprintf("file-%d", i))
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// Thrash the cache operations in parallel on the same file
|
|
func testCacheThrashSame(t *testing.T, h *Handler, c Cache) {
|
|
var wg sync.WaitGroup
|
|
for range 100 {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
// Write a handle
|
|
splitPath := []string{"file"}
|
|
fh := c.ToHandle(h.billyFS, splitPath)
|
|
assert.True(t, len(fh) > 0)
|
|
|
|
// Read the handle back
|
|
newFs, newSplitPath, err := c.FromHandle(fh)
|
|
if err != nil {
|
|
assert.Equal(t, errStaleHandle, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, h.billyFS, newFs)
|
|
assert.Equal(t, splitPath, newSplitPath)
|
|
}
|
|
|
|
// Invalidate the handle
|
|
err = c.InvalidateHandle(h.billyFS, fh)
|
|
require.NoError(t, err)
|
|
|
|
// Check the handle is gone and returning stale handle error
|
|
_, _, err = c.FromHandle(fh)
|
|
if err != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, errStaleHandle, err)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestCache(t *testing.T) {
|
|
// Quieten the flood of ERROR messages!
|
|
ci := fs.GetConfig(context.Background())
|
|
oldLogLevel := ci.LogLevel
|
|
ci.LogLevel = fs.LogLevelEmergency
|
|
//ci.LogLevel = fs.LogLevelDebug
|
|
defer func() {
|
|
ci.LogLevel = oldLogLevel
|
|
}()
|
|
billyFS := &FS{nil} // place holder billyFS
|
|
for _, cacheType := range []handleCache{cacheMemory, cacheDisk, cacheSymlink} {
|
|
cacheType := cacheType
|
|
t.Run(cacheType.String(), func(t *testing.T) {
|
|
h := &Handler{
|
|
vfs: vfs.New(object.MemoryFs, nil),
|
|
billyFS: billyFS,
|
|
}
|
|
h.vfs.Opt.MetadataExtension = ".metadata"
|
|
h.opt.HandleLimit = 1000
|
|
h.opt.HandleCache = cacheType
|
|
h.opt.HandleCacheDir = t.TempDir()
|
|
c, err := h.getCache()
|
|
if err == ErrorSymlinkCacheNotSupported {
|
|
t.Skip(err.Error())
|
|
}
|
|
if err == ErrorSymlinkCacheNoPermission {
|
|
t.Skip("Need more permissions to run symlink cache tests: " + testSymlinkCache)
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Empty", func(t *testing.T) {
|
|
// Write a handle
|
|
splitPath := []string{""}
|
|
fh := c.ToHandle(h.billyFS, splitPath)
|
|
assert.True(t, len(fh) > 0)
|
|
|
|
// Read the handle back
|
|
newFs, newSplitPath, err := c.FromHandle(fh)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, h.billyFS, newFs)
|
|
assert.Equal(t, splitPath, newSplitPath)
|
|
testCacheCRUD(t, h, c, "file")
|
|
})
|
|
t.Run("CRUD", func(t *testing.T) {
|
|
testCacheCRUD(t, h, c, "file")
|
|
})
|
|
// NB the default caching handler is not thread safe!
|
|
if cacheType != cacheMemory {
|
|
t.Run("ThrashDifferent", func(t *testing.T) {
|
|
testCacheThrashDifferent(t, h, c)
|
|
})
|
|
t.Run("ThrashSame", func(t *testing.T) {
|
|
testCacheThrashSame(t, h, c)
|
|
})
|
|
// Metadata file handles only supported on non memory
|
|
t.Run("CRUDMetadata", func(t *testing.T) {
|
|
testCacheCRUD(t, h, c, "file.metadata")
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|