2019-04-24 18:04:40 +02:00
|
|
|
package operations
|
|
|
|
|
|
|
|
import (
|
2019-06-17 10:34:30 +02:00
|
|
|
"context"
|
2019-04-24 18:04:40 +02:00
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs/accounting"
|
2019-08-12 23:09:40 +02:00
|
|
|
"github.com/rclone/rclone/fstest/mockfs"
|
|
|
|
"github.com/rclone/rclone/fstest/mockobject"
|
2019-08-06 13:44:08 +02:00
|
|
|
"github.com/rclone/rclone/lib/random"
|
2019-07-16 13:56:20 +02:00
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fstest"
|
2019-04-24 18:04:40 +02:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2019-08-12 23:09:40 +02:00
|
|
|
func TestDoMultiThreadCopy(t *testing.T) {
|
2020-11-05 12:33:32 +01:00
|
|
|
ctx := context.Background()
|
|
|
|
ci := fs.GetConfig(ctx)
|
2023-04-28 13:01:04 +02:00
|
|
|
f, err := mockfs.NewFs(ctx, "potato", "", nil)
|
|
|
|
require.NoError(t, err)
|
2019-08-12 23:09:40 +02:00
|
|
|
src := mockobject.New("file.txt").WithContent([]byte(random.String(100)), mockobject.SeekModeNone)
|
2023-04-28 13:01:04 +02:00
|
|
|
srcFs, err := mockfs.NewFs(ctx, "sausage", "", nil)
|
|
|
|
require.NoError(t, err)
|
2019-08-12 23:09:40 +02:00
|
|
|
src.SetFs(srcFs)
|
|
|
|
|
2020-11-05 12:33:32 +01:00
|
|
|
oldStreams := ci.MultiThreadStreams
|
|
|
|
oldCutoff := ci.MultiThreadCutoff
|
|
|
|
oldIsSet := ci.MultiThreadSet
|
2019-08-12 23:09:40 +02:00
|
|
|
defer func() {
|
2020-11-05 12:33:32 +01:00
|
|
|
ci.MultiThreadStreams = oldStreams
|
|
|
|
ci.MultiThreadCutoff = oldCutoff
|
|
|
|
ci.MultiThreadSet = oldIsSet
|
2019-08-12 23:09:40 +02:00
|
|
|
}()
|
|
|
|
|
2020-11-05 12:33:32 +01:00
|
|
|
ci.MultiThreadStreams, ci.MultiThreadCutoff = 4, 50
|
|
|
|
ci.MultiThreadSet = false
|
2019-08-12 23:09:40 +02:00
|
|
|
|
|
|
|
nullWriterAt := func(ctx context.Context, remote string, size int64) (fs.WriterAtCloser, error) {
|
|
|
|
panic("don't call me")
|
|
|
|
}
|
|
|
|
f.Features().OpenWriterAt = nullWriterAt
|
|
|
|
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
|
2020-11-05 12:33:32 +01:00
|
|
|
ci.MultiThreadStreams = 0
|
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadStreams = 1
|
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadStreams = 2
|
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
|
2020-11-05 12:33:32 +01:00
|
|
|
ci.MultiThreadCutoff = 200
|
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadCutoff = 101
|
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadCutoff = 100
|
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
|
|
|
|
f.Features().OpenWriterAt = nil
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
f.Features().OpenWriterAt = nullWriterAt
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
|
|
|
|
f.Features().IsLocal = true
|
|
|
|
srcFs.Features().IsLocal = true
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadSet = true
|
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
|
|
|
ci.MultiThreadSet = false
|
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
srcFs.Features().IsLocal = false
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
srcFs.Features().IsLocal = true
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.False(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
f.Features().IsLocal = false
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
srcFs.Features().IsLocal = false
|
2020-11-05 12:33:32 +01:00
|
|
|
assert.True(t, doMultiThreadCopy(ctx, f, src))
|
2019-08-12 23:09:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-24 18:04:40 +02:00
|
|
|
func TestMultithreadCalculateChunks(t *testing.T) {
|
|
|
|
for _, test := range []struct {
|
|
|
|
size int64
|
|
|
|
streams int
|
|
|
|
wantPartSize int64
|
|
|
|
wantStreams int
|
|
|
|
}{
|
|
|
|
{size: 1, streams: 10, wantPartSize: multithreadChunkSize, wantStreams: 1},
|
|
|
|
{size: 1 << 20, streams: 1, wantPartSize: 1 << 20, wantStreams: 1},
|
|
|
|
{size: 1 << 20, streams: 2, wantPartSize: 1 << 19, wantStreams: 2},
|
|
|
|
{size: (1 << 20) + 1, streams: 2, wantPartSize: (1 << 19) + multithreadChunkSize, wantStreams: 2},
|
|
|
|
{size: (1 << 20) - 1, streams: 2, wantPartSize: (1 << 19), wantStreams: 2},
|
|
|
|
} {
|
|
|
|
t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) {
|
|
|
|
mc := &multiThreadCopyState{
|
|
|
|
size: test.size,
|
|
|
|
streams: test.streams,
|
|
|
|
}
|
|
|
|
mc.calculateChunks()
|
|
|
|
assert.Equal(t, test.wantPartSize, mc.partSize)
|
|
|
|
assert.Equal(t, test.wantStreams, mc.streams)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMultithreadCopy(t *testing.T) {
|
|
|
|
r := fstest.NewRun(t)
|
2020-11-05 17:27:01 +01:00
|
|
|
ctx := context.Background()
|
2019-04-24 18:04:40 +02:00
|
|
|
|
|
|
|
for _, test := range []struct {
|
|
|
|
size int
|
|
|
|
streams int
|
|
|
|
}{
|
|
|
|
{size: multithreadChunkSize*2 - 1, streams: 2},
|
|
|
|
{size: multithreadChunkSize * 2, streams: 2},
|
|
|
|
{size: multithreadChunkSize*2 + 1, streams: 2},
|
|
|
|
} {
|
|
|
|
t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) {
|
2019-09-16 19:52:41 +02:00
|
|
|
if *fstest.SizeLimit > 0 && int64(test.size) > *fstest.SizeLimit {
|
|
|
|
t.Skipf("exceeded file size limit %d > %d", test.size, *fstest.SizeLimit)
|
|
|
|
}
|
2019-07-16 13:56:20 +02:00
|
|
|
var err error
|
2019-08-06 13:44:08 +02:00
|
|
|
contents := random.String(test.size)
|
2019-04-24 18:04:40 +02:00
|
|
|
t1 := fstest.Time("2001-02-03T04:05:06.499999999Z")
|
2020-11-05 17:27:01 +01:00
|
|
|
file1 := r.WriteObject(ctx, "file1", contents, t1)
|
2021-11-09 12:43:36 +01:00
|
|
|
r.CheckRemoteItems(t, file1)
|
|
|
|
r.CheckLocalItems(t)
|
2019-04-24 18:04:40 +02:00
|
|
|
|
2020-11-05 17:27:01 +01:00
|
|
|
src, err := r.Fremote.NewObject(ctx, "file1")
|
2019-04-24 18:04:40 +02:00
|
|
|
require.NoError(t, err)
|
2019-07-18 12:13:54 +02:00
|
|
|
accounting.GlobalStats().ResetCounters()
|
|
|
|
tr := accounting.GlobalStats().NewTransfer(src)
|
2019-04-24 18:04:40 +02:00
|
|
|
|
2019-07-16 13:56:20 +02:00
|
|
|
defer func() {
|
2020-11-05 17:59:59 +01:00
|
|
|
tr.Done(ctx, err)
|
2019-07-16 13:56:20 +02:00
|
|
|
}()
|
2020-11-05 17:27:01 +01:00
|
|
|
dst, err := multiThreadCopy(ctx, r.Flocal, "file1", src, 2, tr)
|
2019-04-24 18:04:40 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, src.Size(), dst.Size())
|
|
|
|
assert.Equal(t, "file1", dst.Remote())
|
|
|
|
|
2020-11-05 17:27:01 +01:00
|
|
|
fstest.CheckListingWithPrecision(t, r.Flocal, []fstest.Item{file1}, nil, fs.GetModifyWindow(ctx, r.Flocal, r.Fremote))
|
|
|
|
require.NoError(t, dst.Remove(ctx))
|
2019-04-24 18:04:40 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|