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 }