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

     1  // Package fstests provides generic integration tests for the Fs and
     2  // Object interfaces.
     3  //
     4  // These tests are concerned with the basic functionality of a
     5  // backend.  The tests in fs/sync and fs/operations tests more
     6  // cornercases that these tests don't.
     7  package fstests
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"math/bits"
    16  	"os"
    17  	"path"
    18  	"path/filepath"
    19  	"reflect"
    20  	"sort"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/ncw/rclone/fs"
    26  	"github.com/ncw/rclone/fs/config"
    27  	"github.com/ncw/rclone/fs/fserrors"
    28  	"github.com/ncw/rclone/fs/hash"
    29  	"github.com/ncw/rclone/fs/object"
    30  	"github.com/ncw/rclone/fs/operations"
    31  	"github.com/ncw/rclone/fs/walk"
    32  	"github.com/ncw/rclone/fstest"
    33  	"github.com/ncw/rclone/lib/readers"
    34  	"github.com/pkg/errors"
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  // InternalTester is an optional interface for Fs which allows to execute internal tests
    40  //
    41  // This interface should be implemented in 'backend'_internal_test.go and not in 'backend'.go
    42  type InternalTester interface {
    43  	InternalTest(*testing.T)
    44  }
    45  
    46  // ChunkedUploadConfig contains the values used by TestFsPutChunked
    47  // to determine the limits of chunked uploading
    48  type ChunkedUploadConfig struct {
    49  	// Minimum allowed chunk size
    50  	MinChunkSize fs.SizeSuffix
    51  	// Maximum allowed chunk size, 0 is no limit
    52  	MaxChunkSize fs.SizeSuffix
    53  	// Rounds the given chunk size up to the next valid value
    54  	// nil will disable rounding
    55  	// e.g. the next power of 2
    56  	CeilChunkSize func(fs.SizeSuffix) fs.SizeSuffix
    57  	// More than one chunk is required on upload
    58  	NeedMultipleChunks bool
    59  }
    60  
    61  // SetUploadChunkSizer is a test only interface to change the upload chunk size at runtime
    62  type SetUploadChunkSizer interface {
    63  	// Change the configured UploadChunkSize.
    64  	// Will only be called while no transfer is in progress.
    65  	SetUploadChunkSize(fs.SizeSuffix) (fs.SizeSuffix, error)
    66  }
    67  
    68  // SetUploadCutoffer is a test only interface to change the upload cutoff size at runtime
    69  type SetUploadCutoffer interface {
    70  	// Change the configured UploadCutoff.
    71  	// Will only be called while no transfer is in progress.
    72  	SetUploadCutoff(fs.SizeSuffix) (fs.SizeSuffix, error)
    73  }
    74  
    75  // NextPowerOfTwo returns the current or next bigger power of two.
    76  // All values less or equal 0 will return 0
    77  func NextPowerOfTwo(i fs.SizeSuffix) fs.SizeSuffix {
    78  	return 1 << uint(64-bits.LeadingZeros64(uint64(i)-1))
    79  }
    80  
    81  // NextMultipleOf returns a function that can be used as a CeilChunkSize function.
    82  // This function will return the next multiple of m that is equal or bigger than i.
    83  // All values less or equal 0 will return 0.
    84  func NextMultipleOf(m fs.SizeSuffix) func(fs.SizeSuffix) fs.SizeSuffix {
    85  	if m <= 0 {
    86  		panic(fmt.Sprintf("invalid multiplier %s", m))
    87  	}
    88  	return func(i fs.SizeSuffix) fs.SizeSuffix {
    89  		if i <= 0 {
    90  			return 0
    91  		}
    92  
    93  		return (((i - 1) / m) + 1) * m
    94  	}
    95  }
    96  
    97  // dirsToNames returns a sorted list of names
    98  func dirsToNames(dirs []fs.Directory) []string {
    99  	names := []string{}
   100  	for _, dir := range dirs {
   101  		names = append(names, fstest.WinPath(fstest.Normalize(dir.Remote())))
   102  	}
   103  	sort.Strings(names)
   104  	return names
   105  }
   106  
   107  // objsToNames returns a sorted list of object names
   108  func objsToNames(objs []fs.Object) []string {
   109  	names := []string{}
   110  	for _, obj := range objs {
   111  		names = append(names, fstest.WinPath(fstest.Normalize(obj.Remote())))
   112  	}
   113  	sort.Strings(names)
   114  	return names
   115  }
   116  
   117  // findObject finds the object on the remote
   118  func findObject(t *testing.T, f fs.Fs, Name string) fs.Object {
   119  	var obj fs.Object
   120  	var err error
   121  	sleepTime := 1 * time.Second
   122  	for i := 1; i <= *fstest.ListRetries; i++ {
   123  		obj, err = f.NewObject(context.Background(), Name)
   124  		if err == nil {
   125  			break
   126  		}
   127  		t.Logf("Sleeping for %v for findObject eventual consistency: %d/%d (%v)", sleepTime, i, *fstest.ListRetries, err)
   128  		time.Sleep(sleepTime)
   129  		sleepTime = (sleepTime * 3) / 2
   130  	}
   131  	require.NoError(t, err)
   132  	return obj
   133  }
   134  
   135  // retry f() until no retriable error
   136  func retry(t *testing.T, what string, f func() error) {
   137  	const maxTries = 10
   138  	var err error
   139  	for tries := 1; tries <= maxTries; tries++ {
   140  		err = f()
   141  		// exit if no error, or error is not retriable
   142  		if err == nil || !fserrors.IsRetryError(err) {
   143  			break
   144  		}
   145  		t.Logf("%s error: %v - low level retry %d/%d", what, err, tries, maxTries)
   146  		time.Sleep(2 * time.Second)
   147  	}
   148  	require.NoError(t, err, what)
   149  }
   150  
   151  // testPut puts file to the remote
   152  func testPut(t *testing.T, f fs.Fs, file *fstest.Item) (string, fs.Object) {
   153  	var (
   154  		err        error
   155  		obj        fs.Object
   156  		uploadHash *hash.MultiHasher
   157  		contents   string
   158  	)
   159  	retry(t, "Put", func() error {
   160  		contents = fstest.RandomString(100)
   161  		buf := bytes.NewBufferString(contents)
   162  		uploadHash = hash.NewMultiHasher()
   163  		in := io.TeeReader(buf, uploadHash)
   164  
   165  		file.Size = int64(buf.Len())
   166  		obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
   167  		obj, err = f.Put(context.Background(), in, obji)
   168  		return err
   169  	})
   170  	file.Hashes = uploadHash.Sums()
   171  	file.Check(t, obj, f.Precision())
   172  	// Re-read the object and check again
   173  	obj = findObject(t, f, file.Path)
   174  	file.Check(t, obj, f.Precision())
   175  	return contents, obj
   176  }
   177  
   178  // testPutLarge puts file to the remote, checks it and removes it on success.
   179  func testPutLarge(t *testing.T, f fs.Fs, file *fstest.Item) {
   180  	var (
   181  		err        error
   182  		obj        fs.Object
   183  		uploadHash *hash.MultiHasher
   184  	)
   185  	retry(t, "PutLarge", func() error {
   186  		r := readers.NewPatternReader(file.Size)
   187  		uploadHash = hash.NewMultiHasher()
   188  		in := io.TeeReader(r, uploadHash)
   189  
   190  		obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
   191  		obj, err = f.Put(context.Background(), in, obji)
   192  		if file.Size == 0 && err == fs.ErrorCantUploadEmptyFiles {
   193  			t.Skip("Can't upload zero length files")
   194  		}
   195  		return err
   196  	})
   197  	file.Hashes = uploadHash.Sums()
   198  	file.Check(t, obj, f.Precision())
   199  
   200  	// Re-read the object and check again
   201  	obj = findObject(t, f, file.Path)
   202  	file.Check(t, obj, f.Precision())
   203  
   204  	// Download the object and check it is OK
   205  	downloadHash := hash.NewMultiHasher()
   206  	download, err := obj.Open(context.Background())
   207  	require.NoError(t, err)
   208  	n, err := io.Copy(downloadHash, download)
   209  	require.NoError(t, err)
   210  	assert.Equal(t, file.Size, n)
   211  	require.NoError(t, download.Close())
   212  	assert.Equal(t, file.Hashes, downloadHash.Sums())
   213  
   214  	// Remove the object
   215  	require.NoError(t, obj.Remove(context.Background()))
   216  }
   217  
   218  // errorReader just returns an error on Read
   219  type errorReader struct {
   220  	err error
   221  }
   222  
   223  // Read returns an error immediately
   224  func (er errorReader) Read(p []byte) (n int, err error) {
   225  	return 0, er.err
   226  }
   227  
   228  // read the contents of an object as a string
   229  func readObject(t *testing.T, obj fs.Object, limit int64, options ...fs.OpenOption) string {
   230  	what := fmt.Sprintf("readObject(%q) limit=%d, options=%+v", obj, limit, options)
   231  	in, err := obj.Open(context.Background(), options...)
   232  	require.NoError(t, err, what)
   233  	var r io.Reader = in
   234  	if limit >= 0 {
   235  		r = &io.LimitedReader{R: r, N: limit}
   236  	}
   237  	contents, err := ioutil.ReadAll(r)
   238  	require.NoError(t, err, what)
   239  	err = in.Close()
   240  	require.NoError(t, err, what)
   241  	return string(contents)
   242  }
   243  
   244  // ExtraConfigItem describes a config item for the tests
   245  type ExtraConfigItem struct{ Name, Key, Value string }
   246  
   247  // Opt is options for Run
   248  type Opt struct {
   249  	RemoteName                   string
   250  	NilObject                    fs.Object
   251  	ExtraConfig                  []ExtraConfigItem
   252  	SkipBadWindowsCharacters     bool     // skips unusable characters for windows if set
   253  	SkipFsMatch                  bool     // if set skip exact matching of Fs value
   254  	TiersToTest                  []string // List of tiers which can be tested in setTier test
   255  	ChunkedUpload                ChunkedUploadConfig
   256  	UnimplementableFsMethods     []string // List of methods which can't be implemented in this wrapping Fs
   257  	UnimplementableObjectMethods []string // List of methods which can't be implemented in this wrapping Fs
   258  	SkipFsCheckWrap              bool     // if set skip FsCheckWrap
   259  	SkipObjectCheckWrap          bool     // if set skip ObjectCheckWrap
   260  }
   261  
   262  // returns true if x is found in ss
   263  func stringsContains(x string, ss []string) bool {
   264  	for _, s := range ss {
   265  		if x == s {
   266  			return true
   267  		}
   268  	}
   269  	return false
   270  }
   271  
   272  // Run runs the basic integration tests for a remote using the options passed in.
   273  //
   274  // They are structured in a hierarchical way so that dependencies for the tests can be created.
   275  //
   276  // For example some tests require the directory to be created - these
   277  // are inside the "FsMkdir" test.  Some tests require some tests files
   278  // - these are inside the "FsPutFiles" test.
   279  func Run(t *testing.T, opt *Opt) {
   280  	var (
   281  		remote        fs.Fs
   282  		remoteName    = opt.RemoteName
   283  		subRemoteName string
   284  		subRemoteLeaf string
   285  		file1         = fstest.Item{
   286  			ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
   287  			Path:    "file name.txt",
   288  		}
   289  		file1Contents string
   290  		file2         = fstest.Item{
   291  			ModTime: fstest.Time("2001-02-03T04:05:10.123123123Z"),
   292  			Path:    `hello? sausage/êé/Hello, 世界/ " ' @ < > & ? + ≠/z.txt`,
   293  			WinPath: `hello_ sausage/êé/Hello, 世界/ _ ' @ _ _ & _ + ≠/z.txt`,
   294  		}
   295  		isLocalRemote bool
   296  		purged        bool // whether the dir has been purged or not
   297  	)
   298  
   299  	// Skip the test if the remote isn't configured
   300  	skipIfNotOk := func(t *testing.T) {
   301  		if remote == nil {
   302  			t.Skipf("WARN: %q not configured", remoteName)
   303  		}
   304  	}
   305  
   306  	// Skip if remote is not ListR capable, otherwise set the useListR
   307  	// flag, returning a function to restore its value
   308  	skipIfNotListR := func(t *testing.T) func() {
   309  		skipIfNotOk(t)
   310  		if remote.Features().ListR == nil {
   311  			t.Skip("FS has no ListR interface")
   312  		}
   313  		previous := fs.Config.UseListR
   314  		fs.Config.UseListR = true
   315  		return func() {
   316  			fs.Config.UseListR = previous
   317  		}
   318  	}
   319  
   320  	// Skip if remote is not SetTier and GetTier capable
   321  	skipIfNotSetTier := func(t *testing.T) {
   322  		skipIfNotOk(t)
   323  		if remote.Features().SetTier == false ||
   324  			remote.Features().GetTier == false {
   325  			t.Skip("FS has no SetTier & GetTier interfaces")
   326  		}
   327  	}
   328  
   329  	// Return true if f (or any of the things it wraps) is bucket
   330  	// based but not at the root.
   331  	isBucketBasedButNotRoot := func(f fs.Fs) bool {
   332  		for {
   333  			doUnWrap := f.Features().UnWrap
   334  			if doUnWrap == nil {
   335  				break
   336  			}
   337  			f = doUnWrap()
   338  		}
   339  		return f.Features().BucketBased && strings.Contains(strings.Trim(f.Root(), "/"), "/")
   340  	}
   341  
   342  	// Remove bad characters from Windows file name if set
   343  	if opt.SkipBadWindowsCharacters {
   344  		t.Logf("Removing bad windows characters from test file")
   345  		file2.Path = fstest.WinPath(file2.Path)
   346  	}
   347  
   348  	// Initialise the remote
   349  	fstest.Initialise()
   350  
   351  	// Set extra config if supplied
   352  	for _, item := range opt.ExtraConfig {
   353  		config.FileSet(item.Name, item.Key, item.Value)
   354  	}
   355  	if *fstest.RemoteName != "" {
   356  		remoteName = *fstest.RemoteName
   357  	}
   358  	t.Logf("Using remote %q", remoteName)
   359  	var err error
   360  	if remoteName == "" {
   361  		remoteName, err = fstest.LocalRemote()
   362  		require.NoError(t, err)
   363  		isLocalRemote = true
   364  	}
   365  
   366  	// Make the Fs we are testing with, initialising the local variables
   367  	// subRemoteName - name of the remote after the TestRemote:
   368  	// subRemoteLeaf - a subdirectory to use under that
   369  	// remote - the result of  fs.NewFs(TestRemote:subRemoteName)
   370  	subRemoteName, subRemoteLeaf, err = fstest.RandomRemoteName(remoteName)
   371  	require.NoError(t, err)
   372  	remote, err = fs.NewFs(subRemoteName)
   373  	if err == fs.ErrorNotFoundInConfigFile {
   374  		t.Logf("Didn't find %q in config file - skipping tests", remoteName)
   375  		return
   376  	}
   377  	require.NoError(t, err, fmt.Sprintf("unexpected error: %v", err))
   378  
   379  	// Skip the rest if it failed
   380  	skipIfNotOk(t)
   381  
   382  	// Check to see if Fs that wrap other Fs implement all the optional methods
   383  	t.Run("FsCheckWrap", func(t *testing.T) {
   384  		skipIfNotOk(t)
   385  		if opt.SkipFsCheckWrap {
   386  			t.Skip("Skipping FsCheckWrap on this Fs")
   387  		}
   388  		ft := new(fs.Features).Fill(remote)
   389  		if ft.UnWrap == nil {
   390  			t.Skip("Not a wrapping Fs")
   391  		}
   392  		v := reflect.ValueOf(ft).Elem()
   393  		vType := v.Type()
   394  		for i := 0; i < v.NumField(); i++ {
   395  			vName := vType.Field(i).Name
   396  			if stringsContains(vName, opt.UnimplementableFsMethods) {
   397  				continue
   398  			}
   399  			field := v.Field(i)
   400  			// skip the bools
   401  			if field.Type().Kind() == reflect.Bool {
   402  				continue
   403  			}
   404  			if field.IsNil() {
   405  				t.Errorf("Missing Fs wrapper for %s", vName)
   406  			}
   407  		}
   408  	})
   409  
   410  	// TestFsRmdirNotFound tests deleting a non existent directory
   411  	t.Run("FsRmdirNotFound", func(t *testing.T) {
   412  		skipIfNotOk(t)
   413  		if isBucketBasedButNotRoot(remote) {
   414  			t.Skip("Skipping test as non root bucket based remote")
   415  		}
   416  		err := remote.Rmdir(context.Background(), "")
   417  		assert.Error(t, err, "Expecting error on Rmdir non existent")
   418  	})
   419  
   420  	// Make the directory
   421  	err = remote.Mkdir(context.Background(), "")
   422  	require.NoError(t, err)
   423  	fstest.CheckListing(t, remote, []fstest.Item{})
   424  
   425  	// TestFsString tests the String method
   426  	t.Run("FsString", func(t *testing.T) {
   427  		skipIfNotOk(t)
   428  		str := remote.String()
   429  		require.NotEqual(t, "", str)
   430  	})
   431  
   432  	// TestFsName tests the Name method
   433  	t.Run("FsName", func(t *testing.T) {
   434  		skipIfNotOk(t)
   435  		got := remote.Name()
   436  		want := remoteName[:strings.IndexRune(remoteName, ':')+1]
   437  		if isLocalRemote {
   438  			want = "local:"
   439  		}
   440  		require.Equal(t, want, got+":")
   441  	})
   442  
   443  	// TestFsRoot tests the Root method
   444  	t.Run("FsRoot", func(t *testing.T) {
   445  		skipIfNotOk(t)
   446  		name := remote.Name() + ":"
   447  		root := remote.Root()
   448  		if isLocalRemote {
   449  			// only check last path element on local
   450  			require.Equal(t, filepath.Base(subRemoteName), filepath.Base(root))
   451  		} else {
   452  			require.Equal(t, subRemoteName, name+root)
   453  		}
   454  	})
   455  
   456  	// TestFsRmdirEmpty tests deleting an empty directory
   457  	t.Run("FsRmdirEmpty", func(t *testing.T) {
   458  		skipIfNotOk(t)
   459  		err := remote.Rmdir(context.Background(), "")
   460  		require.NoError(t, err)
   461  	})
   462  
   463  	// TestFsMkdir tests making a directory
   464  	//
   465  	// Tests that require the directory to be made are within this
   466  	t.Run("FsMkdir", func(t *testing.T) {
   467  		skipIfNotOk(t)
   468  
   469  		err := remote.Mkdir(context.Background(), "")
   470  		require.NoError(t, err)
   471  		fstest.CheckListing(t, remote, []fstest.Item{})
   472  
   473  		err = remote.Mkdir(context.Background(), "")
   474  		require.NoError(t, err)
   475  
   476  		// TestFsMkdirRmdirSubdir tests making and removing a sub directory
   477  		t.Run("FsMkdirRmdirSubdir", func(t *testing.T) {
   478  			skipIfNotOk(t)
   479  			dir := "dir/subdir"
   480  			err := operations.Mkdir(context.Background(), remote, dir)
   481  			require.NoError(t, err)
   482  			fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir", "dir/subdir"}, fs.GetModifyWindow(remote))
   483  
   484  			err = operations.Rmdir(context.Background(), remote, dir)
   485  			require.NoError(t, err)
   486  			fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir"}, fs.GetModifyWindow(remote))
   487  
   488  			err = operations.Rmdir(context.Background(), remote, "dir")
   489  			require.NoError(t, err)
   490  			fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.GetModifyWindow(remote))
   491  		})
   492  
   493  		// TestFsListEmpty tests listing an empty directory
   494  		t.Run("FsListEmpty", func(t *testing.T) {
   495  			skipIfNotOk(t)
   496  			fstest.CheckListing(t, remote, []fstest.Item{})
   497  		})
   498  
   499  		// TestFsListDirEmpty tests listing the directories from an empty directory
   500  		TestFsListDirEmpty := func(t *testing.T) {
   501  			skipIfNotOk(t)
   502  			objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, 1)
   503  			require.NoError(t, err)
   504  			assert.Equal(t, []string{}, objsToNames(objs))
   505  			assert.Equal(t, []string{}, dirsToNames(dirs))
   506  		}
   507  		t.Run("FsListDirEmpty", TestFsListDirEmpty)
   508  
   509  		// TestFsListRDirEmpty tests listing the directories from an empty directory using ListR
   510  		t.Run("FsListRDirEmpty", func(t *testing.T) {
   511  			defer skipIfNotListR(t)()
   512  			TestFsListDirEmpty(t)
   513  		})
   514  
   515  		// TestFsListDirNotFound tests listing the directories from an empty directory
   516  		TestFsListDirNotFound := func(t *testing.T) {
   517  			skipIfNotOk(t)
   518  			objs, dirs, err := walk.GetAll(context.Background(), remote, "does not exist", true, 1)
   519  			if !remote.Features().CanHaveEmptyDirectories {
   520  				if err != fs.ErrorDirNotFound {
   521  					assert.NoError(t, err)
   522  					assert.Equal(t, 0, len(objs)+len(dirs))
   523  				}
   524  			} else {
   525  				assert.Equal(t, fs.ErrorDirNotFound, err)
   526  			}
   527  		}
   528  		t.Run("FsListDirNotFound", TestFsListDirNotFound)
   529  
   530  		// TestFsListRDirNotFound tests listing the directories from an empty directory using ListR
   531  		t.Run("FsListRDirNotFound", func(t *testing.T) {
   532  			defer skipIfNotListR(t)()
   533  			TestFsListDirNotFound(t)
   534  		})
   535  
   536  		// TestFsNewObjectNotFound tests not finding a object
   537  		t.Run("FsNewObjectNotFound", func(t *testing.T) {
   538  			skipIfNotOk(t)
   539  			// Object in an existing directory
   540  			o, err := remote.NewObject(context.Background(), "potato")
   541  			assert.Nil(t, o)
   542  			assert.Equal(t, fs.ErrorObjectNotFound, err)
   543  			// Now try an object in a non existing directory
   544  			o, err = remote.NewObject(context.Background(), "directory/not/found/potato")
   545  			assert.Nil(t, o)
   546  			assert.Equal(t, fs.ErrorObjectNotFound, err)
   547  		})
   548  
   549  		// TestFsPutError tests uploading a file where there is an error
   550  		//
   551  		// It makes sure that aborting a file half way through does not create
   552  		// a file on the remote.
   553  		//
   554  		// go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutError)$'
   555  		t.Run("FsPutError", func(t *testing.T) {
   556  			skipIfNotOk(t)
   557  
   558  			const N = 5 * 1024
   559  			// Read N bytes then produce an error
   560  			contents := fstest.RandomString(N)
   561  			buf := bytes.NewBufferString(contents)
   562  			er := &errorReader{errors.New("potato")}
   563  			in := io.MultiReader(buf, er)
   564  
   565  			obji := object.NewStaticObjectInfo(file2.Path, file2.ModTime, 2*N, true, nil, nil)
   566  			_, err := remote.Put(context.Background(), in, obji)
   567  			// assert.Nil(t, obj) - FIXME some remotes return the object even on nil
   568  			assert.NotNil(t, err)
   569  
   570  			obj, err := remote.NewObject(context.Background(), file2.Path)
   571  			assert.Nil(t, obj)
   572  			assert.Equal(t, fs.ErrorObjectNotFound, err)
   573  		})
   574  
   575  		t.Run("FsPutChunked", func(t *testing.T) {
   576  			skipIfNotOk(t)
   577  			if testing.Short() {
   578  				t.Skip("not running with -short")
   579  			}
   580  
   581  			setUploadChunkSizer, _ := remote.(SetUploadChunkSizer)
   582  			if setUploadChunkSizer == nil {
   583  				t.Skipf("%T does not implement SetUploadChunkSizer", remote)
   584  			}
   585  
   586  			setUploadCutoffer, _ := remote.(SetUploadCutoffer)
   587  
   588  			minChunkSize := opt.ChunkedUpload.MinChunkSize
   589  			if minChunkSize < 100 {
   590  				minChunkSize = 100
   591  			}
   592  			if opt.ChunkedUpload.CeilChunkSize != nil {
   593  				minChunkSize = opt.ChunkedUpload.CeilChunkSize(minChunkSize)
   594  			}
   595  
   596  			maxChunkSize := 2 * fs.MebiByte
   597  			if maxChunkSize < 2*minChunkSize {
   598  				maxChunkSize = 2 * minChunkSize
   599  			}
   600  			if opt.ChunkedUpload.MaxChunkSize > 0 && maxChunkSize > opt.ChunkedUpload.MaxChunkSize {
   601  				maxChunkSize = opt.ChunkedUpload.MaxChunkSize
   602  			}
   603  			if opt.ChunkedUpload.CeilChunkSize != nil {
   604  				maxChunkSize = opt.ChunkedUpload.CeilChunkSize(maxChunkSize)
   605  			}
   606  
   607  			next := func(f func(fs.SizeSuffix) fs.SizeSuffix) fs.SizeSuffix {
   608  				s := f(minChunkSize)
   609  				if s > maxChunkSize {
   610  					s = minChunkSize
   611  				}
   612  				return s
   613  			}
   614  
   615  			chunkSizes := fs.SizeSuffixList{
   616  				minChunkSize,
   617  				minChunkSize + (maxChunkSize-minChunkSize)/3,
   618  				next(NextPowerOfTwo),
   619  				next(NextMultipleOf(100000)),
   620  				next(NextMultipleOf(100001)),
   621  				maxChunkSize,
   622  			}
   623  			chunkSizes.Sort()
   624  
   625  			// Set the minimum chunk size, upload cutoff and reset it at the end
   626  			oldChunkSize, err := setUploadChunkSizer.SetUploadChunkSize(minChunkSize)
   627  			require.NoError(t, err)
   628  			var oldUploadCutoff fs.SizeSuffix
   629  			if setUploadCutoffer != nil {
   630  				oldUploadCutoff, err = setUploadCutoffer.SetUploadCutoff(minChunkSize)
   631  				require.NoError(t, err)
   632  			}
   633  			defer func() {
   634  				_, err := setUploadChunkSizer.SetUploadChunkSize(oldChunkSize)
   635  				assert.NoError(t, err)
   636  				if setUploadCutoffer != nil {
   637  					_, err := setUploadCutoffer.SetUploadCutoff(oldUploadCutoff)
   638  					assert.NoError(t, err)
   639  				}
   640  			}()
   641  
   642  			var lastCs fs.SizeSuffix
   643  			for _, cs := range chunkSizes {
   644  				if cs <= lastCs {
   645  					continue
   646  				}
   647  				if opt.ChunkedUpload.CeilChunkSize != nil {
   648  					cs = opt.ChunkedUpload.CeilChunkSize(cs)
   649  				}
   650  				lastCs = cs
   651  
   652  				t.Run(cs.String(), func(t *testing.T) {
   653  					_, err := setUploadChunkSizer.SetUploadChunkSize(cs)
   654  					require.NoError(t, err)
   655  					if setUploadCutoffer != nil {
   656  						_, err = setUploadCutoffer.SetUploadCutoff(cs)
   657  						require.NoError(t, err)
   658  					}
   659  
   660  					var testChunks []fs.SizeSuffix
   661  					if opt.ChunkedUpload.NeedMultipleChunks {
   662  						// If NeedMultipleChunks is set then test with > cs
   663  						testChunks = []fs.SizeSuffix{cs + 1, 2 * cs, 2*cs + 1}
   664  					} else {
   665  						testChunks = []fs.SizeSuffix{cs - 1, cs, 2*cs + 1}
   666  					}
   667  
   668  					for _, fileSize := range testChunks {
   669  						t.Run(fmt.Sprintf("%d", fileSize), func(t *testing.T) {
   670  							testPutLarge(t, remote, &fstest.Item{
   671  								ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
   672  								Path:    fmt.Sprintf("chunked-%s-%s.bin", cs.String(), fileSize.String()),
   673  								Size:    int64(fileSize),
   674  							})
   675  						})
   676  					}
   677  				})
   678  			}
   679  		})
   680  
   681  		t.Run("FsPutZeroLength", func(t *testing.T) {
   682  			skipIfNotOk(t)
   683  
   684  			testPutLarge(t, remote, &fstest.Item{
   685  				ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
   686  				Path:    fmt.Sprintf("zero-length-file"),
   687  				Size:    int64(0),
   688  			})
   689  		})
   690  
   691  		t.Run("FsOpenWriterAt", func(t *testing.T) {
   692  			skipIfNotOk(t)
   693  			openWriterAt := remote.Features().OpenWriterAt
   694  			if openWriterAt == nil {
   695  				t.Skip("FS has no OpenWriterAt interface")
   696  			}
   697  			path := "writer-at-subdir/writer-at-file"
   698  			out, err := openWriterAt(context.Background(), path, -1)
   699  			require.NoError(t, err)
   700  
   701  			var n int
   702  			n, err = out.WriteAt([]byte("def"), 3)
   703  			assert.NoError(t, err)
   704  			assert.Equal(t, 3, n)
   705  			n, err = out.WriteAt([]byte("ghi"), 6)
   706  			assert.NoError(t, err)
   707  			assert.Equal(t, 3, n)
   708  			n, err = out.WriteAt([]byte("abc"), 0)
   709  			assert.NoError(t, err)
   710  			assert.Equal(t, 3, n)
   711  
   712  			assert.NoError(t, out.Close())
   713  
   714  			obj := findObject(t, remote, path)
   715  			assert.Equal(t, "abcdefghi", readObject(t, obj, -1), "contents of file differ")
   716  
   717  			assert.NoError(t, obj.Remove(context.Background()))
   718  			assert.NoError(t, remote.Rmdir(context.Background(), "writer-at-subdir"))
   719  		})
   720  
   721  		// TestFsChangeNotify tests that changes are properly
   722  		// propagated
   723  		//
   724  		// go test -v -remote TestDrive: -run '^Test(Setup|Init|FsChangeNotify)$' -verbose
   725  		t.Run("FsChangeNotify", func(t *testing.T) {
   726  			skipIfNotOk(t)
   727  
   728  			// Check have ChangeNotify
   729  			doChangeNotify := remote.Features().ChangeNotify
   730  			if doChangeNotify == nil {
   731  				t.Skip("FS has no ChangeNotify interface")
   732  			}
   733  
   734  			err := operations.Mkdir(context.Background(), remote, "dir")
   735  			require.NoError(t, err)
   736  
   737  			pollInterval := make(chan time.Duration)
   738  			dirChanges := map[string]struct{}{}
   739  			objChanges := map[string]struct{}{}
   740  			doChangeNotify(context.Background(), func(x string, e fs.EntryType) {
   741  				fs.Debugf(nil, "doChangeNotify(%q, %+v)", x, e)
   742  				if strings.HasPrefix(x, file1.Path[:5]) || strings.HasPrefix(x, file2.Path[:5]) {
   743  					fs.Debugf(nil, "Ignoring notify for file1 or file2: %q, %v", x, e)
   744  					return
   745  				}
   746  				if e == fs.EntryDirectory {
   747  					dirChanges[x] = struct{}{}
   748  				} else if e == fs.EntryObject {
   749  					objChanges[x] = struct{}{}
   750  				}
   751  			}, pollInterval)
   752  			defer func() { close(pollInterval) }()
   753  			pollInterval <- time.Second
   754  
   755  			var dirs []string
   756  			for _, idx := range []int{1, 3, 2} {
   757  				dir := fmt.Sprintf("dir/subdir%d", idx)
   758  				err = operations.Mkdir(context.Background(), remote, dir)
   759  				require.NoError(t, err)
   760  				dirs = append(dirs, dir)
   761  			}
   762  
   763  			var objs []fs.Object
   764  			for _, idx := range []int{2, 4, 3} {
   765  				file := fstest.Item{
   766  					ModTime: time.Now(),
   767  					Path:    fmt.Sprintf("dir/file%d", idx),
   768  				}
   769  				_, o := testPut(t, remote, &file)
   770  				objs = append(objs, o)
   771  			}
   772  
   773  			// Looks for each item in wants in changes -
   774  			// if they are all found it returns true
   775  			contains := func(changes map[string]struct{}, wants []string) bool {
   776  				for _, want := range wants {
   777  					_, ok := changes[want]
   778  					if !ok {
   779  						return false
   780  					}
   781  				}
   782  				return true
   783  			}
   784  
   785  			// Wait a little while for the changes to come in
   786  			wantDirChanges := []string{"dir/subdir1", "dir/subdir3", "dir/subdir2"}
   787  			wantObjChanges := []string{"dir/file2", "dir/file4", "dir/file3"}
   788  			ok := false
   789  			for tries := 1; tries < 10; tries++ {
   790  				ok = contains(dirChanges, wantDirChanges) && contains(objChanges, wantObjChanges)
   791  				if ok {
   792  					break
   793  				}
   794  				t.Logf("Try %d/10 waiting for dirChanges and objChanges", tries)
   795  				time.Sleep(3 * time.Second)
   796  			}
   797  			if !ok {
   798  				t.Errorf("%+v does not contain %+v or \n%+v does not contain %+v", dirChanges, wantDirChanges, objChanges, wantObjChanges)
   799  			}
   800  
   801  			// tidy up afterwards
   802  			for _, o := range objs {
   803  				assert.NoError(t, o.Remove(context.Background()))
   804  			}
   805  			dirs = append(dirs, "dir")
   806  			for _, dir := range dirs {
   807  				assert.NoError(t, remote.Rmdir(context.Background(), dir))
   808  			}
   809  		})
   810  
   811  		// TestFsPut files writes file1, file2 and tests an update
   812  		//
   813  		// Tests that require file1, file2 are within this
   814  		t.Run("FsPutFiles", func(t *testing.T) {
   815  			skipIfNotOk(t)
   816  			file1Contents, _ = testPut(t, remote, &file1)
   817  			/* file2Contents = */ testPut(t, remote, &file2)
   818  			file1Contents, _ = testPut(t, remote, &file1)
   819  			// Note that the next test will check there are no duplicated file names
   820  
   821  			// TestFsListDirFile2 tests the files are correctly uploaded by doing
   822  			// Depth 1 directory listings
   823  			TestFsListDirFile2 := func(t *testing.T) {
   824  				skipIfNotOk(t)
   825  				list := func(dir string, expectedDirNames, expectedObjNames []string) {
   826  					var objNames, dirNames []string
   827  					for i := 1; i <= *fstest.ListRetries; i++ {
   828  						objs, dirs, err := walk.GetAll(context.Background(), remote, dir, true, 1)
   829  						if errors.Cause(err) == fs.ErrorDirNotFound {
   830  							objs, dirs, err = walk.GetAll(context.Background(), remote, fstest.WinPath(dir), true, 1)
   831  						}
   832  						require.NoError(t, err)
   833  						objNames = objsToNames(objs)
   834  						dirNames = dirsToNames(dirs)
   835  						if len(objNames) >= len(expectedObjNames) && len(dirNames) >= len(expectedDirNames) {
   836  							break
   837  						}
   838  						t.Logf("Sleeping for 1 second for TestFsListDirFile2 eventual consistency: %d/%d", i, *fstest.ListRetries)
   839  						time.Sleep(1 * time.Second)
   840  					}
   841  					assert.Equal(t, expectedDirNames, dirNames)
   842  					assert.Equal(t, expectedObjNames, objNames)
   843  				}
   844  				dir := file2.Path
   845  				deepest := true
   846  				for dir != "" {
   847  					expectedObjNames := []string{}
   848  					expectedDirNames := []string{}
   849  					child := dir
   850  					dir = path.Dir(dir)
   851  					if dir == "." {
   852  						dir = ""
   853  						expectedObjNames = append(expectedObjNames, fstest.WinPath(file1.Path))
   854  					}
   855  					if deepest {
   856  						expectedObjNames = append(expectedObjNames, fstest.WinPath(file2.Path))
   857  						deepest = false
   858  					} else {
   859  						expectedDirNames = append(expectedDirNames, fstest.WinPath(child))
   860  					}
   861  					list(dir, expectedDirNames, expectedObjNames)
   862  				}
   863  			}
   864  			t.Run("FsListDirFile2", TestFsListDirFile2)
   865  
   866  			// TestFsListRDirFile2 tests the files are correctly uploaded by doing
   867  			// Depth 1 directory listings using ListR
   868  			t.Run("FsListRDirFile2", func(t *testing.T) {
   869  				defer skipIfNotListR(t)()
   870  				TestFsListDirFile2(t)
   871  			})
   872  
   873  			// Test the files are all there with walk.ListR recursive listings
   874  			t.Run("FsListR", func(t *testing.T) {
   875  				skipIfNotOk(t)
   876  				objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, -1)
   877  				require.NoError(t, err)
   878  				assert.Equal(t, []string{
   879  					"hello_ sausage",
   880  					"hello_ sausage/êé",
   881  					"hello_ sausage/êé/Hello, 世界",
   882  					"hello_ sausage/êé/Hello, 世界/ _ ' @ _ _ & _ + ≠",
   883  				}, dirsToNames(dirs))
   884  				assert.Equal(t, []string{
   885  					"file name.txt",
   886  					"hello_ sausage/êé/Hello, 世界/ _ ' @ _ _ & _ + ≠/z.txt",
   887  				}, objsToNames(objs))
   888  			})
   889  
   890  			// Test the files are all there with
   891  			// walk.ListR recursive listings on a sub dir
   892  			t.Run("FsListRSubdir", func(t *testing.T) {
   893  				skipIfNotOk(t)
   894  				objs, dirs, err := walk.GetAll(context.Background(), remote, path.Dir(path.Dir(path.Dir(path.Dir(file2.Path)))), true, -1)
   895  				require.NoError(t, err)
   896  				assert.Equal(t, []string{
   897  					"hello_ sausage/êé",
   898  					"hello_ sausage/êé/Hello, 世界",
   899  					"hello_ sausage/êé/Hello, 世界/ _ ' @ _ _ & _ + ≠",
   900  				}, dirsToNames(dirs))
   901  				assert.Equal(t, []string{
   902  					"hello_ sausage/êé/Hello, 世界/ _ ' @ _ _ & _ + ≠/z.txt",
   903  				}, objsToNames(objs))
   904  			})
   905  
   906  			// TestFsListDirRoot tests that DirList works in the root
   907  			TestFsListDirRoot := func(t *testing.T) {
   908  				skipIfNotOk(t)
   909  				rootRemote, err := fs.NewFs(remoteName)
   910  				require.NoError(t, err)
   911  				_, dirs, err := walk.GetAll(context.Background(), rootRemote, "", true, 1)
   912  				require.NoError(t, err)
   913  				assert.Contains(t, dirsToNames(dirs), subRemoteLeaf, "Remote leaf not found")
   914  			}
   915  			t.Run("FsListDirRoot", TestFsListDirRoot)
   916  
   917  			// TestFsListRDirRoot tests that DirList works in the root using ListR
   918  			t.Run("FsListRDirRoot", func(t *testing.T) {
   919  				defer skipIfNotListR(t)()
   920  				TestFsListDirRoot(t)
   921  			})
   922  
   923  			// TestFsListSubdir tests List works for a subdirectory
   924  			TestFsListSubdir := func(t *testing.T) {
   925  				skipIfNotOk(t)
   926  				fileName := file2.Path
   927  				var err error
   928  				var objs []fs.Object
   929  				var dirs []fs.Directory
   930  				for i := 0; i < 2; i++ {
   931  					dir, _ := path.Split(fileName)
   932  					dir = dir[:len(dir)-1]
   933  					objs, dirs, err = walk.GetAll(context.Background(), remote, dir, true, -1)
   934  					if err != fs.ErrorDirNotFound {
   935  						break
   936  					}
   937  					fileName = file2.WinPath
   938  				}
   939  				require.NoError(t, err)
   940  				require.Len(t, objs, 1)
   941  				assert.Equal(t, fileName, objs[0].Remote())
   942  				require.Len(t, dirs, 0)
   943  			}
   944  			t.Run("FsListSubdir", TestFsListSubdir)
   945  
   946  			// TestFsListRSubdir tests List works for a subdirectory using ListR
   947  			t.Run("FsListRSubdir", func(t *testing.T) {
   948  				defer skipIfNotListR(t)()
   949  				TestFsListSubdir(t)
   950  			})
   951  
   952  			// TestFsListLevel2 tests List works for 2 levels
   953  			TestFsListLevel2 := func(t *testing.T) {
   954  				skipIfNotOk(t)
   955  				objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, 2)
   956  				if err == fs.ErrorLevelNotSupported {
   957  					return
   958  				}
   959  				require.NoError(t, err)
   960  				assert.Equal(t, []string{file1.Path}, objsToNames(objs))
   961  				assert.Equal(t, []string{`hello_ sausage`, `hello_ sausage/êé`}, dirsToNames(dirs))
   962  			}
   963  			t.Run("FsListLevel2", TestFsListLevel2)
   964  
   965  			// TestFsListRLevel2 tests List works for 2 levels using ListR
   966  			t.Run("FsListRLevel2", func(t *testing.T) {
   967  				defer skipIfNotListR(t)()
   968  				TestFsListLevel2(t)
   969  			})
   970  
   971  			// TestFsListFile1 tests file present
   972  			t.Run("FsListFile1", func(t *testing.T) {
   973  				skipIfNotOk(t)
   974  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
   975  			})
   976  
   977  			// TestFsNewObject tests NewObject
   978  			t.Run("FsNewObject", func(t *testing.T) {
   979  				skipIfNotOk(t)
   980  				obj := findObject(t, remote, file1.Path)
   981  				file1.Check(t, obj, remote.Precision())
   982  			})
   983  
   984  			// TestFsListFile1and2 tests two files present
   985  			t.Run("FsListFile1and2", func(t *testing.T) {
   986  				skipIfNotOk(t)
   987  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
   988  			})
   989  
   990  			// TestFsNewObjectDir tests NewObject on a directory which should produce an error
   991  			t.Run("FsNewObjectDir", func(t *testing.T) {
   992  				skipIfNotOk(t)
   993  				dir := path.Dir(file2.Path)
   994  				obj, err := remote.NewObject(context.Background(), dir)
   995  				assert.Nil(t, obj)
   996  				assert.NotNil(t, err)
   997  			})
   998  
   999  			// TestFsCopy tests Copy
  1000  			t.Run("FsCopy", func(t *testing.T) {
  1001  				skipIfNotOk(t)
  1002  
  1003  				// Check have Copy
  1004  				doCopy := remote.Features().Copy
  1005  				if doCopy == nil {
  1006  					t.Skip("FS has no Copier interface")
  1007  				}
  1008  
  1009  				// Test with file2 so have + and ' ' in file name
  1010  				var file2Copy = file2
  1011  				file2Copy.Path += "-copy"
  1012  
  1013  				// do the copy
  1014  				src := findObject(t, remote, file2.Path)
  1015  				dst, err := doCopy(context.Background(), src, file2Copy.Path)
  1016  				if err == fs.ErrorCantCopy {
  1017  					t.Skip("FS can't copy")
  1018  				}
  1019  				require.NoError(t, err, fmt.Sprintf("Error: %#v", err))
  1020  
  1021  				// check file exists in new listing
  1022  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2, file2Copy})
  1023  
  1024  				// Check dst lightly - list above has checked ModTime/Hashes
  1025  				assert.Equal(t, file2Copy.Path, dst.Remote())
  1026  
  1027  				// Delete copy
  1028  				err = dst.Remove(context.Background())
  1029  				require.NoError(t, err)
  1030  
  1031  			})
  1032  
  1033  			// TestFsMove tests Move
  1034  			t.Run("FsMove", func(t *testing.T) {
  1035  				skipIfNotOk(t)
  1036  
  1037  				// Check have Move
  1038  				doMove := remote.Features().Move
  1039  				if doMove == nil {
  1040  					t.Skip("FS has no Mover interface")
  1041  				}
  1042  
  1043  				// state of files now:
  1044  				// 1: file name.txt
  1045  				// 2: hello sausage?/../z.txt
  1046  
  1047  				var file1Move = file1
  1048  				var file2Move = file2
  1049  
  1050  				// check happy path, i.e. no naming conflicts when rename and move are two
  1051  				// separate operations
  1052  				file2Move.Path = "other.txt"
  1053  				file2Move.WinPath = ""
  1054  				src := findObject(t, remote, file2.Path)
  1055  				dst, err := doMove(context.Background(), src, file2Move.Path)
  1056  				if err == fs.ErrorCantMove {
  1057  					t.Skip("FS can't move")
  1058  				}
  1059  				require.NoError(t, err)
  1060  				// check file exists in new listing
  1061  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2Move})
  1062  				// Check dst lightly - list above has checked ModTime/Hashes
  1063  				assert.Equal(t, file2Move.Path, dst.Remote())
  1064  				// 1: file name.txt
  1065  				// 2: other.txt
  1066  
  1067  				// Check conflict on "rename, then move"
  1068  				file1Move.Path = "moveTest/other.txt"
  1069  				src = findObject(t, remote, file1.Path)
  1070  				_, err = doMove(context.Background(), src, file1Move.Path)
  1071  				require.NoError(t, err)
  1072  				fstest.CheckListing(t, remote, []fstest.Item{file1Move, file2Move})
  1073  				// 1: moveTest/other.txt
  1074  				// 2: other.txt
  1075  
  1076  				// Check conflict on "move, then rename"
  1077  				src = findObject(t, remote, file1Move.Path)
  1078  				_, err = doMove(context.Background(), src, file1.Path)
  1079  				require.NoError(t, err)
  1080  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2Move})
  1081  				// 1: file name.txt
  1082  				// 2: other.txt
  1083  
  1084  				src = findObject(t, remote, file2Move.Path)
  1085  				_, err = doMove(context.Background(), src, file2.Path)
  1086  				require.NoError(t, err)
  1087  				fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
  1088  				// 1: file name.txt
  1089  				// 2: hello sausage?/../z.txt
  1090  
  1091  				// Tidy up moveTest directory
  1092  				require.NoError(t, remote.Rmdir(context.Background(), "moveTest"))
  1093  			})
  1094  
  1095  			// Move src to this remote using server side move operations.
  1096  			//
  1097  			// Will only be called if src.Fs().Name() == f.Name()
  1098  			//
  1099  			// If it isn't possible then return fs.ErrorCantDirMove
  1100  			//
  1101  			// If destination exists then return fs.ErrorDirExists
  1102  
  1103  			// TestFsDirMove tests DirMove
  1104  			//
  1105  			// go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|FsDirMove)$
  1106  			t.Run("FsDirMove", func(t *testing.T) {
  1107  				skipIfNotOk(t)
  1108  
  1109  				// Check have DirMove
  1110  				doDirMove := remote.Features().DirMove
  1111  				if doDirMove == nil {
  1112  					t.Skip("FS has no DirMover interface")
  1113  				}
  1114  
  1115  				// Check it can't move onto itself
  1116  				err := doDirMove(context.Background(), remote, "", "")
  1117  				require.Equal(t, fs.ErrorDirExists, err)
  1118  
  1119  				// new remote
  1120  				newRemote, _, removeNewRemote, err := fstest.RandomRemote(remoteName, false)
  1121  				require.NoError(t, err)
  1122  				defer removeNewRemote()
  1123  
  1124  				const newName = "new_name/sub_new_name"
  1125  				// try the move
  1126  				err = newRemote.Features().DirMove(context.Background(), remote, "", newName)
  1127  				require.NoError(t, err)
  1128  
  1129  				// check remotes
  1130  				// remote should not exist here
  1131  				_, err = remote.List(context.Background(), "")
  1132  				assert.Equal(t, fs.ErrorDirNotFound, errors.Cause(err))
  1133  				//fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, remote.Precision())
  1134  				file1Copy := file1
  1135  				file1Copy.Path = path.Join(newName, file1.Path)
  1136  				file2Copy := file2
  1137  				file2Copy.Path = path.Join(newName, file2.Path)
  1138  				file2Copy.WinPath = path.Join(newName, file2.WinPath)
  1139  				fstest.CheckListingWithPrecision(t, newRemote, []fstest.Item{file2Copy, file1Copy}, []string{
  1140  					"new_name",
  1141  					"new_name/sub_new_name",
  1142  					"new_name/sub_new_name/hello? sausage",
  1143  					"new_name/sub_new_name/hello? sausage/êé",
  1144  					"new_name/sub_new_name/hello? sausage/êé/Hello, 世界",
  1145  					"new_name/sub_new_name/hello? sausage/êé/Hello, 世界/ \" ' @ < > & ? + ≠",
  1146  				}, newRemote.Precision())
  1147  
  1148  				// move it back
  1149  				err = doDirMove(context.Background(), newRemote, newName, "")
  1150  				require.NoError(t, err)
  1151  
  1152  				// check remotes
  1153  				fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file2, file1}, []string{
  1154  					"hello? sausage",
  1155  					"hello? sausage/êé",
  1156  					"hello? sausage/êé/Hello, 世界",
  1157  					"hello? sausage/êé/Hello, 世界/ \" ' @ < > & ? + ≠",
  1158  				}, remote.Precision())
  1159  				fstest.CheckListingWithPrecision(t, newRemote, []fstest.Item{}, []string{
  1160  					"new_name",
  1161  				}, newRemote.Precision())
  1162  			})
  1163  
  1164  			// TestFsRmdirFull tests removing a non empty directory
  1165  			t.Run("FsRmdirFull", func(t *testing.T) {
  1166  				skipIfNotOk(t)
  1167  				if isBucketBasedButNotRoot(remote) {
  1168  					t.Skip("Skipping test as non root bucket based remote")
  1169  				}
  1170  				err := remote.Rmdir(context.Background(), "")
  1171  				require.Error(t, err, "Expecting error on RMdir on non empty remote")
  1172  			})
  1173  
  1174  			// TestFsPrecision tests the Precision of the Fs
  1175  			t.Run("FsPrecision", func(t *testing.T) {
  1176  				skipIfNotOk(t)
  1177  				precision := remote.Precision()
  1178  				if precision == fs.ModTimeNotSupported {
  1179  					return
  1180  				}
  1181  				if precision > time.Second || precision < 0 {
  1182  					t.Fatalf("Precision out of range %v", precision)
  1183  				}
  1184  				// FIXME check expected precision
  1185  			})
  1186  
  1187  			// TestObjectString tests the Object String method
  1188  			t.Run("ObjectString", func(t *testing.T) {
  1189  				skipIfNotOk(t)
  1190  				obj := findObject(t, remote, file1.Path)
  1191  				assert.Equal(t, file1.Path, obj.String())
  1192  				if opt.NilObject != nil {
  1193  					assert.Equal(t, "<nil>", opt.NilObject.String())
  1194  				}
  1195  			})
  1196  
  1197  			// TestObjectFs tests the object can be found
  1198  			t.Run("ObjectFs", func(t *testing.T) {
  1199  				skipIfNotOk(t)
  1200  				obj := findObject(t, remote, file1.Path)
  1201  				// If this is set we don't do the direct comparison of
  1202  				// the Fs from the object as it may be different
  1203  				if opt.SkipFsMatch {
  1204  					return
  1205  				}
  1206  				testRemote := remote
  1207  				if obj.Fs() != testRemote {
  1208  					// Check to see if this wraps something else
  1209  					if doUnWrap := testRemote.Features().UnWrap; doUnWrap != nil {
  1210  						testRemote = doUnWrap()
  1211  					}
  1212  				}
  1213  				assert.Equal(t, obj.Fs(), testRemote)
  1214  			})
  1215  
  1216  			// TestObjectRemote tests the Remote is correct
  1217  			t.Run("ObjectRemote", func(t *testing.T) {
  1218  				skipIfNotOk(t)
  1219  				obj := findObject(t, remote, file1.Path)
  1220  				assert.Equal(t, file1.Path, obj.Remote())
  1221  			})
  1222  
  1223  			// TestObjectHashes checks all the hashes the object supports
  1224  			t.Run("ObjectHashes", func(t *testing.T) {
  1225  				skipIfNotOk(t)
  1226  				obj := findObject(t, remote, file1.Path)
  1227  				file1.CheckHashes(t, obj)
  1228  			})
  1229  
  1230  			// TestObjectModTime tests the ModTime of the object is correct
  1231  			TestObjectModTime := func(t *testing.T) {
  1232  				skipIfNotOk(t)
  1233  				obj := findObject(t, remote, file1.Path)
  1234  				file1.CheckModTime(t, obj, obj.ModTime(context.Background()), remote.Precision())
  1235  			}
  1236  			t.Run("ObjectModTime", TestObjectModTime)
  1237  
  1238  			// TestObjectMimeType tests the MimeType of the object is correct
  1239  			t.Run("ObjectMimeType", func(t *testing.T) {
  1240  				skipIfNotOk(t)
  1241  				obj := findObject(t, remote, file1.Path)
  1242  				do, ok := obj.(fs.MimeTyper)
  1243  				if !ok {
  1244  					t.Skip("MimeType method not supported")
  1245  				}
  1246  				mimeType := do.MimeType(context.Background())
  1247  				if strings.ContainsRune(mimeType, ';') {
  1248  					assert.Equal(t, "text/plain; charset=utf-8", mimeType)
  1249  				} else {
  1250  					assert.Equal(t, "text/plain", mimeType)
  1251  				}
  1252  			})
  1253  
  1254  			// TestObjectSetModTime tests that SetModTime works
  1255  			t.Run("ObjectSetModTime", func(t *testing.T) {
  1256  				skipIfNotOk(t)
  1257  				newModTime := fstest.Time("2011-12-13T14:15:16.999999999Z")
  1258  				obj := findObject(t, remote, file1.Path)
  1259  				err := obj.SetModTime(context.Background(), newModTime)
  1260  				if err == fs.ErrorCantSetModTime || err == fs.ErrorCantSetModTimeWithoutDelete {
  1261  					t.Log(err)
  1262  					return
  1263  				}
  1264  				require.NoError(t, err)
  1265  				file1.ModTime = newModTime
  1266  				file1.CheckModTime(t, obj, obj.ModTime(context.Background()), remote.Precision())
  1267  				// And make a new object and read it from there too
  1268  				TestObjectModTime(t)
  1269  			})
  1270  
  1271  			// TestObjectSize tests that Size works
  1272  			t.Run("ObjectSize", func(t *testing.T) {
  1273  				skipIfNotOk(t)
  1274  				obj := findObject(t, remote, file1.Path)
  1275  				assert.Equal(t, file1.Size, obj.Size())
  1276  			})
  1277  
  1278  			// TestObjectOpen tests that Open works
  1279  			t.Run("ObjectOpen", func(t *testing.T) {
  1280  				skipIfNotOk(t)
  1281  				obj := findObject(t, remote, file1.Path)
  1282  				assert.Equal(t, file1Contents, readObject(t, obj, -1), "contents of file1 differ")
  1283  			})
  1284  
  1285  			// TestObjectOpenSeek tests that Open works with SeekOption
  1286  			t.Run("ObjectOpenSeek", func(t *testing.T) {
  1287  				skipIfNotOk(t)
  1288  				obj := findObject(t, remote, file1.Path)
  1289  				assert.Equal(t, file1Contents[50:], readObject(t, obj, -1, &fs.SeekOption{Offset: 50}), "contents of file1 differ after seek")
  1290  			})
  1291  
  1292  			// TestObjectOpenRange tests that Open works with RangeOption
  1293  			//
  1294  			// go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|ObjectOpenRange)$'
  1295  			t.Run("ObjectOpenRange", func(t *testing.T) {
  1296  				skipIfNotOk(t)
  1297  				obj := findObject(t, remote, file1.Path)
  1298  				for _, test := range []struct {
  1299  					ro                 fs.RangeOption
  1300  					wantStart, wantEnd int
  1301  				}{
  1302  					{fs.RangeOption{Start: 5, End: 15}, 5, 16},
  1303  					{fs.RangeOption{Start: 80, End: -1}, 80, 100},
  1304  					{fs.RangeOption{Start: 81, End: 100000}, 81, 100},
  1305  					{fs.RangeOption{Start: -1, End: 20}, 80, 100}, // if start is omitted this means get the final bytes
  1306  					// {fs.RangeOption{Start: -1, End: -1}, 0, 100}, - this seems to work but the RFC doesn't define it
  1307  				} {
  1308  					got := readObject(t, obj, -1, &test.ro)
  1309  					foundAt := strings.Index(file1Contents, got)
  1310  					help := fmt.Sprintf("%#v failed want [%d:%d] got [%d:%d]", test.ro, test.wantStart, test.wantEnd, foundAt, foundAt+len(got))
  1311  					assert.Equal(t, file1Contents[test.wantStart:test.wantEnd], got, help)
  1312  				}
  1313  			})
  1314  
  1315  			// TestObjectPartialRead tests that reading only part of the object does the correct thing
  1316  			t.Run("ObjectPartialRead", func(t *testing.T) {
  1317  				skipIfNotOk(t)
  1318  				obj := findObject(t, remote, file1.Path)
  1319  				assert.Equal(t, file1Contents[:50], readObject(t, obj, 50), "contents of file1 differ after limited read")
  1320  			})
  1321  
  1322  			// TestObjectUpdate tests that Update works
  1323  			t.Run("ObjectUpdate", func(t *testing.T) {
  1324  				skipIfNotOk(t)
  1325  				contents := fstest.RandomString(200)
  1326  				buf := bytes.NewBufferString(contents)
  1327  				hash := hash.NewMultiHasher()
  1328  				in := io.TeeReader(buf, hash)
  1329  
  1330  				file1.Size = int64(buf.Len())
  1331  				obj := findObject(t, remote, file1.Path)
  1332  				obji := object.NewStaticObjectInfo(file1.Path, file1.ModTime, int64(len(contents)), true, nil, obj.Fs())
  1333  				err := obj.Update(context.Background(), in, obji)
  1334  				require.NoError(t, err)
  1335  				file1.Hashes = hash.Sums()
  1336  
  1337  				// check the object has been updated
  1338  				file1.Check(t, obj, remote.Precision())
  1339  
  1340  				// Re-read the object and check again
  1341  				obj = findObject(t, remote, file1.Path)
  1342  				file1.Check(t, obj, remote.Precision())
  1343  
  1344  				// check contents correct
  1345  				assert.Equal(t, contents, readObject(t, obj, -1), "contents of updated file1 differ")
  1346  				file1Contents = contents
  1347  			})
  1348  
  1349  			// TestObjectStorable tests that Storable works
  1350  			t.Run("ObjectStorable", func(t *testing.T) {
  1351  				skipIfNotOk(t)
  1352  				obj := findObject(t, remote, file1.Path)
  1353  				require.NotNil(t, !obj.Storable(), "Expecting object to be storable")
  1354  			})
  1355  
  1356  			// TestFsIsFile tests that an error is returned along with a valid fs
  1357  			// which points to the parent directory.
  1358  			t.Run("FsIsFile", func(t *testing.T) {
  1359  				skipIfNotOk(t)
  1360  				remoteName := subRemoteName + "/" + file2.Path
  1361  				file2Copy := file2
  1362  				file2Copy.Path = "z.txt"
  1363  				file2Copy.WinPath = ""
  1364  				fileRemote, err := fs.NewFs(remoteName)
  1365  				require.NotNil(t, fileRemote)
  1366  				assert.Equal(t, fs.ErrorIsFile, err)
  1367  				fstest.CheckListing(t, fileRemote, []fstest.Item{file2Copy})
  1368  			})
  1369  
  1370  			// TestFsIsFileNotFound tests that an error is not returned if no object is found
  1371  			t.Run("FsIsFileNotFound", func(t *testing.T) {
  1372  				skipIfNotOk(t)
  1373  				remoteName := subRemoteName + "/not found.txt"
  1374  				fileRemote, err := fs.NewFs(remoteName)
  1375  				require.NoError(t, err)
  1376  				fstest.CheckListing(t, fileRemote, []fstest.Item{})
  1377  			})
  1378  
  1379  			// TestPublicLink tests creation of sharable, public links
  1380  			// go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|PublicLink)$'
  1381  			t.Run("PublicLink", func(t *testing.T) {
  1382  				skipIfNotOk(t)
  1383  
  1384  				doPublicLink := remote.Features().PublicLink
  1385  				if doPublicLink == nil {
  1386  					t.Skip("FS has no PublicLinker interface")
  1387  				}
  1388  
  1389  				// if object not found
  1390  				link, err := doPublicLink(context.Background(), file1.Path+"_does_not_exist")
  1391  				require.Error(t, err, "Expected to get error when file doesn't exist")
  1392  				require.Equal(t, "", link, "Expected link to be empty on error")
  1393  
  1394  				// sharing file for the first time
  1395  				link1, err := doPublicLink(context.Background(), file1.Path)
  1396  				require.NoError(t, err)
  1397  				require.NotEqual(t, "", link1, "Link should not be empty")
  1398  
  1399  				link2, err := doPublicLink(context.Background(), file2.Path)
  1400  				require.NoError(t, err)
  1401  				require.NotEqual(t, "", link2, "Link should not be empty")
  1402  
  1403  				require.NotEqual(t, link1, link2, "Links to different files should differ")
  1404  
  1405  				// sharing file for the 2nd time
  1406  				link1, err = doPublicLink(context.Background(), file1.Path)
  1407  				require.NoError(t, err)
  1408  				require.NotEqual(t, "", link1, "Link should not be empty")
  1409  
  1410  				// sharing directory for the first time
  1411  				path := path.Dir(file2.Path)
  1412  				link3, err := doPublicLink(context.Background(), path)
  1413  				if err != nil && errors.Cause(err) == fs.ErrorCantShareDirectories {
  1414  					t.Log("skipping directory tests as not supported on this backend")
  1415  				} else {
  1416  					require.NoError(t, err)
  1417  					require.NotEqual(t, "", link3, "Link should not be empty")
  1418  
  1419  					// sharing directory for the second time
  1420  					link3, err = doPublicLink(context.Background(), path)
  1421  					require.NoError(t, err)
  1422  					require.NotEqual(t, "", link3, "Link should not be empty")
  1423  
  1424  					// sharing the "root" directory in a subremote
  1425  					subRemote, _, removeSubRemote, err := fstest.RandomRemote(remoteName, false)
  1426  					require.NoError(t, err)
  1427  					defer removeSubRemote()
  1428  					// ensure sub remote isn't empty
  1429  					buf := bytes.NewBufferString("somecontent")
  1430  					obji := object.NewStaticObjectInfo("somefile", time.Now(), int64(buf.Len()), true, nil, nil)
  1431  					_, err = subRemote.Put(context.Background(), buf, obji)
  1432  					require.NoError(t, err)
  1433  
  1434  					link4, err := subRemote.Features().PublicLink(context.Background(), "")
  1435  					require.NoError(t, err, "Sharing root in a sub-remote should work")
  1436  					require.NotEqual(t, "", link4, "Link should not be empty")
  1437  				}
  1438  			})
  1439  
  1440  			// TestSetTier tests SetTier and GetTier functionality
  1441  			t.Run("SetTier", func(t *testing.T) {
  1442  				skipIfNotSetTier(t)
  1443  				obj := findObject(t, remote, file1.Path)
  1444  				setter, ok := obj.(fs.SetTierer)
  1445  				assert.NotNil(t, ok)
  1446  				getter, ok := obj.(fs.GetTierer)
  1447  				assert.NotNil(t, ok)
  1448  				// If interfaces are supported TiersToTest should contain
  1449  				// at least one entry
  1450  				supportedTiers := opt.TiersToTest
  1451  				assert.NotEmpty(t, supportedTiers)
  1452  				// test set tier changes on supported storage classes or tiers
  1453  				for _, tier := range supportedTiers {
  1454  					err := setter.SetTier(tier)
  1455  					assert.Nil(t, err)
  1456  					got := getter.GetTier()
  1457  					assert.Equal(t, tier, got)
  1458  				}
  1459  			})
  1460  
  1461  			// Check to see if Fs that wrap other Objects implement all the optional methods
  1462  			t.Run("ObjectCheckWrap", func(t *testing.T) {
  1463  				skipIfNotOk(t)
  1464  				if opt.SkipObjectCheckWrap {
  1465  					t.Skip("Skipping FsCheckWrap on this Fs")
  1466  				}
  1467  				ft := new(fs.Features).Fill(remote)
  1468  				if ft.UnWrap == nil {
  1469  					t.Skip("Not a wrapping Fs")
  1470  				}
  1471  				obj := findObject(t, remote, file1.Path)
  1472  				_, unsupported := fs.ObjectOptionalInterfaces(obj)
  1473  				for _, name := range unsupported {
  1474  					if !stringsContains(name, opt.UnimplementableObjectMethods) {
  1475  						t.Errorf("Missing Object wrapper for %s", name)
  1476  					}
  1477  				}
  1478  			})
  1479  
  1480  			// TestObjectRemove tests Remove
  1481  			t.Run("ObjectRemove", func(t *testing.T) {
  1482  				skipIfNotOk(t)
  1483  				obj := findObject(t, remote, file1.Path)
  1484  				err := obj.Remove(context.Background())
  1485  				require.NoError(t, err)
  1486  				// check listing without modtime as TestPublicLink may change the modtime
  1487  				fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file2}, nil, fs.ModTimeNotSupported)
  1488  			})
  1489  
  1490  			// TestFsPutStream tests uploading files when size is not known in advance
  1491  			t.Run("FsPutStream", func(t *testing.T) {
  1492  				skipIfNotOk(t)
  1493  				if remote.Features().PutStream == nil {
  1494  					t.Skip("FS has no PutStream interface")
  1495  				}
  1496  
  1497  				file := fstest.Item{
  1498  					ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
  1499  					Path:    "piped data.txt",
  1500  					Size:    -1, // use unknown size during upload
  1501  				}
  1502  
  1503  				var (
  1504  					err         error
  1505  					obj         fs.Object
  1506  					uploadHash  *hash.MultiHasher
  1507  					contentSize = 100
  1508  				)
  1509  				retry(t, "PutStream", func() error {
  1510  					contents := fstest.RandomString(contentSize)
  1511  					buf := bytes.NewBufferString(contents)
  1512  					uploadHash = hash.NewMultiHasher()
  1513  					in := io.TeeReader(buf, uploadHash)
  1514  
  1515  					file.Size = -1
  1516  					obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
  1517  					obj, err = remote.Features().PutStream(context.Background(), in, obji)
  1518  					return err
  1519  				})
  1520  				file.Hashes = uploadHash.Sums()
  1521  				file.Size = int64(contentSize) // use correct size when checking
  1522  				file.Check(t, obj, remote.Precision())
  1523  				// Re-read the object and check again
  1524  				obj = findObject(t, remote, file.Path)
  1525  				file.Check(t, obj, remote.Precision())
  1526  			})
  1527  
  1528  			// TestAbout tests the About optional interface
  1529  			t.Run("ObjectAbout", func(t *testing.T) {
  1530  				skipIfNotOk(t)
  1531  
  1532  				// Check have About
  1533  				doAbout := remote.Features().About
  1534  				if doAbout == nil {
  1535  					t.Skip("FS does not support About")
  1536  				}
  1537  
  1538  				// Can't really check the output much!
  1539  				usage, err := doAbout(context.Background())
  1540  				require.NoError(t, err)
  1541  				require.NotNil(t, usage)
  1542  				assert.NotEqual(t, int64(0), usage.Total)
  1543  			})
  1544  
  1545  			// TestInternal calls InternalTest() on the Fs
  1546  			t.Run("Internal", func(t *testing.T) {
  1547  				skipIfNotOk(t)
  1548  				if it, ok := remote.(InternalTester); ok {
  1549  					it.InternalTest(t)
  1550  				} else {
  1551  					t.Skipf("%T does not implement InternalTester", remote)
  1552  				}
  1553  			})
  1554  
  1555  		})
  1556  
  1557  		// TestFsUploadUnknownSize ensures Fs.Put() and Object.Update() don't panic when
  1558  		// src.Size() == -1
  1559  		t.Run("FsUploadUnknownSize", func(t *testing.T) {
  1560  			skipIfNotOk(t)
  1561  
  1562  			t.Run("FsPutUnknownSize", func(t *testing.T) {
  1563  				defer func() {
  1564  					assert.Nil(t, recover(), "Fs.Put() should not panic when src.Size() == -1")
  1565  				}()
  1566  
  1567  				contents := fstest.RandomString(100)
  1568  				in := bytes.NewBufferString(contents)
  1569  
  1570  				obji := object.NewStaticObjectInfo("unknown-size-put.txt", fstest.Time("2002-02-03T04:05:06.499999999Z"), -1, true, nil, nil)
  1571  				obj, err := remote.Put(context.Background(), in, obji)
  1572  				if err == nil {
  1573  					require.NoError(t, obj.Remove(context.Background()), "successfully uploaded unknown-sized file but failed to remove")
  1574  				}
  1575  				// if err != nil: it's okay as long as no panic
  1576  			})
  1577  
  1578  			t.Run("FsUpdateUnknownSize", func(t *testing.T) {
  1579  				unknownSizeUpdateFile := fstest.Item{
  1580  					ModTime: fstest.Time("2002-02-03T04:05:06.499999999Z"),
  1581  					Path:    "unknown-size-update.txt",
  1582  				}
  1583  
  1584  				testPut(t, remote, &unknownSizeUpdateFile)
  1585  
  1586  				defer func() {
  1587  					assert.Nil(t, recover(), "Object.Update() should not panic when src.Size() == -1")
  1588  				}()
  1589  
  1590  				newContents := fstest.RandomString(200)
  1591  				in := bytes.NewBufferString(newContents)
  1592  
  1593  				obj := findObject(t, remote, unknownSizeUpdateFile.Path)
  1594  				obji := object.NewStaticObjectInfo(unknownSizeUpdateFile.Path, unknownSizeUpdateFile.ModTime, -1, true, nil, obj.Fs())
  1595  				err := obj.Update(context.Background(), in, obji)
  1596  				if err == nil {
  1597  					require.NoError(t, obj.Remove(context.Background()), "successfully updated object with unknown-sized source but failed to remove")
  1598  				}
  1599  				// if err != nil: it's okay as long as no panic
  1600  			})
  1601  
  1602  		})
  1603  
  1604  		// TestFsRootCollapse tests if the root of an fs "collapses" to the
  1605  		// absolute root. It creates a new fs of the same backend type with its
  1606  		// root set to a *non-existent* folder, and attempts to read the info of
  1607  		// an object in that folder, whose name is taken from a directory that
  1608  		// exists in the absolute root.
  1609  		// This test is added after
  1610  		// https://github.com/ncw/rclone/issues/3164.
  1611  		t.Run("FsRootCollapse", func(t *testing.T) {
  1612  			deepRemoteName := subRemoteName + "/deeper/nonexisting/directory"
  1613  			deepRemote, err := fs.NewFs(deepRemoteName)
  1614  			require.NoError(t, err)
  1615  
  1616  			colonIndex := strings.IndexRune(deepRemoteName, ':')
  1617  			firstSlashIndex := strings.IndexRune(deepRemoteName, '/')
  1618  			firstDir := deepRemoteName[colonIndex+1 : firstSlashIndex]
  1619  			_, err = deepRemote.NewObject(context.Background(), firstDir)
  1620  			require.Equal(t, fs.ErrorObjectNotFound, err)
  1621  			// If err is not fs.ErrorObjectNotFound, it means the backend is
  1622  			// somehow confused about root and absolute root.
  1623  		})
  1624  
  1625  		// Purge the folder
  1626  		err = operations.Purge(context.Background(), remote, "")
  1627  		require.NoError(t, err)
  1628  		purged = true
  1629  		fstest.CheckListing(t, remote, []fstest.Item{})
  1630  
  1631  		// Check purging again if not bucket based
  1632  		if !isBucketBasedButNotRoot(remote) {
  1633  			err = operations.Purge(context.Background(), remote, "")
  1634  			assert.Error(t, err, "Expecting error after on second purge")
  1635  		}
  1636  
  1637  	})
  1638  
  1639  	// Check directory is purged
  1640  	if !purged {
  1641  		_ = operations.Purge(context.Background(), remote, "")
  1642  	}
  1643  
  1644  	// Remove the local directory so we don't clutter up /tmp
  1645  	if strings.HasPrefix(remoteName, "/") {
  1646  		t.Log("remoteName", remoteName)
  1647  		// Remove temp directory
  1648  		err := os.Remove(remoteName)
  1649  		require.NoError(t, err)
  1650  	}
  1651  }