github.com/artpar/rclone@v1.67.3/backend/cache/cache_internal_test.go (about)

     1  //go:build !plan9 && !js && !race
     2  
     3  package cache_test
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/base64"
     9  	"errors"
    10  	goflag "flag"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"math/rand"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"runtime"
    19  	"runtime/debug"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/artpar/rclone/backend/cache"
    25  	"github.com/artpar/rclone/backend/crypt"
    26  	_ "github.com/artpar/rclone/backend/drive"
    27  	"github.com/artpar/rclone/backend/local"
    28  	"github.com/artpar/rclone/fs"
    29  	"github.com/artpar/rclone/fs/config"
    30  	"github.com/artpar/rclone/fs/config/configmap"
    31  	"github.com/artpar/rclone/fs/object"
    32  	"github.com/artpar/rclone/fs/operations"
    33  	"github.com/artpar/rclone/fstest"
    34  	"github.com/artpar/rclone/fstest/testy"
    35  	"github.com/artpar/rclone/lib/random"
    36  	"github.com/artpar/rclone/vfs/vfsflags"
    37  	"github.com/stretchr/testify/require"
    38  )
    39  
    40  const (
    41  	// these 2 passwords are test random
    42  	cryptPassword1     = "3XcvMMdsV3d-HGAReTMdNH-5FcX5q32_lUeA"                                                     // oGJdUbQc7s8
    43  	cryptPassword2     = "NlgTBEIe-qibA7v-FoMfuX6Cw8KlLai_aMvV"                                                     // mv4mZW572HM
    44  	cryptedTextBase64  = "UkNMT05FAAC320i2xIee0BiNyknSPBn+Qcw3q9FhIFp3tvq6qlqvbsno3PnxmEFeJG3jDBnR/wku2gHWeQ=="     // one content
    45  	cryptedText2Base64 = "UkNMT05FAAATcQkVsgjBh8KafCKcr0wdTa1fMmV0U8hsCLGFoqcvxKVmvv7wx3Hf5EXxFcki2FFV4sdpmSrb9Q==" // updated content
    46  	cryptedText3Base64 = "UkNMT05FAAB/f7YtYKbPfmk9+OX/ffN3qG3OEdWT+z74kxCX9V/YZwJ4X2DN3HOnUC3gKQ4Gcoud5UtNvQ=="     // test content
    47  	letterBytes        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    48  )
    49  
    50  var (
    51  	remoteName                  string
    52  	uploadDir                   string
    53  	runInstance                 *run
    54  	errNotSupported             = errors.New("not supported")
    55  	decryptedToEncryptedRemotes = map[string]string{
    56  		"one":                  "lm4u7jjt3c85bf56vjqgeenuno",
    57  		"second":               "qvt1ochrkcfbptp5mu9ugb2l14",
    58  		"test":                 "jn4tegjtpqro30t3o11thb4b5s",
    59  		"test2":                "qakvqnh8ttei89e0gc76crpql4",
    60  		"data.bin":             "0q2847tfko6mhj3dag3r809qbc",
    61  		"ticw/data.bin":        "5mv97b0ule6pht33srae5pice8/0q2847tfko6mhj3dag3r809qbc",
    62  		"tiuufo/test/one":      "vi6u1olqhirqv14cd8qlej1mgo/jn4tegjtpqro30t3o11thb4b5s/lm4u7jjt3c85bf56vjqgeenuno",
    63  		"tiuufo/test/second":   "vi6u1olqhirqv14cd8qlej1mgo/jn4tegjtpqro30t3o11thb4b5s/qvt1ochrkcfbptp5mu9ugb2l14",
    64  		"tiutfo/test/one":      "legd371aa8ol36tjfklt347qnc/jn4tegjtpqro30t3o11thb4b5s/lm4u7jjt3c85bf56vjqgeenuno",
    65  		"tiutfo/second/one":    "legd371aa8ol36tjfklt347qnc/qvt1ochrkcfbptp5mu9ugb2l14/lm4u7jjt3c85bf56vjqgeenuno",
    66  		"second/one":           "qvt1ochrkcfbptp5mu9ugb2l14/lm4u7jjt3c85bf56vjqgeenuno",
    67  		"test/one":             "jn4tegjtpqro30t3o11thb4b5s/lm4u7jjt3c85bf56vjqgeenuno",
    68  		"test/second":          "jn4tegjtpqro30t3o11thb4b5s/qvt1ochrkcfbptp5mu9ugb2l14",
    69  		"one/test":             "lm4u7jjt3c85bf56vjqgeenuno/jn4tegjtpqro30t3o11thb4b5s",
    70  		"one/test/data.bin":    "lm4u7jjt3c85bf56vjqgeenuno/jn4tegjtpqro30t3o11thb4b5s/0q2847tfko6mhj3dag3r809qbc",
    71  		"second/test/data.bin": "qvt1ochrkcfbptp5mu9ugb2l14/jn4tegjtpqro30t3o11thb4b5s/0q2847tfko6mhj3dag3r809qbc",
    72  		"test/third":           "jn4tegjtpqro30t3o11thb4b5s/2nd7fjiop5h3ihfj1vl953aa5g",
    73  		"test/0.bin":           "jn4tegjtpqro30t3o11thb4b5s/e6frddt058b6kvbpmlstlndmtk",
    74  		"test/1.bin":           "jn4tegjtpqro30t3o11thb4b5s/kck472nt1k7qbmob0mt1p1crgc",
    75  		"test/2.bin":           "jn4tegjtpqro30t3o11thb4b5s/744oe9ven2rmak4u27if51qk24",
    76  		"test/3.bin":           "jn4tegjtpqro30t3o11thb4b5s/2bjd8kef0u5lmsu6qhqll34bcs",
    77  		"test/4.bin":           "jn4tegjtpqro30t3o11thb4b5s/cvjs73iv0a82v0c7r67avllh7s",
    78  		"test/5.bin":           "jn4tegjtpqro30t3o11thb4b5s/0plkdo790b6bnmt33qsdqmhv9c",
    79  		"test/6.bin":           "jn4tegjtpqro30t3o11thb4b5s/s5r633srnjtbh83893jovjt5d0",
    80  		"test/7.bin":           "jn4tegjtpqro30t3o11thb4b5s/6rq45tr9bjsammku622flmqsu4",
    81  		"test/8.bin":           "jn4tegjtpqro30t3o11thb4b5s/37bc6tcl3e31qb8cadvjb749vk",
    82  		"test/9.bin":           "jn4tegjtpqro30t3o11thb4b5s/t4pr35hnls32789o8fk0chk1ec",
    83  	}
    84  )
    85  
    86  func init() {
    87  	goflag.StringVar(&remoteName, "remote-internal", "TestInternalCache", "Remote to test with, defaults to local filesystem")
    88  	goflag.StringVar(&uploadDir, "upload-dir-internal", "", "")
    89  }
    90  
    91  // TestMain drives the tests
    92  func TestMain(m *testing.M) {
    93  	goflag.Parse()
    94  	var rc int
    95  
    96  	log.Printf("Running with the following params: \n remote: %v", remoteName)
    97  	runInstance = newRun()
    98  	rc = m.Run()
    99  	os.Exit(rc)
   100  }
   101  
   102  func TestInternalListRootAndInnerRemotes(t *testing.T) {
   103  	id := fmt.Sprintf("tilrair%v", time.Now().Unix())
   104  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, true, true, nil)
   105  
   106  	// Instantiate inner fs
   107  	innerFolder := "inner"
   108  	runInstance.mkdir(t, rootFs, innerFolder)
   109  	rootFs2, _ := runInstance.newCacheFs(t, remoteName, id+"/"+innerFolder, true, true, nil)
   110  
   111  	runInstance.writeObjectString(t, rootFs2, "one", "content")
   112  	listRoot, err := runInstance.list(t, rootFs, "")
   113  	require.NoError(t, err)
   114  	listRootInner, err := runInstance.list(t, rootFs, innerFolder)
   115  	require.NoError(t, err)
   116  	listInner, err := rootFs2.List(context.Background(), "")
   117  	require.NoError(t, err)
   118  
   119  	require.Len(t, listRoot, 1)
   120  	require.Len(t, listRootInner, 1)
   121  	require.Len(t, listInner, 1)
   122  }
   123  
   124  /* TODO: is this testing something?
   125  func TestInternalVfsCache(t *testing.T) {
   126  	vfsflags.Opt.DirCacheTime = time.Second * 30
   127  	testSize := int64(524288000)
   128  
   129  	vfsflags.Opt.CacheMode = vfs.CacheModeWrites
   130  	id := "tiuufo"
   131  	rootFs, boltDb := runInstance.newCacheFs(t, remoteName, id, true, true, nil, map[string]string{"writes": "true", "info_age": "1h"})
   132  	defer runInstance.cleanupFs(t, rootFs, boltDb)
   133  
   134  	err := rootFs.Mkdir(context.Background(), "test")
   135  	require.NoError(t, err)
   136  	runInstance.writeObjectString(t, rootFs, "test/second", "content")
   137  	_, err = rootFs.List(context.Background(), "test")
   138  	require.NoError(t, err)
   139  
   140  	testReader := runInstance.randomReader(t, testSize)
   141  	writeCh := make(chan interface{})
   142  	//write2Ch := make(chan interface{})
   143  	readCh := make(chan interface{})
   144  	cacheCh := make(chan interface{})
   145  	// write the main file
   146  	go func() {
   147  		defer func() {
   148  			writeCh <- true
   149  		}()
   150  
   151  		log.Printf("========== started writing file 'test/one'")
   152  		runInstance.writeRemoteReader(t, rootFs, "test/one", testReader)
   153  		log.Printf("========== done writing file 'test/one'")
   154  	}()
   155  	// routine to check which cache has what, autostarts
   156  	go func() {
   157  		for {
   158  			select {
   159  			case <-cacheCh:
   160  				log.Printf("========== finished checking caches")
   161  				return
   162  			default:
   163  			}
   164  			li2 := [2]string{path.Join("test", "one"), path.Join("test", "second")}
   165  			for _, r := range li2 {
   166  				var err error
   167  				ci, err := os.ReadDir(path.Join(runInstance.chunkPath, runInstance.encryptRemoteIfNeeded(t, path.Join(id, r))))
   168  				if err != nil || len(ci) == 0 {
   169  					log.Printf("========== '%v' not in cache", r)
   170  				} else {
   171  					log.Printf("========== '%v' IN CACHE", r)
   172  				}
   173  				_, err = os.Stat(path.Join(runInstance.vfsCachePath, id, r))
   174  				if err != nil {
   175  					log.Printf("========== '%v' not in vfs", r)
   176  				} else {
   177  					log.Printf("========== '%v' IN VFS", r)
   178  				}
   179  			}
   180  			time.Sleep(time.Second * 10)
   181  		}
   182  	}()
   183  	// routine to list, autostarts
   184  	go func() {
   185  		for {
   186  			select {
   187  			case <-readCh:
   188  				log.Printf("========== finished checking listings and readings")
   189  				return
   190  			default:
   191  			}
   192  			li, err := runInstance.list(t, rootFs, "test")
   193  			if err != nil {
   194  				log.Printf("========== error listing 'test' folder: %v", err)
   195  			} else {
   196  				log.Printf("========== list 'test' folder count: %v", len(li))
   197  			}
   198  
   199  			time.Sleep(time.Second * 10)
   200  		}
   201  	}()
   202  
   203  	// wait for main file to be written
   204  	<-writeCh
   205  	log.Printf("========== waiting for VFS to expire")
   206  	time.Sleep(time.Second * 120)
   207  
   208  	// try a final read
   209  	li2 := [2]string{"test/one", "test/second"}
   210  	for _, r := range li2 {
   211  		_, err := runInstance.readDataFromRemote(t, rootFs, r, int64(0), int64(2), false)
   212  		if err != nil {
   213  			log.Printf("========== error reading '%v': %v", r, err)
   214  		} else {
   215  			log.Printf("========== read '%v'", r)
   216  		}
   217  	}
   218  	// close the cache and list checkers
   219  	cacheCh <- true
   220  	readCh <- true
   221  }
   222  */
   223  
   224  func TestInternalObjWrapFsFound(t *testing.T) {
   225  	id := fmt.Sprintf("tiowff%v", time.Now().Unix())
   226  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, true, true, nil)
   227  
   228  	cfs, err := runInstance.getCacheFs(rootFs)
   229  	require.NoError(t, err)
   230  	wrappedFs := cfs.UnWrap()
   231  
   232  	var testData []byte
   233  	if runInstance.rootIsCrypt {
   234  		testData, err = base64.StdEncoding.DecodeString(cryptedTextBase64)
   235  		require.NoError(t, err)
   236  	} else {
   237  		testData = []byte("test content")
   238  	}
   239  
   240  	runInstance.writeObjectBytes(t, wrappedFs, runInstance.encryptRemoteIfNeeded(t, "test"), testData)
   241  	listRoot, err := runInstance.list(t, rootFs, "")
   242  	require.NoError(t, err)
   243  	require.Len(t, listRoot, 1)
   244  
   245  	cachedData, err := runInstance.readDataFromRemote(t, rootFs, "test", 0, int64(len([]byte("test content"))), false)
   246  	require.NoError(t, err)
   247  	require.Equal(t, "test content", string(cachedData))
   248  
   249  	err = runInstance.rm(t, rootFs, "test")
   250  	require.NoError(t, err)
   251  	listRoot, err = runInstance.list(t, rootFs, "")
   252  	require.NoError(t, err)
   253  	require.Len(t, listRoot, 0)
   254  }
   255  
   256  func TestInternalObjNotFound(t *testing.T) {
   257  	id := fmt.Sprintf("tionf%v", time.Now().Unix())
   258  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   259  
   260  	obj, err := rootFs.NewObject(context.Background(), "404")
   261  	require.Error(t, err)
   262  	require.Nil(t, obj)
   263  }
   264  
   265  func TestInternalCachedWrittenContentMatches(t *testing.T) {
   266  	testy.SkipUnreliable(t)
   267  	id := fmt.Sprintf("ticwcm%v", time.Now().Unix())
   268  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   269  
   270  	cfs, err := runInstance.getCacheFs(rootFs)
   271  	require.NoError(t, err)
   272  	chunkSize := cfs.ChunkSize()
   273  
   274  	// create some rand test data
   275  	testData := randStringBytes(int(chunkSize*4 + chunkSize/2))
   276  
   277  	// write the object
   278  	runInstance.writeRemoteBytes(t, rootFs, "data.bin", testData)
   279  
   280  	// check sample of data from in-file
   281  	sampleStart := chunkSize / 2
   282  	sampleEnd := chunkSize
   283  	testSample := testData[sampleStart:sampleEnd]
   284  	checkSample, err := runInstance.readDataFromRemote(t, rootFs, "data.bin", sampleStart, sampleEnd, false)
   285  	require.NoError(t, err)
   286  	require.Equal(t, int64(len(checkSample)), sampleEnd-sampleStart)
   287  	require.Equal(t, checkSample, testSample)
   288  }
   289  
   290  func TestInternalDoubleWrittenContentMatches(t *testing.T) {
   291  	if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
   292  		t.Skip("Skip test on windows/386")
   293  	}
   294  	id := fmt.Sprintf("tidwcm%v", time.Now().Unix())
   295  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   296  
   297  	// write the object
   298  	runInstance.writeRemoteString(t, rootFs, "one", "one content")
   299  	err := runInstance.updateData(t, rootFs, "one", "one content", " updated")
   300  	require.NoError(t, err)
   301  	err = runInstance.updateData(t, rootFs, "one", "one content updated", " double")
   302  	require.NoError(t, err)
   303  
   304  	// check sample of data from in-file
   305  	data, err := runInstance.readDataFromRemote(t, rootFs, "one", int64(0), int64(len("one content updated double")), true)
   306  	require.NoError(t, err)
   307  	require.Equal(t, "one content updated double", string(data))
   308  }
   309  
   310  func TestInternalCachedUpdatedContentMatches(t *testing.T) {
   311  	testy.SkipUnreliable(t)
   312  	id := fmt.Sprintf("ticucm%v", time.Now().Unix())
   313  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   314  	var err error
   315  
   316  	// create some rand test data
   317  	var testData1 []byte
   318  	var testData2 []byte
   319  	if runInstance.rootIsCrypt {
   320  		testData1, err = base64.StdEncoding.DecodeString(cryptedTextBase64)
   321  		require.NoError(t, err)
   322  		testData2, err = base64.StdEncoding.DecodeString(cryptedText2Base64)
   323  		require.NoError(t, err)
   324  	} else {
   325  		testData1 = []byte(random.String(100))
   326  		testData2 = []byte(random.String(200))
   327  	}
   328  
   329  	// write the object
   330  	o := runInstance.updateObjectRemote(t, rootFs, "data.bin", testData1, testData2)
   331  	require.Equal(t, o.Size(), int64(len(testData2)))
   332  
   333  	// check data from in-file
   334  	checkSample, err := runInstance.readDataFromRemote(t, rootFs, "data.bin", 0, int64(len(testData2)), false)
   335  	require.NoError(t, err)
   336  	require.Equal(t, checkSample, testData2)
   337  }
   338  
   339  func TestInternalWrappedWrittenContentMatches(t *testing.T) {
   340  	id := fmt.Sprintf("tiwwcm%v", time.Now().Unix())
   341  	vfsflags.Opt.DirCacheTime = time.Second
   342  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, true, true, nil)
   343  	if runInstance.rootIsCrypt {
   344  		t.Skip("test skipped with crypt remote")
   345  	}
   346  
   347  	cfs, err := runInstance.getCacheFs(rootFs)
   348  	require.NoError(t, err)
   349  	chunkSize := cfs.ChunkSize()
   350  
   351  	// create some rand test data
   352  	testSize := chunkSize*4 + chunkSize/2
   353  	testData := randStringBytes(int(testSize))
   354  
   355  	// write the object
   356  	o := runInstance.writeObjectBytes(t, cfs.UnWrap(), "data.bin", testData)
   357  	require.Equal(t, o.Size(), testSize)
   358  	time.Sleep(time.Second * 3)
   359  
   360  	checkSample, err := runInstance.readDataFromRemote(t, rootFs, "data.bin", 0, testSize, false)
   361  	require.NoError(t, err)
   362  	require.Equal(t, int64(len(checkSample)), o.Size())
   363  
   364  	for i := 0; i < len(checkSample); i++ {
   365  		require.Equal(t, testData[i], checkSample[i])
   366  	}
   367  }
   368  
   369  func TestInternalLargeWrittenContentMatches(t *testing.T) {
   370  	id := fmt.Sprintf("tilwcm%v", time.Now().Unix())
   371  	vfsflags.Opt.DirCacheTime = time.Second
   372  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, true, true, nil)
   373  	if runInstance.rootIsCrypt {
   374  		t.Skip("test skipped with crypt remote")
   375  	}
   376  
   377  	cfs, err := runInstance.getCacheFs(rootFs)
   378  	require.NoError(t, err)
   379  	chunkSize := cfs.ChunkSize()
   380  
   381  	// create some rand test data
   382  	testSize := chunkSize*10 + chunkSize/2
   383  	testData := randStringBytes(int(testSize))
   384  
   385  	// write the object
   386  	runInstance.writeObjectBytes(t, cfs.UnWrap(), "data.bin", testData)
   387  	time.Sleep(time.Second * 3)
   388  
   389  	readData, err := runInstance.readDataFromRemote(t, rootFs, "data.bin", 0, testSize, false)
   390  	require.NoError(t, err)
   391  	for i := 0; i < len(readData); i++ {
   392  		require.Equalf(t, testData[i], readData[i], "at byte %v", i)
   393  	}
   394  }
   395  
   396  func TestInternalWrappedFsChangeNotSeen(t *testing.T) {
   397  	id := fmt.Sprintf("tiwfcns%v", time.Now().Unix())
   398  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   399  
   400  	cfs, err := runInstance.getCacheFs(rootFs)
   401  	require.NoError(t, err)
   402  	chunkSize := cfs.ChunkSize()
   403  
   404  	// create some rand test data
   405  	testData := randStringBytes(int(chunkSize*4 + chunkSize/2))
   406  	runInstance.writeRemoteBytes(t, rootFs, "data.bin", testData)
   407  
   408  	// update in the wrapped fs
   409  	originalSize, err := runInstance.size(t, rootFs, "data.bin")
   410  	require.NoError(t, err)
   411  	log.Printf("original size: %v", originalSize)
   412  
   413  	o, err := cfs.UnWrap().NewObject(context.Background(), runInstance.encryptRemoteIfNeeded(t, "data.bin"))
   414  	require.NoError(t, err)
   415  	expectedSize := int64(len([]byte("test content")))
   416  	var data2 []byte
   417  	if runInstance.rootIsCrypt {
   418  		data2, err = base64.StdEncoding.DecodeString(cryptedText3Base64)
   419  		require.NoError(t, err)
   420  		expectedSize = expectedSize + 1 // FIXME newline gets in, likely test data issue
   421  	} else {
   422  		data2 = []byte("test content")
   423  	}
   424  	objInfo := object.NewStaticObjectInfo(runInstance.encryptRemoteIfNeeded(t, "data.bin"), time.Now(), int64(len(data2)), true, nil, cfs.UnWrap())
   425  	err = o.Update(context.Background(), bytes.NewReader(data2), objInfo)
   426  	require.NoError(t, err)
   427  	require.Equal(t, int64(len(data2)), o.Size())
   428  	log.Printf("updated size: %v", len(data2))
   429  
   430  	// get a new instance from the cache
   431  	if runInstance.wrappedIsExternal {
   432  		err = runInstance.retryBlock(func() error {
   433  			coSize, err := runInstance.size(t, rootFs, "data.bin")
   434  			if err != nil {
   435  				return err
   436  			}
   437  			if coSize != expectedSize {
   438  				return fmt.Errorf("%v <> %v", coSize, expectedSize)
   439  			}
   440  			return nil
   441  		}, 12, time.Second*10)
   442  		require.NoError(t, err)
   443  	} else {
   444  		coSize, err := runInstance.size(t, rootFs, "data.bin")
   445  		require.NoError(t, err)
   446  		require.NotEqual(t, coSize, expectedSize)
   447  	}
   448  }
   449  
   450  func TestInternalMoveWithNotify(t *testing.T) {
   451  	id := fmt.Sprintf("timwn%v", time.Now().Unix())
   452  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   453  	if !runInstance.wrappedIsExternal {
   454  		t.Skipf("Not external")
   455  	}
   456  
   457  	cfs, err := runInstance.getCacheFs(rootFs)
   458  	require.NoError(t, err)
   459  
   460  	srcName := runInstance.encryptRemoteIfNeeded(t, "test") + "/" + runInstance.encryptRemoteIfNeeded(t, "one") + "/" + runInstance.encryptRemoteIfNeeded(t, "data.bin")
   461  	dstName := runInstance.encryptRemoteIfNeeded(t, "test") + "/" + runInstance.encryptRemoteIfNeeded(t, "second") + "/" + runInstance.encryptRemoteIfNeeded(t, "data.bin")
   462  	// create some rand test data
   463  	var testData []byte
   464  	if runInstance.rootIsCrypt {
   465  		testData, err = base64.StdEncoding.DecodeString(cryptedTextBase64)
   466  		require.NoError(t, err)
   467  	} else {
   468  		testData = []byte("test content")
   469  	}
   470  	_ = cfs.UnWrap().Mkdir(context.Background(), runInstance.encryptRemoteIfNeeded(t, "test"))
   471  	_ = cfs.UnWrap().Mkdir(context.Background(), runInstance.encryptRemoteIfNeeded(t, "test/one"))
   472  	_ = cfs.UnWrap().Mkdir(context.Background(), runInstance.encryptRemoteIfNeeded(t, "test/second"))
   473  	srcObj := runInstance.writeObjectBytes(t, cfs.UnWrap(), srcName, testData)
   474  
   475  	// list in mount
   476  	_, err = runInstance.list(t, rootFs, "test")
   477  	require.NoError(t, err)
   478  	_, err = runInstance.list(t, rootFs, "test/one")
   479  	require.NoError(t, err)
   480  
   481  	// move file
   482  	_, err = cfs.UnWrap().Features().Move(context.Background(), srcObj, dstName)
   483  	require.NoError(t, err)
   484  
   485  	err = runInstance.retryBlock(func() error {
   486  		li, err := runInstance.list(t, rootFs, "test")
   487  		if err != nil {
   488  			log.Printf("err: %v", err)
   489  			return err
   490  		}
   491  		if len(li) != 2 {
   492  			log.Printf("not expected listing /test: %v", li)
   493  			return fmt.Errorf("not expected listing /test: %v", li)
   494  		}
   495  
   496  		li, err = runInstance.list(t, rootFs, "test/one")
   497  		if err != nil {
   498  			log.Printf("err: %v", err)
   499  			return err
   500  		}
   501  		if len(li) != 0 {
   502  			log.Printf("not expected listing /test/one: %v", li)
   503  			return fmt.Errorf("not expected listing /test/one: %v", li)
   504  		}
   505  
   506  		li, err = runInstance.list(t, rootFs, "test/second")
   507  		if err != nil {
   508  			log.Printf("err: %v", err)
   509  			return err
   510  		}
   511  		if len(li) != 1 {
   512  			log.Printf("not expected listing /test/second: %v", li)
   513  			return fmt.Errorf("not expected listing /test/second: %v", li)
   514  		}
   515  		if fi, ok := li[0].(os.FileInfo); ok {
   516  			if fi.Name() != "data.bin" {
   517  				log.Printf("not expected name: %v", fi.Name())
   518  				return fmt.Errorf("not expected name: %v", fi.Name())
   519  			}
   520  		} else if di, ok := li[0].(fs.DirEntry); ok {
   521  			if di.Remote() != "test/second/data.bin" {
   522  				log.Printf("not expected remote: %v", di.Remote())
   523  				return fmt.Errorf("not expected remote: %v", di.Remote())
   524  			}
   525  		} else {
   526  			log.Printf("unexpected listing: %v", li)
   527  			return fmt.Errorf("unexpected listing: %v", li)
   528  		}
   529  
   530  		log.Printf("complete listing: %v", li)
   531  		return nil
   532  	}, 12, time.Second*10)
   533  	require.NoError(t, err)
   534  }
   535  
   536  func TestInternalNotifyCreatesEmptyParts(t *testing.T) {
   537  	id := fmt.Sprintf("tincep%v", time.Now().Unix())
   538  	rootFs, boltDb := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   539  	if !runInstance.wrappedIsExternal {
   540  		t.Skipf("Not external")
   541  	}
   542  	cfs, err := runInstance.getCacheFs(rootFs)
   543  	require.NoError(t, err)
   544  
   545  	srcName := runInstance.encryptRemoteIfNeeded(t, "test") + "/" + runInstance.encryptRemoteIfNeeded(t, "one") + "/" + runInstance.encryptRemoteIfNeeded(t, "test")
   546  	dstName := runInstance.encryptRemoteIfNeeded(t, "test") + "/" + runInstance.encryptRemoteIfNeeded(t, "one") + "/" + runInstance.encryptRemoteIfNeeded(t, "test2")
   547  	// create some rand test data
   548  	var testData []byte
   549  	if runInstance.rootIsCrypt {
   550  		testData, err = base64.StdEncoding.DecodeString(cryptedTextBase64)
   551  		require.NoError(t, err)
   552  	} else {
   553  		testData = []byte("test content")
   554  	}
   555  	err = rootFs.Mkdir(context.Background(), "test")
   556  	require.NoError(t, err)
   557  	err = rootFs.Mkdir(context.Background(), "test/one")
   558  	require.NoError(t, err)
   559  	srcObj := runInstance.writeObjectBytes(t, cfs.UnWrap(), srcName, testData)
   560  
   561  	// list in mount
   562  	_, err = runInstance.list(t, rootFs, "test")
   563  	require.NoError(t, err)
   564  	_, err = runInstance.list(t, rootFs, "test/one")
   565  	require.NoError(t, err)
   566  
   567  	found := boltDb.HasEntry(path.Join(cfs.Root(), runInstance.encryptRemoteIfNeeded(t, "test")))
   568  	require.True(t, found)
   569  	boltDb.Purge()
   570  	found = boltDb.HasEntry(path.Join(cfs.Root(), runInstance.encryptRemoteIfNeeded(t, "test")))
   571  	require.False(t, found)
   572  
   573  	// move file
   574  	_, err = cfs.UnWrap().Features().Move(context.Background(), srcObj, dstName)
   575  	require.NoError(t, err)
   576  
   577  	err = runInstance.retryBlock(func() error {
   578  		found = boltDb.HasEntry(path.Join(cfs.Root(), runInstance.encryptRemoteIfNeeded(t, "test")))
   579  		if !found {
   580  			log.Printf("not found /test")
   581  			return fmt.Errorf("not found /test")
   582  		}
   583  		found = boltDb.HasEntry(path.Join(cfs.Root(), runInstance.encryptRemoteIfNeeded(t, "test"), runInstance.encryptRemoteIfNeeded(t, "one")))
   584  		if !found {
   585  			log.Printf("not found /test/one")
   586  			return fmt.Errorf("not found /test/one")
   587  		}
   588  		found = boltDb.HasEntry(path.Join(cfs.Root(), runInstance.encryptRemoteIfNeeded(t, "test"), runInstance.encryptRemoteIfNeeded(t, "one"), runInstance.encryptRemoteIfNeeded(t, "test2")))
   589  		if !found {
   590  			log.Printf("not found /test/one/test2")
   591  			return fmt.Errorf("not found /test/one/test2")
   592  		}
   593  		li, err := runInstance.list(t, rootFs, "test/one")
   594  		if err != nil {
   595  			log.Printf("err: %v", err)
   596  			return err
   597  		}
   598  		if len(li) != 1 {
   599  			log.Printf("not expected listing /test/one: %v", li)
   600  			return fmt.Errorf("not expected listing /test/one: %v", li)
   601  		}
   602  		if fi, ok := li[0].(os.FileInfo); ok {
   603  			if fi.Name() != "test2" {
   604  				log.Printf("not expected name: %v", fi.Name())
   605  				return fmt.Errorf("not expected name: %v", fi.Name())
   606  			}
   607  		} else if di, ok := li[0].(fs.DirEntry); ok {
   608  			if di.Remote() != "test/one/test2" {
   609  				log.Printf("not expected remote: %v", di.Remote())
   610  				return fmt.Errorf("not expected remote: %v", di.Remote())
   611  			}
   612  		} else {
   613  			log.Printf("unexpected listing: %v", li)
   614  			return fmt.Errorf("unexpected listing: %v", li)
   615  		}
   616  		log.Printf("complete listing /test/one/test2")
   617  		return nil
   618  	}, 12, time.Second*10)
   619  	require.NoError(t, err)
   620  }
   621  
   622  func TestInternalChangeSeenAfterDirCacheFlush(t *testing.T) {
   623  	id := fmt.Sprintf("ticsadcf%v", time.Now().Unix())
   624  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, nil)
   625  
   626  	cfs, err := runInstance.getCacheFs(rootFs)
   627  	require.NoError(t, err)
   628  	chunkSize := cfs.ChunkSize()
   629  
   630  	// create some rand test data
   631  	testData := randStringBytes(int(chunkSize*4 + chunkSize/2))
   632  	runInstance.writeRemoteBytes(t, rootFs, "data.bin", testData)
   633  
   634  	// update in the wrapped fs
   635  	o, err := cfs.UnWrap().NewObject(context.Background(), runInstance.encryptRemoteIfNeeded(t, "data.bin"))
   636  	require.NoError(t, err)
   637  	wrappedTime := time.Now().Add(-1 * time.Hour)
   638  	err = o.SetModTime(context.Background(), wrappedTime)
   639  	require.NoError(t, err)
   640  
   641  	// get a new instance from the cache
   642  	co, err := rootFs.NewObject(context.Background(), "data.bin")
   643  	require.NoError(t, err)
   644  	require.NotEqual(t, o.ModTime(context.Background()).String(), co.ModTime(context.Background()).String())
   645  
   646  	cfs.DirCacheFlush() // flush the cache
   647  
   648  	// get a new instance from the cache
   649  	co, err = rootFs.NewObject(context.Background(), "data.bin")
   650  	require.NoError(t, err)
   651  	require.Equal(t, wrappedTime.Unix(), co.ModTime(context.Background()).Unix())
   652  }
   653  
   654  func TestInternalCacheWrites(t *testing.T) {
   655  	id := "ticw"
   656  	rootFs, boltDb := runInstance.newCacheFs(t, remoteName, id, false, true, map[string]string{"writes": "true"})
   657  
   658  	cfs, err := runInstance.getCacheFs(rootFs)
   659  	require.NoError(t, err)
   660  	chunkSize := cfs.ChunkSize()
   661  
   662  	// create some rand test data
   663  	earliestTime := time.Now()
   664  	testData := randStringBytes(int(chunkSize*4 + chunkSize/2))
   665  	runInstance.writeRemoteBytes(t, rootFs, "data.bin", testData)
   666  	expectedTs := time.Now()
   667  	ts, err := boltDb.GetChunkTs(runInstance.encryptRemoteIfNeeded(t, path.Join(rootFs.Root(), "data.bin")), 0)
   668  	require.NoError(t, err)
   669  	require.WithinDuration(t, expectedTs, ts, expectedTs.Sub(earliestTime))
   670  }
   671  
   672  func TestInternalMaxChunkSizeRespected(t *testing.T) {
   673  	if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
   674  		t.Skip("Skip test on windows/386")
   675  	}
   676  	id := fmt.Sprintf("timcsr%v", time.Now().Unix())
   677  	rootFs, boltDb := runInstance.newCacheFs(t, remoteName, id, false, true, map[string]string{"workers": "1"})
   678  
   679  	cfs, err := runInstance.getCacheFs(rootFs)
   680  	require.NoError(t, err)
   681  	chunkSize := cfs.ChunkSize()
   682  	totalChunks := 20
   683  
   684  	// create some rand test data
   685  	testData := randStringBytes(int(int64(totalChunks-1)*chunkSize + chunkSize/2))
   686  	runInstance.writeRemoteBytes(t, rootFs, "data.bin", testData)
   687  	o, err := cfs.NewObject(context.Background(), runInstance.encryptRemoteIfNeeded(t, "data.bin"))
   688  	require.NoError(t, err)
   689  	co, ok := o.(*cache.Object)
   690  	require.True(t, ok)
   691  
   692  	for i := 0; i < 4; i++ { // read first 4
   693  		_ = runInstance.readDataFromObj(t, co, chunkSize*int64(i), chunkSize*int64(i+1), false)
   694  	}
   695  	cfs.CleanUpCache(true)
   696  	// the last 2 **must** be in the cache
   697  	require.True(t, boltDb.HasChunk(co, chunkSize*2))
   698  	require.True(t, boltDb.HasChunk(co, chunkSize*3))
   699  
   700  	for i := 4; i < 6; i++ { // read next 2
   701  		_ = runInstance.readDataFromObj(t, co, chunkSize*int64(i), chunkSize*int64(i+1), false)
   702  	}
   703  	cfs.CleanUpCache(true)
   704  	// the last 2 **must** be in the cache
   705  	require.True(t, boltDb.HasChunk(co, chunkSize*4))
   706  	require.True(t, boltDb.HasChunk(co, chunkSize*5))
   707  }
   708  
   709  func TestInternalExpiredEntriesRemoved(t *testing.T) {
   710  	id := fmt.Sprintf("tieer%v", time.Now().Unix())
   711  	vfsflags.Opt.DirCacheTime = time.Second * 4 // needs to be lower than the defined
   712  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, true, true, nil)
   713  	cfs, err := runInstance.getCacheFs(rootFs)
   714  	require.NoError(t, err)
   715  
   716  	// create some rand test data
   717  	runInstance.writeRemoteString(t, rootFs, "one", "one content")
   718  	runInstance.mkdir(t, rootFs, "test")
   719  	runInstance.writeRemoteString(t, rootFs, "test/second", "second content")
   720  
   721  	l, err := runInstance.list(t, rootFs, "test")
   722  	require.NoError(t, err)
   723  	require.Len(t, l, 1)
   724  
   725  	err = cfs.UnWrap().Mkdir(context.Background(), runInstance.encryptRemoteIfNeeded(t, "test/third"))
   726  	require.NoError(t, err)
   727  
   728  	l, err = runInstance.list(t, rootFs, "test")
   729  	require.NoError(t, err)
   730  	require.Len(t, l, 1)
   731  
   732  	err = runInstance.retryBlock(func() error {
   733  		l, err = runInstance.list(t, rootFs, "test")
   734  		if err != nil {
   735  			return err
   736  		}
   737  		if len(l) != 2 {
   738  			return errors.New("list is not 2")
   739  		}
   740  		return nil
   741  	}, 10, time.Second)
   742  	require.NoError(t, err)
   743  }
   744  
   745  func TestInternalBug2117(t *testing.T) {
   746  	vfsflags.Opt.DirCacheTime = time.Second * 10
   747  
   748  	id := fmt.Sprintf("tib2117%v", time.Now().Unix())
   749  	rootFs, _ := runInstance.newCacheFs(t, remoteName, id, false, true, map[string]string{"info_age": "72h", "chunk_clean_interval": "15m"})
   750  
   751  	if runInstance.rootIsCrypt {
   752  		t.Skipf("skipping crypt")
   753  	}
   754  
   755  	cfs, err := runInstance.getCacheFs(rootFs)
   756  	require.NoError(t, err)
   757  
   758  	err = cfs.UnWrap().Mkdir(context.Background(), "test")
   759  	require.NoError(t, err)
   760  	for i := 1; i <= 4; i++ {
   761  		err = cfs.UnWrap().Mkdir(context.Background(), fmt.Sprintf("test/dir%d", i))
   762  		require.NoError(t, err)
   763  
   764  		for j := 1; j <= 4; j++ {
   765  			err = cfs.UnWrap().Mkdir(context.Background(), fmt.Sprintf("test/dir%d/dir%d", i, j))
   766  			require.NoError(t, err)
   767  
   768  			runInstance.writeObjectString(t, cfs.UnWrap(), fmt.Sprintf("test/dir%d/dir%d/test.txt", i, j), "test")
   769  		}
   770  	}
   771  
   772  	di, err := runInstance.list(t, rootFs, "test/dir1/dir2")
   773  	require.NoError(t, err)
   774  	log.Printf("len: %v", len(di))
   775  	require.Len(t, di, 1)
   776  
   777  	time.Sleep(time.Second * 30)
   778  
   779  	di, err = runInstance.list(t, rootFs, "test/dir1/dir2")
   780  	require.NoError(t, err)
   781  	log.Printf("len: %v", len(di))
   782  	require.Len(t, di, 1)
   783  
   784  	di, err = runInstance.list(t, rootFs, "test/dir1")
   785  	require.NoError(t, err)
   786  	log.Printf("len: %v", len(di))
   787  	require.Len(t, di, 4)
   788  
   789  	di, err = runInstance.list(t, rootFs, "test")
   790  	require.NoError(t, err)
   791  	log.Printf("len: %v", len(di))
   792  	require.Len(t, di, 4)
   793  }
   794  
   795  // run holds the remotes for a test run
   796  type run struct {
   797  	okDiff            time.Duration
   798  	runDefaultCfgMap  configmap.Simple
   799  	tmpUploadDir      string
   800  	rootIsCrypt       bool
   801  	wrappedIsExternal bool
   802  	tempFiles         []*os.File
   803  	dbPath            string
   804  	chunkPath         string
   805  	vfsCachePath      string
   806  }
   807  
   808  func newRun() *run {
   809  	var err error
   810  	r := &run{
   811  		okDiff: time.Second * 9, // really big diff here but the build machines seem to be slow. need a different way for this
   812  	}
   813  
   814  	// Read in all the defaults for all the options
   815  	fsInfo, err := fs.Find("cache")
   816  	if err != nil {
   817  		panic(fmt.Sprintf("Couldn't find cache remote: %v", err))
   818  	}
   819  	r.runDefaultCfgMap = configmap.Simple{}
   820  	for _, option := range fsInfo.Options {
   821  		r.runDefaultCfgMap.Set(option.Name, fmt.Sprint(option.Default))
   822  	}
   823  
   824  	if uploadDir == "" {
   825  		r.tmpUploadDir, err = os.MkdirTemp("", "rclonecache-tmp")
   826  		if err != nil {
   827  			panic(fmt.Sprintf("Failed to create temp dir: %v", err))
   828  		}
   829  	} else {
   830  		r.tmpUploadDir = uploadDir
   831  	}
   832  	log.Printf("Temp Upload Dir: %v", r.tmpUploadDir)
   833  
   834  	return r
   835  }
   836  
   837  func (r *run) encryptRemoteIfNeeded(t *testing.T, remote string) string {
   838  	if !runInstance.rootIsCrypt || len(decryptedToEncryptedRemotes) == 0 {
   839  		return remote
   840  	}
   841  
   842  	enc, ok := decryptedToEncryptedRemotes[remote]
   843  	if !ok {
   844  		t.Fatalf("Failed to find decrypted -> encrypted mapping for '%v'", remote)
   845  		return remote
   846  	}
   847  	return enc
   848  }
   849  
   850  func (r *run) newCacheFs(t *testing.T, remote, id string, needRemote, purge bool, flags map[string]string) (fs.Fs, *cache.Persistent) {
   851  	fstest.Initialise()
   852  	remoteExists := false
   853  	for _, s := range config.FileSections() {
   854  		if s == remote {
   855  			remoteExists = true
   856  		}
   857  	}
   858  	if !remoteExists && needRemote {
   859  		t.Skipf("Need remote (%v) to exist", remote)
   860  		return nil, nil
   861  	}
   862  
   863  	// Config to pass to NewFs
   864  	m := configmap.Simple{}
   865  	for k, v := range r.runDefaultCfgMap {
   866  		m.Set(k, v)
   867  	}
   868  	for k, v := range flags {
   869  		m.Set(k, v)
   870  	}
   871  
   872  	// if the remote doesn't exist, create a new one with a local one for it
   873  	// identify which is the cache remote (it can be wrapped by a crypt too)
   874  	rootIsCrypt := false
   875  	cacheRemote := remote
   876  	if !remoteExists {
   877  		localRemote := remote + "-local"
   878  		config.FileSet(localRemote, "type", "local")
   879  		config.FileSet(localRemote, "nounc", "true")
   880  		m.Set("type", "cache")
   881  		m.Set("remote", localRemote+":"+filepath.Join(os.TempDir(), localRemote))
   882  	} else {
   883  		remoteType := config.FileGet(remote, "type")
   884  		if remoteType == "" {
   885  			t.Skipf("skipped due to invalid remote type for %v", remote)
   886  			return nil, nil
   887  		}
   888  		if remoteType != "cache" {
   889  			if remoteType == "crypt" {
   890  				rootIsCrypt = true
   891  				m.Set("password", cryptPassword1)
   892  				m.Set("password2", cryptPassword2)
   893  			}
   894  			remoteRemote := config.FileGet(remote, "remote")
   895  			if remoteRemote == "" {
   896  				t.Skipf("skipped due to invalid remote wrapper for %v", remote)
   897  				return nil, nil
   898  			}
   899  			remoteRemoteParts := strings.Split(remoteRemote, ":")
   900  			remoteWrapping := remoteRemoteParts[0]
   901  			remoteType := config.FileGet(remoteWrapping, "type")
   902  			if remoteType != "cache" {
   903  				t.Skipf("skipped due to invalid remote type for %v: '%v'", remoteWrapping, remoteType)
   904  				return nil, nil
   905  			}
   906  			cacheRemote = remoteWrapping
   907  		}
   908  	}
   909  	runInstance.rootIsCrypt = rootIsCrypt
   910  	runInstance.dbPath = filepath.Join(config.GetCacheDir(), "cache-backend", cacheRemote+".db")
   911  	runInstance.chunkPath = filepath.Join(config.GetCacheDir(), "cache-backend", cacheRemote)
   912  	runInstance.vfsCachePath = filepath.Join(config.GetCacheDir(), "vfs", remote)
   913  	boltDb, err := cache.GetPersistent(runInstance.dbPath, runInstance.chunkPath, &cache.Features{PurgeDb: true})
   914  	require.NoError(t, err)
   915  
   916  	ci := fs.GetConfig(context.Background())
   917  	ci.LowLevelRetries = 1
   918  
   919  	// Instantiate root
   920  	if purge {
   921  		boltDb.PurgeTempUploads()
   922  		_ = os.RemoveAll(path.Join(runInstance.tmpUploadDir, id))
   923  	}
   924  	f, err := cache.NewFs(context.Background(), remote, id, m)
   925  	require.NoError(t, err)
   926  	cfs, err := r.getCacheFs(f)
   927  	require.NoError(t, err)
   928  	_, isCache := cfs.Features().UnWrap().(*cache.Fs)
   929  	_, isCrypt := cfs.Features().UnWrap().(*crypt.Fs)
   930  	_, isLocal := cfs.Features().UnWrap().(*local.Fs)
   931  	if isCache || isCrypt || isLocal {
   932  		r.wrappedIsExternal = false
   933  	} else {
   934  		r.wrappedIsExternal = true
   935  	}
   936  
   937  	if purge {
   938  		_ = operations.Purge(context.Background(), f, "")
   939  	}
   940  	err = f.Mkdir(context.Background(), "")
   941  	require.NoError(t, err)
   942  
   943  	t.Cleanup(func() {
   944  		runInstance.cleanupFs(t, f)
   945  	})
   946  
   947  	return f, boltDb
   948  }
   949  
   950  func (r *run) cleanupFs(t *testing.T, f fs.Fs) {
   951  	err := operations.Purge(context.Background(), f, "")
   952  	require.NoError(t, err)
   953  	cfs, err := r.getCacheFs(f)
   954  	require.NoError(t, err)
   955  	cfs.StopBackgroundRunners()
   956  
   957  	err = os.RemoveAll(r.tmpUploadDir)
   958  	require.NoError(t, err)
   959  
   960  	for _, f := range r.tempFiles {
   961  		_ = f.Close()
   962  		_ = os.Remove(f.Name())
   963  	}
   964  	r.tempFiles = nil
   965  	debug.FreeOSMemory()
   966  }
   967  
   968  func (r *run) randomReader(t *testing.T, size int64) io.ReadCloser {
   969  	chunk := int64(1024)
   970  	cnt := size / chunk
   971  	left := size % chunk
   972  	f, err := os.CreateTemp("", "rclonecache-tempfile")
   973  	require.NoError(t, err)
   974  
   975  	for i := 0; i < int(cnt); i++ {
   976  		data := randStringBytes(int(chunk))
   977  		_, _ = f.Write(data)
   978  	}
   979  	data := randStringBytes(int(left))
   980  	_, _ = f.Write(data)
   981  	_, _ = f.Seek(int64(0), io.SeekStart)
   982  	r.tempFiles = append(r.tempFiles, f)
   983  
   984  	return f
   985  }
   986  
   987  func (r *run) writeRemoteString(t *testing.T, f fs.Fs, remote, content string) {
   988  	r.writeRemoteBytes(t, f, remote, []byte(content))
   989  }
   990  
   991  func (r *run) writeObjectString(t *testing.T, f fs.Fs, remote, content string) fs.Object {
   992  	return r.writeObjectBytes(t, f, remote, []byte(content))
   993  }
   994  
   995  func (r *run) writeRemoteBytes(t *testing.T, f fs.Fs, remote string, data []byte) {
   996  	r.writeObjectBytes(t, f, remote, data)
   997  }
   998  
   999  func (r *run) writeRemoteReader(t *testing.T, f fs.Fs, remote string, in io.ReadCloser) {
  1000  	r.writeObjectReader(t, f, remote, in)
  1001  }
  1002  
  1003  func (r *run) writeObjectBytes(t *testing.T, f fs.Fs, remote string, data []byte) fs.Object {
  1004  	in := bytes.NewReader(data)
  1005  	_ = r.writeObjectReader(t, f, remote, in)
  1006  	o, err := f.NewObject(context.Background(), remote)
  1007  	require.NoError(t, err)
  1008  	require.Equal(t, int64(len(data)), o.Size())
  1009  	return o
  1010  }
  1011  
  1012  func (r *run) writeObjectReader(t *testing.T, f fs.Fs, remote string, in io.Reader) fs.Object {
  1013  	modTime := time.Now()
  1014  	objInfo := object.NewStaticObjectInfo(remote, modTime, -1, true, nil, f)
  1015  	obj, err := f.Put(context.Background(), in, objInfo)
  1016  	require.NoError(t, err)
  1017  	return obj
  1018  }
  1019  
  1020  func (r *run) updateObjectRemote(t *testing.T, f fs.Fs, remote string, data1 []byte, data2 []byte) fs.Object {
  1021  	var err error
  1022  	var obj fs.Object
  1023  
  1024  	in1 := bytes.NewReader(data1)
  1025  	in2 := bytes.NewReader(data2)
  1026  	objInfo1 := object.NewStaticObjectInfo(remote, time.Now(), int64(len(data1)), true, nil, f)
  1027  	objInfo2 := object.NewStaticObjectInfo(remote, time.Now(), int64(len(data2)), true, nil, f)
  1028  
  1029  	_, err = f.Put(context.Background(), in1, objInfo1)
  1030  	require.NoError(t, err)
  1031  	obj, err = f.NewObject(context.Background(), remote)
  1032  	require.NoError(t, err)
  1033  	err = obj.Update(context.Background(), in2, objInfo2)
  1034  	require.NoError(t, err)
  1035  
  1036  	return obj
  1037  }
  1038  
  1039  func (r *run) readDataFromRemote(t *testing.T, f fs.Fs, remote string, offset, end int64, noLengthCheck bool) ([]byte, error) {
  1040  	size := end - offset
  1041  	checkSample := make([]byte, size)
  1042  
  1043  	co, err := f.NewObject(context.Background(), remote)
  1044  	if err != nil {
  1045  		return checkSample, err
  1046  	}
  1047  	checkSample = r.readDataFromObj(t, co, offset, end, noLengthCheck)
  1048  
  1049  	if !noLengthCheck && size != int64(len(checkSample)) {
  1050  		return checkSample, fmt.Errorf("read size doesn't match expected: %v <> %v", len(checkSample), size)
  1051  	}
  1052  	return checkSample, nil
  1053  }
  1054  
  1055  func (r *run) readDataFromObj(t *testing.T, o fs.Object, offset, end int64, noLengthCheck bool) []byte {
  1056  	size := end - offset
  1057  	checkSample := make([]byte, size)
  1058  	reader, err := o.Open(context.Background(), &fs.SeekOption{Offset: offset})
  1059  	require.NoError(t, err)
  1060  	totalRead, err := io.ReadFull(reader, checkSample)
  1061  	if (err == io.EOF || err == io.ErrUnexpectedEOF) && noLengthCheck {
  1062  		err = nil
  1063  		checkSample = checkSample[:totalRead]
  1064  	}
  1065  	require.NoError(t, err, "with string -%v-", string(checkSample))
  1066  	_ = reader.Close()
  1067  	return checkSample
  1068  }
  1069  
  1070  func (r *run) mkdir(t *testing.T, f fs.Fs, remote string) {
  1071  	err := f.Mkdir(context.Background(), remote)
  1072  	require.NoError(t, err)
  1073  }
  1074  
  1075  func (r *run) rm(t *testing.T, f fs.Fs, remote string) error {
  1076  	var err error
  1077  
  1078  	var obj fs.Object
  1079  	obj, err = f.NewObject(context.Background(), remote)
  1080  	if err != nil {
  1081  		err = f.Rmdir(context.Background(), remote)
  1082  	} else {
  1083  		err = obj.Remove(context.Background())
  1084  	}
  1085  
  1086  	return err
  1087  }
  1088  
  1089  func (r *run) list(t *testing.T, f fs.Fs, remote string) ([]interface{}, error) {
  1090  	var err error
  1091  	var l []interface{}
  1092  	var list fs.DirEntries
  1093  	list, err = f.List(context.Background(), remote)
  1094  	for _, ll := range list {
  1095  		l = append(l, ll)
  1096  	}
  1097  	return l, err
  1098  }
  1099  
  1100  func (r *run) dirMove(t *testing.T, rootFs fs.Fs, src, dst string) error {
  1101  	var err error
  1102  
  1103  	if rootFs.Features().DirMove != nil {
  1104  		err = rootFs.Features().DirMove(context.Background(), rootFs, src, dst)
  1105  		if err != nil {
  1106  			return err
  1107  		}
  1108  	} else {
  1109  		t.Logf("DirMove not supported by %v", rootFs)
  1110  		return errNotSupported
  1111  	}
  1112  
  1113  	return err
  1114  }
  1115  
  1116  func (r *run) move(t *testing.T, rootFs fs.Fs, src, dst string) error {
  1117  	var err error
  1118  
  1119  	if rootFs.Features().Move != nil {
  1120  		obj1, err := rootFs.NewObject(context.Background(), src)
  1121  		if err != nil {
  1122  			return err
  1123  		}
  1124  		_, err = rootFs.Features().Move(context.Background(), obj1, dst)
  1125  		if err != nil {
  1126  			return err
  1127  		}
  1128  	} else {
  1129  		t.Logf("Move not supported by %v", rootFs)
  1130  		return errNotSupported
  1131  	}
  1132  
  1133  	return err
  1134  }
  1135  
  1136  func (r *run) copy(t *testing.T, rootFs fs.Fs, src, dst string) error {
  1137  	var err error
  1138  
  1139  	if rootFs.Features().Copy != nil {
  1140  		obj, err := rootFs.NewObject(context.Background(), src)
  1141  		if err != nil {
  1142  			return err
  1143  		}
  1144  		_, err = rootFs.Features().Copy(context.Background(), obj, dst)
  1145  		if err != nil {
  1146  			return err
  1147  		}
  1148  	} else {
  1149  		t.Logf("Copy not supported by %v", rootFs)
  1150  		return errNotSupported
  1151  	}
  1152  
  1153  	return err
  1154  }
  1155  
  1156  func (r *run) modTime(t *testing.T, rootFs fs.Fs, src string) (time.Time, error) {
  1157  	var err error
  1158  
  1159  	obj1, err := rootFs.NewObject(context.Background(), src)
  1160  	if err != nil {
  1161  		return time.Time{}, err
  1162  	}
  1163  	return obj1.ModTime(context.Background()), nil
  1164  }
  1165  
  1166  func (r *run) size(t *testing.T, rootFs fs.Fs, src string) (int64, error) {
  1167  	var err error
  1168  
  1169  	obj1, err := rootFs.NewObject(context.Background(), src)
  1170  	if err != nil {
  1171  		return int64(0), err
  1172  	}
  1173  	return obj1.Size(), nil
  1174  }
  1175  
  1176  func (r *run) updateData(t *testing.T, rootFs fs.Fs, src, data, append string) error {
  1177  	var err error
  1178  
  1179  	var obj1 fs.Object
  1180  	obj1, err = rootFs.NewObject(context.Background(), src)
  1181  	if err != nil {
  1182  		return err
  1183  	}
  1184  	data1 := []byte(data + append)
  1185  	reader := bytes.NewReader(data1)
  1186  	objInfo1 := object.NewStaticObjectInfo(src, time.Now(), int64(len(data1)), true, nil, rootFs)
  1187  	err = obj1.Update(context.Background(), reader, objInfo1)
  1188  
  1189  	return err
  1190  }
  1191  
  1192  func (r *run) cleanSize(t *testing.T, size int64) int64 {
  1193  	if r.rootIsCrypt {
  1194  		denominator := int64(65536 + 16)
  1195  		size = size - 32
  1196  		quotient := size / denominator
  1197  		remainder := size % denominator
  1198  		return (quotient*65536 + remainder - 16)
  1199  	}
  1200  
  1201  	return size
  1202  }
  1203  
  1204  func (r *run) listenForBackgroundUpload(t *testing.T, f fs.Fs, remote string) chan error {
  1205  	cfs, err := r.getCacheFs(f)
  1206  	require.NoError(t, err)
  1207  	buCh := cfs.GetBackgroundUploadChannel()
  1208  	require.NotNil(t, buCh)
  1209  	maxDuration := time.Minute * 3
  1210  	if r.wrappedIsExternal {
  1211  		maxDuration = time.Minute * 10
  1212  	}
  1213  
  1214  	waitCh := make(chan error)
  1215  	go func() {
  1216  		var err error
  1217  		var state cache.BackgroundUploadState
  1218  
  1219  		for i := 0; i < 2; i++ {
  1220  			select {
  1221  			case state = <-buCh:
  1222  				// continue
  1223  			case <-time.After(maxDuration):
  1224  				waitCh <- fmt.Errorf("Timed out waiting for background upload: %v", remote)
  1225  				return
  1226  			}
  1227  			checkRemote := state.Remote
  1228  			if r.rootIsCrypt {
  1229  				cryptFs := f.(*crypt.Fs)
  1230  				checkRemote, err = cryptFs.DecryptFileName(checkRemote)
  1231  				if err != nil {
  1232  					waitCh <- err
  1233  					return
  1234  				}
  1235  			}
  1236  			if checkRemote == remote && cache.BackgroundUploadStarted != state.Status {
  1237  				waitCh <- state.Error
  1238  				return
  1239  			}
  1240  		}
  1241  		waitCh <- fmt.Errorf("Too many attempts to wait for the background upload: %v", remote)
  1242  	}()
  1243  	return waitCh
  1244  }
  1245  
  1246  func (r *run) completeBackgroundUpload(t *testing.T, remote string, waitCh chan error) {
  1247  	var err error
  1248  	maxDuration := time.Minute * 3
  1249  	if r.wrappedIsExternal {
  1250  		maxDuration = time.Minute * 10
  1251  	}
  1252  	select {
  1253  	case err = <-waitCh:
  1254  		// continue
  1255  	case <-time.After(maxDuration):
  1256  		t.Fatalf("Timed out waiting to complete the background upload %v", remote)
  1257  		return
  1258  	}
  1259  	require.NoError(t, err)
  1260  }
  1261  
  1262  func (r *run) completeAllBackgroundUploads(t *testing.T, f fs.Fs, lastRemote string) {
  1263  	var state cache.BackgroundUploadState
  1264  	var err error
  1265  
  1266  	maxDuration := time.Minute * 5
  1267  	if r.wrappedIsExternal {
  1268  		maxDuration = time.Minute * 15
  1269  	}
  1270  	cfs, err := r.getCacheFs(f)
  1271  	require.NoError(t, err)
  1272  	buCh := cfs.GetBackgroundUploadChannel()
  1273  	require.NotNil(t, buCh)
  1274  
  1275  	for {
  1276  		select {
  1277  		case state = <-buCh:
  1278  			checkRemote := state.Remote
  1279  			if r.rootIsCrypt {
  1280  				cryptFs := f.(*crypt.Fs)
  1281  				checkRemote, err = cryptFs.DecryptFileName(checkRemote)
  1282  				require.NoError(t, err)
  1283  			}
  1284  			if checkRemote == lastRemote && cache.BackgroundUploadCompleted == state.Status {
  1285  				require.NoError(t, state.Error)
  1286  				return
  1287  			}
  1288  		case <-time.After(maxDuration):
  1289  			t.Fatalf("Timed out waiting to complete the background upload %v", lastRemote)
  1290  			return
  1291  		}
  1292  	}
  1293  }
  1294  
  1295  func (r *run) retryBlock(block func() error, maxRetries int, rate time.Duration) error {
  1296  	var err error
  1297  	for i := 0; i < maxRetries; i++ {
  1298  		err = block()
  1299  		if err == nil {
  1300  			return nil
  1301  		}
  1302  		time.Sleep(rate)
  1303  	}
  1304  	return err
  1305  }
  1306  
  1307  func (r *run) getCacheFs(f fs.Fs) (*cache.Fs, error) {
  1308  	cfs, ok := f.(*cache.Fs)
  1309  	if ok {
  1310  		return cfs, nil
  1311  	}
  1312  	if f.Features().UnWrap != nil {
  1313  		cfs, ok := f.Features().UnWrap().(*cache.Fs)
  1314  		if ok {
  1315  			return cfs, nil
  1316  		}
  1317  	}
  1318  	return nil, errors.New("didn't found a cache fs")
  1319  }
  1320  
  1321  func randStringBytes(n int) []byte {
  1322  	b := make([]byte, n)
  1323  	for i := range b {
  1324  		b[i] = letterBytes[rand.Intn(len(letterBytes))]
  1325  	}
  1326  	return b
  1327  }
  1328  
  1329  var (
  1330  	_ fs.Fs = (*cache.Fs)(nil)
  1331  	_ fs.Fs = (*local.Fs)(nil)
  1332  )