330 lines · 8.3 KB · Go
RawDownload
package vfs import (	"context"	"io"	"os"	"sync"	"testing"	"time" 	"github.com/pkg/errors"	"github.com/rclone/rclone/fs"	"github.com/rclone/rclone/fstest"	"github.com/rclone/rclone/lib/random"	"github.com/stretchr/testify/assert"	"github.com/stretchr/testify/require") // Open a file for writefunc writeHandleCreate(t *testing.T) (r *fstest.Run, vfs *VFS, fh *WriteFileHandle, cleanup func()) {	r, vfs, cleanup = newTestVFS(t) 	h, err := vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)	require.NoError(t, err)	fh, ok := h.(*WriteFileHandle)	require.True(t, ok) 	return r, vfs, fh, cleanup} func TestWriteFileHandleMethods(t *testing.T) {	r, vfs, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	// String	assert.Equal(t, "file1 (w)", fh.String())	assert.Equal(t, "<nil *WriteFileHandle>", (*WriteFileHandle)(nil).String())	assert.Equal(t, "<nil *WriteFileHandle.file>", new(WriteFileHandle).String()) 	// Node	node := fh.Node()	assert.Equal(t, "file1", node.Name()) 	// Offset #1	assert.Equal(t, int64(0), fh.Offset())	assert.Equal(t, int64(0), node.Size()) 	// Write (smoke test only since heavy lifting done in WriteAt)	n, err := fh.Write([]byte("hello"))	assert.NoError(t, err)	assert.Equal(t, 5, n) 	// Offset #2	assert.Equal(t, int64(5), fh.Offset())	assert.Equal(t, int64(5), node.Size()) 	// Stat	var fi os.FileInfo	fi, err = fh.Stat()	assert.NoError(t, err)	assert.Equal(t, int64(5), fi.Size())	assert.Equal(t, "file1", fi.Name()) 	// Read	var buf = make([]byte, 16)	_, err = fh.Read(buf)	assert.Equal(t, EPERM, err) 	// ReadAt	_, err = fh.ReadAt(buf, 0)	assert.Equal(t, EPERM, err) 	// Sync	err = fh.Sync()	assert.NoError(t, err) 	// Truncate - can only truncate where the file pointer is	err = fh.Truncate(5)	assert.NoError(t, err)	err = fh.Truncate(6)	assert.Equal(t, EPERM, 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,5,false"}) 	// check the underlying r.Fremote but not the modtime	file1 := fstest.NewItem("file1", "hello", t1)	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported) 	// Check trying to open the file now it exists then closing it	// immediately is OK	h, err := vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)	require.NoError(t, err)	assert.NoError(t, h.Close())	checkListing(t, root, []string{"file1,5,false"}) 	// Check trying to open the file and writing it now it exists	// returns an error	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)	require.NoError(t, err)	_, err = h.Write([]byte("hello1"))	require.Equal(t, EPERM, err)	assert.NoError(t, h.Close())	checkListing(t, root, []string{"file1,5,false"}) 	// Check opening the file with O_TRUNC does actually truncate	// it even if we don't write to it	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)	require.NoError(t, err)	err = h.Close()	if errors.Cause(err) != fs.ErrorCantUploadEmptyFiles {		assert.NoError(t, err)		checkListing(t, root, []string{"file1,0,false"})	} 	// Check opening the file with O_TRUNC and writing does work	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)	require.NoError(t, err)	_, err = h.WriteString("hello12")	require.NoError(t, err)	assert.NoError(t, h.Close())	checkListing(t, root, []string{"file1,7,false"})} func TestWriteFileHandleWriteAt(t *testing.T) {	r, vfs, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	// Preconditions	assert.Equal(t, int64(0), fh.offset)	assert.False(t, fh.writeCalled) 	// Write the data	n, err := fh.WriteAt([]byte("hello"), 0)	assert.NoError(t, err)	assert.Equal(t, 5, n) 	// After write	assert.Equal(t, int64(5), fh.offset)	assert.True(t, fh.writeCalled) 	// Check can't seek	n, err = fh.WriteAt([]byte("hello"), 100)	assert.Equal(t, ESPIPE, err)	assert.Equal(t, 0, n) 	// 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)	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)} func TestWriteFileHandleFlush(t *testing.T) {	_, vfs, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	// Check Flush already creates file for unwritten handles, without closing it	err := fh.Flush()	assert.NoError(t, err)	assert.False(t, fh.closed)	root, err := vfs.Root()	assert.NoError(t, err)	checkListing(t, root, []string{"file1,0,false"}) 	// Write some data	n, err := fh.Write([]byte("hello"))	assert.NoError(t, err)	assert.Equal(t, 5, n) 	// Check Flush closes file if write called	err = fh.Flush()	assert.NoError(t, err)	assert.True(t, fh.closed) 	// Check flush does nothing if called again	err = fh.Flush()	assert.NoError(t, err)	assert.True(t, fh.closed) 	// Check file was written properly	root, err = vfs.Root()	assert.NoError(t, err)	checkListing(t, root, []string{"file1,5,false"})} func TestWriteFileHandleRelease(t *testing.T) {	_, _, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	// Check Release closes file	err := fh.Release()	if errors.Cause(err) == fs.ErrorCantUploadEmptyFiles {		t.Logf("skipping test: %v", err)		return	}	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)} var (	canSetModTimeOnce  sync.Once	canSetModTimeValue = true) // returns whether the remote can set modtimefunc canSetModTime(t *testing.T, r *fstest.Run) bool {	canSetModTimeOnce.Do(func() {		mtime1 := time.Date(2008, time.November, 18, 17, 32, 31, 0, time.UTC)		_ = r.WriteObject(context.Background(), "time_test", "stuff", mtime1)		obj, err := r.Fremote.NewObject(context.Background(), "time_test")		require.NoError(t, err)		mtime2 := time.Date(2009, time.November, 18, 17, 32, 31, 0, time.UTC)		err = obj.SetModTime(context.Background(), mtime2)		switch err {		case nil:			canSetModTimeValue = true		case fs.ErrorCantSetModTime, fs.ErrorCantSetModTimeWithoutDelete:			canSetModTimeValue = false		default:			require.NoError(t, err)		}		require.NoError(t, obj.Remove(context.Background()))		fs.Debugf(nil, "Can set mod time: %v", canSetModTimeValue)	})	return canSetModTimeValue} // tests mod time on open filesfunc TestWriteFileModTimeWithOpenWriters(t *testing.T) {	r, vfs, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	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) 	err = fh.Close()	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())	}} func testFileReadAt(t *testing.T, n int) {	_, vfs, fh, cleanup := writeHandleCreate(t)	defer cleanup() 	contents := []byte(random.String(n))	if n != 0 {		written, err := fh.Write(contents)		require.NoError(t, err)		assert.Equal(t, n, written)	} 	// Close the file without writing to it if n==0	err := fh.Close()	if errors.Cause(err) == fs.ErrorCantUploadEmptyFiles {		t.Logf("skipping test: %v", err)		return	}	assert.NoError(t, err) 	// read the file back in using ReadAt into a buffer	// this simulates what mount does	rd, err := vfs.OpenFile("file1", os.O_RDONLY, 0)	require.NoError(t, err) 	buf := make([]byte, 1024)	read, err := rd.ReadAt(buf, 0)	if err != io.EOF {		assert.NoError(t, err)	}	assert.Equal(t, read, n)	assert.Equal(t, contents, buf[:read]) 	err = rd.Close()	assert.NoError(t, err)} func TestFileReadAtZeroLength(t *testing.T) {	testFileReadAt(t, 0)} func TestFileReadAtNonZeroLength(t *testing.T) {	testFileReadAt(t, 100)}