Add more tests for List() and fix resulting problems
This commit is contained in:
parent
68ec6a9f5b
commit
c2d0e86431
19 changed files with 369 additions and 137 deletions
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
53
b2/b2.go
53
b2/b2.go
|
@ -329,6 +329,34 @@ func (f *Fs) NewFsObject(remote string) fs.Object {
|
|||
return f.newFsObjectWithInfo(remote, nil)
|
||||
}
|
||||
|
||||
// sendDir works out given a lastDir and a remote which directories should be sent
|
||||
func sendDir(lastDir string, remote string, level int) (dirNames []string, newLastDir string) {
|
||||
dir := path.Dir(remote)
|
||||
if dir == "." {
|
||||
// No slashes - nothing to do!
|
||||
return nil, lastDir
|
||||
}
|
||||
if dir == lastDir {
|
||||
// Still in same directory
|
||||
return nil, lastDir
|
||||
}
|
||||
newLastDir = lastDir
|
||||
for {
|
||||
slashes := strings.Count(dir, "/")
|
||||
if !strings.HasPrefix(lastDir, dir) && slashes < level {
|
||||
dirNames = append([]string{dir}, dirNames...)
|
||||
}
|
||||
if newLastDir == lastDir {
|
||||
newLastDir = dir
|
||||
}
|
||||
dir = path.Dir(dir)
|
||||
if dir == "." {
|
||||
break
|
||||
}
|
||||
}
|
||||
return dirNames, newLastDir
|
||||
}
|
||||
|
||||
// listFn is called from list to handle an object
|
||||
type listFn func(remote string, object *api.File, isDirectory bool) error
|
||||
|
||||
|
@ -377,7 +405,7 @@ func (f *Fs) list(dir string, level int, prefix string, limit int, hidden bool,
|
|||
if hidden {
|
||||
opts.Path = "/b2_list_file_versions"
|
||||
}
|
||||
lastDir := ""
|
||||
lastDir := dir
|
||||
for {
|
||||
err := f.pacer.Call(func() (bool, error) {
|
||||
resp, err := f.srv.CallJSON(&opts, &request, &response)
|
||||
|
@ -395,24 +423,21 @@ func (f *Fs) list(dir string, level int, prefix string, limit int, hidden bool,
|
|||
remote := file.Name[len(f.root):]
|
||||
slashes := strings.Count(remote, "/")
|
||||
|
||||
// Check if this file makes a new directory
|
||||
if slash := strings.IndexRune(remote, '/'); slash >= 0 {
|
||||
if dir := remote[:slash]; dir != lastDir {
|
||||
if slashes-1 < fs.MaxLevel {
|
||||
err = fn(dir, nil, true)
|
||||
if err != nil {
|
||||
if err == errEndList {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Check if this file makes a new directories
|
||||
var dirNames []string
|
||||
dirNames, lastDir = sendDir(lastDir, remote, level)
|
||||
for _, dirName := range dirNames {
|
||||
err = fn(dirName, nil, true)
|
||||
if err != nil {
|
||||
if err == errEndList {
|
||||
return nil
|
||||
}
|
||||
lastDir = dir
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Send the file
|
||||
if slashes < fs.MaxLevel {
|
||||
if slashes < level {
|
||||
err = fn(remote, file, false)
|
||||
if err != nil {
|
||||
if err == errEndList {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ncw/rclone/fstest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test b2 string encoding
|
||||
|
@ -168,3 +169,116 @@ func TestParseTimeString(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSendDir(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
lastDir string
|
||||
remote string
|
||||
level int
|
||||
dirNames []string
|
||||
newLastDir string
|
||||
}{
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "test.txt",
|
||||
level: 100,
|
||||
dirNames: nil,
|
||||
newLastDir: "",
|
||||
},
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "potato/test.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"potato"},
|
||||
newLastDir: "potato",
|
||||
},
|
||||
{
|
||||
lastDir: "potato",
|
||||
remote: "potato/test.txt",
|
||||
level: 100,
|
||||
dirNames: nil,
|
||||
newLastDir: "potato",
|
||||
},
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "potato/sausage/test.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"potato", "potato/sausage"},
|
||||
newLastDir: "potato/sausage",
|
||||
},
|
||||
{
|
||||
lastDir: "potato",
|
||||
remote: "potato/sausage/test.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"potato/sausage"},
|
||||
newLastDir: "potato/sausage",
|
||||
},
|
||||
{
|
||||
lastDir: "potato/sausage",
|
||||
remote: "potato/sausage/test.txt",
|
||||
level: 100,
|
||||
dirNames: nil,
|
||||
newLastDir: "potato/sausage",
|
||||
},
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"a", "a/b", "a/b/c", "a/b/c/d", "a/b/c/d/e"},
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
{
|
||||
lastDir: "a/b/c/d/e",
|
||||
remote: "a/b/c/d/E/f.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"a/b/c/d/E"},
|
||||
newLastDir: "a/b/c/d/E",
|
||||
},
|
||||
{
|
||||
lastDir: "a/b/c/d/e",
|
||||
remote: "a/b/C/D/E/f.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"a/b/C", "a/b/C/D", "a/b/C/D/E"},
|
||||
newLastDir: "a/b/C/D/E",
|
||||
},
|
||||
{
|
||||
lastDir: "a/b/c",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 100,
|
||||
dirNames: []string{"a/b/c/d", "a/b/c/d/e"},
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 1,
|
||||
dirNames: []string{"a"},
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
{
|
||||
lastDir: "a/b/c",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 1,
|
||||
dirNames: nil,
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
{
|
||||
lastDir: "",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 3,
|
||||
dirNames: []string{"a", "a/b", "a/b/c"},
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
{
|
||||
lastDir: "a/b/C/D/E",
|
||||
remote: "a/b/c/d/e/f.txt",
|
||||
level: 3,
|
||||
dirNames: []string{"a/b/c"},
|
||||
newLastDir: "a/b/c/d/e",
|
||||
},
|
||||
} {
|
||||
dirNames, newLastDir := sendDir(test.lastDir, test.remote, test.level)
|
||||
assert.Equal(t, test.dirNames, dirNames, "dirNames fail for sendDir(%q,%q,%v)", test.lastDir, test.remote, test.level)
|
||||
assert.Equal(t, test.newLastDir, newLastDir, "newLastDir fail for sendDir(%q,%q,%v)", test.lastDir, test.remote, test.level)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -462,7 +462,7 @@ func (f *Fs) ListDir(out fs.ListOpts, job dircache.ListDirJob) (jobs []dircache.
|
|||
case item.MimeType == driveFolderType:
|
||||
if out.IncludeDirectory(remote) {
|
||||
dir := &fs.Dir{
|
||||
Name: item.Title,
|
||||
Name: remote,
|
||||
Bytes: -1,
|
||||
Count: -1,
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -226,12 +226,25 @@ func (f *Fs) NewFsObject(remote string) fs.Object {
|
|||
}
|
||||
|
||||
// Strips the root off path and returns it
|
||||
func (f *Fs) stripRoot(path string) (string, error) {
|
||||
lowercase := strings.ToLower(path)
|
||||
if !strings.HasPrefix(lowercase, f.slashRootSlash) {
|
||||
return "", fmt.Errorf("Path %q is not under root %q", path, f.slashRootSlash)
|
||||
func strip(path, root string) (string, error) {
|
||||
if len(root) > 0 {
|
||||
if root[0] != '/' {
|
||||
root = "/" + root
|
||||
}
|
||||
if root[len(root)-1] != '/' {
|
||||
root += "/"
|
||||
}
|
||||
}
|
||||
return path[len(f.slashRootSlash):], nil
|
||||
lowercase := strings.ToLower(path)
|
||||
if !strings.HasPrefix(lowercase, root) {
|
||||
return "", fmt.Errorf("Path %q is not under root %q", path, root)
|
||||
}
|
||||
return path[len(root):], nil
|
||||
}
|
||||
|
||||
// Strips the root off path and returns it
|
||||
func (f *Fs) stripRoot(path string) (string, error) {
|
||||
return strip(path, f.slashRootSlash)
|
||||
}
|
||||
|
||||
// Walk the root returning a channel of FsObjects
|
||||
|
@ -252,81 +265,80 @@ func (f *Fs) list(out fs.ListOpts, dir string) {
|
|||
for {
|
||||
deltaPage, err := f.db.Delta(cursor, root)
|
||||
if err != nil {
|
||||
out.SetError(fmt.Errorf("Couldn't list: %s", err))
|
||||
return
|
||||
}
|
||||
if deltaPage.Reset && cursor != "" {
|
||||
fs.ErrorLog(f, "Unexpected reset during listing - try again")
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(f, "Couldn't list: %s", err)
|
||||
break
|
||||
} else {
|
||||
if deltaPage.Reset && cursor != "" {
|
||||
fs.ErrorLog(f, "Unexpected reset during listing - try again")
|
||||
fs.Stats.Error()
|
||||
break
|
||||
}
|
||||
fs.Debug(f, "%d delta entries received", len(deltaPage.Entries))
|
||||
for i := range deltaPage.Entries {
|
||||
deltaEntry := &deltaPage.Entries[i]
|
||||
entry := deltaEntry.Entry
|
||||
if entry == nil {
|
||||
// This notifies of a deleted object
|
||||
}
|
||||
fs.Debug(f, "%d delta entries received", len(deltaPage.Entries))
|
||||
for i := range deltaPage.Entries {
|
||||
deltaEntry := &deltaPage.Entries[i]
|
||||
entry := deltaEntry.Entry
|
||||
if entry == nil {
|
||||
// This notifies of a deleted object
|
||||
} else {
|
||||
if len(entry.Path) <= 1 || entry.Path[0] != '/' {
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(f, "dropbox API inconsistency: a path should always start with a slash and be at least 2 characters: %s", entry.Path)
|
||||
continue
|
||||
}
|
||||
|
||||
lastSlashIndex := strings.LastIndex(entry.Path, "/")
|
||||
|
||||
var parentPath string
|
||||
if lastSlashIndex == 0 {
|
||||
parentPath = ""
|
||||
} else {
|
||||
if len(entry.Path) <= 1 || entry.Path[0] != '/' {
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(f, "dropbox API inconsistency: a path should always start with a slash and be at least 2 characters: %s", entry.Path)
|
||||
continue
|
||||
parentPath = entry.Path[1:lastSlashIndex]
|
||||
}
|
||||
lastComponent := entry.Path[lastSlashIndex+1:]
|
||||
|
||||
if entry.IsDir {
|
||||
nameTree.PutCaseCorrectDirectoryName(parentPath, lastComponent)
|
||||
name, err := f.stripRoot(entry.Path + "/")
|
||||
if err != nil {
|
||||
out.SetError(err)
|
||||
return
|
||||
}
|
||||
|
||||
lastSlashIndex := strings.LastIndex(entry.Path, "/")
|
||||
|
||||
var parentPath string
|
||||
if lastSlashIndex == 0 {
|
||||
parentPath = ""
|
||||
} else {
|
||||
parentPath = entry.Path[1:lastSlashIndex]
|
||||
name = strings.Trim(name, "/")
|
||||
if name != "" && name != dir {
|
||||
dir := &fs.Dir{
|
||||
Name: name,
|
||||
When: time.Time(entry.ClientMtime),
|
||||
Bytes: entry.Bytes,
|
||||
Count: -1,
|
||||
}
|
||||
if out.AddDir(dir) {
|
||||
return
|
||||
}
|
||||
}
|
||||
lastComponent := entry.Path[lastSlashIndex+1:]
|
||||
|
||||
if entry.IsDir {
|
||||
nameTree.PutCaseCorrectDirectoryName(parentPath, lastComponent)
|
||||
name, err := f.stripRoot(entry.Path + "/")
|
||||
} else {
|
||||
parentPathCorrectCase := nameTree.GetPathWithCorrectCase(parentPath)
|
||||
if parentPathCorrectCase != nil {
|
||||
path, err := f.stripRoot(*parentPathCorrectCase + "/" + lastComponent)
|
||||
if err != nil {
|
||||
out.SetError(err)
|
||||
return
|
||||
}
|
||||
name = strings.Trim(name, "/")
|
||||
if name != "" {
|
||||
dir := &fs.Dir{
|
||||
Name: name,
|
||||
When: time.Time(entry.ClientMtime),
|
||||
Bytes: entry.Bytes,
|
||||
Count: -1,
|
||||
}
|
||||
if out.AddDir(dir) {
|
||||
if o := f.newFsObjectWithInfo(path, entry); o != nil {
|
||||
if out.Add(o) {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parentPathCorrectCase := nameTree.GetPathWithCorrectCase(parentPath)
|
||||
if parentPathCorrectCase != nil {
|
||||
path, err := f.stripRoot(*parentPathCorrectCase + "/" + lastComponent)
|
||||
if err != nil {
|
||||
out.SetError(err)
|
||||
return
|
||||
}
|
||||
if o := f.newFsObjectWithInfo(path, entry); o != nil {
|
||||
if out.Add(o) {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nameTree.PutFile(parentPath, lastComponent, entry)
|
||||
}
|
||||
nameTree.PutFile(parentPath, lastComponent, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !deltaPage.HasMore {
|
||||
break
|
||||
}
|
||||
cursor = deltaPage.Cursor.Cursor
|
||||
}
|
||||
if !deltaPage.HasMore {
|
||||
break
|
||||
}
|
||||
cursor = deltaPage.Cursor.Cursor
|
||||
|
||||
}
|
||||
|
||||
walkFunc := func(caseCorrectFilePath string, entry *dropbox.Entry) error {
|
||||
|
@ -347,41 +359,56 @@ func (f *Fs) list(out fs.ListOpts, dir string) {
|
|||
}
|
||||
}
|
||||
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *Fs) List(out fs.ListOpts, dir string) {
|
||||
defer out.Finished()
|
||||
f.list(out, dir)
|
||||
}
|
||||
|
||||
// ListDir walks the path returning a channel of FsObjects
|
||||
func (f *Fs) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
defer close(out)
|
||||
entry, err := f.db.Metadata(f.root, true, false, "", "", metadataLimit)
|
||||
// listOneLevel walks the path one level deep
|
||||
func (f *Fs) listOneLevel(out fs.ListOpts, dir string) {
|
||||
root := f.root
|
||||
if dir != "" {
|
||||
root += "/" + dir
|
||||
}
|
||||
entry, err := f.db.Metadata(root, true, false, "", "", metadataLimit)
|
||||
if err != nil {
|
||||
out.SetError(fmt.Errorf("Couldn't list single level: %s", err))
|
||||
return
|
||||
}
|
||||
for i := range entry.Contents {
|
||||
entry := &entry.Contents[i]
|
||||
remote, err := strip(entry.Path, root)
|
||||
if err != nil {
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(f, "Couldn't list directories in root: %s", err)
|
||||
out.SetError(err)
|
||||
return
|
||||
}
|
||||
if entry.IsDir {
|
||||
dir := &fs.Dir{
|
||||
Name: remote,
|
||||
When: time.Time(entry.ClientMtime),
|
||||
Bytes: entry.Bytes,
|
||||
Count: -1,
|
||||
}
|
||||
if out.AddDir(dir) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for i := range entry.Contents {
|
||||
entry := &entry.Contents[i]
|
||||
if entry.IsDir {
|
||||
name, err := f.stripRoot(entry.Path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out <- &fs.Dir{
|
||||
Name: name,
|
||||
When: time.Time(entry.ClientMtime),
|
||||
Bytes: entry.Bytes,
|
||||
Count: -1,
|
||||
}
|
||||
if o := f.newFsObjectWithInfo(remote, entry); o != nil {
|
||||
if out.Add(o) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *Fs) List(out fs.ListOpts, dir string) {
|
||||
defer out.Finished()
|
||||
level := out.Level()
|
||||
switch level {
|
||||
case 1:
|
||||
f.listOneLevel(out, dir)
|
||||
case fs.MaxLevel:
|
||||
f.list(out, dir)
|
||||
default:
|
||||
out.SetError(fs.ErrorLevelNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
// A read closer which doesn't close the input
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
17
fs/fs.go
17
fs/fs.go
|
@ -457,6 +457,23 @@ func (o *Lister) Get() (Object, *Dir, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get all the objects and dirs from the listing.
|
||||
func (o *Lister) GetAll() (objs []Object, dirs []*Dir, err error) {
|
||||
for {
|
||||
obj, dir, err := o.Get()
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, nil, err
|
||||
case obj != nil:
|
||||
objs = append(objs, obj)
|
||||
case dir != nil:
|
||||
dirs = append(dirs, dir)
|
||||
default:
|
||||
return objs, dirs, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetObject will return an object from the listing.
|
||||
// It will skip over any directories.
|
||||
// Will return (nil, nil) when all objects have been returned.
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -129,14 +130,42 @@ func TestFsListEmpty(t *testing.T) {
|
|||
fstest.CheckListing(t, remote, []fstest.Item{})
|
||||
}
|
||||
|
||||
// winPath converts a path into a windows safe path
|
||||
func winPath(s string) string {
|
||||
s = strings.Replace(s, "?", "_", -1)
|
||||
s = strings.Replace(s, `"`, "_", -1)
|
||||
s = strings.Replace(s, "<", "_", -1)
|
||||
s = strings.Replace(s, ">", "_", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
// dirsToNames returns a sorted list of names
|
||||
func dirsToNames(dirs []*fs.Dir) []string {
|
||||
names := []string{}
|
||||
for _, dir := range dirs {
|
||||
names = append(names, winPath(dir.Name))
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// objsToNames returns a sorted list of object names
|
||||
func objsToNames(objs []fs.Object) []string {
|
||||
names := []string{}
|
||||
for _, obj := range objs {
|
||||
names = append(names, winPath(obj.Remote()))
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// TestFsListDirEmpty tests listing the directories from an empty directory
|
||||
func TestFsListDirEmpty(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetDirs()
|
||||
objs, dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetAll()
|
||||
require.NoError(t, err)
|
||||
for _, dir := range dirs {
|
||||
t.Errorf("Found unexpected item %q", dir.Name)
|
||||
}
|
||||
assert.Equal(t, []string{}, objsToNames(objs))
|
||||
assert.Equal(t, []string{}, dirsToNames(dirs))
|
||||
}
|
||||
|
||||
// TestFsNewFsObjectNotFound tests not finding a object
|
||||
|
@ -164,14 +193,24 @@ func findObject(t *testing.T, Name string) fs.Object {
|
|||
}
|
||||
|
||||
func testPut(t *testing.T, file *fstest.Item) {
|
||||
again:
|
||||
buf := bytes.NewBufferString(fstest.RandomString(100))
|
||||
hash := fs.NewMultiHasher()
|
||||
in := io.TeeReader(buf, hash)
|
||||
|
||||
tries := 1
|
||||
const maxTries = 10
|
||||
file.Size = int64(buf.Len())
|
||||
obji := fs.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
|
||||
obj, err := remote.Put(in, obji)
|
||||
if err != nil {
|
||||
// Retry if err returned a retry error
|
||||
if r, ok := err.(fs.Retry); ok && r.Retry() && tries < maxTries {
|
||||
t.Logf("Put error: %v - low level retry %d/%d", err, tries, maxTries)
|
||||
|
||||
tries++
|
||||
goto again
|
||||
}
|
||||
t.Fatal("Put error", err)
|
||||
}
|
||||
file.Hashes = hash.Sums()
|
||||
|
@ -196,26 +235,20 @@ func TestFsPutFile2(t *testing.T) {
|
|||
// TestFsListDirFile2 tests the files are correctly uploaded
|
||||
func TestFsListDirFile2(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
found := false
|
||||
var objNames, dirNames []string
|
||||
for i := 1; i <= eventualConsistencyRetries; i++ {
|
||||
dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetDirs()
|
||||
objs, dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetAll()
|
||||
require.NoError(t, err)
|
||||
for _, dir := range dirs {
|
||||
if dir.Name != `hello? sausage` && dir.Name != `hello_ sausage` {
|
||||
t.Errorf("Found unexpected item %q", dir.Name)
|
||||
} else {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if found {
|
||||
objNames = objsToNames(objs)
|
||||
dirNames = dirsToNames(dirs)
|
||||
if len(objNames) >= 1 && len(dirNames) >= 1 {
|
||||
break
|
||||
}
|
||||
t.Logf("Sleeping for 1 second for TestFsListDirFile2 eventual consistency: %d/%d", i, eventualConsistencyRetries)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Didn't find %q", `hello? sausage`)
|
||||
}
|
||||
assert.Equal(t, []string{`hello_ sausage`}, dirNames)
|
||||
assert.Equal(t, []string{file1.Path}, objNames)
|
||||
}
|
||||
|
||||
// TestFsListDirRoot tests that DirList works in the root
|
||||
|
@ -225,17 +258,9 @@ func TestFsListDirRoot(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to make remote %q: %v", RemoteName, err)
|
||||
}
|
||||
found := false
|
||||
dirs, err := fs.NewLister().SetLevel(1).Start(rootRemote, "").GetDirs()
|
||||
require.NoError(t, err)
|
||||
for _, dir := range dirs {
|
||||
if dir.Name == subRemoteLeaf {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Didn't find %q", subRemoteLeaf)
|
||||
}
|
||||
assert.Contains(t, dirsToNames(dirs), subRemoteLeaf, "Remote leaf not found")
|
||||
}
|
||||
|
||||
// TestFsListSubdir tests List works for a subdirectory
|
||||
|
@ -244,18 +269,31 @@ func TestFsListSubdir(t *testing.T) {
|
|||
test := func(fileName string) bool {
|
||||
dir, _ := path.Split(fileName)
|
||||
dir = dir[:len(dir)-1]
|
||||
objs, err := fs.NewLister().Start(remote, dir).GetObjects()
|
||||
objs, dirs, err := fs.NewLister().Start(remote, dir).GetAll()
|
||||
if err == fs.ErrorDirNotFound {
|
||||
return false
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objs, 1)
|
||||
assert.Equal(t, fileName, objs[0].Remote())
|
||||
require.Len(t, dirs, 0)
|
||||
return true
|
||||
}
|
||||
assert.True(t, test(file2.Path) || test(file2.WinPath), "normal and alternative lists failed")
|
||||
}
|
||||
|
||||
// TestFsListLevel2 tests List works for 2 levels
|
||||
func TestFsListLevel2(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
objs, dirs, err := fs.NewLister().SetLevel(2).Start(remote, "").GetAll()
|
||||
if err == fs.ErrorLevelNotSupported {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{file1.Path}, objsToNames(objs))
|
||||
assert.Equal(t, []string{`hello_ sausage`, `hello_ sausage/êé`}, dirsToNames(dirs))
|
||||
}
|
||||
|
||||
// TestFsListFile1 tests file present
|
||||
func TestFsListFile1(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
|
|
|
@ -305,6 +305,7 @@ type listFn func(remote string, object *storage.Object, isDirectory bool) error
|
|||
// If directories is set it only sends directories
|
||||
func (f *Fs) list(dir string, level int, fn listFn) error {
|
||||
root := f.root
|
||||
rootLength := len(root)
|
||||
if dir != "" {
|
||||
root += dir + "/"
|
||||
}
|
||||
|
@ -316,7 +317,6 @@ func (f *Fs) list(dir string, level int, fn listFn) error {
|
|||
default:
|
||||
return fs.ErrorLevelNotSupported
|
||||
}
|
||||
rootLength := len(root)
|
||||
for {
|
||||
objects, err := list.Do()
|
||||
if err != nil {
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -225,7 +225,7 @@ func (f *Fs) List(out fs.ListOpts, dir string) {
|
|||
|
||||
// Start the process
|
||||
traversing.Add(1)
|
||||
in <- listArgs{remote: "", dirpath: root, level: out.Level() - 1}
|
||||
in <- listArgs{remote: dir, dirpath: root, level: out.Level() - 1}
|
||||
for i := 0; i < fs.Config.Checkers; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) }
|
|||
func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) }
|
||||
func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) }
|
||||
func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) }
|
||||
func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) }
|
||||
func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) }
|
||||
func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) }
|
||||
func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }
|
||||
|
|
Loading…
Reference in a new issue