sync: fix creation of empty directories when --create-empty-src-dirs=false
In v1.66.0 the changes to enable metadata preservation on directories introduced a regression, namely that empty directories were created despite the state of the --create-empty-src-dirs flag. This patch fixes the problem by letting the normal rclone directory creation create the directories and fixing up their timestamps and metadata afterwards if --create-empty-src-dirs=false. Fixes #7689 See: https://forum.rclone.org/t/empty-dirs-not-wanted/45059/ See: https://forum.rclone.org/t/how-to-ignore-empty-directories-when-uploading-from-windows/45057/
This commit is contained in:
parent
2a2ec06ec1
commit
10eb4742dd
4 changed files with 162 additions and 22 deletions
|
@ -108,6 +108,8 @@ var logReplacements = []string{
|
||||||
`^(INFO : .*?: (Made directory with|Set directory) (metadata|modification time)).*$`, dropMe,
|
`^(INFO : .*?: (Made directory with|Set directory) (metadata|modification time)).*$`, dropMe,
|
||||||
// ignore sizes in directory time updates
|
// ignore sizes in directory time updates
|
||||||
`^(NOTICE: .*?: Skipped set directory modification time as --dry-run is set).*$`, dropMe,
|
`^(NOTICE: .*?: Skipped set directory modification time as --dry-run is set).*$`, dropMe,
|
||||||
|
// ignore sizes in directory metadata updates
|
||||||
|
`^(NOTICE: .*?: Skipped update directory metadata as --dry-run is set).*$`, dropMe,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some dry-run messages differ depending on the particular remote.
|
// Some dry-run messages differ depending on the particular remote.
|
||||||
|
|
|
@ -98,6 +98,7 @@ type syncCopyMove struct {
|
||||||
|
|
||||||
// For keeping track of delayed modtime sets
|
// For keeping track of delayed modtime sets
|
||||||
type setDirModTime struct {
|
type setDirModTime struct {
|
||||||
|
src fs.Directory // if set the metadata should be set too
|
||||||
dst fs.Directory
|
dst fs.Directory
|
||||||
dir string
|
dir string
|
||||||
modTime time.Time
|
modTime time.Time
|
||||||
|
@ -157,7 +158,7 @@ func newSyncCopyMove(ctx context.Context, fdst, fsrc fs.Fs, deleteMode fs.Delete
|
||||||
checkFirst: ci.CheckFirst,
|
checkFirst: ci.CheckFirst,
|
||||||
setDirMetadata: ci.Metadata && fsrc.Features().ReadDirMetadata && fdst.Features().WriteDirMetadata,
|
setDirMetadata: ci.Metadata && fsrc.Features().ReadDirMetadata && fdst.Features().WriteDirMetadata,
|
||||||
setDirModTime: (!ci.NoUpdateDirModTime && fsrc.Features().CanHaveEmptyDirectories) && (fdst.Features().WriteDirSetModTime || fdst.Features().MkdirMetadata != nil || fdst.Features().DirSetModTime != nil),
|
setDirModTime: (!ci.NoUpdateDirModTime && fsrc.Features().CanHaveEmptyDirectories) && (fdst.Features().WriteDirSetModTime || fdst.Features().MkdirMetadata != nil || fdst.Features().DirSetModTime != nil),
|
||||||
setDirModTimeAfter: !ci.NoUpdateDirModTime && fsrc.Features().CanHaveEmptyDirectories && fdst.Features().DirModTimeUpdatesOnWrite,
|
setDirModTimeAfter: !ci.NoUpdateDirModTime && (!copyEmptySrcDirs || fsrc.Features().CanHaveEmptyDirectories && fdst.Features().DirModTimeUpdatesOnWrite),
|
||||||
modifiedDirs: make(map[string]struct{}),
|
modifiedDirs: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,9 +1129,10 @@ func (s *syncCopyMove) copyDirMetadata(ctx context.Context, f fs.Fs, dst fs.Dire
|
||||||
if !s.setDirModTimeAfter && equal {
|
if !s.setDirModTimeAfter && equal {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
setMeta := true
|
||||||
if s.setDirModTimeAfter && equal {
|
if s.setDirModTimeAfter && equal {
|
||||||
newDst = dst
|
newDst = dst
|
||||||
} else {
|
} else if s.copyEmptySrcDirs {
|
||||||
if s.setDirMetadata {
|
if s.setDirMetadata {
|
||||||
newDst, err = operations.CopyDirMetadata(ctx, f, dst, dir, src)
|
newDst, err = operations.CopyDirMetadata(ctx, f, dst, dir, src)
|
||||||
} else if s.setDirModTime {
|
} else if s.setDirModTime {
|
||||||
|
@ -1143,6 +1145,8 @@ func (s *syncCopyMove) copyDirMetadata(ctx context.Context, f fs.Fs, dst fs.Dire
|
||||||
// Create the directory if it doesn't exist
|
// Create the directory if it doesn't exist
|
||||||
err = operations.Mkdir(ctx, f, dir)
|
err = operations.Mkdir(ctx, f, dir)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setMeta = s.setDirMetadata
|
||||||
}
|
}
|
||||||
// If we need to set modtime after and we created a dir, then save it for later
|
// If we need to set modtime after and we created a dir, then save it for later
|
||||||
if s.setDirModTime && s.setDirModTimeAfter && err == nil {
|
if s.setDirModTime && s.setDirModTimeAfter && err == nil {
|
||||||
|
@ -1159,12 +1163,16 @@ func (s *syncCopyMove) copyDirMetadata(ctx context.Context, f fs.Fs, dst fs.Dire
|
||||||
if level > s.setDirModTimesMaxLevel {
|
if level > s.setDirModTimesMaxLevel {
|
||||||
s.setDirModTimesMaxLevel = level
|
s.setDirModTimesMaxLevel = level
|
||||||
}
|
}
|
||||||
s.setDirModTimes = append(s.setDirModTimes, setDirModTime{
|
set := setDirModTime{
|
||||||
dst: newDst,
|
dst: newDst,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
modTime: src.ModTime(ctx),
|
modTime: src.ModTime(ctx),
|
||||||
level: level,
|
level: level,
|
||||||
})
|
}
|
||||||
|
if setMeta {
|
||||||
|
set.src = src
|
||||||
|
}
|
||||||
|
s.setDirModTimes = append(s.setDirModTimes, set)
|
||||||
s.setDirModTimeMu.Unlock()
|
s.setDirModTimeMu.Unlock()
|
||||||
fs.Debugf(nil, "Added delayed dir = %q, newDst=%v", dir, newDst)
|
fs.Debugf(nil, "Added delayed dir = %q, newDst=%v", dir, newDst)
|
||||||
}
|
}
|
||||||
|
@ -1195,15 +1203,28 @@ func (s *syncCopyMove) setDelayedDirModTimes(ctx context.Context) error {
|
||||||
if gCtx.Err() != nil {
|
if gCtx.Err() != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
item := item
|
if item.src == nil {
|
||||||
if _, ok := s.modifiedDirs[item.dir]; !ok {
|
if _, ok := s.modifiedDirs[item.dir]; !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !s.copyEmptySrcDirs {
|
||||||
|
if _, isEmpty := s.srcEmptyDirs[item.dir]; isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item := item
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
_, err := operations.SetDirModTime(gCtx, s.fdst, item.dst, item.dir, item.modTime)
|
var err error
|
||||||
|
// if item.src is set must copy full metadata
|
||||||
|
if item.src != nil {
|
||||||
|
_, err = operations.CopyDirMetadata(gCtx, s.fdst, item.dst, item.dir, item.src)
|
||||||
|
} else {
|
||||||
|
_, err = operations.SetDirModTime(gCtx, s.fdst, item.dst, item.dir, item.modTime)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fs.CountError(err)
|
err = fs.CountError(err)
|
||||||
fs.Errorf(item.dir, "Failed to timestamp directory: %v", err)
|
fs.Errorf(item.dir, "Failed to update directory timestamp or metadata: %v", err)
|
||||||
errCount.Add(err)
|
errCount.Add(err)
|
||||||
}
|
}
|
||||||
return nil // don't return errors, just count them
|
return nil // don't return errors, just count them
|
||||||
|
|
|
@ -85,7 +85,7 @@ func TestCopy(t *testing.T) {
|
||||||
r.CheckDirectoryModTimes(t, "sub dir")
|
r.CheckDirectoryModTimes(t, "sub dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyMetadata(t *testing.T) {
|
func testCopyMetadata(t *testing.T, createEmptySrcDirs bool) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, ci := fs.AddConfig(ctx)
|
ctx, ci := fs.AddConfig(ctx)
|
||||||
ci.Metadata = true
|
ci.Metadata = true
|
||||||
|
@ -99,6 +99,7 @@ func TestCopyMetadata(t *testing.T) {
|
||||||
|
|
||||||
const content = "hello metadata world!"
|
const content = "hello metadata world!"
|
||||||
const dirPath = "metadata sub dir"
|
const dirPath = "metadata sub dir"
|
||||||
|
const emptyDirPath = "empty metadata sub dir"
|
||||||
const filePath = dirPath + "/hello metadata world"
|
const filePath = dirPath + "/hello metadata world"
|
||||||
|
|
||||||
fileMetadata := fs.Metadata{
|
fileMetadata := fs.Metadata{
|
||||||
|
@ -119,6 +120,10 @@ func TestCopyMetadata(t *testing.T) {
|
||||||
_, err := operations.MkdirMetadata(ctx, r.Flocal, dirPath, dirMetadata)
|
_, err := operations.MkdirMetadata(ctx, r.Flocal, dirPath, dirMetadata)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make the empty directory with metadata - may fall back to Mkdir
|
||||||
|
_, err = operations.MkdirMetadata(ctx, r.Flocal, emptyDirPath, dirMetadata)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Upload the file with metadata
|
// Upload the file with metadata
|
||||||
in := io.NopCloser(bytes.NewBufferString(content))
|
in := io.NopCloser(bytes.NewBufferString(content))
|
||||||
_, err = operations.Rcat(ctx, r.Flocal, filePath, in, t1, fileMetadata)
|
_, err = operations.Rcat(ctx, r.Flocal, filePath, in, t1, fileMetadata)
|
||||||
|
@ -132,7 +137,7 @@ func TestCopyMetadata(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = predictDstFromLogger(ctx)
|
ctx = predictDstFromLogger(ctx)
|
||||||
err = CopyDir(ctx, r.Fremote, r.Flocal, false)
|
err = CopyDir(ctx, r.Fremote, r.Flocal, createEmptySrcDirs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testLoggerVsLsf(ctx, r.Fremote, operations.GetLoggerOpt(ctx).JSON, t)
|
testLoggerVsLsf(ctx, r.Fremote, operations.GetLoggerOpt(ctx).JSON, t)
|
||||||
|
|
||||||
|
@ -149,6 +154,26 @@ func TestCopyMetadata(t *testing.T) {
|
||||||
if features.ReadDirMetadata {
|
if features.ReadDirMetadata {
|
||||||
fstest.CheckEntryMetadata(ctx, t, r.Fremote, fstest.NewDirectory(ctx, t, r.Fremote, dirPath), dirMetadata)
|
fstest.CheckEntryMetadata(ctx, t, r.Fremote, fstest.NewDirectory(ctx, t, r.Fremote, dirPath), dirMetadata)
|
||||||
}
|
}
|
||||||
|
if !createEmptySrcDirs {
|
||||||
|
// dir must not exist
|
||||||
|
_, err := fstest.NewDirectoryRetries(ctx, t, r.Fremote, emptyDirPath, 1)
|
||||||
|
assert.Error(t, err, "Not expecting to find empty directory")
|
||||||
|
assert.True(t, errors.Is(err, fs.ErrorDirNotFound), fmt.Sprintf("expecting wrapped %#v not: %#v", fs.ErrorDirNotFound, err))
|
||||||
|
} else {
|
||||||
|
// dir must exist
|
||||||
|
dir := fstest.NewDirectory(ctx, t, r.Fremote, emptyDirPath)
|
||||||
|
if features.ReadDirMetadata {
|
||||||
|
fstest.CheckEntryMetadata(ctx, t, r.Fremote, dir, dirMetadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyMetadata(t *testing.T) {
|
||||||
|
testCopyMetadata(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyMetadataNoEmptyDirs(t *testing.T) {
|
||||||
|
testCopyMetadata(t, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyMissingDirectory(t *testing.T) {
|
func TestCopyMissingDirectory(t *testing.T) {
|
||||||
|
@ -309,6 +334,29 @@ func TestCopyEmptyDirectories(t *testing.T) {
|
||||||
r.CheckDirectoryModTimes(t, "sub dir", "sub dir2")
|
r.CheckDirectoryModTimes(t, "sub dir", "sub dir2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test copy empty directories when we are configured not to create them
|
||||||
|
func TestCopyNoEmptyDirectories(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
r := fstest.NewRun(t)
|
||||||
|
file1 := r.WriteFile("sub dir/hello world", "hello world", t1)
|
||||||
|
err := operations.Mkdir(ctx, r.Flocal, "sub dir2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r.Mkdir(ctx, r.Fremote)
|
||||||
|
|
||||||
|
err = CopyDir(ctx, r.Fremote, r.Flocal, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
r.CheckRemoteListing(
|
||||||
|
t,
|
||||||
|
[]fstest.Item{
|
||||||
|
file1,
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"sub dir",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Test move empty directories
|
// Test move empty directories
|
||||||
func TestMoveEmptyDirectories(t *testing.T) {
|
func TestMoveEmptyDirectories(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -383,6 +431,29 @@ func TestSyncNoUpdateDirModtime(t *testing.T) {
|
||||||
fstest.AssertTimeEqualWithPrecision(t, name, wantT, gotT, r.Fremote.Precision())
|
fstest.AssertTimeEqualWithPrecision(t, name, wantT, gotT, r.Fremote.Precision())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test move empty directories when we are not configured to create them
|
||||||
|
func TestMoveNoEmptyDirectories(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
r := fstest.NewRun(t)
|
||||||
|
file1 := r.WriteFile("sub dir/hello world", "hello world", t1)
|
||||||
|
err := operations.Mkdir(ctx, r.Flocal, "sub dir2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r.Mkdir(ctx, r.Fremote)
|
||||||
|
|
||||||
|
err = MoveDir(ctx, r.Fremote, r.Flocal, false, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
r.CheckRemoteListing(
|
||||||
|
t,
|
||||||
|
[]fstest.Item{
|
||||||
|
file1,
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"sub dir",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Test sync empty directories
|
// Test sync empty directories
|
||||||
func TestSyncEmptyDirectories(t *testing.T) {
|
func TestSyncEmptyDirectories(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -474,6 +545,29 @@ func TestSyncSetDelayedModTimes(t *testing.T) {
|
||||||
r.CheckDirectoryModTimes(t, dirs...)
|
r.CheckDirectoryModTimes(t, dirs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test sync empty directories when we are not configured to create them
|
||||||
|
func TestSyncNoEmptyDirectories(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
r := fstest.NewRun(t)
|
||||||
|
file1 := r.WriteFile("sub dir/hello world", "hello world", t1)
|
||||||
|
err := operations.Mkdir(ctx, r.Flocal, "sub dir2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r.Mkdir(ctx, r.Fremote)
|
||||||
|
|
||||||
|
err = Sync(ctx, r.Fremote, r.Flocal, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
r.CheckRemoteListing(
|
||||||
|
t,
|
||||||
|
[]fstest.Item{
|
||||||
|
file1,
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"sub dir",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Test a server-side copy if possible, or the backup path if not
|
// Test a server-side copy if possible, or the backup path if not
|
||||||
func TestServerSideCopy(t *testing.T) {
|
func TestServerSideCopy(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -2541,7 +2635,7 @@ func TestSyncConcurrentTruncate(t *testing.T) {
|
||||||
|
|
||||||
// Tests that nothing is transferred when src and dst already match
|
// Tests that nothing is transferred when src and dst already match
|
||||||
// Run the same sync twice, ensure no action is taken the second time
|
// Run the same sync twice, ensure no action is taken the second time
|
||||||
func TestNothingToTransfer(t *testing.T) {
|
func testNothingToTransfer(t *testing.T, copyEmptySrcDirs bool) {
|
||||||
accounting.GlobalStats().ResetCounters()
|
accounting.GlobalStats().ResetCounters()
|
||||||
ctx, _ := fs.AddConfig(context.Background())
|
ctx, _ := fs.AddConfig(context.Background())
|
||||||
r := fstest.NewRun(t)
|
r := fstest.NewRun(t)
|
||||||
|
@ -2566,7 +2660,7 @@ func TestNothingToTransfer(t *testing.T) {
|
||||||
accounting.GlobalStats().ResetCounters()
|
accounting.GlobalStats().ResetCounters()
|
||||||
ctx = predictDstFromLogger(ctx)
|
ctx = predictDstFromLogger(ctx)
|
||||||
output := bilib.CaptureOutput(func() {
|
output := bilib.CaptureOutput(func() {
|
||||||
err = CopyDir(ctx, r.Fremote, r.Flocal, true)
|
err = CopyDir(ctx, r.Fremote, r.Flocal, copyEmptySrcDirs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
require.NotNil(t, output)
|
require.NotNil(t, output)
|
||||||
|
@ -2580,6 +2674,7 @@ func TestNothingToTransfer(t *testing.T) {
|
||||||
assert.True(t, strings.Contains(string(output), "Copied"), `expected to find at least one "Copied" log: `+string(output))
|
assert.True(t, strings.Contains(string(output), "Copied"), `expected to find at least one "Copied" log: `+string(output))
|
||||||
if r.Fremote.Features().DirSetModTime != nil || r.Fremote.Features().MkdirMetadata != nil {
|
if r.Fremote.Features().DirSetModTime != nil || r.Fremote.Features().MkdirMetadata != nil {
|
||||||
assert.True(t, strings.Contains(string(output), "Set directory modification time"), `expected to find at least one "Set directory modification time" log: `+string(output))
|
assert.True(t, strings.Contains(string(output), "Set directory modification time"), `expected to find at least one "Set directory modification time" log: `+string(output))
|
||||||
|
assert.True(t, strings.Contains(string(output), "Made directory with metadata"), `expected to find at least one "Made directory with metadata" log: `+string(output))
|
||||||
}
|
}
|
||||||
assert.False(t, strings.Contains(string(output), "There was nothing to transfer"), `expected to find no "There was nothing to transfer" logs, but found one: `+string(output))
|
assert.False(t, strings.Contains(string(output), "There was nothing to transfer"), `expected to find no "There was nothing to transfer" logs, but found one: `+string(output))
|
||||||
assert.True(t, accounting.GlobalStats().GetTransfers() >= 2)
|
assert.True(t, accounting.GlobalStats().GetTransfers() >= 2)
|
||||||
|
@ -2588,7 +2683,7 @@ func TestNothingToTransfer(t *testing.T) {
|
||||||
accounting.GlobalStats().ResetCounters()
|
accounting.GlobalStats().ResetCounters()
|
||||||
ctx = predictDstFromLogger(ctx)
|
ctx = predictDstFromLogger(ctx)
|
||||||
output = bilib.CaptureOutput(func() {
|
output = bilib.CaptureOutput(func() {
|
||||||
err = CopyDir(ctx, r.Fremote, r.Flocal, true)
|
err = CopyDir(ctx, r.Fremote, r.Flocal, copyEmptySrcDirs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
require.NotNil(t, output)
|
require.NotNil(t, output)
|
||||||
|
@ -2602,11 +2697,21 @@ func TestNothingToTransfer(t *testing.T) {
|
||||||
assert.False(t, strings.Contains(string(output), "Copied"), `expected to find no "Copied" logs, but found one: `+string(output))
|
assert.False(t, strings.Contains(string(output), "Copied"), `expected to find no "Copied" logs, but found one: `+string(output))
|
||||||
if r.Fremote.Features().DirSetModTime != nil || r.Fremote.Features().MkdirMetadata != nil {
|
if r.Fremote.Features().DirSetModTime != nil || r.Fremote.Features().MkdirMetadata != nil {
|
||||||
assert.False(t, strings.Contains(string(output), "Set directory modification time"), `expected to find no "Set directory modification time" logs, but found one: `+string(output))
|
assert.False(t, strings.Contains(string(output), "Set directory modification time"), `expected to find no "Set directory modification time" logs, but found one: `+string(output))
|
||||||
|
assert.False(t, strings.Contains(string(output), "Updated directory metadata"), `expected to find no "Updated directory metadata" logs, but found one: `+string(output))
|
||||||
|
assert.False(t, strings.Contains(string(output), "directory"), `expected to find no "directory"-related logs, but found one: `+string(output)) // catch-all
|
||||||
}
|
}
|
||||||
assert.True(t, strings.Contains(string(output), "There was nothing to transfer"), `expected to find a "There was nothing to transfer" log: `+string(output))
|
assert.True(t, strings.Contains(string(output), "There was nothing to transfer"), `expected to find a "There was nothing to transfer" log: `+string(output))
|
||||||
assert.Equal(t, int64(0), accounting.GlobalStats().GetTransfers())
|
assert.Equal(t, int64(0), accounting.GlobalStats().GetTransfers())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNothingToTransferWithEmptyDirs(t *testing.T) {
|
||||||
|
testNothingToTransfer(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNothingToTransferWithoutEmptyDirs(t *testing.T) {
|
||||||
|
testNothingToTransfer(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
// for testing logger:
|
// for testing logger:
|
||||||
func predictDstFromLogger(ctx context.Context) context.Context {
|
func predictDstFromLogger(ctx context.Context) context.Context {
|
||||||
opt := operations.NewLoggerOpt()
|
opt := operations.NewLoggerOpt()
|
||||||
|
|
|
@ -539,10 +539,12 @@ func NewObject(ctx context.Context, t *testing.T, f fs.Fs, remote string) fs.Obj
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDirectory finds the directory with remote in f
|
// NewDirectoryRetries finds the directory with remote in f
|
||||||
|
//
|
||||||
|
// If directory can't be found it returns an error wrapping fs.ErrorDirNotFound
|
||||||
//
|
//
|
||||||
// One day this will be an rclone primitive
|
// One day this will be an rclone primitive
|
||||||
func NewDirectory(ctx context.Context, t *testing.T, f fs.Fs, remote string) fs.Directory {
|
func NewDirectoryRetries(ctx context.Context, t *testing.T, f fs.Fs, remote string, retries int) (fs.Directory, error) {
|
||||||
var err error
|
var err error
|
||||||
var dir fs.Directory
|
var dir fs.Directory
|
||||||
sleepTime := 1 * time.Second
|
sleepTime := 1 * time.Second
|
||||||
|
@ -550,7 +552,7 @@ func NewDirectory(ctx context.Context, t *testing.T, f fs.Fs, remote string) fs.
|
||||||
if root == "." {
|
if root == "." {
|
||||||
root = ""
|
root = ""
|
||||||
}
|
}
|
||||||
for i := 1; i <= *ListRetries; i++ {
|
for i := 1; i <= retries; i++ {
|
||||||
var entries fs.DirEntries
|
var entries fs.DirEntries
|
||||||
entries, err = f.List(ctx, root)
|
entries, err = f.List(ctx, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -560,14 +562,24 @@ func NewDirectory(ctx context.Context, t *testing.T, f fs.Fs, remote string) fs.
|
||||||
var ok bool
|
var ok bool
|
||||||
dir, ok = entry.(fs.Directory)
|
dir, ok = entry.(fs.Directory)
|
||||||
if ok && dir.Remote() == remote {
|
if ok && dir.Remote() == remote {
|
||||||
return dir
|
return dir, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("directory %q not found in %q", remote, root)
|
err = fmt.Errorf("directory %q not found in %q: %w", remote, root, fs.ErrorDirNotFound)
|
||||||
t.Logf("Sleeping for %v for findDir eventual consistency: %d/%d (%v)", sleepTime, i, *ListRetries, err)
|
if i < retries {
|
||||||
|
t.Logf("Sleeping for %v for NewDirectoryRetries eventual consistency: %d/%d (%v)", sleepTime, i, retries, err)
|
||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTime)
|
||||||
sleepTime = (sleepTime * 3) / 2
|
sleepTime = (sleepTime * 3) / 2
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return dir, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDirectory finds the directory with remote in f
|
||||||
|
//
|
||||||
|
// One day this will be an rclone primitive
|
||||||
|
func NewDirectory(ctx context.Context, t *testing.T, f fs.Fs, remote string) fs.Directory {
|
||||||
|
dir, err := NewDirectoryRetries(ctx, t, f, remote, *ListRetries)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue