github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/vfs/write_test.go (about)

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