2017-11-06 22:38:52 +01:00
|
|
|
package vfs
|
|
|
|
|
|
|
|
import (
|
2019-06-17 10:34:30 +02:00
|
|
|
"context"
|
2021-11-04 11:12:57 +01:00
|
|
|
"errors"
|
2020-01-19 13:52:48 +01:00
|
|
|
"fmt"
|
2017-11-06 22:38:52 +01:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"testing"
|
2018-03-06 21:47:11 +01:00
|
|
|
"time"
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
2020-01-14 10:47:31 +01:00
|
|
|
"github.com/rclone/rclone/fs/operations"
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fstest"
|
2020-02-28 15:44:15 +01:00
|
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
2017-11-06 22:38:52 +01:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2020-03-19 15:55:07 +01:00
|
|
|
// 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)
|
|
|
|
)
|
|
|
|
|
2019-11-25 12:31:44 +01:00
|
|
|
// Create a file and open it with the flags passed in
|
2022-12-08 13:43:53 +01:00
|
|
|
func rwHandleCreateFlags(t *testing.T, create bool, filename string, flags int) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
2020-02-28 15:44:15 +01:00
|
|
|
opt := vfscommon.DefaultOpt
|
|
|
|
opt.CacheMode = vfscommon.CacheModeFull
|
2020-04-17 12:18:58 +02:00
|
|
|
opt.WriteBack = writeBackDelay
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs = newTestVFSOpt(t, &opt)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2019-11-25 12:31:44 +01:00
|
|
|
if create {
|
|
|
|
file1 := r.WriteObject(context.Background(), filename, "0123456789abcdef", t1)
|
2021-11-09 12:43:36 +01:00
|
|
|
r.CheckRemoteItems(t, file1)
|
2019-11-25 12:31:44 +01:00
|
|
|
}
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2019-11-25 12:31:44 +01:00
|
|
|
h, err := vfs.OpenFile(filename, flags, 0777)
|
2017-11-06 22:38:52 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
fh, ok := h.(*RWFileHandle)
|
|
|
|
require.True(t, ok)
|
|
|
|
|
2022-12-08 13:43:53 +01:00
|
|
|
return r, vfs, fh
|
2017-11-06 22:38:52 +01:00
|
|
|
}
|
|
|
|
|
2019-11-25 12:31:44 +01:00
|
|
|
// Open a file for read
|
2022-12-08 13:43:53 +01:00
|
|
|
func rwHandleCreateReadOnly(t *testing.T) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
2020-04-17 12:18:58 +02:00
|
|
|
return rwHandleCreateFlags(t, true, "dir/file1", os.O_RDONLY)
|
2019-11-25 12:31:44 +01:00
|
|
|
}
|
|
|
|
|
2017-11-06 22:38:52 +01:00
|
|
|
// Open a file for write
|
2022-12-08 13:43:53 +01:00
|
|
|
func rwHandleCreateWriteOnly(t *testing.T) (r *fstest.Run, vfs *VFS, fh *RWFileHandle) {
|
2020-04-17 12:18:58 +02:00
|
|
|
return rwHandleCreateFlags(t, false, "file1", os.O_WRONLY|os.O_CREATE)
|
2017-11-06 22:38:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
2017-11-18 16:48:49 +01:00
|
|
|
// Sync
|
|
|
|
err = fh.Sync()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2017-11-06 22:38:52 +01:00
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2018-02-22 18:28:21 +01:00
|
|
|
assert.Equal(t, fh.opened, false)
|
|
|
|
|
|
|
|
// Check null seeks don't open the file
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err := fh.Seek(0, io.SeekStart)
|
2018-02-22 18:28:21 +01:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(0), n)
|
|
|
|
assert.Equal(t, fh.opened, false)
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err = fh.Seek(0, io.SeekCurrent)
|
2018-02-22 18:28:21 +01:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(0), n)
|
|
|
|
assert.Equal(t, fh.opened, false)
|
|
|
|
|
2017-11-06 22:38:52 +01:00
|
|
|
assert.Equal(t, "0", rwReadString(t, fh, 1))
|
|
|
|
|
|
|
|
// 0 means relative to the origin of the file,
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err = fh.Seek(5, io.SeekStart)
|
2017-11-06 22:38:52 +01:00
|
|
|
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
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err = fh.Seek(-3, io.SeekCurrent)
|
2017-11-06 22:38:52 +01:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(3), n)
|
|
|
|
assert.Equal(t, "3", rwReadString(t, fh, 1))
|
|
|
|
|
|
|
|
// 2 means relative to the end.
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err = fh.Seek(-3, io.SeekEnd)
|
2017-11-06 22:38:52 +01:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(13), n)
|
|
|
|
assert.Equal(t, "d", rwReadString(t, fh, 1))
|
|
|
|
|
|
|
|
// Seek off the end
|
2018-05-04 16:19:50 +02:00
|
|
|
_, err = fh.Seek(100, io.SeekStart)
|
2017-11-06 22:38:52 +01:00
|
|
|
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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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
|
2018-05-04 16:19:50 +02:00
|
|
|
_, err = fh.ReadAt(buf, 100)
|
2017-11-06 22:38:52 +01:00
|
|
|
assert.Equal(t, ECLOSED, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleFlushRead(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateReadOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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 {
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err := fh.Seek(0, io.SeekCurrent)
|
2017-11-06 22:38:52 +01:00
|
|
|
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)
|
|
|
|
|
2017-11-27 20:51:35 +01:00
|
|
|
// Sync
|
|
|
|
err = fh.Sync()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2017-11-06 22:38:52 +01:00
|
|
|
// 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()
|
2018-05-04 16:19:50 +02:00
|
|
|
require.NoError(t, err)
|
2017-11-06 22:38:52 +01:00
|
|
|
checkListing(t, root, []string{"file1,11,false"})
|
|
|
|
|
|
|
|
// check the underlying r.Fremote but not the modtime
|
|
|
|
file1 := fstest.NewItem("file1", "hello world", t1)
|
2020-04-17 12:18:58 +02:00
|
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
2017-11-06 22:38:52 +01:00
|
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleWriteAt(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
offset := func() int64 {
|
2018-04-06 20:53:06 +02:00
|
|
|
n, err := fh.Seek(0, io.SeekCurrent)
|
2017-11-06 22:38:52 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2023-08-31 15:28:10 +02:00
|
|
|
// Name
|
|
|
|
assert.Equal(t, "file1", fh.Name())
|
|
|
|
|
2017-11-06 22:38:52 +01:00
|
|
|
// Preconditions
|
|
|
|
assert.Equal(t, int64(0), offset())
|
2018-02-18 14:12:26 +01:00
|
|
|
assert.True(t, fh.opened)
|
2018-02-26 22:26:32 +01:00
|
|
|
assert.False(t, fh.writeCalled)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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()
|
2018-05-04 16:19:50 +02:00
|
|
|
require.NoError(t, err)
|
2017-11-06 22:38:52 +01:00
|
|
|
checkListing(t, root, []string{"file1,11,false"})
|
|
|
|
|
|
|
|
// check the underlying r.Fremote but not the modtime
|
|
|
|
file1 := fstest.NewItem("file1", "hello world", t1)
|
2020-04-17 12:18:58 +02:00
|
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
2017-11-06 22:38:52 +01:00
|
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleWriteNoWrite(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// Close the file without writing to it
|
|
|
|
err := fh.Close()
|
2021-11-04 11:12:57 +01:00
|
|
|
if errors.Is(err, fs.ErrorCantUploadEmptyFiles) {
|
2019-06-29 12:37:10 +02:00
|
|
|
t.Logf("skipping test: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2017-11-06 22:38:52 +01:00
|
|
|
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()
|
2018-05-04 16:19:50 +02:00
|
|
|
require.NoError(t, err)
|
2017-11-06 22:38:52 +01:00
|
|
|
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)
|
2020-04-17 12:18:58 +02:00
|
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
2017-11-06 22:38:52 +01:00
|
|
|
fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file2}, []string{}, fs.ModTimeNotSupported)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleFlushWrite(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateWriteOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2018-02-18 14:12:26 +01:00
|
|
|
// Check that the file has been create and is open
|
|
|
|
assert.True(t, fh.opened)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// Write some data
|
|
|
|
n, err := fh.Write([]byte("hello"))
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 5, n)
|
2020-02-29 19:08:22 +01:00
|
|
|
assert.True(t, fh.opened)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
2019-10-06 22:05:21 +02:00
|
|
|
// Check Flush does not close file if write called
|
2017-11-06 22:38:52 +01:00
|
|
|
err = fh.Flush()
|
|
|
|
assert.NoError(t, err)
|
2019-10-06 22:05:21 +02:00
|
|
|
assert.False(t, fh.closed)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// Check flush does nothing if called again
|
|
|
|
err = fh.Flush()
|
|
|
|
assert.NoError(t, err)
|
2019-10-06 22:05:21 +02:00
|
|
|
assert.False(t, fh.closed)
|
|
|
|
|
|
|
|
// Check that Close closes the file
|
|
|
|
err = fh.Close()
|
|
|
|
assert.NoError(t, err)
|
2017-11-06 22:38:52 +01:00
|
|
|
assert.True(t, fh.closed)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleReleaseWrite(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, _, fh := rwHandleCreateWriteOnly(t)
|
2017-11-06 22:38:52 +01:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2017-11-27 20:51:35 +01:00
|
|
|
|
2019-11-25 12:31:44 +01:00
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, vfs, fh := rwHandleCreateFlags(t, true, "dir/file1", os.O_WRONLY|os.O_TRUNC)
|
2019-11-25 12:31:44 +01:00
|
|
|
|
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, vfs, fh := rwHandleCreateFlags(t, true, "dir/file1", os.O_WRONLY|os.O_CREATE)
|
2019-11-25 12:31:44 +01:00
|
|
|
|
|
|
|
// 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) {
|
2022-12-08 13:43:53 +01:00
|
|
|
_, vfs, fh := rwHandleCreateFlags(t, false, "file1", os.O_WRONLY|os.O_CREATE)
|
2019-11-25 12:31:44 +01:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2017-11-27 20:51:35 +01:00
|
|
|
func testRWFileHandleOpenTest(t *testing.T, vfs *VFS, test *openTest) {
|
|
|
|
fileName := "open-test-file"
|
|
|
|
|
2020-06-03 16:49:41 +02:00
|
|
|
// Make sure we delete the file on failure too
|
|
|
|
defer func() {
|
|
|
|
_ = vfs.Remove(fileName)
|
|
|
|
}()
|
|
|
|
|
2017-11-27 20:51:35 +01:00
|
|
|
// first try with file not existing
|
|
|
|
_, err := vfs.Stat(fileName)
|
2020-02-29 19:08:22 +01:00
|
|
|
require.True(t, os.IsNotExist(err))
|
2017-11-27 20:51:35 +01:00
|
|
|
|
|
|
|
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()
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// write the file
|
|
|
|
f, err = vfs.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0777)
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
_, err = f.Write([]byte("hello"))
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
err = f.Close()
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
|
|
|
|
// 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()
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// read the file
|
|
|
|
f, err = vfs.OpenFile(fileName, os.O_RDONLY, 0)
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2022-08-20 16:38:02 +02:00
|
|
|
buf, err := io.ReadAll(f)
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
err = f.Close()
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
contents := string(buf)
|
|
|
|
|
|
|
|
// remove file
|
|
|
|
node, err := vfs.Stat(fileName)
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
err = node.Remove()
|
2020-02-29 19:08:22 +01:00
|
|
|
require.NoError(t, err)
|
2017-11-27 20:51:35 +01:00
|
|
|
|
|
|
|
// check
|
2020-02-29 19:08:22 +01:00
|
|
|
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)
|
2017-11-27 20:51:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRWFileHandleOpenTests(t *testing.T) {
|
2022-11-24 11:30:01 +01:00
|
|
|
for _, cacheMode := range []vfscommon.CacheMode{vfscommon.CacheModeWrites, vfscommon.CacheModeFull} {
|
|
|
|
t.Run(cacheMode.String(), func(t *testing.T) {
|
|
|
|
opt := vfscommon.DefaultOpt
|
|
|
|
opt.CacheMode = cacheMode
|
|
|
|
opt.WriteBack = writeBackDelay
|
2022-12-08 13:43:53 +01:00
|
|
|
_, vfs := newTestVFSOpt(t, &opt)
|
2022-11-24 11:30:01 +01:00
|
|
|
|
|
|
|
for _, test := range openTests {
|
|
|
|
t.Run(test.what, func(t *testing.T) {
|
|
|
|
testRWFileHandleOpenTest(t, vfs, &test)
|
|
|
|
})
|
|
|
|
}
|
2020-02-29 19:08:22 +01:00
|
|
|
})
|
2017-11-27 20:51:35 +01:00
|
|
|
}
|
|
|
|
}
|
2018-03-06 21:47:11 +01:00
|
|
|
|
|
|
|
// tests mod time on open files
|
|
|
|
func TestRWFileModTimeWithOpenWriters(t *testing.T) {
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs, fh := rwHandleCreateWriteOnly(t)
|
2019-02-27 18:14:32 +01:00
|
|
|
if !canSetModTime(t, r) {
|
2020-04-17 12:18:58 +02:00
|
|
|
t.Skip("can't set mod time")
|
2019-02-27 18:14:32 +01:00
|
|
|
}
|
2018-03-06 21:47:11 +01:00
|
|
|
|
2018-08-04 12:16:43 +02:00
|
|
|
mtime := time.Date(2012, time.November, 18, 17, 32, 31, 0, time.UTC)
|
2018-03-06 21:47:11 +01:00
|
|
|
|
|
|
|
_, err := fh.Write([]byte{104, 105})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = fh.Node().SetModTime(mtime)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-01-19 13:52:48 +01:00
|
|
|
// Using Flush/Release to mimic mount instead of Close
|
|
|
|
|
|
|
|
err = fh.Flush()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = fh.Release()
|
2018-03-06 21:47:11 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
info, err := vfs.Stat("file1")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-02-27 18:14:32 +01:00
|
|
|
if r.Fremote.Precision() != fs.ModTimeNotSupported {
|
|
|
|
// avoid errors because of timezone differences
|
2020-01-19 13:52:48 +01:00
|
|
|
assert.Equal(t, info.ModTime().Unix(), mtime.Unix(), fmt.Sprintf("Time mismatch: %v != %v", info.ModTime(), mtime))
|
2019-02-27 18:14:32 +01:00
|
|
|
}
|
2020-01-19 13:52:48 +01:00
|
|
|
|
|
|
|
file1 := fstest.NewItem("file1", "hi", mtime)
|
2020-04-17 12:18:58 +02:00
|
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
2021-11-09 12:43:36 +01:00
|
|
|
r.CheckRemoteItems(t, file1)
|
2018-03-06 21:47:11 +01:00
|
|
|
}
|
2019-10-28 18:05:41 +01:00
|
|
|
|
2019-12-05 13:58:24 +01:00
|
|
|
func TestRWCacheRename(t *testing.T) {
|
2020-04-17 12:18:58 +02:00
|
|
|
opt := vfscommon.DefaultOpt
|
|
|
|
opt.CacheMode = vfscommon.CacheModeFull
|
|
|
|
opt.WriteBack = writeBackDelay
|
2022-12-08 13:43:53 +01:00
|
|
|
r, vfs := newTestVFSOpt(t, &opt)
|
2019-10-28 18:05:41 +01:00
|
|
|
|
2020-01-14 10:47:31 +01:00
|
|
|
if !operations.CanServerSideMove(r.Fremote) {
|
|
|
|
t.Skip("skip as can't rename files")
|
|
|
|
}
|
|
|
|
|
2019-10-28 18:05:41 +01:00
|
|
|
h, err := vfs.OpenFile("rename_me", os.O_WRONLY|os.O_CREATE, 0777)
|
|
|
|
require.NoError(t, err)
|
2019-11-15 10:26:40 +01:00
|
|
|
_, err = h.WriteString("hello")
|
|
|
|
require.NoError(t, err)
|
2019-10-28 18:05:41 +01:00
|
|
|
fh, ok := h.(*RWFileHandle)
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
|
|
err = fh.Sync()
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = fh.Close()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-02-28 15:44:15 +01:00
|
|
|
assert.True(t, vfs.cache.Exists("rename_me"))
|
2019-10-28 18:05:41 +01:00
|
|
|
|
|
|
|
err = vfs.Rename("rename_me", "i_was_renamed")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-02-28 15:44:15 +01:00
|
|
|
assert.False(t, vfs.cache.Exists("rename_me"))
|
|
|
|
assert.True(t, vfs.cache.Exists("i_was_renamed"))
|
2019-10-28 18:05:41 +01:00
|
|
|
}
|