mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 09:41:44 +08:00
5d6b8141ec
As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code.
387 lines
9.7 KiB
Go
387 lines
9.7 KiB
Go
package accounting
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"unicode/utf8"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/asyncreader"
|
|
"github.com/rclone/rclone/fs/fserrors"
|
|
"github.com/rclone/rclone/lib/readers"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Check it satisfies the interfaces
|
|
var (
|
|
_ io.ReadCloser = &Account{}
|
|
_ io.WriterTo = &Account{}
|
|
_ io.Reader = &accountStream{}
|
|
_ Accounter = &Account{}
|
|
_ Accounter = &accountStream{}
|
|
)
|
|
|
|
func TestNewAccountSizeName(t *testing.T) {
|
|
ctx := context.Background()
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1}))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(context.Background(), stats, in, 1, "test")
|
|
assert.Equal(t, in, acc.in)
|
|
assert.Equal(t, acc, stats.inProgress.get("test"))
|
|
err := acc.Close()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, acc, stats.inProgress.get("test"))
|
|
acc.Done()
|
|
assert.Nil(t, stats.inProgress.get("test"))
|
|
assert.False(t, acc.HasBuffer())
|
|
}
|
|
|
|
func TestAccountWithBuffer(t *testing.T) {
|
|
ctx := context.Background()
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1}))
|
|
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, -1, "test")
|
|
assert.False(t, acc.HasBuffer())
|
|
acc.WithBuffer()
|
|
assert.True(t, acc.HasBuffer())
|
|
// should have a buffer for an unknown size
|
|
_, ok := acc.in.(*asyncreader.AsyncReader)
|
|
require.True(t, ok)
|
|
assert.NoError(t, acc.Close())
|
|
|
|
acc = newAccountSizeName(ctx, stats, in, 1, "test")
|
|
acc.WithBuffer()
|
|
// should not have a buffer for a small size
|
|
_, ok = acc.in.(*asyncreader.AsyncReader)
|
|
require.False(t, ok)
|
|
assert.NoError(t, acc.Close())
|
|
}
|
|
|
|
func TestAccountGetUpdateReader(t *testing.T) {
|
|
ctx := context.Background()
|
|
test := func(doClose bool) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1}))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
assert.Equal(t, in, acc.GetReader())
|
|
assert.Equal(t, acc, stats.inProgress.get("test"))
|
|
|
|
if doClose {
|
|
// close the account before swapping it out
|
|
require.NoError(t, acc.Close())
|
|
}
|
|
|
|
in2 := io.NopCloser(bytes.NewBuffer([]byte{1}))
|
|
acc.UpdateReader(ctx, in2)
|
|
|
|
assert.Equal(t, in2, acc.GetReader())
|
|
assert.Equal(t, acc, stats.inProgress.get("test"))
|
|
|
|
assert.NoError(t, acc.Close())
|
|
}
|
|
}
|
|
t.Run("NoClose", test(false))
|
|
t.Run("Close", test(true))
|
|
}
|
|
|
|
func TestAccountRead(t *testing.T) {
|
|
ctx := context.Background()
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
assert.True(t, acc.values.start.IsZero())
|
|
acc.values.mu.Lock()
|
|
assert.Equal(t, 0, acc.values.lpBytes)
|
|
assert.Equal(t, int64(0), acc.values.bytes)
|
|
acc.values.mu.Unlock()
|
|
assert.Equal(t, int64(0), stats.bytes)
|
|
|
|
var buf = make([]byte, 2)
|
|
n, err := acc.Read(buf)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, n)
|
|
assert.Equal(t, []byte{1, 2}, buf[:n])
|
|
|
|
assert.False(t, acc.values.start.IsZero())
|
|
acc.values.mu.Lock()
|
|
assert.Equal(t, 2, acc.values.lpBytes)
|
|
assert.Equal(t, int64(2), acc.values.bytes)
|
|
acc.values.mu.Unlock()
|
|
assert.Equal(t, int64(2), stats.bytes)
|
|
|
|
n, err = acc.Read(buf)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, n)
|
|
assert.Equal(t, []byte{3}, buf[:n])
|
|
|
|
n, err = acc.Read(buf)
|
|
assert.Equal(t, io.EOF, err)
|
|
assert.Equal(t, 0, n)
|
|
|
|
assert.NoError(t, acc.Close())
|
|
}
|
|
|
|
func testAccountWriteTo(t *testing.T, withBuffer bool) {
|
|
ctx := context.Background()
|
|
buf := make([]byte, 2*asyncreader.BufferSize+1)
|
|
for i := range buf {
|
|
buf[i] = byte(i % 251)
|
|
}
|
|
in := io.NopCloser(bytes.NewBuffer(buf))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, int64(len(buf)), "test")
|
|
if withBuffer {
|
|
acc = acc.WithBuffer()
|
|
}
|
|
|
|
assert.True(t, acc.values.start.IsZero())
|
|
acc.values.mu.Lock()
|
|
assert.Equal(t, 0, acc.values.lpBytes)
|
|
assert.Equal(t, int64(0), acc.values.bytes)
|
|
acc.values.mu.Unlock()
|
|
assert.Equal(t, int64(0), stats.bytes)
|
|
|
|
var out bytes.Buffer
|
|
|
|
n, err := acc.WriteTo(&out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(len(buf)), n)
|
|
assert.Equal(t, buf, out.Bytes())
|
|
|
|
assert.False(t, acc.values.start.IsZero())
|
|
acc.values.mu.Lock()
|
|
assert.Equal(t, len(buf), acc.values.lpBytes)
|
|
assert.Equal(t, int64(len(buf)), acc.values.bytes)
|
|
acc.values.mu.Unlock()
|
|
assert.Equal(t, int64(len(buf)), stats.bytes)
|
|
|
|
assert.NoError(t, acc.Close())
|
|
}
|
|
|
|
func TestAccountWriteTo(t *testing.T) {
|
|
testAccountWriteTo(t, false)
|
|
}
|
|
|
|
func TestAccountWriteToWithBuffer(t *testing.T) {
|
|
testAccountWriteTo(t, true)
|
|
}
|
|
|
|
func TestAccountString(t *testing.T) {
|
|
ctx := context.Background()
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 3, "test")
|
|
|
|
// FIXME not an exhaustive test!
|
|
|
|
assert.Equal(t, "test: 0% /3, 0/s, -", strings.TrimSpace(acc.String()))
|
|
|
|
var buf = make([]byte, 2)
|
|
n, err := acc.Read(buf)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, n)
|
|
|
|
assert.Equal(t, "test: 66% /3, 0/s, -", strings.TrimSpace(acc.String()))
|
|
|
|
assert.NoError(t, acc.Close())
|
|
}
|
|
|
|
// Test the Accounter interface methods on Account and accountStream
|
|
func TestAccountAccounter(t *testing.T) {
|
|
ctx := context.Background()
|
|
in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 3, "test")
|
|
|
|
assert.True(t, in == acc.OldStream())
|
|
|
|
in2 := io.NopCloser(bytes.NewBuffer([]byte{2, 3, 4}))
|
|
|
|
acc.SetStream(in2)
|
|
assert.True(t, in2 == acc.OldStream())
|
|
|
|
r := acc.WrapStream(in)
|
|
as, ok := r.(Accounter)
|
|
require.True(t, ok)
|
|
assert.True(t, in == as.OldStream())
|
|
assert.True(t, in2 == acc.OldStream())
|
|
accs, ok := r.(*accountStream)
|
|
require.True(t, ok)
|
|
assert.Equal(t, acc, accs.acc)
|
|
assert.True(t, in == accs.in)
|
|
|
|
// Check Read on the accountStream
|
|
var buf = make([]byte, 2)
|
|
n, err := r.Read(buf)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, n)
|
|
assert.Equal(t, []byte{1, 2}, buf[:n])
|
|
|
|
// Test that we can get another accountstream out
|
|
in3 := io.NopCloser(bytes.NewBuffer([]byte{3, 1, 2}))
|
|
r2 := as.WrapStream(in3)
|
|
as2, ok := r2.(Accounter)
|
|
require.True(t, ok)
|
|
assert.True(t, in3 == as2.OldStream())
|
|
assert.True(t, in2 == acc.OldStream())
|
|
accs2, ok := r2.(*accountStream)
|
|
require.True(t, ok)
|
|
assert.Equal(t, acc, accs2.acc)
|
|
assert.True(t, in3 == accs2.in)
|
|
|
|
// Test we can set this new accountStream
|
|
as2.SetStream(in)
|
|
assert.True(t, in == as2.OldStream())
|
|
|
|
// Test UnWrap on accountStream
|
|
unwrapped, wrap := UnWrap(r2)
|
|
assert.True(t, unwrapped == in)
|
|
r3 := wrap(in2)
|
|
assert.True(t, in2 == r3.(Accounter).OldStream())
|
|
|
|
// TestUnWrap on a normal io.Reader
|
|
unwrapped, wrap = UnWrap(in2)
|
|
assert.True(t, unwrapped == in2)
|
|
assert.True(t, wrap(in3) == in3)
|
|
|
|
}
|
|
|
|
func TestAccountMaxTransfer(t *testing.T) {
|
|
ctx := context.Background()
|
|
ci := fs.GetConfig(ctx)
|
|
old := ci.MaxTransfer
|
|
oldMode := ci.CutoffMode
|
|
|
|
ci.MaxTransfer = 15
|
|
defer func() {
|
|
ci.MaxTransfer = old
|
|
ci.CutoffMode = oldMode
|
|
}()
|
|
|
|
in := io.NopCloser(bytes.NewBuffer(make([]byte, 100)))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
var b = make([]byte, 10)
|
|
|
|
n, err := acc.Read(b)
|
|
assert.Equal(t, 10, n)
|
|
assert.NoError(t, err)
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 5, n)
|
|
assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 0, n)
|
|
assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
|
|
assert.True(t, fserrors.IsFatalError(err))
|
|
|
|
ci.CutoffMode = fs.CutoffModeSoft
|
|
stats = NewStats(ctx)
|
|
acc = newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 10, n)
|
|
assert.NoError(t, err)
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 10, n)
|
|
assert.NoError(t, err)
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 10, n)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAccountMaxTransferWriteTo(t *testing.T) {
|
|
ctx := context.Background()
|
|
ci := fs.GetConfig(ctx)
|
|
old := ci.MaxTransfer
|
|
oldMode := ci.CutoffMode
|
|
|
|
ci.MaxTransfer = 15
|
|
defer func() {
|
|
ci.MaxTransfer = old
|
|
ci.CutoffMode = oldMode
|
|
}()
|
|
|
|
in := io.NopCloser(readers.NewPatternReader(1024))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
var b bytes.Buffer
|
|
|
|
n, err := acc.WriteTo(&b)
|
|
assert.Equal(t, int64(15), n)
|
|
assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
|
|
}
|
|
|
|
func TestAccountReadCtx(t *testing.T) {
|
|
ctx := context.Background()
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
in := io.NopCloser(bytes.NewBuffer(make([]byte, 100)))
|
|
stats := NewStats(ctx)
|
|
acc := newAccountSizeName(ctx, stats, in, 1, "test")
|
|
|
|
var b = make([]byte, 10)
|
|
|
|
n, err := acc.Read(b)
|
|
assert.Equal(t, 10, n)
|
|
assert.NoError(t, err)
|
|
|
|
cancel()
|
|
|
|
n, err = acc.Read(b)
|
|
assert.Equal(t, 0, n)
|
|
assert.Equal(t, context.Canceled, err)
|
|
}
|
|
|
|
func TestShortenName(t *testing.T) {
|
|
for _, test := range []struct {
|
|
in string
|
|
size int
|
|
want string
|
|
}{
|
|
{"", 0, ""},
|
|
{"abcde", 10, "abcde"},
|
|
{"abcde", 0, "abcde"},
|
|
{"abcde", -1, "abcde"},
|
|
{"abcde", 5, "abcde"},
|
|
{"abcde", 4, "ab…e"},
|
|
{"abcde", 3, "a…e"},
|
|
{"abcde", 2, "a…"},
|
|
{"abcde", 1, "…"},
|
|
{"abcdef", 6, "abcdef"},
|
|
{"abcdef", 5, "ab…ef"},
|
|
{"abcdef", 4, "ab…f"},
|
|
{"abcdef", 3, "a…f"},
|
|
{"abcdef", 2, "a…"},
|
|
{"áßcdèf", 1, "…"},
|
|
{"áßcdè", 5, "áßcdè"},
|
|
{"áßcdè", 4, "áß…è"},
|
|
{"áßcdè", 3, "á…è"},
|
|
{"áßcdè", 2, "á…"},
|
|
{"áßcdè", 1, "…"},
|
|
{"áßcdèł", 6, "áßcdèł"},
|
|
{"áßcdèł", 5, "áß…èł"},
|
|
{"áßcdèł", 4, "áß…ł"},
|
|
{"áßcdèł", 3, "á…ł"},
|
|
{"áßcdèł", 2, "á…"},
|
|
{"áßcdèł", 1, "…"},
|
|
} {
|
|
t.Run(fmt.Sprintf("in=%q, size=%d", test.in, test.size), func(t *testing.T) {
|
|
got := shortenName(test.in, test.size)
|
|
assert.Equal(t, test.want, got)
|
|
if test.size > 0 {
|
|
assert.True(t, utf8.RuneCountInString(got) <= test.size, "too big")
|
|
}
|
|
})
|
|
}
|
|
}
|