github.com/divyam234/rclone@v1.64.1/fs/accounting/accounting_test.go (about)

     1  package accounting
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"testing"
    10  	"unicode/utf8"
    11  
    12  	"github.com/divyam234/rclone/fs"
    13  	"github.com/divyam234/rclone/fs/asyncreader"
    14  	"github.com/divyam234/rclone/fs/fserrors"
    15  	"github.com/divyam234/rclone/lib/readers"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  // Check it satisfies the interfaces
    21  var (
    22  	_ io.ReadCloser = &Account{}
    23  	_ io.WriterTo   = &Account{}
    24  	_ io.Reader     = &accountStream{}
    25  	_ Accounter     = &Account{}
    26  	_ Accounter     = &accountStream{}
    27  )
    28  
    29  func TestNewAccountSizeName(t *testing.T) {
    30  	ctx := context.Background()
    31  	in := io.NopCloser(bytes.NewBuffer([]byte{1}))
    32  	stats := NewStats(ctx)
    33  	acc := newAccountSizeName(context.Background(), stats, in, 1, "test")
    34  	assert.Equal(t, in, acc.in)
    35  	assert.Equal(t, acc, stats.inProgress.get("test"))
    36  	err := acc.Close()
    37  	assert.NoError(t, err)
    38  	assert.Equal(t, acc, stats.inProgress.get("test"))
    39  	acc.Done()
    40  	assert.Nil(t, stats.inProgress.get("test"))
    41  	assert.False(t, acc.HasBuffer())
    42  }
    43  
    44  func TestAccountWithBuffer(t *testing.T) {
    45  	ctx := context.Background()
    46  	in := io.NopCloser(bytes.NewBuffer([]byte{1}))
    47  
    48  	stats := NewStats(ctx)
    49  	acc := newAccountSizeName(ctx, stats, in, -1, "test")
    50  	assert.False(t, acc.HasBuffer())
    51  	acc.WithBuffer()
    52  	assert.True(t, acc.HasBuffer())
    53  	// should have a buffer for an unknown size
    54  	_, ok := acc.in.(*asyncreader.AsyncReader)
    55  	require.True(t, ok)
    56  	assert.NoError(t, acc.Close())
    57  
    58  	acc = newAccountSizeName(ctx, stats, in, 1, "test")
    59  	acc.WithBuffer()
    60  	// should not have a buffer for a small size
    61  	_, ok = acc.in.(*asyncreader.AsyncReader)
    62  	require.False(t, ok)
    63  	assert.NoError(t, acc.Close())
    64  }
    65  
    66  func TestAccountGetUpdateReader(t *testing.T) {
    67  	ctx := context.Background()
    68  	test := func(doClose bool) func(t *testing.T) {
    69  		return func(t *testing.T) {
    70  			in := io.NopCloser(bytes.NewBuffer([]byte{1}))
    71  			stats := NewStats(ctx)
    72  			acc := newAccountSizeName(ctx, stats, in, 1, "test")
    73  
    74  			assert.Equal(t, in, acc.GetReader())
    75  			assert.Equal(t, acc, stats.inProgress.get("test"))
    76  
    77  			if doClose {
    78  				// close the account before swapping it out
    79  				require.NoError(t, acc.Close())
    80  			}
    81  
    82  			in2 := io.NopCloser(bytes.NewBuffer([]byte{1}))
    83  			acc.UpdateReader(ctx, in2)
    84  
    85  			assert.Equal(t, in2, acc.GetReader())
    86  			assert.Equal(t, acc, stats.inProgress.get("test"))
    87  
    88  			assert.NoError(t, acc.Close())
    89  		}
    90  	}
    91  	t.Run("NoClose", test(false))
    92  	t.Run("Close", test(true))
    93  }
    94  
    95  func TestAccountRead(t *testing.T) {
    96  	ctx := context.Background()
    97  	in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
    98  	stats := NewStats(ctx)
    99  	acc := newAccountSizeName(ctx, stats, in, 1, "test")
   100  
   101  	assert.True(t, acc.values.start.IsZero())
   102  	acc.values.mu.Lock()
   103  	assert.Equal(t, 0, acc.values.lpBytes)
   104  	assert.Equal(t, int64(0), acc.values.bytes)
   105  	acc.values.mu.Unlock()
   106  	assert.Equal(t, int64(0), stats.bytes)
   107  
   108  	var buf = make([]byte, 2)
   109  	n, err := acc.Read(buf)
   110  	assert.NoError(t, err)
   111  	assert.Equal(t, 2, n)
   112  	assert.Equal(t, []byte{1, 2}, buf[:n])
   113  
   114  	assert.False(t, acc.values.start.IsZero())
   115  	acc.values.mu.Lock()
   116  	assert.Equal(t, 2, acc.values.lpBytes)
   117  	assert.Equal(t, int64(2), acc.values.bytes)
   118  	acc.values.mu.Unlock()
   119  	assert.Equal(t, int64(2), stats.bytes)
   120  
   121  	n, err = acc.Read(buf)
   122  	assert.NoError(t, err)
   123  	assert.Equal(t, 1, n)
   124  	assert.Equal(t, []byte{3}, buf[:n])
   125  
   126  	n, err = acc.Read(buf)
   127  	assert.Equal(t, io.EOF, err)
   128  	assert.Equal(t, 0, n)
   129  
   130  	assert.NoError(t, acc.Close())
   131  }
   132  
   133  func testAccountWriteTo(t *testing.T, withBuffer bool) {
   134  	ctx := context.Background()
   135  	buf := make([]byte, 2*asyncreader.BufferSize+1)
   136  	for i := range buf {
   137  		buf[i] = byte(i % 251)
   138  	}
   139  	in := io.NopCloser(bytes.NewBuffer(buf))
   140  	stats := NewStats(ctx)
   141  	acc := newAccountSizeName(ctx, stats, in, int64(len(buf)), "test")
   142  	if withBuffer {
   143  		acc = acc.WithBuffer()
   144  	}
   145  
   146  	assert.True(t, acc.values.start.IsZero())
   147  	acc.values.mu.Lock()
   148  	assert.Equal(t, 0, acc.values.lpBytes)
   149  	assert.Equal(t, int64(0), acc.values.bytes)
   150  	acc.values.mu.Unlock()
   151  	assert.Equal(t, int64(0), stats.bytes)
   152  
   153  	var out bytes.Buffer
   154  
   155  	n, err := acc.WriteTo(&out)
   156  	assert.NoError(t, err)
   157  	assert.Equal(t, int64(len(buf)), n)
   158  	assert.Equal(t, buf, out.Bytes())
   159  
   160  	assert.False(t, acc.values.start.IsZero())
   161  	acc.values.mu.Lock()
   162  	assert.Equal(t, len(buf), acc.values.lpBytes)
   163  	assert.Equal(t, int64(len(buf)), acc.values.bytes)
   164  	acc.values.mu.Unlock()
   165  	assert.Equal(t, int64(len(buf)), stats.bytes)
   166  
   167  	assert.NoError(t, acc.Close())
   168  }
   169  
   170  func TestAccountWriteTo(t *testing.T) {
   171  	testAccountWriteTo(t, false)
   172  }
   173  
   174  func TestAccountWriteToWithBuffer(t *testing.T) {
   175  	testAccountWriteTo(t, true)
   176  }
   177  
   178  func TestAccountString(t *testing.T) {
   179  	ctx := context.Background()
   180  	in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
   181  	stats := NewStats(ctx)
   182  	acc := newAccountSizeName(ctx, stats, in, 3, "test")
   183  
   184  	// FIXME not an exhaustive test!
   185  
   186  	assert.Equal(t, "test:  0% /3, 0/s, -", strings.TrimSpace(acc.String()))
   187  
   188  	var buf = make([]byte, 2)
   189  	n, err := acc.Read(buf)
   190  	assert.NoError(t, err)
   191  	assert.Equal(t, 2, n)
   192  
   193  	assert.Equal(t, "test: 66% /3, 0/s, -", strings.TrimSpace(acc.String()))
   194  
   195  	assert.NoError(t, acc.Close())
   196  }
   197  
   198  // Test the Accounter interface methods on Account and accountStream
   199  func TestAccountAccounter(t *testing.T) {
   200  	ctx := context.Background()
   201  	in := io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
   202  	stats := NewStats(ctx)
   203  	acc := newAccountSizeName(ctx, stats, in, 3, "test")
   204  
   205  	assert.True(t, in == acc.OldStream())
   206  
   207  	in2 := io.NopCloser(bytes.NewBuffer([]byte{2, 3, 4}))
   208  
   209  	acc.SetStream(in2)
   210  	assert.True(t, in2 == acc.OldStream())
   211  
   212  	r := acc.WrapStream(in)
   213  	as, ok := r.(Accounter)
   214  	require.True(t, ok)
   215  	assert.True(t, in == as.OldStream())
   216  	assert.True(t, in2 == acc.OldStream())
   217  	accs, ok := r.(*accountStream)
   218  	require.True(t, ok)
   219  	assert.Equal(t, acc, accs.acc)
   220  	assert.True(t, in == accs.in)
   221  
   222  	// Check Read on the accountStream
   223  	var buf = make([]byte, 2)
   224  	n, err := r.Read(buf)
   225  	assert.NoError(t, err)
   226  	assert.Equal(t, 2, n)
   227  	assert.Equal(t, []byte{1, 2}, buf[:n])
   228  
   229  	// Test that we can get another accountstream out
   230  	in3 := io.NopCloser(bytes.NewBuffer([]byte{3, 1, 2}))
   231  	r2 := as.WrapStream(in3)
   232  	as2, ok := r2.(Accounter)
   233  	require.True(t, ok)
   234  	assert.True(t, in3 == as2.OldStream())
   235  	assert.True(t, in2 == acc.OldStream())
   236  	accs2, ok := r2.(*accountStream)
   237  	require.True(t, ok)
   238  	assert.Equal(t, acc, accs2.acc)
   239  	assert.True(t, in3 == accs2.in)
   240  
   241  	// Test we can set this new accountStream
   242  	as2.SetStream(in)
   243  	assert.True(t, in == as2.OldStream())
   244  
   245  	// Test UnWrap on accountStream
   246  	unwrapped, wrap := UnWrap(r2)
   247  	assert.True(t, unwrapped == in)
   248  	r3 := wrap(in2)
   249  	assert.True(t, in2 == r3.(Accounter).OldStream())
   250  
   251  	// TestUnWrap on a normal io.Reader
   252  	unwrapped, wrap = UnWrap(in2)
   253  	assert.True(t, unwrapped == in2)
   254  	assert.True(t, wrap(in3) == in3)
   255  
   256  }
   257  
   258  func TestAccountMaxTransfer(t *testing.T) {
   259  	ctx := context.Background()
   260  	ci := fs.GetConfig(ctx)
   261  	old := ci.MaxTransfer
   262  	oldMode := ci.CutoffMode
   263  
   264  	ci.MaxTransfer = 15
   265  	defer func() {
   266  		ci.MaxTransfer = old
   267  		ci.CutoffMode = oldMode
   268  	}()
   269  
   270  	in := io.NopCloser(bytes.NewBuffer(make([]byte, 100)))
   271  	stats := NewStats(ctx)
   272  	acc := newAccountSizeName(ctx, stats, in, 1, "test")
   273  
   274  	var b = make([]byte, 10)
   275  
   276  	n, err := acc.Read(b)
   277  	assert.Equal(t, 10, n)
   278  	assert.NoError(t, err)
   279  	n, err = acc.Read(b)
   280  	assert.Equal(t, 5, n)
   281  	assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
   282  	n, err = acc.Read(b)
   283  	assert.Equal(t, 0, n)
   284  	assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
   285  	assert.True(t, fserrors.IsFatalError(err))
   286  
   287  	ci.CutoffMode = fs.CutoffModeSoft
   288  	stats = NewStats(ctx)
   289  	acc = newAccountSizeName(ctx, stats, in, 1, "test")
   290  
   291  	n, err = acc.Read(b)
   292  	assert.Equal(t, 10, n)
   293  	assert.NoError(t, err)
   294  	n, err = acc.Read(b)
   295  	assert.Equal(t, 10, n)
   296  	assert.NoError(t, err)
   297  	n, err = acc.Read(b)
   298  	assert.Equal(t, 10, n)
   299  	assert.NoError(t, err)
   300  }
   301  
   302  func TestAccountMaxTransferWriteTo(t *testing.T) {
   303  	ctx := context.Background()
   304  	ci := fs.GetConfig(ctx)
   305  	old := ci.MaxTransfer
   306  	oldMode := ci.CutoffMode
   307  
   308  	ci.MaxTransfer = 15
   309  	defer func() {
   310  		ci.MaxTransfer = old
   311  		ci.CutoffMode = oldMode
   312  	}()
   313  
   314  	in := io.NopCloser(readers.NewPatternReader(1024))
   315  	stats := NewStats(ctx)
   316  	acc := newAccountSizeName(ctx, stats, in, 1, "test")
   317  
   318  	var b bytes.Buffer
   319  
   320  	n, err := acc.WriteTo(&b)
   321  	assert.Equal(t, int64(15), n)
   322  	assert.Equal(t, ErrorMaxTransferLimitReachedFatal, err)
   323  }
   324  
   325  func TestAccountReadCtx(t *testing.T) {
   326  	ctx := context.Background()
   327  	ctx, cancel := context.WithCancel(ctx)
   328  	in := io.NopCloser(bytes.NewBuffer(make([]byte, 100)))
   329  	stats := NewStats(ctx)
   330  	acc := newAccountSizeName(ctx, stats, in, 1, "test")
   331  
   332  	var b = make([]byte, 10)
   333  
   334  	n, err := acc.Read(b)
   335  	assert.Equal(t, 10, n)
   336  	assert.NoError(t, err)
   337  
   338  	cancel()
   339  
   340  	n, err = acc.Read(b)
   341  	assert.Equal(t, 0, n)
   342  	assert.Equal(t, context.Canceled, err)
   343  }
   344  
   345  func TestShortenName(t *testing.T) {
   346  	for _, test := range []struct {
   347  		in   string
   348  		size int
   349  		want string
   350  	}{
   351  		{"", 0, ""},
   352  		{"abcde", 10, "abcde"},
   353  		{"abcde", 0, "abcde"},
   354  		{"abcde", -1, "abcde"},
   355  		{"abcde", 5, "abcde"},
   356  		{"abcde", 4, "ab…e"},
   357  		{"abcde", 3, "a…e"},
   358  		{"abcde", 2, "a…"},
   359  		{"abcde", 1, "…"},
   360  		{"abcdef", 6, "abcdef"},
   361  		{"abcdef", 5, "ab…ef"},
   362  		{"abcdef", 4, "ab…f"},
   363  		{"abcdef", 3, "a…f"},
   364  		{"abcdef", 2, "a…"},
   365  		{"áßcdèf", 1, "…"},
   366  		{"áßcdè", 5, "áßcdè"},
   367  		{"áßcdè", 4, "áß…è"},
   368  		{"áßcdè", 3, "á…è"},
   369  		{"áßcdè", 2, "á…"},
   370  		{"áßcdè", 1, "…"},
   371  		{"áßcdèł", 6, "áßcdèł"},
   372  		{"áßcdèł", 5, "áß…èł"},
   373  		{"áßcdèł", 4, "áß…ł"},
   374  		{"áßcdèł", 3, "á…ł"},
   375  		{"áßcdèł", 2, "á…"},
   376  		{"áßcdèł", 1, "…"},
   377  	} {
   378  		t.Run(fmt.Sprintf("in=%q, size=%d", test.in, test.size), func(t *testing.T) {
   379  			got := shortenName(test.in, test.size)
   380  			assert.Equal(t, test.want, got)
   381  			if test.size > 0 {
   382  				assert.True(t, utf8.RuneCountInString(got) <= test.size, "too big")
   383  			}
   384  		})
   385  	}
   386  }