mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 08:58:26 +08:00
a28287e96d
This also - move in use options (Opt) from vfsflags to vfscommon - change os.FileMode to vfscommon.FileMode in parameters - rework vfscommon.FileMode and add tests
761 lines
19 KiB
Go
761 lines
19 KiB
Go
package vfs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/operations"
|
|
"github.com/rclone/rclone/fstest"
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Check interfaces
|
|
var (
|
|
_ io.Reader = (*RWFileHandle)(nil)
|
|
_ io.ReaderAt = (*RWFileHandle)(nil)
|
|
_ io.Writer = (*RWFileHandle)(nil)
|
|
_ io.WriterAt = (*RWFileHandle)(nil)
|
|
_ io.Seeker = (*RWFileHandle)(nil)
|
|
_ io.Closer = (*RWFileHandle)(nil)
|
|
_ Handle = (*RWFileHandle)(nil)
|
|
)
|
|
|
|
// Create a file and open it with the flags passed in
|
|
func rwHandleCreateFlags(t *testing.T, create bool, filename string, flags int) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
|
opt := vfscommon.Opt
|
|
opt.CacheMode = vfscommon.CacheModeFull
|
|
opt.WriteBack = writeBackDelay
|
|
r, vfs = newTestVFSOpt(t, &opt)
|
|
|
|
if create {
|
|
file1 := r.WriteObject(context.Background(), filename, "0123456789abcdef", t1)
|
|
r.CheckRemoteItems(t, file1)
|
|
}
|
|
|
|
h, err := vfs.OpenFile(filename, flags, 0777)
|
|
require.NoError(t, err)
|
|
fh, ok := h.(*RWFileHandle)
|
|
require.True(t, ok)
|
|
|
|
return r, vfs, fh
|
|
}
|
|
|
|
// Open a file for read
|
|
func rwHandleCreateReadOnly(t *testing.T) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
|
return rwHandleCreateFlags(t, true, "dir/file1", os.O_RDONLY)
|
|
}
|
|
|
|
// Open a file for write
|
|
func rwHandleCreateWriteOnly(t *testing.T) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
|
return rwHandleCreateFlags(t, false, "file1", os.O_WRONLY|os.O_CREATE)
|
|
}
|
|
|
|
// read data from the string
|
|
func rwReadString(t *testing.T, fh *RWFileHandle, n int) string {
|
|
buf := make([]byte, n)
|
|
n, err := fh.Read(buf)
|
|
if err != io.EOF {
|
|
assert.NoError(t, err)
|
|
}
|
|
return string(buf[:n])
|
|
}
|
|
|
|
func TestRWFileHandleMethodsRead(t *testing.T) {
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
|
|
|
// String
|
|
assert.Equal(t, "dir/file1 (rw)", fh.String())
|
|
assert.Equal(t, "<nil *RWFileHandle>", (*RWFileHandle)(nil).String())
|
|
assert.Equal(t, "<nil *RWFileHandle.file>", new(RWFileHandle).String())
|
|
|
|
// Node
|
|
node := fh.Node()
|
|
assert.Equal(t, "file1", node.Name())
|
|
|
|
// Size
|
|
assert.Equal(t, int64(16), fh.Size())
|
|
|
|
// Read 1
|
|
assert.Equal(t, "0", rwReadString(t, fh, 1))
|
|
|
|
// Read remainder
|
|
assert.Equal(t, "123456789abcdef", rwReadString(t, fh, 256))
|
|
|
|
// Read EOF
|
|
buf := make([]byte, 16)
|
|
_, err := fh.Read(buf)
|
|
assert.Equal(t, io.EOF, err)
|
|
|
|
// Sync
|
|
err = fh.Sync()
|
|
assert.NoError(t, err)
|
|
|
|
// Stat
|
|
var fi os.FileInfo
|
|
fi, err = fh.Stat()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(16), fi.Size())
|
|
assert.Equal(t, "file1", fi.Name())
|
|
|
|
// Close
|
|
assert.False(t, fh.closed)
|
|
assert.Equal(t, nil, fh.Close())
|
|
assert.True(t, fh.closed)
|
|
|
|
// Close again
|
|
assert.Equal(t, ECLOSED, fh.Close())
|
|
}
|
|
|
|
func TestRWFileHandleSeek(t *testing.T) {
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
|
|
|
assert.Equal(t, fh.opened, false)
|
|
|
|
// Check null seeks don't open the file
|
|
n, err := fh.Seek(0, io.SeekStart)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(0), n)
|
|
assert.Equal(t, fh.opened, false)
|
|
n, err = fh.Seek(0, io.SeekCurrent)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(0), n)
|
|
assert.Equal(t, fh.opened, false)
|
|
|
|
assert.Equal(t, "0", rwReadString(t, fh, 1))
|
|
|
|
// 0 means relative to the origin of the file,
|
|
n, err = fh.Seek(5, io.SeekStart)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(5), n)
|
|
assert.Equal(t, "5", rwReadString(t, fh, 1))
|
|
|
|
// 1 means relative to the current offset
|
|
n, err = fh.Seek(-3, io.SeekCurrent)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(3), n)
|
|
assert.Equal(t, "3", rwReadString(t, fh, 1))
|
|
|
|
// 2 means relative to the end.
|
|
n, err = fh.Seek(-3, io.SeekEnd)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(13), n)
|
|
assert.Equal(t, "d", rwReadString(t, fh, 1))
|
|
|
|
// Seek off the end
|
|
_, err = fh.Seek(100, io.SeekStart)
|
|
assert.NoError(t, err)
|
|
|
|
// Get the error on read
|
|
buf := make([]byte, 16)
|
|
l, err := fh.Read(buf)
|
|
assert.Equal(t, io.EOF, err)
|
|
assert.Equal(t, 0, l)
|
|
|
|
// Close
|
|
assert.Equal(t, nil, fh.Close())
|
|
}
|
|
|
|
func TestRWFileHandleReadAt(t *testing.T) {
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
|
|
|
// read from start
|
|
buf := make([]byte, 1)
|
|
n, err := fh.ReadAt(buf, 0)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, n)
|
|
assert.Equal(t, "0", string(buf[:n]))
|
|
|
|
// seek forwards
|
|
n, err = fh.ReadAt(buf, 5)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, n)
|
|
assert.Equal(t, "5", string(buf[:n]))
|
|
|
|
// seek backwards
|
|
n, err = fh.ReadAt(buf, 1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, n)
|
|
assert.Equal(t, "1", string(buf[:n]))
|
|
|
|
// read exactly to the end
|
|
buf = make([]byte, 6)
|
|
n, err = fh.ReadAt(buf, 10)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 6, n)
|
|
assert.Equal(t, "abcdef", string(buf[:n]))
|
|
|
|
// read off the end
|
|
buf = make([]byte, 256)
|
|
n, err = fh.ReadAt(buf, 10)
|
|
assert.Equal(t, io.EOF, err)
|
|
assert.Equal(t, 6, n)
|
|
assert.Equal(t, "abcdef", string(buf[:n]))
|
|
|
|
// read starting off the end
|
|
n, err = fh.ReadAt(buf, 100)
|
|
assert.Equal(t, io.EOF, err)
|
|
assert.Equal(t, 0, n)
|
|
|
|
// Properly close the file
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// check reading on closed file
|
|
_, err = fh.ReadAt(buf, 100)
|
|
assert.Equal(t, ECLOSED, err)
|
|
}
|
|
|
|
func TestRWFileHandleFlushRead(t *testing.T) {
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
|
|
|
// Check Flush does nothing if read not called
|
|
err := fh.Flush()
|
|
assert.NoError(t, err)
|
|
assert.False(t, fh.closed)
|
|
|
|
// Read data
|
|
buf := make([]byte, 256)
|
|
n, err := fh.Read(buf)
|
|
assert.True(t, err == io.EOF || err == nil)
|
|
assert.Equal(t, 16, n)
|
|
|
|
// Check Flush does nothing if read called
|
|
err = fh.Flush()
|
|
assert.NoError(t, err)
|
|
assert.False(t, fh.closed)
|
|
|
|
// Check flush does nothing if called again
|
|
err = fh.Flush()
|
|
assert.NoError(t, err)
|
|
assert.False(t, fh.closed)
|
|
|
|
// Properly close the file
|
|
assert.NoError(t, fh.Close())
|
|
}
|
|
|
|
func TestRWFileHandleReleaseRead(t *testing.T) {
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
|
|
|
// Read data
|
|
buf := make([]byte, 256)
|
|
n, err := fh.Read(buf)
|
|
assert.True(t, err == io.EOF || err == nil)
|
|
assert.Equal(t, 16, n)
|
|
|
|
// Check Release closes file
|
|
err = fh.Release()
|
|
assert.NoError(t, err)
|
|
assert.True(t, fh.closed)
|
|
|
|
// Check Release does nothing if called again
|
|
err = fh.Release()
|
|
assert.NoError(t, err)
|
|
assert.True(t, fh.closed)
|
|
}
|
|
|
|
/// ------------------------------------------------------------
|
|
|
|
func TestRWFileHandleMethodsWrite(t *testing.T) {
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
|
|
|
// String
|
|
assert.Equal(t, "file1 (rw)", fh.String())
|
|
assert.Equal(t, "<nil *RWFileHandle>", (*RWFileHandle)(nil).String())
|
|
assert.Equal(t, "<nil *RWFileHandle.file>", new(RWFileHandle).String())
|
|
|
|
// Node
|
|
node := fh.Node()
|
|
assert.Equal(t, "file1", node.Name())
|
|
|
|
offset := func() int64 {
|
|
n, err := fh.Seek(0, io.SeekCurrent)
|
|
require.NoError(t, err)
|
|
return n
|
|
}
|
|
|
|
// Offset #1
|
|
assert.Equal(t, int64(0), offset())
|
|
assert.Equal(t, int64(0), node.Size())
|
|
|
|
// Size #1
|
|
assert.Equal(t, int64(0), fh.Size())
|
|
|
|
// Write
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// Offset #2
|
|
assert.Equal(t, int64(5), offset())
|
|
assert.Equal(t, int64(5), node.Size())
|
|
|
|
// Size #2
|
|
assert.Equal(t, int64(5), fh.Size())
|
|
|
|
// WriteString
|
|
n, err = fh.WriteString(" world!")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 7, n)
|
|
|
|
// Sync
|
|
err = fh.Sync()
|
|
assert.NoError(t, err)
|
|
|
|
// Stat
|
|
var fi os.FileInfo
|
|
fi, err = fh.Stat()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(12), fi.Size())
|
|
assert.Equal(t, "file1", fi.Name())
|
|
|
|
// Truncate
|
|
err = fh.Truncate(11)
|
|
assert.NoError(t, err)
|
|
|
|
// Close
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// Check double close
|
|
err = fh.Close()
|
|
assert.Equal(t, ECLOSED, err)
|
|
|
|
// check vfs
|
|
root, err := vfs.Root()
|
|
require.NoError(t, err)
|
|
checkListing(t, root, []string{"file1,11,false"})
|
|
|
|
// check the underlying r.Fremote but not the modtime
|
|
file1 := fstest.NewItem("file1", "hello world", t1)
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
|
|
}
|
|
|
|
func TestRWFileHandleWriteAt(t *testing.T) {
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
|
|
|
offset := func() int64 {
|
|
n, err := fh.Seek(0, io.SeekCurrent)
|
|
require.NoError(t, err)
|
|
return n
|
|
}
|
|
|
|
// Name
|
|
assert.Equal(t, "file1", fh.Name())
|
|
|
|
// Preconditions
|
|
assert.Equal(t, int64(0), offset())
|
|
assert.True(t, fh.opened)
|
|
assert.False(t, fh.writeCalled)
|
|
|
|
// Write the data
|
|
n, err := fh.WriteAt([]byte("hello**"), 0)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 7, n)
|
|
|
|
// After write
|
|
assert.Equal(t, int64(0), offset())
|
|
assert.True(t, fh.writeCalled)
|
|
|
|
// Write more data
|
|
n, err = fh.WriteAt([]byte(" world"), 5)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 6, n)
|
|
|
|
// Close
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// Check can't write on closed handle
|
|
n, err = fh.WriteAt([]byte("hello"), 0)
|
|
assert.Equal(t, ECLOSED, err)
|
|
assert.Equal(t, 0, n)
|
|
|
|
// check vfs
|
|
root, err := vfs.Root()
|
|
require.NoError(t, err)
|
|
checkListing(t, root, []string{"file1,11,false"})
|
|
|
|
// check the underlying r.Fremote but not the modtime
|
|
file1 := fstest.NewItem("file1", "hello world", t1)
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
|
|
}
|
|
|
|
func TestRWFileHandleWriteNoWrite(t *testing.T) {
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
|
|
|
// Close the file without writing to it
|
|
err := fh.Close()
|
|
if errors.Is(err, fs.ErrorCantUploadEmptyFiles) {
|
|
t.Logf("skipping test: %v", err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
|
|
// Create a different file (not in the cache)
|
|
h, err := vfs.OpenFile("file2", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
|
|
require.NoError(t, err)
|
|
|
|
// Close it with Flush and Release
|
|
err = h.Flush()
|
|
assert.NoError(t, err)
|
|
err = h.Release()
|
|
assert.NoError(t, err)
|
|
|
|
// check vfs
|
|
root, err := vfs.Root()
|
|
require.NoError(t, err)
|
|
checkListing(t, root, []string{"file1,0,false", "file2,0,false"})
|
|
|
|
// check the underlying r.Fremote but not the modtime
|
|
file1 := fstest.NewItem("file1", "", t1)
|
|
file2 := fstest.NewItem("file2", "", t1)
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file2}, []string{}, fs.ModTimeNotSupported)
|
|
}
|
|
|
|
func TestRWFileHandleFlushWrite(t *testing.T) {
|
|
_, _, fh := rwHandleCreateWriteOnly(t)
|
|
|
|
// Check that the file has been create and is open
|
|
assert.True(t, fh.opened)
|
|
|
|
// Write some data
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
assert.True(t, fh.opened)
|
|
|
|
// Check Flush does not close file if write called
|
|
err = fh.Flush()
|
|
assert.NoError(t, err)
|
|
assert.False(t, fh.closed)
|
|
|
|
// Check flush does nothing if called again
|
|
err = fh.Flush()
|
|
assert.NoError(t, err)
|
|
assert.False(t, fh.closed)
|
|
|
|
// Check that Close closes the file
|
|
err = fh.Close()
|
|
assert.NoError(t, err)
|
|
assert.True(t, fh.closed)
|
|
}
|
|
|
|
func TestRWFileHandleReleaseWrite(t *testing.T) {
|
|
_, _, fh := rwHandleCreateWriteOnly(t)
|
|
|
|
// Write some data
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// Check Release closes file
|
|
err = fh.Release()
|
|
assert.NoError(t, err)
|
|
assert.True(t, fh.closed)
|
|
|
|
// Check Release does nothing if called again
|
|
err = fh.Release()
|
|
assert.NoError(t, err)
|
|
assert.True(t, fh.closed)
|
|
}
|
|
|
|
// check the size of the file through the open file (if not nil) and via stat
|
|
func assertSize(t *testing.T, vfs *VFS, fh *RWFileHandle, filepath string, size int64) {
|
|
if fh != nil {
|
|
assert.Equal(t, size, fh.Size())
|
|
}
|
|
fi, err := vfs.Stat(filepath)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, size, fi.Size())
|
|
}
|
|
|
|
func TestRWFileHandleSizeTruncateExisting(t *testing.T) {
|
|
_, vfs, fh := rwHandleCreateFlags(t, true, "dir/file1", os.O_WRONLY|os.O_TRUNC)
|
|
|
|
// check initial size after opening
|
|
assertSize(t, vfs, fh, "dir/file1", 0)
|
|
|
|
// write some bytes
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// check size after writing
|
|
assertSize(t, vfs, fh, "dir/file1", 5)
|
|
|
|
// close
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// check size after close
|
|
assertSize(t, vfs, nil, "dir/file1", 5)
|
|
}
|
|
|
|
func TestRWFileHandleSizeCreateExisting(t *testing.T) {
|
|
_, vfs, fh := rwHandleCreateFlags(t, true, "dir/file1", os.O_WRONLY|os.O_CREATE)
|
|
|
|
// check initial size after opening
|
|
assertSize(t, vfs, fh, "dir/file1", 16)
|
|
|
|
// write some bytes
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// check size after writing
|
|
assertSize(t, vfs, fh, "dir/file1", 16)
|
|
|
|
// write some more bytes
|
|
n, err = fh.Write([]byte("helloHELLOhello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 15, n)
|
|
|
|
// check size after writing
|
|
assertSize(t, vfs, fh, "dir/file1", 20)
|
|
|
|
// close
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// check size after close
|
|
assertSize(t, vfs, nil, "dir/file1", 20)
|
|
}
|
|
|
|
func TestRWFileHandleSizeCreateNew(t *testing.T) {
|
|
_, vfs, fh := rwHandleCreateFlags(t, false, "file1", os.O_WRONLY|os.O_CREATE)
|
|
|
|
// check initial size after opening
|
|
assertSize(t, vfs, fh, "file1", 0)
|
|
|
|
// write some bytes
|
|
n, err := fh.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// check size after writing
|
|
assertSize(t, vfs, fh, "file1", 5)
|
|
|
|
// check size after writing
|
|
assertSize(t, vfs, fh, "file1", 5)
|
|
|
|
// close
|
|
assert.NoError(t, fh.Close())
|
|
|
|
// check size after close
|
|
assertSize(t, vfs, nil, "file1", 5)
|
|
}
|
|
|
|
func testRWFileHandleOpenTest(t *testing.T, vfs *VFS, test *openTest) {
|
|
fileName := "open-test-file"
|
|
|
|
// Make sure we delete the file on failure too
|
|
defer func() {
|
|
_ = vfs.Remove(fileName)
|
|
}()
|
|
|
|
// first try with file not existing
|
|
_, err := vfs.Stat(fileName)
|
|
require.True(t, os.IsNotExist(err))
|
|
|
|
f, openNonExistentErr := vfs.OpenFile(fileName, test.flags, 0666)
|
|
|
|
var readNonExistentErr error
|
|
var writeNonExistentErr error
|
|
if openNonExistentErr == nil {
|
|
// read some bytes
|
|
buf := []byte{0, 0}
|
|
_, readNonExistentErr = f.Read(buf)
|
|
|
|
// write some bytes
|
|
_, writeNonExistentErr = f.Write([]byte("hello"))
|
|
|
|
// close
|
|
err = f.Close()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// write the file
|
|
f, err = vfs.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0777)
|
|
require.NoError(t, err)
|
|
_, err = f.Write([]byte("hello"))
|
|
require.NoError(t, err)
|
|
err = f.Close()
|
|
require.NoError(t, err)
|
|
|
|
// then open file and try with file existing
|
|
|
|
f, openExistingErr := vfs.OpenFile(fileName, test.flags, 0666)
|
|
var readExistingErr error
|
|
var writeExistingErr error
|
|
if openExistingErr == nil {
|
|
// read some bytes
|
|
buf := []byte{0, 0}
|
|
_, readExistingErr = f.Read(buf)
|
|
|
|
// write some bytes
|
|
_, writeExistingErr = f.Write([]byte("HEL"))
|
|
|
|
// close
|
|
err = f.Close()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// read the file
|
|
f, err = vfs.OpenFile(fileName, os.O_RDONLY, 0)
|
|
require.NoError(t, err)
|
|
buf, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
err = f.Close()
|
|
require.NoError(t, err)
|
|
contents := string(buf)
|
|
|
|
// remove file
|
|
node, err := vfs.Stat(fileName)
|
|
require.NoError(t, err)
|
|
err = node.Remove()
|
|
require.NoError(t, err)
|
|
|
|
// check
|
|
assert.Equal(t, test.openNonExistentErr, openNonExistentErr, "openNonExistentErr: want=%v, got=%v", test.openNonExistentErr, openNonExistentErr)
|
|
assert.Equal(t, test.readNonExistentErr, readNonExistentErr, "readNonExistentErr: want=%v, got=%v", test.readNonExistentErr, readNonExistentErr)
|
|
assert.Equal(t, test.writeNonExistentErr, writeNonExistentErr, "writeNonExistentErr: want=%v, got=%v", test.writeNonExistentErr, writeNonExistentErr)
|
|
assert.Equal(t, test.openExistingErr, openExistingErr, "openExistingErr: want=%v, got=%v", test.openExistingErr, openExistingErr)
|
|
assert.Equal(t, test.readExistingErr, readExistingErr, "readExistingErr: want=%v, got=%v", test.readExistingErr, readExistingErr)
|
|
assert.Equal(t, test.writeExistingErr, writeExistingErr, "writeExistingErr: want=%v, got=%v", test.writeExistingErr, writeExistingErr)
|
|
assert.Equal(t, test.contents, contents)
|
|
}
|
|
|
|
func TestRWFileHandleOpenTests(t *testing.T) {
|
|
for _, cacheMode := range []vfscommon.CacheMode{vfscommon.CacheModeWrites, vfscommon.CacheModeFull} {
|
|
t.Run(cacheMode.String(), func(t *testing.T) {
|
|
opt := vfscommon.Opt
|
|
opt.CacheMode = cacheMode
|
|
opt.WriteBack = writeBackDelay
|
|
_, vfs := newTestVFSOpt(t, &opt)
|
|
|
|
for _, test := range openTests {
|
|
t.Run(test.what, func(t *testing.T) {
|
|
testRWFileHandleOpenTest(t, vfs, &test)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// tests mod time on open files
|
|
func TestRWFileModTimeWithOpenWriters(t *testing.T) {
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
|
if !canSetModTime(t, r) {
|
|
t.Skip("can't set mod time")
|
|
}
|
|
|
|
mtime := time.Date(2012, time.November, 18, 17, 32, 31, 0, time.UTC)
|
|
|
|
_, err := fh.Write([]byte{104, 105})
|
|
require.NoError(t, err)
|
|
|
|
err = fh.Node().SetModTime(mtime)
|
|
require.NoError(t, err)
|
|
|
|
// Using Flush/Release to mimic mount instead of Close
|
|
|
|
err = fh.Flush()
|
|
require.NoError(t, err)
|
|
|
|
err = fh.Release()
|
|
require.NoError(t, err)
|
|
|
|
info, err := vfs.Stat("file1")
|
|
require.NoError(t, err)
|
|
|
|
if r.Fremote.Precision() != fs.ModTimeNotSupported {
|
|
// avoid errors because of timezone differences
|
|
assert.Equal(t, info.ModTime().Unix(), mtime.Unix(), fmt.Sprintf("Time mismatch: %v != %v", info.ModTime(), mtime))
|
|
}
|
|
|
|
file1 := fstest.NewItem("file1", "hi", mtime)
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
r.CheckRemoteItems(t, file1)
|
|
}
|
|
|
|
func TestRWCacheRename(t *testing.T) {
|
|
opt := vfscommon.Opt
|
|
opt.CacheMode = vfscommon.CacheModeFull
|
|
opt.WriteBack = writeBackDelay
|
|
r, vfs := newTestVFSOpt(t, &opt)
|
|
|
|
if !operations.CanServerSideMove(r.Fremote) {
|
|
t.Skip("skip as can't rename files")
|
|
}
|
|
|
|
h, err := vfs.OpenFile("rename_me", os.O_WRONLY|os.O_CREATE, 0777)
|
|
require.NoError(t, err)
|
|
_, err = h.WriteString("hello")
|
|
require.NoError(t, err)
|
|
fh, ok := h.(*RWFileHandle)
|
|
require.True(t, ok)
|
|
|
|
err = fh.Sync()
|
|
require.NoError(t, err)
|
|
err = fh.Close()
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, vfs.cache.Exists("rename_me"))
|
|
|
|
err = vfs.Rename("rename_me", "i_was_renamed")
|
|
require.NoError(t, err)
|
|
|
|
assert.False(t, vfs.cache.Exists("rename_me"))
|
|
assert.True(t, vfs.cache.Exists("i_was_renamed"))
|
|
}
|
|
|
|
// Test the cache reading a file that is updated externally
|
|
//
|
|
// See: https://github.com/rclone/rclone/issues/6053
|
|
func TestRWCacheUpdate(t *testing.T) {
|
|
opt := vfscommon.Opt
|
|
opt.CacheMode = vfscommon.CacheModeFull
|
|
opt.WriteBack = writeBackDelay
|
|
opt.DirCacheTime = fs.Duration(100 * time.Millisecond)
|
|
r, vfs := newTestVFSOpt(t, &opt)
|
|
|
|
if r.Fremote.Precision() == fs.ModTimeNotSupported {
|
|
t.Skip("skip as modtime not supported")
|
|
}
|
|
|
|
const filename = "TestRWCacheUpdate"
|
|
|
|
modTime := time.Now().Add(-time.Hour)
|
|
for i := 0; i < 10; i++ {
|
|
modTime = modTime.Add(time.Minute)
|
|
// Refresh test file
|
|
contents := fmt.Sprintf("TestRWCacheUpdate%03d", i)
|
|
// Increase the size for second half of test
|
|
for j := 5; j < i; j++ {
|
|
contents += "*"
|
|
}
|
|
file1 := r.WriteObject(context.Background(), filename, contents, modTime)
|
|
r.CheckRemoteItems(t, file1)
|
|
|
|
// Wait for directory cache to expire
|
|
time.Sleep(time.Duration(2 * opt.DirCacheTime))
|
|
|
|
// Check the file is OK via the VFS
|
|
data, err := vfs.ReadFile(filename)
|
|
require.NoError(t, err)
|
|
require.Equal(t, contents, string(data))
|
|
|
|
// Check Stat
|
|
fi, err := vfs.Stat(filename)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(len(contents)), fi.Size())
|
|
fstest.AssertTimeEqualWithPrecision(t, filename, modTime, fi.ModTime(), r.Fremote.Precision())
|
|
}
|
|
}
|