fstests: make integration tests to check all backends can store any file name

This tests the encoder is working properly
This commit is contained in:
Nick Craig-Wood 2019-07-25 15:16:39 +01:00
parent f0c2249086
commit 57c1284df7
3 changed files with 179 additions and 119 deletions

View File

@ -1,6 +1,7 @@
package chunker package chunker
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"testing" "testing"
@ -19,7 +20,7 @@ var (
// test that chunking does not break large uploads // test that chunking does not break large uploads
func (f *Fs) InternalTestPutLarge(t *testing.T, kilobytes int) { func (f *Fs) InternalTestPutLarge(t *testing.T, kilobytes int) {
t.Run(fmt.Sprintf("PutLarge%dk", kilobytes), func(t *testing.T) { t.Run(fmt.Sprintf("PutLarge%dk", kilobytes), func(t *testing.T) {
fstests.TestPutLarge(t, f, &fstest.Item{ fstests.TestPutLarge(context.Background(), t, f, &fstest.Item{
ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"), ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
Path: fmt.Sprintf("chunker-upload-%dk", kilobytes), Path: fmt.Sprintf("chunker-upload-%dk", kilobytes),
Size: int64(kilobytes) * int64(fs.KibiByte), Size: int64(kilobytes) * int64(fs.KibiByte),

View File

@ -32,6 +32,7 @@ import (
"github.com/rclone/rclone/fs/operations" "github.com/rclone/rclone/fs/operations"
"github.com/rclone/rclone/fs/walk" "github.com/rclone/rclone/fs/walk"
"github.com/rclone/rclone/fstest" "github.com/rclone/rclone/fstest"
"github.com/rclone/rclone/lib/encoder"
"github.com/rclone/rclone/lib/random" "github.com/rclone/rclone/lib/random"
"github.com/rclone/rclone/lib/readers" "github.com/rclone/rclone/lib/readers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -117,12 +118,12 @@ func objsToNames(objs []fs.Object) []string {
} }
// findObject finds the object on the remote // findObject finds the object on the remote
func findObject(t *testing.T, f fs.Fs, Name string) fs.Object { func findObject(ctx context.Context, t *testing.T, f fs.Fs, Name string) fs.Object {
var obj fs.Object var obj fs.Object
var err error var err error
sleepTime := 1 * time.Second sleepTime := 1 * time.Second
for i := 1; i <= *fstest.ListRetries; i++ { for i := 1; i <= *fstest.ListRetries; i++ {
obj, err = f.NewObject(context.Background(), Name) obj, err = f.NewObject(ctx, Name)
if err == nil { if err == nil {
break break
} }
@ -151,7 +152,7 @@ func retry(t *testing.T, what string, f func() error) {
} }
// testPut puts file to the remote // testPut puts file to the remote
func testPut(t *testing.T, f fs.Fs, file *fstest.Item) (string, fs.Object) { func testPut(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item) (string, fs.Object) {
var ( var (
err error err error
obj fs.Object obj fs.Object
@ -166,19 +167,19 @@ func testPut(t *testing.T, f fs.Fs, file *fstest.Item) (string, fs.Object) {
file.Size = int64(buf.Len()) file.Size = int64(buf.Len())
obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil) obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
obj, err = f.Put(context.Background(), in, obji) obj, err = f.Put(ctx, in, obji)
return err return err
}) })
file.Hashes = uploadHash.Sums() file.Hashes = uploadHash.Sums()
file.Check(t, obj, f.Precision()) file.Check(t, obj, f.Precision())
// Re-read the object and check again // Re-read the object and check again
obj = findObject(t, f, file.Path) obj = findObject(ctx, t, f, file.Path)
file.Check(t, obj, f.Precision()) file.Check(t, obj, f.Precision())
return contents, obj return contents, obj
} }
// TestPutLarge puts file to the remote, checks it and removes it on success. // TestPutLarge puts file to the remote, checks it and removes it on success.
func TestPutLarge(t *testing.T, f fs.Fs, file *fstest.Item) { func TestPutLarge(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item) {
var ( var (
err error err error
obj fs.Object obj fs.Object
@ -190,7 +191,7 @@ func TestPutLarge(t *testing.T, f fs.Fs, file *fstest.Item) {
in := io.TeeReader(r, uploadHash) in := io.TeeReader(r, uploadHash)
obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil) obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
obj, err = f.Put(context.Background(), in, obji) obj, err = f.Put(ctx, in, obji)
if file.Size == 0 && err == fs.ErrorCantUploadEmptyFiles { if file.Size == 0 && err == fs.ErrorCantUploadEmptyFiles {
t.Skip("Can't upload zero length files") t.Skip("Can't upload zero length files")
} }
@ -200,12 +201,12 @@ func TestPutLarge(t *testing.T, f fs.Fs, file *fstest.Item) {
file.Check(t, obj, f.Precision()) file.Check(t, obj, f.Precision())
// Re-read the object and check again // Re-read the object and check again
obj = findObject(t, f, file.Path) obj = findObject(ctx, t, f, file.Path)
file.Check(t, obj, f.Precision()) file.Check(t, obj, f.Precision())
// Download the object and check it is OK // Download the object and check it is OK
downloadHash := hash.NewMultiHasher() downloadHash := hash.NewMultiHasher()
download, err := obj.Open(context.Background()) download, err := obj.Open(ctx)
require.NoError(t, err) require.NoError(t, err)
n, err := io.Copy(downloadHash, download) n, err := io.Copy(downloadHash, download)
require.NoError(t, err) require.NoError(t, err)
@ -214,7 +215,7 @@ func TestPutLarge(t *testing.T, f fs.Fs, file *fstest.Item) {
assert.Equal(t, file.Hashes, downloadHash.Sums()) assert.Equal(t, file.Hashes, downloadHash.Sums())
// Remove the object // Remove the object
require.NoError(t, obj.Remove(context.Background())) require.NoError(t, obj.Remove(ctx))
} }
// errorReader just returns an error on Read // errorReader just returns an error on Read
@ -228,9 +229,9 @@ func (er errorReader) Read(p []byte) (n int, err error) {
} }
// read the contents of an object as a string // read the contents of an object as a string
func readObject(t *testing.T, obj fs.Object, limit int64, options ...fs.OpenOption) string { func readObject(ctx context.Context, t *testing.T, obj fs.Object, limit int64, options ...fs.OpenOption) string {
what := fmt.Sprintf("readObject(%q) limit=%d, options=%+v", obj, limit, options) what := fmt.Sprintf("readObject(%q) limit=%d, options=%+v", obj, limit, options)
in, err := obj.Open(context.Background(), options...) in, err := obj.Open(ctx, options...)
require.NoError(t, err, what) require.NoError(t, err, what)
var r io.Reader = in var r io.Reader = in
if limit >= 0 { if limit >= 0 {
@ -295,6 +296,7 @@ func Run(t *testing.T, opt *Opt) {
} }
isLocalRemote bool isLocalRemote bool
purged bool // whether the dir has been purged or not purged bool // whether the dir has been purged or not
ctx = context.Background()
) )
// Skip the test if the remote isn't configured // Skip the test if the remote isn't configured
@ -408,12 +410,12 @@ func Run(t *testing.T, opt *Opt) {
if isBucketBasedButNotRoot(remote) { if isBucketBasedButNotRoot(remote) {
t.Skip("Skipping test as non root bucket based remote") t.Skip("Skipping test as non root bucket based remote")
} }
err := remote.Rmdir(context.Background(), "") err := remote.Rmdir(ctx, "")
assert.Error(t, err, "Expecting error on Rmdir non existent") assert.Error(t, err, "Expecting error on Rmdir non existent")
}) })
// Make the directory // Make the directory
err = remote.Mkdir(context.Background(), "") err = remote.Mkdir(ctx, "")
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListing(t, remote, []fstest.Item{}) fstest.CheckListing(t, remote, []fstest.Item{})
@ -451,7 +453,7 @@ func Run(t *testing.T, opt *Opt) {
// TestFsRmdirEmpty tests deleting an empty directory // TestFsRmdirEmpty tests deleting an empty directory
t.Run("FsRmdirEmpty", func(t *testing.T) { t.Run("FsRmdirEmpty", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
err := remote.Rmdir(context.Background(), "") err := remote.Rmdir(ctx, "")
require.NoError(t, err) require.NoError(t, err)
}) })
@ -461,26 +463,26 @@ func Run(t *testing.T, opt *Opt) {
t.Run("FsMkdir", func(t *testing.T) { t.Run("FsMkdir", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
err := remote.Mkdir(context.Background(), "") err := remote.Mkdir(ctx, "")
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListing(t, remote, []fstest.Item{}) fstest.CheckListing(t, remote, []fstest.Item{})
err = remote.Mkdir(context.Background(), "") err = remote.Mkdir(ctx, "")
require.NoError(t, err) require.NoError(t, err)
// TestFsMkdirRmdirSubdir tests making and removing a sub directory // TestFsMkdirRmdirSubdir tests making and removing a sub directory
t.Run("FsMkdirRmdirSubdir", func(t *testing.T) { t.Run("FsMkdirRmdirSubdir", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
dir := "dir/subdir" dir := "dir/subdir"
err := operations.Mkdir(context.Background(), remote, dir) err := operations.Mkdir(ctx, remote, dir)
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir", "dir/subdir"}, fs.GetModifyWindow(remote)) fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir", "dir/subdir"}, fs.GetModifyWindow(remote))
err = operations.Rmdir(context.Background(), remote, dir) err = operations.Rmdir(ctx, remote, dir)
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir"}, fs.GetModifyWindow(remote)) fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir"}, fs.GetModifyWindow(remote))
err = operations.Rmdir(context.Background(), remote, "dir") err = operations.Rmdir(ctx, remote, "dir")
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.GetModifyWindow(remote)) fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.GetModifyWindow(remote))
}) })
@ -494,7 +496,7 @@ func Run(t *testing.T, opt *Opt) {
// TestFsListDirEmpty tests listing the directories from an empty directory // TestFsListDirEmpty tests listing the directories from an empty directory
TestFsListDirEmpty := func(t *testing.T) { TestFsListDirEmpty := func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, 1) objs, dirs, err := walk.GetAll(ctx, remote, "", true, 1)
if !remote.Features().CanHaveEmptyDirectories { if !remote.Features().CanHaveEmptyDirectories {
if err != fs.ErrorDirNotFound { if err != fs.ErrorDirNotFound {
require.NoError(t, err) require.NoError(t, err)
@ -516,7 +518,7 @@ func Run(t *testing.T, opt *Opt) {
// TestFsListDirNotFound tests listing the directories from an empty directory // TestFsListDirNotFound tests listing the directories from an empty directory
TestFsListDirNotFound := func(t *testing.T) { TestFsListDirNotFound := func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
objs, dirs, err := walk.GetAll(context.Background(), remote, "does not exist", true, 1) objs, dirs, err := walk.GetAll(ctx, remote, "does not exist", true, 1)
if !remote.Features().CanHaveEmptyDirectories { if !remote.Features().CanHaveEmptyDirectories {
if err != fs.ErrorDirNotFound { if err != fs.ErrorDirNotFound {
assert.NoError(t, err) assert.NoError(t, err)
@ -534,15 +536,66 @@ func Run(t *testing.T, opt *Opt) {
TestFsListDirNotFound(t) TestFsListDirNotFound(t)
}) })
// FsEncoding tests that file name encodings are
// working by uploading a series of unusual files
// Must be run in an empty directory
t.Run("FsEncoding", func(t *testing.T) {
skipIfNotOk(t)
// check no files or dirs as pre-requisite
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.GetModifyWindow(remote))
for _, test := range []struct {
name string
path string
}{
// See lib/encoder/encoder.go for list of things that go here
{"control chars", "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F"},
{"dot", "."},
{"dot dot", ".."},
{"punctuation", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"},
{"leading space", " leading space"},
{"leading tilde", "~leading tilde"},
{"leading CR", "\rleading CR"},
{"leading LF", "\nleading LF"},
{"leading HT", "\tleading HT"},
{"leading VT", "\vleading VT"},
{"trailing space", "trailing space "},
{"trailing CR", "trailing CR\r"},
{"trailing LF", "trailing LF\n"},
{"trailing HT", "trailing HT\t"},
{"trailing VT", "trailing VT\v"},
{"trailing dot", "trailing dot."},
{"invalid UTF-8", "invalid utf-8\xfe"},
} {
t.Run(test.name, func(t *testing.T) {
// turn raw strings into Standard encoding
fileName := encoder.Standard.Encode(test.path)
dirName := fileName
t.Logf("testing %q", fileName)
assert.NoError(t, remote.Mkdir(ctx, dirName))
file := fstest.Item{
ModTime: time.Now(),
Path: dirName + "/" + fileName, // test creating a file and dir with that name
}
_, o := testPut(context.Background(), t, remote, &file)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file}, []string{dirName}, fs.GetModifyWindow(remote))
assert.NoError(t, o.Remove(ctx))
assert.NoError(t, remote.Rmdir(ctx, dirName))
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.GetModifyWindow(remote))
})
}
})
// TestFsNewObjectNotFound tests not finding a object // TestFsNewObjectNotFound tests not finding a object
t.Run("FsNewObjectNotFound", func(t *testing.T) { t.Run("FsNewObjectNotFound", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
// Object in an existing directory // Object in an existing directory
o, err := remote.NewObject(context.Background(), "potato") o, err := remote.NewObject(ctx, "potato")
assert.Nil(t, o) assert.Nil(t, o)
assert.Equal(t, fs.ErrorObjectNotFound, err) assert.Equal(t, fs.ErrorObjectNotFound, err)
// Now try an object in a non existing directory // Now try an object in a non existing directory
o, err = remote.NewObject(context.Background(), "directory/not/found/potato") o, err = remote.NewObject(ctx, "directory/not/found/potato")
assert.Nil(t, o) assert.Nil(t, o)
assert.Equal(t, fs.ErrorObjectNotFound, err) assert.Equal(t, fs.ErrorObjectNotFound, err)
}) })
@ -569,11 +622,11 @@ func Run(t *testing.T, opt *Opt) {
in := io.MultiReader(buf, er) in := io.MultiReader(buf, er)
obji := object.NewStaticObjectInfo(file2.Path, file2.ModTime, 2*N, true, nil, nil) obji := object.NewStaticObjectInfo(file2.Path, file2.ModTime, 2*N, true, nil, nil)
_, err := remote.Put(context.Background(), in, obji) _, err := remote.Put(ctx, in, obji)
// assert.Nil(t, obj) - FIXME some remotes return the object even on nil // assert.Nil(t, obj) - FIXME some remotes return the object even on nil
assert.NotNil(t, err) assert.NotNil(t, err)
obj, err := remote.NewObject(context.Background(), file2.Path) obj, err := remote.NewObject(ctx, file2.Path)
assert.Nil(t, obj) assert.Nil(t, obj)
assert.Equal(t, fs.ErrorObjectNotFound, err) assert.Equal(t, fs.ErrorObjectNotFound, err)
}) })
@ -581,7 +634,7 @@ func Run(t *testing.T, opt *Opt) {
t.Run("FsPutZeroLength", func(t *testing.T) { t.Run("FsPutZeroLength", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
TestPutLarge(t, remote, &fstest.Item{ TestPutLarge(ctx, t, remote, &fstest.Item{
ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"), ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
Path: fmt.Sprintf("zero-length-file"), Path: fmt.Sprintf("zero-length-file"),
Size: int64(0), Size: int64(0),
@ -595,7 +648,7 @@ func Run(t *testing.T, opt *Opt) {
t.Skip("FS has no OpenWriterAt interface") t.Skip("FS has no OpenWriterAt interface")
} }
path := "writer-at-subdir/writer-at-file" path := "writer-at-subdir/writer-at-file"
out, err := openWriterAt(context.Background(), path, -1) out, err := openWriterAt(ctx, path, -1)
require.NoError(t, err) require.NoError(t, err)
var n int var n int
@ -611,11 +664,11 @@ func Run(t *testing.T, opt *Opt) {
assert.NoError(t, out.Close()) assert.NoError(t, out.Close())
obj := findObject(t, remote, path) obj := findObject(ctx, t, remote, path)
assert.Equal(t, "abcdefghi", readObject(t, obj, -1), "contents of file differ") assert.Equal(t, "abcdefghi", readObject(ctx, t, obj, -1), "contents of file differ")
assert.NoError(t, obj.Remove(context.Background())) assert.NoError(t, obj.Remove(ctx))
assert.NoError(t, remote.Rmdir(context.Background(), "writer-at-subdir")) assert.NoError(t, remote.Rmdir(ctx, "writer-at-subdir"))
}) })
// TestFsChangeNotify tests that changes are properly // TestFsChangeNotify tests that changes are properly
@ -631,13 +684,13 @@ func Run(t *testing.T, opt *Opt) {
t.Skip("FS has no ChangeNotify interface") t.Skip("FS has no ChangeNotify interface")
} }
err := operations.Mkdir(context.Background(), remote, "dir") err := operations.Mkdir(ctx, remote, "dir")
require.NoError(t, err) require.NoError(t, err)
pollInterval := make(chan time.Duration) pollInterval := make(chan time.Duration)
dirChanges := map[string]struct{}{} dirChanges := map[string]struct{}{}
objChanges := map[string]struct{}{} objChanges := map[string]struct{}{}
doChangeNotify(context.Background(), func(x string, e fs.EntryType) { doChangeNotify(ctx, func(x string, e fs.EntryType) {
fs.Debugf(nil, "doChangeNotify(%q, %+v)", x, e) fs.Debugf(nil, "doChangeNotify(%q, %+v)", x, e)
if strings.HasPrefix(x, file1.Path[:5]) || strings.HasPrefix(x, file2.Path[:5]) { if strings.HasPrefix(x, file1.Path[:5]) || strings.HasPrefix(x, file2.Path[:5]) {
fs.Debugf(nil, "Ignoring notify for file1 or file2: %q, %v", x, e) fs.Debugf(nil, "Ignoring notify for file1 or file2: %q, %v", x, e)
@ -655,7 +708,7 @@ func Run(t *testing.T, opt *Opt) {
var dirs []string var dirs []string
for _, idx := range []int{1, 3, 2} { for _, idx := range []int{1, 3, 2} {
dir := fmt.Sprintf("dir/subdir%d", idx) dir := fmt.Sprintf("dir/subdir%d", idx)
err = operations.Mkdir(context.Background(), remote, dir) err = operations.Mkdir(ctx, remote, dir)
require.NoError(t, err) require.NoError(t, err)
dirs = append(dirs, dir) dirs = append(dirs, dir)
} }
@ -666,7 +719,7 @@ func Run(t *testing.T, opt *Opt) {
ModTime: time.Now(), ModTime: time.Now(),
Path: fmt.Sprintf("dir/file%d", idx), Path: fmt.Sprintf("dir/file%d", idx),
} }
_, o := testPut(t, remote, &file) _, o := testPut(ctx, t, remote, &file)
objs = append(objs, o) objs = append(objs, o)
} }
@ -700,11 +753,11 @@ func Run(t *testing.T, opt *Opt) {
// tidy up afterwards // tidy up afterwards
for _, o := range objs { for _, o := range objs {
assert.NoError(t, o.Remove(context.Background())) assert.NoError(t, o.Remove(ctx))
} }
dirs = append(dirs, "dir") dirs = append(dirs, "dir")
for _, dir := range dirs { for _, dir := range dirs {
assert.NoError(t, remote.Rmdir(context.Background(), dir)) assert.NoError(t, remote.Rmdir(ctx, dir))
} }
}) })
@ -713,9 +766,9 @@ func Run(t *testing.T, opt *Opt) {
// Tests that require file1, file2 are within this // Tests that require file1, file2 are within this
t.Run("FsPutFiles", func(t *testing.T) { t.Run("FsPutFiles", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
file1Contents, _ = testPut(t, remote, &file1) file1Contents, _ = testPut(ctx, t, remote, &file1)
/* file2Contents = */ testPut(t, remote, &file2) /* file2Contents = */ testPut(ctx, t, remote, &file2)
file1Contents, _ = testPut(t, remote, &file1) file1Contents, _ = testPut(ctx, t, remote, &file1)
// Note that the next test will check there are no duplicated file names // Note that the next test will check there are no duplicated file names
// TestFsListDirFile2 tests the files are correctly uploaded by doing // TestFsListDirFile2 tests the files are correctly uploaded by doing
@ -725,9 +778,9 @@ func Run(t *testing.T, opt *Opt) {
list := func(dir string, expectedDirNames, expectedObjNames []string) { list := func(dir string, expectedDirNames, expectedObjNames []string) {
var objNames, dirNames []string var objNames, dirNames []string
for i := 1; i <= *fstest.ListRetries; i++ { for i := 1; i <= *fstest.ListRetries; i++ {
objs, dirs, err := walk.GetAll(context.Background(), remote, dir, true, 1) objs, dirs, err := walk.GetAll(ctx, remote, dir, true, 1)
if errors.Cause(err) == fs.ErrorDirNotFound { if errors.Cause(err) == fs.ErrorDirNotFound {
objs, dirs, err = walk.GetAll(context.Background(), remote, fstest.WinPath(dir), true, 1) objs, dirs, err = walk.GetAll(ctx, remote, fstest.WinPath(dir), true, 1)
} }
require.NoError(t, err) require.NoError(t, err)
objNames = objsToNames(objs) objNames = objsToNames(objs)
@ -773,7 +826,7 @@ func Run(t *testing.T, opt *Opt) {
// Test the files are all there with walk.ListR recursive listings // Test the files are all there with walk.ListR recursive listings
t.Run("FsListR", func(t *testing.T) { t.Run("FsListR", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, -1) objs, dirs, err := walk.GetAll(ctx, remote, "", true, -1)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []string{ assert.Equal(t, []string{
"hello_ sausage", "hello_ sausage",
@ -791,7 +844,7 @@ func Run(t *testing.T, opt *Opt) {
// walk.ListR recursive listings on a sub dir // walk.ListR recursive listings on a sub dir
t.Run("FsListRSubdir", func(t *testing.T) { t.Run("FsListRSubdir", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
objs, dirs, err := walk.GetAll(context.Background(), remote, path.Dir(path.Dir(path.Dir(path.Dir(file2.Path)))), true, -1) objs, dirs, err := walk.GetAll(ctx, remote, path.Dir(path.Dir(path.Dir(path.Dir(file2.Path)))), true, -1)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []string{ assert.Equal(t, []string{
"hello_ sausage/êé", "hello_ sausage/êé",
@ -808,7 +861,7 @@ func Run(t *testing.T, opt *Opt) {
skipIfNotOk(t) skipIfNotOk(t)
rootRemote, err := fs.NewFs(remoteName) rootRemote, err := fs.NewFs(remoteName)
require.NoError(t, err) require.NoError(t, err)
_, dirs, err := walk.GetAll(context.Background(), rootRemote, "", true, 1) _, dirs, err := walk.GetAll(ctx, rootRemote, "", true, 1)
require.NoError(t, err) require.NoError(t, err)
assert.Contains(t, dirsToNames(dirs), subRemoteLeaf, "Remote leaf not found") assert.Contains(t, dirsToNames(dirs), subRemoteLeaf, "Remote leaf not found")
} }
@ -830,7 +883,7 @@ func Run(t *testing.T, opt *Opt) {
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
dir, _ := path.Split(fileName) dir, _ := path.Split(fileName)
dir = dir[:len(dir)-1] dir = dir[:len(dir)-1]
objs, dirs, err = walk.GetAll(context.Background(), remote, dir, true, -1) objs, dirs, err = walk.GetAll(ctx, remote, dir, true, -1)
} }
require.NoError(t, err) require.NoError(t, err)
require.Len(t, objs, 1) require.Len(t, objs, 1)
@ -848,7 +901,7 @@ func Run(t *testing.T, opt *Opt) {
// TestFsListLevel2 tests List works for 2 levels // TestFsListLevel2 tests List works for 2 levels
TestFsListLevel2 := func(t *testing.T) { TestFsListLevel2 := func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
objs, dirs, err := walk.GetAll(context.Background(), remote, "", true, 2) objs, dirs, err := walk.GetAll(ctx, remote, "", true, 2)
if err == fs.ErrorLevelNotSupported { if err == fs.ErrorLevelNotSupported {
return return
} }
@ -873,7 +926,7 @@ func Run(t *testing.T, opt *Opt) {
// TestFsNewObject tests NewObject // TestFsNewObject tests NewObject
t.Run("FsNewObject", func(t *testing.T) { t.Run("FsNewObject", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
file1.Check(t, obj, remote.Precision()) file1.Check(t, obj, remote.Precision())
}) })
@ -887,7 +940,7 @@ func Run(t *testing.T, opt *Opt) {
t.Run("FsNewObjectDir", func(t *testing.T) { t.Run("FsNewObjectDir", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
dir := path.Dir(file2.Path) dir := path.Dir(file2.Path)
obj, err := remote.NewObject(context.Background(), dir) obj, err := remote.NewObject(ctx, dir)
assert.Nil(t, obj) assert.Nil(t, obj)
assert.NotNil(t, err) assert.NotNil(t, err)
}) })
@ -907,8 +960,8 @@ func Run(t *testing.T, opt *Opt) {
file2Copy.Path += "-copy" file2Copy.Path += "-copy"
// do the copy // do the copy
src := findObject(t, remote, file2.Path) src := findObject(ctx, t, remote, file2.Path)
dst, err := doCopy(context.Background(), src, file2Copy.Path) dst, err := doCopy(ctx, src, file2Copy.Path)
if err == fs.ErrorCantCopy { if err == fs.ErrorCantCopy {
t.Skip("FS can't copy") t.Skip("FS can't copy")
} }
@ -921,7 +974,7 @@ func Run(t *testing.T, opt *Opt) {
assert.Equal(t, file2Copy.Path, dst.Remote()) assert.Equal(t, file2Copy.Path, dst.Remote())
// Delete copy // Delete copy
err = dst.Remove(context.Background()) err = dst.Remove(ctx)
require.NoError(t, err) require.NoError(t, err)
}) })
@ -946,8 +999,8 @@ func Run(t *testing.T, opt *Opt) {
// check happy path, i.e. no naming conflicts when rename and move are two // check happy path, i.e. no naming conflicts when rename and move are two
// separate operations // separate operations
file2Move.Path = "other.txt" file2Move.Path = "other.txt"
src := findObject(t, remote, file2.Path) src := findObject(ctx, t, remote, file2.Path)
dst, err := doMove(context.Background(), src, file2Move.Path) dst, err := doMove(ctx, src, file2Move.Path)
if err == fs.ErrorCantMove { if err == fs.ErrorCantMove {
t.Skip("FS can't move") t.Skip("FS can't move")
} }
@ -961,30 +1014,30 @@ func Run(t *testing.T, opt *Opt) {
// Check conflict on "rename, then move" // Check conflict on "rename, then move"
file1Move.Path = "moveTest/other.txt" file1Move.Path = "moveTest/other.txt"
src = findObject(t, remote, file1.Path) src = findObject(ctx, t, remote, file1.Path)
_, err = doMove(context.Background(), src, file1Move.Path) _, err = doMove(ctx, src, file1Move.Path)
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListing(t, remote, []fstest.Item{file1Move, file2Move}) fstest.CheckListing(t, remote, []fstest.Item{file1Move, file2Move})
// 1: moveTest/other.txt // 1: moveTest/other.txt
// 2: other.txt // 2: other.txt
// Check conflict on "move, then rename" // Check conflict on "move, then rename"
src = findObject(t, remote, file1Move.Path) src = findObject(ctx, t, remote, file1Move.Path)
_, err = doMove(context.Background(), src, file1.Path) _, err = doMove(ctx, src, file1.Path)
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListing(t, remote, []fstest.Item{file1, file2Move}) fstest.CheckListing(t, remote, []fstest.Item{file1, file2Move})
// 1: file name.txt // 1: file name.txt
// 2: other.txt // 2: other.txt
src = findObject(t, remote, file2Move.Path) src = findObject(ctx, t, remote, file2Move.Path)
_, err = doMove(context.Background(), src, file2.Path) _, err = doMove(ctx, src, file2.Path)
require.NoError(t, err) require.NoError(t, err)
fstest.CheckListing(t, remote, []fstest.Item{file1, file2}) fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
// 1: file name.txt // 1: file name.txt
// 2: hello sausage?/../z.txt // 2: hello sausage?/../z.txt
// Tidy up moveTest directory // Tidy up moveTest directory
require.NoError(t, remote.Rmdir(context.Background(), "moveTest")) require.NoError(t, remote.Rmdir(ctx, "moveTest"))
}) })
// Move src to this remote using server side move operations. // Move src to this remote using server side move operations.
@ -1008,7 +1061,7 @@ func Run(t *testing.T, opt *Opt) {
} }
// Check it can't move onto itself // Check it can't move onto itself
err := doDirMove(context.Background(), remote, "", "") err := doDirMove(ctx, remote, "", "")
require.Equal(t, fs.ErrorDirExists, err) require.Equal(t, fs.ErrorDirExists, err)
// new remote // new remote
@ -1018,12 +1071,12 @@ func Run(t *testing.T, opt *Opt) {
const newName = "new_name/sub_new_name" const newName = "new_name/sub_new_name"
// try the move // try the move
err = newRemote.Features().DirMove(context.Background(), remote, "", newName) err = newRemote.Features().DirMove(ctx, remote, "", newName)
require.NoError(t, err) require.NoError(t, err)
// check remotes // check remotes
// remote should not exist here // remote should not exist here
_, err = remote.List(context.Background(), "") _, err = remote.List(ctx, "")
assert.Equal(t, fs.ErrorDirNotFound, errors.Cause(err)) assert.Equal(t, fs.ErrorDirNotFound, errors.Cause(err))
//fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, remote.Precision()) //fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, remote.Precision())
file1Copy := file1 file1Copy := file1
@ -1040,7 +1093,7 @@ func Run(t *testing.T, opt *Opt) {
}, newRemote.Precision()) }, newRemote.Precision())
// move it back // move it back
err = doDirMove(context.Background(), newRemote, newName, "") err = doDirMove(ctx, newRemote, newName, "")
require.NoError(t, err) require.NoError(t, err)
// check remotes // check remotes
@ -1061,7 +1114,7 @@ func Run(t *testing.T, opt *Opt) {
if isBucketBasedButNotRoot(remote) { if isBucketBasedButNotRoot(remote) {
t.Skip("Skipping test as non root bucket based remote") t.Skip("Skipping test as non root bucket based remote")
} }
err := remote.Rmdir(context.Background(), "") err := remote.Rmdir(ctx, "")
require.Error(t, err, "Expecting error on RMdir on non empty remote") require.Error(t, err, "Expecting error on RMdir on non empty remote")
}) })
@ -1081,7 +1134,7 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectString tests the Object String method // TestObjectString tests the Object String method
t.Run("ObjectString", func(t *testing.T) { t.Run("ObjectString", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1.Path, obj.String()) assert.Equal(t, file1.Path, obj.String())
if opt.NilObject != nil { if opt.NilObject != nil {
assert.Equal(t, "<nil>", opt.NilObject.String()) assert.Equal(t, "<nil>", opt.NilObject.String())
@ -1091,7 +1144,7 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectFs tests the object can be found // TestObjectFs tests the object can be found
t.Run("ObjectFs", func(t *testing.T) { t.Run("ObjectFs", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
// If this is set we don't do the direct comparison of // If this is set we don't do the direct comparison of
// the Fs from the object as it may be different // the Fs from the object as it may be different
if opt.SkipFsMatch { if opt.SkipFsMatch {
@ -1110,34 +1163,34 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectRemote tests the Remote is correct // TestObjectRemote tests the Remote is correct
t.Run("ObjectRemote", func(t *testing.T) { t.Run("ObjectRemote", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1.Path, obj.Remote()) assert.Equal(t, file1.Path, obj.Remote())
}) })
// TestObjectHashes checks all the hashes the object supports // TestObjectHashes checks all the hashes the object supports
t.Run("ObjectHashes", func(t *testing.T) { t.Run("ObjectHashes", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
file1.CheckHashes(t, obj) file1.CheckHashes(t, obj)
}) })
// TestObjectModTime tests the ModTime of the object is correct // TestObjectModTime tests the ModTime of the object is correct
TestObjectModTime := func(t *testing.T) { TestObjectModTime := func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
file1.CheckModTime(t, obj, obj.ModTime(context.Background()), remote.Precision()) file1.CheckModTime(t, obj, obj.ModTime(ctx), remote.Precision())
} }
t.Run("ObjectModTime", TestObjectModTime) t.Run("ObjectModTime", TestObjectModTime)
// TestObjectMimeType tests the MimeType of the object is correct // TestObjectMimeType tests the MimeType of the object is correct
t.Run("ObjectMimeType", func(t *testing.T) { t.Run("ObjectMimeType", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
do, ok := obj.(fs.MimeTyper) do, ok := obj.(fs.MimeTyper)
if !ok { if !ok {
t.Skip("MimeType method not supported") t.Skip("MimeType method not supported")
} }
mimeType := do.MimeType(context.Background()) mimeType := do.MimeType(ctx)
if strings.ContainsRune(mimeType, ';') { if strings.ContainsRune(mimeType, ';') {
assert.Equal(t, "text/plain; charset=utf-8", mimeType) assert.Equal(t, "text/plain; charset=utf-8", mimeType)
} else { } else {
@ -1149,15 +1202,15 @@ func Run(t *testing.T, opt *Opt) {
t.Run("ObjectSetModTime", func(t *testing.T) { t.Run("ObjectSetModTime", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
newModTime := fstest.Time("2011-12-13T14:15:16.999999999Z") newModTime := fstest.Time("2011-12-13T14:15:16.999999999Z")
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
err := obj.SetModTime(context.Background(), newModTime) err := obj.SetModTime(ctx, newModTime)
if err == fs.ErrorCantSetModTime || err == fs.ErrorCantSetModTimeWithoutDelete { if err == fs.ErrorCantSetModTime || err == fs.ErrorCantSetModTimeWithoutDelete {
t.Log(err) t.Log(err)
return return
} }
require.NoError(t, err) require.NoError(t, err)
file1.ModTime = newModTime file1.ModTime = newModTime
file1.CheckModTime(t, obj, obj.ModTime(context.Background()), remote.Precision()) file1.CheckModTime(t, obj, obj.ModTime(ctx), remote.Precision())
// And make a new object and read it from there too // And make a new object and read it from there too
TestObjectModTime(t) TestObjectModTime(t)
}) })
@ -1165,22 +1218,22 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectSize tests that Size works // TestObjectSize tests that Size works
t.Run("ObjectSize", func(t *testing.T) { t.Run("ObjectSize", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1.Size, obj.Size()) assert.Equal(t, file1.Size, obj.Size())
}) })
// TestObjectOpen tests that Open works // TestObjectOpen tests that Open works
t.Run("ObjectOpen", func(t *testing.T) { t.Run("ObjectOpen", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1Contents, readObject(t, obj, -1), "contents of file1 differ") assert.Equal(t, file1Contents, readObject(ctx, t, obj, -1), "contents of file1 differ")
}) })
// TestObjectOpenSeek tests that Open works with SeekOption // TestObjectOpenSeek tests that Open works with SeekOption
t.Run("ObjectOpenSeek", func(t *testing.T) { t.Run("ObjectOpenSeek", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1Contents[50:], readObject(t, obj, -1, &fs.SeekOption{Offset: 50}), "contents of file1 differ after seek") assert.Equal(t, file1Contents[50:], readObject(ctx, t, obj, -1, &fs.SeekOption{Offset: 50}), "contents of file1 differ after seek")
}) })
// TestObjectOpenRange tests that Open works with RangeOption // TestObjectOpenRange tests that Open works with RangeOption
@ -1188,7 +1241,7 @@ func Run(t *testing.T, opt *Opt) {
// go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|ObjectOpenRange)$' // go test -v -run 'TestIntegration/Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|ObjectOpenRange)$'
t.Run("ObjectOpenRange", func(t *testing.T) { t.Run("ObjectOpenRange", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
for _, test := range []struct { for _, test := range []struct {
ro fs.RangeOption ro fs.RangeOption
wantStart, wantEnd int wantStart, wantEnd int
@ -1199,7 +1252,7 @@ func Run(t *testing.T, opt *Opt) {
{fs.RangeOption{Start: -1, End: 20}, 80, 100}, // if start is omitted this means get the final bytes {fs.RangeOption{Start: -1, End: 20}, 80, 100}, // if start is omitted this means get the final bytes
// {fs.RangeOption{Start: -1, End: -1}, 0, 100}, - this seems to work but the RFC doesn't define it // {fs.RangeOption{Start: -1, End: -1}, 0, 100}, - this seems to work but the RFC doesn't define it
} { } {
got := readObject(t, obj, -1, &test.ro) got := readObject(ctx, t, obj, -1, &test.ro)
foundAt := strings.Index(file1Contents, got) foundAt := strings.Index(file1Contents, got)
help := fmt.Sprintf("%#v failed want [%d:%d] got [%d:%d]", test.ro, test.wantStart, test.wantEnd, foundAt, foundAt+len(got)) help := fmt.Sprintf("%#v failed want [%d:%d] got [%d:%d]", test.ro, test.wantStart, test.wantEnd, foundAt, foundAt+len(got))
assert.Equal(t, file1Contents[test.wantStart:test.wantEnd], got, help) assert.Equal(t, file1Contents[test.wantStart:test.wantEnd], got, help)
@ -1209,8 +1262,8 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectPartialRead tests that reading only part of the object does the correct thing // TestObjectPartialRead tests that reading only part of the object does the correct thing
t.Run("ObjectPartialRead", func(t *testing.T) { t.Run("ObjectPartialRead", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
assert.Equal(t, file1Contents[:50], readObject(t, obj, 50), "contents of file1 differ after limited read") assert.Equal(t, file1Contents[:50], readObject(ctx, t, obj, 50), "contents of file1 differ after limited read")
}) })
// TestObjectUpdate tests that Update works // TestObjectUpdate tests that Update works
@ -1222,9 +1275,9 @@ func Run(t *testing.T, opt *Opt) {
in := io.TeeReader(buf, hash) in := io.TeeReader(buf, hash)
file1.Size = int64(buf.Len()) file1.Size = int64(buf.Len())
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
obji := object.NewStaticObjectInfo(file1.Path, file1.ModTime, int64(len(contents)), true, nil, obj.Fs()) obji := object.NewStaticObjectInfo(file1.Path, file1.ModTime, int64(len(contents)), true, nil, obj.Fs())
err := obj.Update(context.Background(), in, obji) err := obj.Update(ctx, in, obji)
require.NoError(t, err) require.NoError(t, err)
file1.Hashes = hash.Sums() file1.Hashes = hash.Sums()
@ -1232,18 +1285,18 @@ func Run(t *testing.T, opt *Opt) {
file1.Check(t, obj, remote.Precision()) file1.Check(t, obj, remote.Precision())
// Re-read the object and check again // Re-read the object and check again
obj = findObject(t, remote, file1.Path) obj = findObject(ctx, t, remote, file1.Path)
file1.Check(t, obj, remote.Precision()) file1.Check(t, obj, remote.Precision())
// check contents correct // check contents correct
assert.Equal(t, contents, readObject(t, obj, -1), "contents of updated file1 differ") assert.Equal(t, contents, readObject(ctx, t, obj, -1), "contents of updated file1 differ")
file1Contents = contents file1Contents = contents
}) })
// TestObjectStorable tests that Storable works // TestObjectStorable tests that Storable works
t.Run("ObjectStorable", func(t *testing.T) { t.Run("ObjectStorable", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
require.NotNil(t, !obj.Storable(), "Expecting object to be storable") require.NotNil(t, !obj.Storable(), "Expecting object to be storable")
}) })
@ -1363,7 +1416,7 @@ func Run(t *testing.T, opt *Opt) {
ModTime: time.Now(), ModTime: time.Now(),
Path: path.Join(configLeaf, "created from root.txt"), Path: path.Join(configLeaf, "created from root.txt"),
} }
_, file3Obj := testPut(t, rootRemote, &file3Root) _, file3Obj := testPut(ctx, t, rootRemote, &file3Root)
fstest.CheckListingWithRoot(t, rootRemote, configLeaf, []fstest.Item{file1Root, file2Root, file3Root}, nil, rootRemote.Precision()) fstest.CheckListingWithRoot(t, rootRemote, configLeaf, []fstest.Item{file1Root, file2Root, file3Root}, nil, rootRemote.Precision())
// And then remove it // And then remove it
@ -1385,29 +1438,29 @@ func Run(t *testing.T, opt *Opt) {
} }
// if object not found // if object not found
link, err := doPublicLink(context.Background(), file1.Path+"_does_not_exist") link, err := doPublicLink(ctx, file1.Path+"_does_not_exist")
require.Error(t, err, "Expected to get error when file doesn't exist") require.Error(t, err, "Expected to get error when file doesn't exist")
require.Equal(t, "", link, "Expected link to be empty on error") require.Equal(t, "", link, "Expected link to be empty on error")
// sharing file for the first time // sharing file for the first time
link1, err := doPublicLink(context.Background(), file1.Path) link1, err := doPublicLink(ctx, file1.Path)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, "", link1, "Link should not be empty") require.NotEqual(t, "", link1, "Link should not be empty")
link2, err := doPublicLink(context.Background(), file2.Path) link2, err := doPublicLink(ctx, file2.Path)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, "", link2, "Link should not be empty") require.NotEqual(t, "", link2, "Link should not be empty")
require.NotEqual(t, link1, link2, "Links to different files should differ") require.NotEqual(t, link1, link2, "Links to different files should differ")
// sharing file for the 2nd time // sharing file for the 2nd time
link1, err = doPublicLink(context.Background(), file1.Path) link1, err = doPublicLink(ctx, file1.Path)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, "", link1, "Link should not be empty") require.NotEqual(t, "", link1, "Link should not be empty")
// sharing directory for the first time // sharing directory for the first time
path := path.Dir(file2.Path) path := path.Dir(file2.Path)
link3, err := doPublicLink(context.Background(), path) link3, err := doPublicLink(ctx, path)
if err != nil && errors.Cause(err) == fs.ErrorCantShareDirectories { if err != nil && errors.Cause(err) == fs.ErrorCantShareDirectories {
t.Log("skipping directory tests as not supported on this backend") t.Log("skipping directory tests as not supported on this backend")
} else { } else {
@ -1415,7 +1468,7 @@ func Run(t *testing.T, opt *Opt) {
require.NotEqual(t, "", link3, "Link should not be empty") require.NotEqual(t, "", link3, "Link should not be empty")
// sharing directory for the second time // sharing directory for the second time
link3, err = doPublicLink(context.Background(), path) link3, err = doPublicLink(ctx, path)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, "", link3, "Link should not be empty") require.NotEqual(t, "", link3, "Link should not be empty")
@ -1426,10 +1479,10 @@ func Run(t *testing.T, opt *Opt) {
// ensure sub remote isn't empty // ensure sub remote isn't empty
buf := bytes.NewBufferString("somecontent") buf := bytes.NewBufferString("somecontent")
obji := object.NewStaticObjectInfo("somefile", time.Now(), int64(buf.Len()), true, nil, nil) obji := object.NewStaticObjectInfo("somefile", time.Now(), int64(buf.Len()), true, nil, nil)
_, err = subRemote.Put(context.Background(), buf, obji) _, err = subRemote.Put(ctx, buf, obji)
require.NoError(t, err) require.NoError(t, err)
link4, err := subRemote.Features().PublicLink(context.Background(), "") link4, err := subRemote.Features().PublicLink(ctx, "")
require.NoError(t, err, "Sharing root in a sub-remote should work") require.NoError(t, err, "Sharing root in a sub-remote should work")
require.NotEqual(t, "", link4, "Link should not be empty") require.NotEqual(t, "", link4, "Link should not be empty")
} }
@ -1438,7 +1491,7 @@ func Run(t *testing.T, opt *Opt) {
// TestSetTier tests SetTier and GetTier functionality // TestSetTier tests SetTier and GetTier functionality
t.Run("SetTier", func(t *testing.T) { t.Run("SetTier", func(t *testing.T) {
skipIfNotSetTier(t) skipIfNotSetTier(t)
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
setter, ok := obj.(fs.SetTierer) setter, ok := obj.(fs.SetTierer)
assert.NotNil(t, ok) assert.NotNil(t, ok)
getter, ok := obj.(fs.GetTierer) getter, ok := obj.(fs.GetTierer)
@ -1466,7 +1519,7 @@ func Run(t *testing.T, opt *Opt) {
if ft.UnWrap == nil { if ft.UnWrap == nil {
t.Skip("Not a wrapping Fs") t.Skip("Not a wrapping Fs")
} }
obj := findObject(t, remote, file1.Path) obj := findObject(ctx, t, remote, file1.Path)
_, unsupported := fs.ObjectOptionalInterfaces(obj) _, unsupported := fs.ObjectOptionalInterfaces(obj)
for _, name := range unsupported { for _, name := range unsupported {
if !stringsContains(name, opt.UnimplementableObjectMethods) { if !stringsContains(name, opt.UnimplementableObjectMethods) {
@ -1478,8 +1531,9 @@ func Run(t *testing.T, opt *Opt) {
// TestObjectRemove tests Remove // TestObjectRemove tests Remove
t.Run("ObjectRemove", func(t *testing.T) { t.Run("ObjectRemove", func(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
obj := findObject(t, remote, file1.Path) // remove file1
err := obj.Remove(context.Background()) obj := findObject(ctx, t, remote, file1.Path)
err := obj.Remove(ctx)
require.NoError(t, err) require.NoError(t, err)
// check listing without modtime as TestPublicLink may change the modtime // check listing without modtime as TestPublicLink may change the modtime
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file2}, nil, fs.ModTimeNotSupported) fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file2}, nil, fs.ModTimeNotSupported)
@ -1502,6 +1556,8 @@ func Run(t *testing.T, opt *Opt) {
assert.NotEqual(t, int64(0), usage.Total) assert.NotEqual(t, int64(0), usage.Total)
}) })
// Just file2 remains for Purge to clean up
// TestFsPutStream tests uploading files when size isn't known in advance. // TestFsPutStream tests uploading files when size isn't known in advance.
// This may trigger large buffer allocation in some backends, keep it // This may trigger large buffer allocation in some backends, keep it
// close to the end of suite. (See fs/operations/xtra_operations_test.go) // close to the end of suite. (See fs/operations/xtra_operations_test.go)
@ -1531,15 +1587,16 @@ func Run(t *testing.T, opt *Opt) {
file.Size = -1 file.Size = -1
obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil) obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
obj, err = remote.Features().PutStream(context.Background(), in, obji) obj, err = remote.Features().PutStream(ctx, in, obji)
return err return err
}) })
file.Hashes = uploadHash.Sums() file.Hashes = uploadHash.Sums()
file.Size = int64(contentSize) // use correct size when checking file.Size = int64(contentSize) // use correct size when checking
file.Check(t, obj, remote.Precision()) file.Check(t, obj, remote.Precision())
// Re-read the object and check again // Re-read the object and check again
obj = findObject(t, remote, file.Path) obj = findObject(ctx, t, remote, file.Path)
file.Check(t, obj, remote.Precision()) file.Check(t, obj, remote.Precision())
require.NoError(t, obj.Remove(ctx))
}) })
// TestInternal calls InternalTest() on the Fs // TestInternal calls InternalTest() on the Fs
@ -1652,7 +1709,7 @@ func Run(t *testing.T, opt *Opt) {
for _, fileSize := range testChunks { for _, fileSize := range testChunks {
t.Run(fmt.Sprintf("%d", fileSize), func(t *testing.T) { t.Run(fmt.Sprintf("%d", fileSize), func(t *testing.T) {
TestPutLarge(t, remote, &fstest.Item{ TestPutLarge(ctx, t, remote, &fstest.Item{
ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"), ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
Path: fmt.Sprintf("chunked-%s-%s.bin", cs.String(), fileSize.String()), Path: fmt.Sprintf("chunked-%s-%s.bin", cs.String(), fileSize.String()),
Size: int64(fileSize), Size: int64(fileSize),
@ -1680,9 +1737,9 @@ func Run(t *testing.T, opt *Opt) {
in := bytes.NewBufferString(contents) in := bytes.NewBufferString(contents)
obji := object.NewStaticObjectInfo("unknown-size-put.txt", fstest.Time("2002-02-03T04:05:06.499999999Z"), -1, true, nil, nil) obji := object.NewStaticObjectInfo("unknown-size-put.txt", fstest.Time("2002-02-03T04:05:06.499999999Z"), -1, true, nil, nil)
obj, err := remote.Put(context.Background(), in, obji) obj, err := remote.Put(ctx, in, obji)
if err == nil { if err == nil {
require.NoError(t, obj.Remove(context.Background()), "successfully uploaded unknown-sized file but failed to remove") require.NoError(t, obj.Remove(ctx), "successfully uploaded unknown-sized file but failed to remove")
} }
// if err != nil: it's okay as long as no panic // if err != nil: it's okay as long as no panic
}) })
@ -1693,7 +1750,7 @@ func Run(t *testing.T, opt *Opt) {
Path: "unknown-size-update.txt", Path: "unknown-size-update.txt",
} }
testPut(t, remote, &unknownSizeUpdateFile) testPut(ctx, t, remote, &unknownSizeUpdateFile)
defer func() { defer func() {
assert.Nil(t, recover(), "Object.Update() should not panic when src.Size() == -1") assert.Nil(t, recover(), "Object.Update() should not panic when src.Size() == -1")
@ -1702,11 +1759,11 @@ func Run(t *testing.T, opt *Opt) {
newContents := random.String(200) newContents := random.String(200)
in := bytes.NewBufferString(newContents) in := bytes.NewBufferString(newContents)
obj := findObject(t, remote, unknownSizeUpdateFile.Path) obj := findObject(ctx, t, remote, unknownSizeUpdateFile.Path)
obji := object.NewStaticObjectInfo(unknownSizeUpdateFile.Path, unknownSizeUpdateFile.ModTime, -1, true, nil, obj.Fs()) obji := object.NewStaticObjectInfo(unknownSizeUpdateFile.Path, unknownSizeUpdateFile.ModTime, -1, true, nil, obj.Fs())
err := obj.Update(context.Background(), in, obji) err := obj.Update(ctx, in, obji)
if err == nil { if err == nil {
require.NoError(t, obj.Remove(context.Background()), "successfully updated object with unknown-sized source but failed to remove") require.NoError(t, obj.Remove(ctx), "successfully updated object with unknown-sized source but failed to remove")
} }
// if err != nil: it's okay as long as no panic // if err != nil: it's okay as long as no panic
}) })
@ -1728,21 +1785,21 @@ func Run(t *testing.T, opt *Opt) {
colonIndex := strings.IndexRune(deepRemoteName, ':') colonIndex := strings.IndexRune(deepRemoteName, ':')
firstSlashIndex := strings.IndexRune(deepRemoteName, '/') firstSlashIndex := strings.IndexRune(deepRemoteName, '/')
firstDir := deepRemoteName[colonIndex+1 : firstSlashIndex] firstDir := deepRemoteName[colonIndex+1 : firstSlashIndex]
_, err = deepRemote.NewObject(context.Background(), firstDir) _, err = deepRemote.NewObject(ctx, firstDir)
require.Equal(t, fs.ErrorObjectNotFound, err) require.Equal(t, fs.ErrorObjectNotFound, err)
// If err is not fs.ErrorObjectNotFound, it means the backend is // If err is not fs.ErrorObjectNotFound, it means the backend is
// somehow confused about root and absolute root. // somehow confused about root and absolute root.
}) })
// Purge the folder // Purge the folder
err = operations.Purge(context.Background(), remote, "") err = operations.Purge(ctx, remote, "")
require.NoError(t, err) require.NoError(t, err)
purged = true purged = true
fstest.CheckListing(t, remote, []fstest.Item{}) fstest.CheckListing(t, remote, []fstest.Item{})
// Check purging again if not bucket based // Check purging again if not bucket based
if !isBucketBasedButNotRoot(remote) { if !isBucketBasedButNotRoot(remote) {
err = operations.Purge(context.Background(), remote, "") err = operations.Purge(ctx, remote, "")
assert.Error(t, err, "Expecting error after on second purge") assert.Error(t, err, "Expecting error after on second purge")
} }
@ -1750,7 +1807,7 @@ func Run(t *testing.T, opt *Opt) {
// Check directory is purged // Check directory is purged
if !purged { if !purged {
_ = operations.Purge(context.Background(), remote, "") _ = operations.Purge(ctx, remote, "")
} }
// Remove the local directory so we don't clutter up /tmp // Remove the local directory so we don't clutter up /tmp

View File

@ -31,6 +31,8 @@ const (
QuoteRune = '' // SINGLE HIGH-REVERSED-9 QUOTATION MARK QuoteRune = '' // SINGLE HIGH-REVERSED-9 QUOTATION MARK
) )
// NB keep the tests in fstests/fstests/fstests.go FsEncoding up to date with this
// Possible flags for the MultiEncoder // Possible flags for the MultiEncoder
const ( const (
EncodeZero uint = 0 // NUL(0x00) EncodeZero uint = 0 // NUL(0x00)