github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/vfs/write_test.go (about)

     1  package vfs
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/rclone/rclone/fs"
    13  	"github.com/rclone/rclone/fstest"
    14  	"github.com/rclone/rclone/lib/random"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  // Open a file for write
    20  func writeHandleCreate(t *testing.T, r *fstest.Run) (*VFS, *WriteFileHandle) {
    21  	vfs := New(r.Fremote, nil)
    22  
    23  	h, err := vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)
    24  	require.NoError(t, err)
    25  	fh, ok := h.(*WriteFileHandle)
    26  	require.True(t, ok)
    27  
    28  	return vfs, fh
    29  }
    30  
    31  func TestWriteFileHandleMethods(t *testing.T) {
    32  	r := fstest.NewRun(t)
    33  	defer r.Finalise()
    34  	vfs, fh := writeHandleCreate(t, r)
    35  
    36  	// String
    37  	assert.Equal(t, "file1 (w)", fh.String())
    38  	assert.Equal(t, "<nil *WriteFileHandle>", (*WriteFileHandle)(nil).String())
    39  	assert.Equal(t, "<nil *WriteFileHandle.file>", new(WriteFileHandle).String())
    40  
    41  	// Node
    42  	node := fh.Node()
    43  	assert.Equal(t, "file1", node.Name())
    44  
    45  	// Offset #1
    46  	assert.Equal(t, int64(0), fh.Offset())
    47  	assert.Equal(t, int64(0), node.Size())
    48  
    49  	// Write (smoke test only since heavy lifting done in WriteAt)
    50  	n, err := fh.Write([]byte("hello"))
    51  	assert.NoError(t, err)
    52  	assert.Equal(t, 5, n)
    53  
    54  	// Offset #2
    55  	assert.Equal(t, int64(5), fh.Offset())
    56  	assert.Equal(t, int64(5), node.Size())
    57  
    58  	// Stat
    59  	var fi os.FileInfo
    60  	fi, err = fh.Stat()
    61  	assert.NoError(t, err)
    62  	assert.Equal(t, int64(5), fi.Size())
    63  	assert.Equal(t, "file1", fi.Name())
    64  
    65  	// Read
    66  	var buf = make([]byte, 16)
    67  	_, err = fh.Read(buf)
    68  	assert.Equal(t, EPERM, err)
    69  
    70  	// ReadAt
    71  	_, err = fh.ReadAt(buf, 0)
    72  	assert.Equal(t, EPERM, err)
    73  
    74  	// Sync
    75  	err = fh.Sync()
    76  	assert.NoError(t, err)
    77  
    78  	// Truncate - can only truncate where the file pointer is
    79  	err = fh.Truncate(5)
    80  	assert.NoError(t, err)
    81  	err = fh.Truncate(6)
    82  	assert.Equal(t, EPERM, err)
    83  
    84  	// Close
    85  	assert.NoError(t, fh.Close())
    86  
    87  	// Check double close
    88  	err = fh.Close()
    89  	assert.Equal(t, ECLOSED, err)
    90  
    91  	// check vfs
    92  	root, err := vfs.Root()
    93  	require.NoError(t, err)
    94  	checkListing(t, root, []string{"file1,5,false"})
    95  
    96  	// check the underlying r.Fremote but not the modtime
    97  	file1 := fstest.NewItem("file1", "hello", t1)
    98  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
    99  
   100  	// Check trying to open the file now it exists then closing it
   101  	// immediately is OK
   102  	h, err := vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)
   103  	require.NoError(t, err)
   104  	assert.NoError(t, h.Close())
   105  	checkListing(t, root, []string{"file1,5,false"})
   106  
   107  	// Check trying to open the file and writing it now it exists
   108  	// returns an error
   109  	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777)
   110  	require.NoError(t, err)
   111  	_, err = h.Write([]byte("hello1"))
   112  	require.Equal(t, EPERM, err)
   113  	assert.NoError(t, h.Close())
   114  	checkListing(t, root, []string{"file1,5,false"})
   115  
   116  	// Check opening the file with O_TRUNC does actually truncate
   117  	// it even if we don't write to it
   118  	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
   119  	require.NoError(t, err)
   120  	err = h.Close()
   121  	if errors.Cause(err) != fs.ErrorCantUploadEmptyFiles {
   122  		assert.NoError(t, err)
   123  		checkListing(t, root, []string{"file1,0,false"})
   124  	}
   125  
   126  	// Check opening the file with O_TRUNC and writing does work
   127  	h, err = vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
   128  	require.NoError(t, err)
   129  	_, err = h.WriteString("hello12")
   130  	require.NoError(t, err)
   131  	assert.NoError(t, h.Close())
   132  	checkListing(t, root, []string{"file1,7,false"})
   133  }
   134  
   135  func TestWriteFileHandleWriteAt(t *testing.T) {
   136  	r := fstest.NewRun(t)
   137  	defer r.Finalise()
   138  	vfs, fh := writeHandleCreate(t, r)
   139  
   140  	// Preconditions
   141  	assert.Equal(t, int64(0), fh.offset)
   142  	assert.False(t, fh.writeCalled)
   143  
   144  	// Write the data
   145  	n, err := fh.WriteAt([]byte("hello"), 0)
   146  	assert.NoError(t, err)
   147  	assert.Equal(t, 5, n)
   148  
   149  	// After write
   150  	assert.Equal(t, int64(5), fh.offset)
   151  	assert.True(t, fh.writeCalled)
   152  
   153  	// Check can't seek
   154  	n, err = fh.WriteAt([]byte("hello"), 100)
   155  	assert.Equal(t, ESPIPE, err)
   156  	assert.Equal(t, 0, n)
   157  
   158  	// Write more data
   159  	n, err = fh.WriteAt([]byte(" world"), 5)
   160  	assert.NoError(t, err)
   161  	assert.Equal(t, 6, n)
   162  
   163  	// Close
   164  	assert.NoError(t, fh.Close())
   165  
   166  	// Check can't write on closed handle
   167  	n, err = fh.WriteAt([]byte("hello"), 0)
   168  	assert.Equal(t, ECLOSED, err)
   169  	assert.Equal(t, 0, n)
   170  
   171  	// check vfs
   172  	root, err := vfs.Root()
   173  	require.NoError(t, err)
   174  	checkListing(t, root, []string{"file1,11,false"})
   175  
   176  	// check the underlying r.Fremote but not the modtime
   177  	file1 := fstest.NewItem("file1", "hello world", t1)
   178  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{}, fs.ModTimeNotSupported)
   179  }
   180  
   181  func TestWriteFileHandleFlush(t *testing.T) {
   182  	r := fstest.NewRun(t)
   183  	defer r.Finalise()
   184  	vfs, fh := writeHandleCreate(t, r)
   185  
   186  	// Check Flush already creates file for unwritten handles, without closing it
   187  	err := fh.Flush()
   188  	assert.NoError(t, err)
   189  	assert.False(t, fh.closed)
   190  	root, err := vfs.Root()
   191  	assert.NoError(t, err)
   192  	checkListing(t, root, []string{"file1,0,false"})
   193  
   194  	// Write some data
   195  	n, err := fh.Write([]byte("hello"))
   196  	assert.NoError(t, err)
   197  	assert.Equal(t, 5, n)
   198  
   199  	// Check Flush closes file if write called
   200  	err = fh.Flush()
   201  	assert.NoError(t, err)
   202  	assert.True(t, fh.closed)
   203  
   204  	// Check flush does nothing if called again
   205  	err = fh.Flush()
   206  	assert.NoError(t, err)
   207  	assert.True(t, fh.closed)
   208  
   209  	// Check file was written properly
   210  	root, err = vfs.Root()
   211  	assert.NoError(t, err)
   212  	checkListing(t, root, []string{"file1,5,false"})
   213  }
   214  
   215  func TestWriteFileHandleRelease(t *testing.T) {
   216  	r := fstest.NewRun(t)
   217  	defer r.Finalise()
   218  	_, fh := writeHandleCreate(t, r)
   219  
   220  	// Check Release closes file
   221  	err := fh.Release()
   222  	if errors.Cause(err) == fs.ErrorCantUploadEmptyFiles {
   223  		t.Logf("skipping test: %v", err)
   224  		return
   225  	}
   226  	assert.NoError(t, err)
   227  	assert.True(t, fh.closed)
   228  
   229  	// Check Release does nothing if called again
   230  	err = fh.Release()
   231  	assert.NoError(t, err)
   232  	assert.True(t, fh.closed)
   233  }
   234  
   235  var (
   236  	canSetModTimeOnce  sync.Once
   237  	canSetModTimeValue = true
   238  )
   239  
   240  // returns whether the remote can set modtime
   241  func canSetModTime(t *testing.T, r *fstest.Run) bool {
   242  	canSetModTimeOnce.Do(func() {
   243  		mtime1 := time.Date(2008, time.November, 18, 17, 32, 31, 0, time.UTC)
   244  		_ = r.WriteObject(context.Background(), "time_test", "stuff", mtime1)
   245  		obj, err := r.Fremote.NewObject(context.Background(), "time_test")
   246  		require.NoError(t, err)
   247  		mtime2 := time.Date(2009, time.November, 18, 17, 32, 31, 0, time.UTC)
   248  		err = obj.SetModTime(context.Background(), mtime2)
   249  		switch err {
   250  		case nil:
   251  			canSetModTimeValue = true
   252  		case fs.ErrorCantSetModTime, fs.ErrorCantSetModTimeWithoutDelete:
   253  			canSetModTimeValue = false
   254  		default:
   255  			require.NoError(t, err)
   256  		}
   257  		require.NoError(t, obj.Remove(context.Background()))
   258  		fs.Debugf(nil, "Can set mod time: %v", canSetModTimeValue)
   259  	})
   260  	return canSetModTimeValue
   261  }
   262  
   263  // tests mod time on open files
   264  func TestWriteFileModTimeWithOpenWriters(t *testing.T) {
   265  	r := fstest.NewRun(t)
   266  	defer r.Finalise()
   267  	if !canSetModTime(t, r) {
   268  		return
   269  	}
   270  	vfs, fh := writeHandleCreate(t, r)
   271  
   272  	mtime := time.Date(2012, time.November, 18, 17, 32, 31, 0, time.UTC)
   273  
   274  	_, err := fh.Write([]byte{104, 105})
   275  	require.NoError(t, err)
   276  
   277  	err = fh.Node().SetModTime(mtime)
   278  	require.NoError(t, err)
   279  
   280  	err = fh.Close()
   281  	require.NoError(t, err)
   282  
   283  	info, err := vfs.Stat("file1")
   284  	require.NoError(t, err)
   285  
   286  	if r.Fremote.Precision() != fs.ModTimeNotSupported {
   287  		// avoid errors because of timezone differences
   288  		assert.Equal(t, info.ModTime().Unix(), mtime.Unix())
   289  	}
   290  }
   291  
   292  func testFileReadAt(t *testing.T, n int) {
   293  	r := fstest.NewRun(t)
   294  	defer r.Finalise()
   295  	vfs, fh := writeHandleCreate(t, r)
   296  
   297  	contents := []byte(random.String(n))
   298  	if n != 0 {
   299  		written, err := fh.Write(contents)
   300  		require.NoError(t, err)
   301  		assert.Equal(t, n, written)
   302  	}
   303  
   304  	// Close the file without writing to it if n==0
   305  	err := fh.Close()
   306  	if errors.Cause(err) == fs.ErrorCantUploadEmptyFiles {
   307  		t.Logf("skipping test: %v", err)
   308  		return
   309  	}
   310  	assert.NoError(t, err)
   311  
   312  	// read the file back in using ReadAt into a buffer
   313  	// this simulates what mount does
   314  	rd, err := vfs.OpenFile("file1", os.O_RDONLY, 0)
   315  	require.NoError(t, err)
   316  
   317  	buf := make([]byte, 1024)
   318  	read, err := rd.ReadAt(buf, 0)
   319  	if err != io.EOF {
   320  		assert.NoError(t, err)
   321  	}
   322  	assert.Equal(t, read, n)
   323  	assert.Equal(t, contents, buf[:read])
   324  
   325  	err = rd.Close()
   326  	assert.NoError(t, err)
   327  }
   328  
   329  func TestFileReadAtZeroLength(t *testing.T) {
   330  	testFileReadAt(t, 0)
   331  }
   332  
   333  func TestFileReadAtNonZeroLength(t *testing.T) {
   334  	testFileReadAt(t, 100)
   335  }