github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/vfscache/cache_test.go (about) 1 package vfscache 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "sort" 9 "testing" 10 "time" 11 12 _ "github.com/rclone/rclone/backend/local" // import the local backend 13 "github.com/rclone/rclone/fs" 14 "github.com/rclone/rclone/fs/config" 15 "github.com/rclone/rclone/fstest" 16 "github.com/rclone/rclone/lib/diskusage" 17 "github.com/rclone/rclone/vfs/vfscommon" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 // TestMain drives the tests 23 func TestMain(m *testing.M) { 24 fstest.TestMain(m) 25 } 26 27 // convert c.item to a string 28 func itemAsString(c *Cache) []string { 29 c.mu.Lock() 30 defer c.mu.Unlock() 31 var out []string 32 for name, item := range c.item { 33 out = append(out, fmt.Sprintf("name=%q opens=%d size=%d", filepath.ToSlash(name), item.opens, item.info.Size)) 34 } 35 sort.Strings(out) 36 return out 37 } 38 39 // convert c.item to a string 40 func itemSpaceAsString(c *Cache) []string { 41 c.mu.Lock() 42 defer c.mu.Unlock() 43 var out []string 44 for name, item := range c.item { 45 space := item.info.Rs.Size() 46 out = append(out, fmt.Sprintf("name=%q opens=%d size=%d space=%d", filepath.ToSlash(name), item.opens, item.info.Size, space)) 47 } 48 sort.Strings(out) 49 return out 50 } 51 52 // open an item and write to it 53 func itemWrite(t *testing.T, item *Item, contents string) { 54 require.NoError(t, item.Open(nil)) 55 _, err := item.WriteAt([]byte(contents), 0) 56 require.NoError(t, err) 57 } 58 59 func assertPathNotExist(t *testing.T, path string) { 60 _, err := os.Stat(path) 61 assert.True(t, os.IsNotExist(err)) 62 } 63 64 func assertPathExist(t *testing.T, path string) os.FileInfo { 65 fi, err := os.Stat(path) 66 assert.NoError(t, err) 67 return fi 68 } 69 70 type avInfo struct { 71 Remote string 72 Size int64 73 IsDir bool 74 } 75 76 var avInfos []avInfo 77 78 func addVirtual(remote string, size int64, isDir bool) error { 79 avInfos = append(avInfos, avInfo{ 80 Remote: remote, 81 Size: size, 82 IsDir: isDir, 83 }) 84 return nil 85 } 86 87 func newTestCacheOpt(t *testing.T, opt vfscommon.Options) (r *fstest.Run, c *Cache) { 88 r = fstest.NewRun(t) 89 90 ctx, cancel := context.WithCancel(context.Background()) 91 92 avInfos = nil 93 c, err := New(ctx, r.Fremote, &opt, addVirtual) 94 require.NoError(t, err) 95 96 t.Cleanup(func() { 97 err := c.CleanUp() 98 require.NoError(t, err) 99 assertPathNotExist(t, c.root) 100 cancel() 101 }) 102 103 return r, c 104 } 105 106 func newTestCache(t *testing.T) (r *fstest.Run, c *Cache) { 107 opt := vfscommon.DefaultOpt 108 109 // Disable the cache cleaner as it interferes with these tests 110 opt.CachePollInterval = 0 111 112 // Disable synchronous write 113 opt.WriteBack = 0 114 115 return newTestCacheOpt(t, opt) 116 } 117 118 func TestCacheNew(t *testing.T) { 119 r, c := newTestCache(t) 120 121 assert.Contains(t, c.root, "vfs") 122 assert.Contains(t, c.fcache.Root(), filepath.Base(r.Fremote.Root())) 123 assert.Equal(t, []string(nil), itemAsString(c)) 124 125 // createItemDir 126 p, err := c.createItemDir("potato") 127 require.NoError(t, err) 128 assert.Equal(t, "potato", filepath.Base(p)) 129 assert.Equal(t, []string(nil), itemAsString(c)) 130 131 fi := assertPathExist(t, filepath.Dir(p)) 132 assert.True(t, fi.IsDir()) 133 134 // get 135 item, _ := c.get("potato") 136 item2, _ := c.get("potato") 137 assert.Equal(t, item, item2) 138 assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second) 139 140 // open 141 assert.Equal(t, []string{ 142 `name="potato" opens=0 size=0`, 143 }, itemAsString(c)) 144 potato := c.Item("/potato") 145 require.NoError(t, potato.Open(nil)) 146 assert.Equal(t, []string{ 147 `name="potato" opens=1 size=0`, 148 }, itemAsString(c)) 149 assert.WithinDuration(t, time.Now(), potato.info.ATime, time.Second) 150 assert.Equal(t, 1, potato.opens) 151 152 // write the file 153 require.NoError(t, potato.Truncate(5)) 154 atime := time.Now() 155 potato.info.ATime = atime 156 157 assert.Equal(t, []string{ 158 `name="potato" opens=1 size=5`, 159 }, itemAsString(c)) 160 assert.True(t, atime.Equal(potato.info.ATime), fmt.Sprintf("%v != %v", atime, potato.info.ATime)) 161 162 // try purging with file open 163 c.purgeOld(10 * time.Second) 164 assertPathExist(t, p) 165 166 // close 167 assert.Equal(t, []string{ 168 `name="potato" opens=1 size=5`, 169 }, itemAsString(c)) 170 require.NoError(t, potato.Truncate(6)) 171 assert.Equal(t, []string{ 172 `name="potato" opens=1 size=6`, 173 }, itemAsString(c)) 174 require.NoError(t, potato.Close(nil)) 175 assert.Equal(t, []string{ 176 `name="potato" opens=0 size=6`, 177 }, itemAsString(c)) 178 item, _ = c.get("potato") 179 assert.WithinDuration(t, time.Now(), item.info.ATime, time.Second) 180 assert.Equal(t, 0, item.opens) 181 182 // try purging with file closed 183 c.purgeOld(10 * time.Second) 184 assertPathExist(t, p) 185 186 //.. purge again with -ve age 187 c.purgeOld(-10 * time.Second) 188 assertPathNotExist(t, p) 189 190 // clean - have tested the internals already 191 c.clean(false) 192 } 193 194 func TestCacheOpens(t *testing.T) { 195 _, c := newTestCache(t) 196 197 assert.Equal(t, []string(nil), itemAsString(c)) 198 potato := c.Item("potato") 199 require.NoError(t, potato.Open(nil)) 200 assert.Equal(t, []string{ 201 `name="potato" opens=1 size=0`, 202 }, itemAsString(c)) 203 require.NoError(t, potato.Open(nil)) 204 assert.Equal(t, []string{ 205 `name="potato" opens=2 size=0`, 206 }, itemAsString(c)) 207 require.NoError(t, potato.Close(nil)) 208 assert.Equal(t, []string{ 209 `name="potato" opens=1 size=0`, 210 }, itemAsString(c)) 211 require.NoError(t, potato.Close(nil)) 212 assert.Equal(t, []string{ 213 `name="potato" opens=0 size=0`, 214 }, itemAsString(c)) 215 216 require.NoError(t, potato.Open(nil)) 217 a1 := c.Item("a//b/c/d/one") 218 a2 := c.Item("a/b/c/d/e/two") 219 a3 := c.Item("a/b/c/d/e/f/three") 220 require.NoError(t, a1.Open(nil)) 221 require.NoError(t, a2.Open(nil)) 222 require.NoError(t, a3.Open(nil)) 223 assert.Equal(t, []string{ 224 `name="a/b/c/d/e/f/three" opens=1 size=0`, 225 `name="a/b/c/d/e/two" opens=1 size=0`, 226 `name="a/b/c/d/one" opens=1 size=0`, 227 `name="potato" opens=1 size=0`, 228 }, itemAsString(c)) 229 require.NoError(t, potato.Close(nil)) 230 require.NoError(t, a1.Close(nil)) 231 require.NoError(t, a2.Close(nil)) 232 require.NoError(t, a3.Close(nil)) 233 assert.Equal(t, []string{ 234 `name="a/b/c/d/e/f/three" opens=0 size=0`, 235 `name="a/b/c/d/e/two" opens=0 size=0`, 236 `name="a/b/c/d/one" opens=0 size=0`, 237 `name="potato" opens=0 size=0`, 238 }, itemAsString(c)) 239 } 240 241 // test the open, createItemDir, purge, close, purge sequence 242 func TestCacheOpenMkdir(t *testing.T) { 243 _, c := newTestCache(t) 244 245 // open 246 potato := c.Item("sub/potato") 247 require.NoError(t, potato.Open(nil)) 248 249 assert.Equal(t, []string{ 250 `name="sub/potato" opens=1 size=0`, 251 }, itemAsString(c)) 252 253 // createItemDir 254 p, err := c.createItemDir("sub/potato") 255 require.NoError(t, err) 256 assert.Equal(t, "potato", filepath.Base(p)) 257 assert.Equal(t, []string{ 258 `name="sub/potato" opens=1 size=0`, 259 }, itemAsString(c)) 260 261 // test directory exists 262 fi := assertPathExist(t, filepath.Dir(p)) 263 assert.True(t, fi.IsDir()) 264 265 // clean the cache 266 c.purgeOld(-10 * time.Second) 267 268 // test directory still exists 269 fi = assertPathExist(t, filepath.Dir(p)) 270 assert.True(t, fi.IsDir()) 271 272 // close 273 require.NoError(t, potato.Close(nil)) 274 275 assert.Equal(t, []string{ 276 `name="sub/potato" opens=0 size=0`, 277 }, itemAsString(c)) 278 279 // clean the cache 280 c.purgeOld(-10 * time.Second) 281 c.purgeEmptyDirs("", true) 282 283 assert.Equal(t, []string(nil), itemAsString(c)) 284 285 // test directory does not exist 286 assertPathNotExist(t, filepath.Dir(p)) 287 } 288 289 func TestCachePurgeOld(t *testing.T) { 290 _, c := newTestCache(t) 291 292 // Test funcs 293 c.purgeOld(-10 * time.Second) 294 295 potato2 := c.Item("sub/dir2/potato2") 296 require.NoError(t, potato2.Open(nil)) 297 potato := c.Item("sub/dir/potato") 298 require.NoError(t, potato.Open(nil)) 299 require.NoError(t, potato2.Close(nil)) 300 require.NoError(t, potato.Open(nil)) 301 302 assert.Equal(t, []string{ 303 `name="sub/dir/potato" opens=2 size=0`, 304 `name="sub/dir2/potato2" opens=0 size=0`, 305 }, itemAsString(c)) 306 307 c.purgeOld(-10 * time.Second) 308 309 assert.Equal(t, []string{ 310 `name="sub/dir/potato" opens=2 size=0`, 311 }, itemAsString(c)) 312 313 require.NoError(t, potato.Close(nil)) 314 315 assert.Equal(t, []string{ 316 `name="sub/dir/potato" opens=1 size=0`, 317 }, itemAsString(c)) 318 319 c.purgeOld(-10 * time.Second) 320 321 assert.Equal(t, []string{ 322 `name="sub/dir/potato" opens=1 size=0`, 323 }, itemAsString(c)) 324 325 require.NoError(t, potato.Close(nil)) 326 327 assert.Equal(t, []string{ 328 `name="sub/dir/potato" opens=0 size=0`, 329 }, itemAsString(c)) 330 331 c.purgeOld(10 * time.Second) 332 333 assert.Equal(t, []string{ 334 `name="sub/dir/potato" opens=0 size=0`, 335 }, itemAsString(c)) 336 337 c.purgeOld(-10 * time.Second) 338 339 assert.Equal(t, []string(nil), itemAsString(c)) 340 } 341 342 func TestCachePurgeOverQuota(t *testing.T) { 343 _, c := newTestCache(t) 344 345 // Test funcs 346 347 // Make some test files 348 potato := c.Item("sub/dir/potato") 349 itemWrite(t, potato, "hello") 350 351 potato2 := c.Item("sub/dir2/potato2") 352 itemWrite(t, potato2, "hello2") 353 354 assert.Equal(t, []string{ 355 `name="sub/dir/potato" opens=1 size=5`, 356 `name="sub/dir2/potato2" opens=1 size=6`, 357 }, itemAsString(c)) 358 359 // Check nothing removed 360 c.opt.CacheMaxSize = 1 361 c.purgeOverQuota() 362 363 // Close the files 364 require.NoError(t, potato.Close(nil)) 365 require.NoError(t, potato2.Close(nil)) 366 367 assert.Equal(t, []string{ 368 `name="sub/dir/potato" opens=0 size=5`, 369 `name="sub/dir2/potato2" opens=0 size=6`, 370 }, itemAsString(c)) 371 372 // Update the stats to read the total size 373 c.updateUsed() 374 375 // make potato2 definitely after potato 376 t1 := time.Now().Add(10 * time.Second) 377 potato2.info.ATime = t1 378 379 // Check only potato removed to get below quota 380 c.opt.CacheMaxSize = 10 381 c.purgeOverQuota() 382 assert.Equal(t, int64(6), c.used) 383 384 assert.Equal(t, []string{ 385 `name="sub/dir2/potato2" opens=0 size=6`, 386 }, itemAsString(c)) 387 388 // Put potato back 389 potato = c.Item("sub/dir/potato") 390 require.NoError(t, potato.Open(nil)) 391 require.NoError(t, potato.Truncate(5)) 392 require.NoError(t, potato.Close(nil)) 393 394 // Update the stats to read the total size 395 c.updateUsed() 396 397 assert.Equal(t, []string{ 398 `name="sub/dir/potato" opens=0 size=5`, 399 `name="sub/dir2/potato2" opens=0 size=6`, 400 }, itemAsString(c)) 401 402 // make potato definitely after potato2 403 t2 := t1.Add(20 * time.Second) 404 potato.info.ATime = t2 405 406 // Check only potato2 removed to get below quota 407 c.opt.CacheMaxSize = 10 408 c.purgeOverQuota() 409 assert.Equal(t, int64(5), c.used) 410 c.purgeEmptyDirs("", true) 411 412 assert.Equal(t, []string{ 413 `name="sub/dir/potato" opens=0 size=5`, 414 }, itemAsString(c)) 415 416 // Now purge everything 417 c.opt.CacheMaxSize = 1 418 c.purgeOverQuota() 419 assert.Equal(t, int64(0), c.used) 420 c.purgeEmptyDirs("", true) 421 422 assert.Equal(t, []string(nil), itemAsString(c)) 423 424 // Check nothing left behind 425 c.clean(false) 426 assert.Equal(t, int64(0), c.used) 427 assert.Equal(t, []string(nil), itemAsString(c)) 428 } 429 430 func TestCachePurgeMinFreeSpace(t *testing.T) { 431 du, err := diskusage.New(config.GetCacheDir()) 432 if err == diskusage.ErrUnsupported { 433 t.Skip(err) 434 } 435 // We've tested the quota mechanism already, so just test the 436 // min free space quota is working. 437 _, c := newTestCache(t) 438 439 // First set free space quota very small and check it is OK 440 c.opt.CacheMinFreeSpace = 1 441 assert.True(t, c.minFreeSpaceQuotaOK()) 442 assert.True(t, c.quotasOK()) 443 444 // Now set it a bit larger than the current disk available and check it is BAD 445 c.opt.CacheMinFreeSpace = fs.SizeSuffix(du.Available) + fs.Gibi 446 assert.False(t, c.minFreeSpaceQuotaOK()) 447 assert.False(t, c.quotasOK()) 448 } 449 450 // test reset clean files 451 func TestCachePurgeClean(t *testing.T) { 452 r, c := newItemTestCache(t) 453 contents, obj, potato1 := newFile(t, r, c, "existing") 454 _ = contents 455 456 // Open the object to create metadata for it 457 require.NoError(t, potato1.Open(obj)) 458 require.NoError(t, potato1.Open(obj)) 459 460 size, err := potato1.GetSize() 461 require.NoError(t, err) 462 assert.Equal(t, int64(100), size) 463 464 // Read something to instantiate the cache file 465 buf := make([]byte, 10) 466 _, err = potato1.ReadAt(buf, 10) 467 require.NoError(t, err) 468 469 // Test cache file present 470 _, err = os.Stat(potato1.c.toOSPath(potato1.name)) 471 require.NoError(t, err) 472 473 // Add some potatoes 474 potato2 := c.Item("sub/dir/potato2") 475 require.NoError(t, potato2.Open(nil)) 476 require.NoError(t, potato2.Truncate(5)) 477 478 potato3 := c.Item("sub/dir/potato3") 479 require.NoError(t, potato3.Open(nil)) 480 require.NoError(t, potato3.Truncate(6)) 481 482 c.updateUsed() 483 c.opt.CacheMaxSize = 1 484 c.purgeClean() 485 assert.Equal(t, []string{ 486 `name="existing" opens=2 size=100 space=0`, 487 `name="sub/dir/potato2" opens=1 size=5 space=5`, 488 `name="sub/dir/potato3" opens=1 size=6 space=6`, 489 }, itemSpaceAsString(c)) 490 assert.Equal(t, int64(11), c.used) 491 492 require.NoError(t, potato2.Close(nil)) 493 c.opt.CacheMaxSize = 1 494 c.purgeClean() 495 assert.Equal(t, []string{ 496 `name="existing" opens=2 size=100 space=0`, 497 `name="sub/dir/potato3" opens=1 size=6 space=6`, 498 }, itemSpaceAsString(c)) 499 assert.Equal(t, int64(6), c.used) 500 501 require.NoError(t, potato1.Close(nil)) 502 require.NoError(t, potato1.Close(nil)) 503 require.NoError(t, potato3.Close(nil)) 504 505 // Remove all files now. The are all not in use. 506 // purgeClean does not remove empty cache files. purgeOverQuota does. 507 // So we use purgeOverQuota here for the cleanup. 508 c.opt.CacheMaxSize = 1 509 c.purgeOverQuota() 510 511 c.purgeEmptyDirs("", true) 512 513 assert.Equal(t, []string(nil), itemAsString(c)) 514 } 515 516 func TestCacheInUse(t *testing.T) { 517 _, c := newTestCache(t) 518 519 assert.False(t, c.InUse("potato")) 520 521 potato := c.Item("potato") 522 523 assert.False(t, c.InUse("potato")) 524 525 require.NoError(t, potato.Open(nil)) 526 527 assert.True(t, c.InUse("potato")) 528 529 require.NoError(t, potato.Close(nil)) 530 531 assert.False(t, c.InUse("potato")) 532 } 533 534 func TestCacheDirtyItem(t *testing.T) { 535 _, c := newTestCache(t) 536 537 assert.Nil(t, c.DirtyItem("potato")) 538 539 potato := c.Item("potato") 540 541 assert.Nil(t, c.DirtyItem("potato")) 542 543 require.NoError(t, potato.Open(nil)) 544 require.NoError(t, potato.Truncate(5)) 545 546 assert.Equal(t, potato, c.DirtyItem("potato")) 547 548 require.NoError(t, potato.Close(nil)) 549 550 assert.Nil(t, c.DirtyItem("potato")) 551 } 552 553 func TestCacheExistsAndRemove(t *testing.T) { 554 _, c := newTestCache(t) 555 556 assert.False(t, c.Exists("potato")) 557 558 potato := c.Item("potato") 559 560 assert.False(t, c.Exists("potato")) 561 562 require.NoError(t, potato.Open(nil)) 563 564 assert.True(t, c.Exists("potato")) 565 566 require.NoError(t, potato.Close(nil)) 567 568 assert.True(t, c.Exists("potato")) 569 570 c.Remove("potato") 571 572 assert.False(t, c.Exists("potato")) 573 574 } 575 576 func TestCacheRename(t *testing.T) { 577 _, c := newTestCache(t) 578 579 // setup 580 581 assert.False(t, c.Exists("potato")) 582 potato := c.Item("potato") 583 require.NoError(t, potato.Open(nil)) 584 require.NoError(t, potato.Close(nil)) 585 assert.True(t, c.Exists("potato")) 586 587 osPath := c.toOSPath("potato") 588 osPathMeta := c.toOSPathMeta("potato") 589 assertPathExist(t, osPath) 590 assertPathExist(t, osPathMeta) 591 592 // rename potato -> newPotato 593 594 require.NoError(t, c.Rename("potato", "newPotato", nil)) 595 assertPathNotExist(t, osPath) 596 assertPathNotExist(t, osPathMeta) 597 assert.False(t, c.Exists("potato")) 598 599 osPath = c.toOSPath("newPotato") 600 osPathMeta = c.toOSPathMeta("newPotato") 601 assertPathExist(t, osPath) 602 assertPathExist(t, osPathMeta) 603 assert.True(t, c.Exists("newPotato")) 604 605 // rename newPotato -> sub/newPotato 606 607 require.NoError(t, c.Rename("newPotato", "sub/newPotato", nil)) 608 assertPathNotExist(t, osPath) 609 assertPathNotExist(t, osPathMeta) 610 assert.False(t, c.Exists("potato")) 611 612 osPath = c.toOSPath("sub/newPotato") 613 osPathMeta = c.toOSPathMeta("sub/newPotato") 614 assertPathExist(t, osPath) 615 assertPathExist(t, osPathMeta) 616 assert.True(t, c.Exists("sub/newPotato")) 617 618 // remove 619 620 c.Remove("sub/newPotato") 621 assertPathNotExist(t, osPath) 622 assertPathNotExist(t, osPathMeta) 623 assert.False(t, c.Exists("sub/newPotato")) 624 625 // nonexistent file - is ignored 626 assert.NoError(t, c.Rename("nonexist", "nonexist2", nil)) 627 } 628 629 func TestCacheCleaner(t *testing.T) { 630 opt := vfscommon.DefaultOpt 631 opt.CachePollInterval = 10 * time.Millisecond 632 opt.CacheMaxAge = 20 * time.Millisecond 633 _, c := newTestCacheOpt(t, opt) 634 635 time.Sleep(2 * opt.CachePollInterval) 636 637 potato := c.Item("potato") 638 potato2, found := c.get("potato") 639 assert.Equal(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2)) 640 assert.True(t, found) 641 642 for i := 0; i < 100; i++ { 643 time.Sleep(10 * opt.CachePollInterval) 644 potato2, found = c.get("potato") 645 if !found { 646 break 647 } 648 } 649 650 assert.NotEqual(t, fmt.Sprintf("%p", potato), fmt.Sprintf("%p", potato2)) 651 assert.False(t, found) 652 } 653 654 func TestCacheSetModTime(t *testing.T) { 655 _, c := newTestCache(t) 656 657 t1 := time.Date(2010, 1, 2, 3, 4, 5, 9, time.UTC) 658 659 potato := c.Item("potato") 660 require.NoError(t, potato.Open(nil)) 661 require.NoError(t, potato.Truncate(5)) 662 require.NoError(t, potato.Close(nil)) 663 664 c.SetModTime("potato", t1) 665 osPath := potato.c.toOSPath("potato") 666 fi, err := os.Stat(osPath) 667 require.NoError(t, err) 668 669 fstest.AssertTimeEqualWithPrecision(t, "potato", t1, fi.ModTime(), time.Second) 670 } 671 672 func TestCacheTotaInUse(t *testing.T) { 673 _, c := newTestCache(t) 674 675 assert.Equal(t, int(0), c.TotalInUse()) 676 677 potato := c.Item("potato") 678 assert.Equal(t, int(0), c.TotalInUse()) 679 680 require.NoError(t, potato.Open(nil)) 681 assert.Equal(t, int(1), c.TotalInUse()) 682 683 require.NoError(t, potato.Truncate(5)) 684 assert.Equal(t, int(1), c.TotalInUse()) 685 686 potato2 := c.Item("potato2") 687 assert.Equal(t, int(1), c.TotalInUse()) 688 689 require.NoError(t, potato2.Open(nil)) 690 assert.Equal(t, int(2), c.TotalInUse()) 691 692 require.NoError(t, potato2.Close(nil)) 693 assert.Equal(t, int(1), c.TotalInUse()) 694 695 require.NoError(t, potato.Close(nil)) 696 assert.Equal(t, int(0), c.TotalInUse()) 697 } 698 699 func TestCacheDump(t *testing.T) { 700 _, c := newTestCache(t) 701 702 out := (*Cache)(nil).Dump() 703 assert.Equal(t, "Cache: <nil>\n", out) 704 705 out = c.Dump() 706 assert.Equal(t, "Cache{\n}\n", out) 707 708 c.Item("potato") 709 710 out = c.Dump() 711 want := "Cache{\n\t\"potato\": " 712 assert.Equal(t, want, out[:len(want)]) 713 714 c.Remove("potato") 715 716 out = c.Dump() 717 assert.Equal(t, "Cache{\n}\n", out) 718 } 719 720 func TestCacheStats(t *testing.T) { 721 _, c := newTestCache(t) 722 723 out := c.Stats() 724 assert.Equal(t, int64(0), out["bytesUsed"]) 725 assert.Equal(t, 0, out["erroredFiles"]) 726 assert.Equal(t, 0, out["files"]) 727 assert.Equal(t, 0, out["uploadsInProgress"]) 728 assert.Equal(t, 0, out["uploadsQueued"]) 729 }