github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/walk/walk_test.go (about) 1 package walk 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "strings" 8 "sync" 9 "testing" 10 11 "github.com/pkg/errors" 12 "github.com/rclone/rclone/fs" 13 _ "github.com/rclone/rclone/fs/accounting" 14 "github.com/rclone/rclone/fs/filter" 15 "github.com/rclone/rclone/fs/fserrors" 16 "github.com/rclone/rclone/fstest/mockdir" 17 "github.com/rclone/rclone/fstest/mockfs" 18 "github.com/rclone/rclone/fstest/mockobject" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 var errDirNotFound, errorBoom error 24 25 func init() { 26 errDirNotFound = fserrors.FsError(fs.ErrorDirNotFound) 27 fserrors.Count(errDirNotFound) 28 errorBoom = fserrors.FsError(errors.New("boom")) 29 fserrors.Count(errorBoom) 30 } 31 32 type ( 33 listResult struct { 34 entries fs.DirEntries 35 err error 36 } 37 38 listResults map[string]listResult 39 40 errorMap map[string]error 41 42 listDirs struct { 43 mu sync.Mutex 44 t *testing.T 45 fs fs.Fs 46 includeAll bool 47 results listResults 48 walkResults listResults 49 walkErrors errorMap 50 finalError error 51 checkMaps bool 52 maxLevel int 53 } 54 ) 55 56 func newListDirs(t *testing.T, f fs.Fs, includeAll bool, results listResults, walkErrors errorMap, finalError error) *listDirs { 57 return &listDirs{ 58 t: t, 59 fs: f, 60 includeAll: includeAll, 61 results: results, 62 walkErrors: walkErrors, 63 walkResults: listResults{}, 64 finalError: finalError, 65 checkMaps: true, 66 maxLevel: -1, 67 } 68 } 69 70 // NoCheckMaps marks the maps as to be ignored at the end 71 func (ls *listDirs) NoCheckMaps() *listDirs { 72 ls.checkMaps = false 73 return ls 74 } 75 76 // SetLevel(1) turns off recursion 77 func (ls *listDirs) SetLevel(maxLevel int) *listDirs { 78 ls.maxLevel = maxLevel 79 return ls 80 } 81 82 // ListDir returns the expected listing for the directory 83 func (ls *listDirs) ListDir(ctx context.Context, f fs.Fs, includeAll bool, dir string) (entries fs.DirEntries, err error) { 84 ls.mu.Lock() 85 defer ls.mu.Unlock() 86 assert.Equal(ls.t, ls.fs, f) 87 assert.Equal(ls.t, ls.includeAll, includeAll) 88 89 // Fetch results for this path 90 result, ok := ls.results[dir] 91 if !ok { 92 ls.t.Errorf("Unexpected list of %q", dir) 93 return nil, errors.New("unexpected list") 94 } 95 delete(ls.results, dir) 96 97 // Put expected results for call of WalkFn 98 ls.walkResults[dir] = result 99 100 return result.entries, result.err 101 } 102 103 // ListR returns the expected listing for the directory using ListR 104 func (ls *listDirs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) { 105 ls.mu.Lock() 106 defer ls.mu.Unlock() 107 108 var errorReturn error 109 for dirPath, result := range ls.results { 110 // Put expected results for call of WalkFn 111 // Note that we don't call the function at all if we got an error 112 if result.err != nil { 113 errorReturn = result.err 114 } 115 if errorReturn == nil { 116 err = callback(result.entries) 117 require.NoError(ls.t, err) 118 ls.walkResults[dirPath] = result 119 } 120 } 121 ls.results = listResults{} 122 return errorReturn 123 } 124 125 // IsFinished checks everything expected was used up 126 func (ls *listDirs) IsFinished() { 127 if ls.checkMaps { 128 assert.Equal(ls.t, errorMap{}, ls.walkErrors) 129 assert.Equal(ls.t, listResults{}, ls.results) 130 assert.Equal(ls.t, listResults{}, ls.walkResults) 131 } 132 } 133 134 // WalkFn is called by the walk to test the expectations 135 func (ls *listDirs) WalkFn(dir string, entries fs.DirEntries, err error) error { 136 ls.mu.Lock() 137 defer ls.mu.Unlock() 138 // ls.t.Logf("WalkFn(%q, %v, %q)", dir, entries, err) 139 140 // Fetch expected entries and err 141 result, ok := ls.walkResults[dir] 142 if !ok { 143 ls.t.Errorf("Unexpected walk of %q (result not found)", dir) 144 return errors.New("result not found") 145 } 146 delete(ls.walkResults, dir) 147 148 // Check arguments are as expected 149 assert.Equal(ls.t, result.entries, entries) 150 assert.Equal(ls.t, result.err, err) 151 152 // Fetch return value 153 returnErr, ok := ls.walkErrors[dir] 154 if !ok { 155 ls.t.Errorf("Unexpected walk of %q (error not found)", dir) 156 return errors.New("error not found") 157 } 158 delete(ls.walkErrors, dir) 159 160 return returnErr 161 } 162 163 // Walk does the walk and tests the expectations 164 func (ls *listDirs) Walk() { 165 err := walk(context.Background(), nil, "", ls.includeAll, ls.maxLevel, ls.WalkFn, ls.ListDir) 166 assert.Equal(ls.t, ls.finalError, err) 167 ls.IsFinished() 168 } 169 170 // WalkR does the walkR and tests the expectations 171 func (ls *listDirs) WalkR() { 172 err := walkR(context.Background(), nil, "", ls.includeAll, ls.maxLevel, ls.WalkFn, ls.ListR) 173 assert.Equal(ls.t, ls.finalError, err) 174 if ls.finalError == nil { 175 ls.IsFinished() 176 } 177 } 178 179 func testWalkEmpty(t *testing.T) *listDirs { 180 return newListDirs(t, nil, false, 181 listResults{ 182 "": {entries: fs.DirEntries{}, err: nil}, 183 }, 184 errorMap{ 185 "": nil, 186 }, 187 nil, 188 ) 189 } 190 func TestWalkEmpty(t *testing.T) { testWalkEmpty(t).Walk() } 191 func TestWalkREmpty(t *testing.T) { testWalkEmpty(t).WalkR() } 192 193 func testWalkEmptySkip(t *testing.T) *listDirs { 194 return newListDirs(t, nil, true, 195 listResults{ 196 "": {entries: fs.DirEntries{}, err: nil}, 197 }, 198 errorMap{ 199 "": ErrorSkipDir, 200 }, 201 nil, 202 ) 203 } 204 func TestWalkEmptySkip(t *testing.T) { testWalkEmptySkip(t).Walk() } 205 func TestWalkREmptySkip(t *testing.T) { testWalkEmptySkip(t).WalkR() } 206 207 func testWalkNotFound(t *testing.T) *listDirs { 208 return newListDirs(t, nil, true, 209 listResults{ 210 "": {err: errDirNotFound}, 211 }, 212 errorMap{ 213 "": errDirNotFound, 214 }, 215 errDirNotFound, 216 ) 217 } 218 func TestWalkNotFound(t *testing.T) { testWalkNotFound(t).Walk() } 219 func TestWalkRNotFound(t *testing.T) { testWalkNotFound(t).WalkR() } 220 221 func TestWalkNotFoundMaskError(t *testing.T) { 222 // this doesn't work for WalkR 223 newListDirs(t, nil, true, 224 listResults{ 225 "": {err: errDirNotFound}, 226 }, 227 errorMap{ 228 "": nil, 229 }, 230 nil, 231 ).Walk() 232 } 233 234 func TestWalkNotFoundSkipError(t *testing.T) { 235 // this doesn't work for WalkR 236 newListDirs(t, nil, true, 237 listResults{ 238 "": {err: errDirNotFound}, 239 }, 240 errorMap{ 241 "": ErrorSkipDir, 242 }, 243 nil, 244 ).Walk() 245 } 246 247 func testWalkLevels(t *testing.T, maxLevel int) *listDirs { 248 da := mockdir.New("a") 249 oA := mockobject.Object("A") 250 db := mockdir.New("a/b") 251 oB := mockobject.Object("a/B") 252 dc := mockdir.New("a/b/c") 253 oC := mockobject.Object("a/b/C") 254 dd := mockdir.New("a/b/c/d") 255 oD := mockobject.Object("a/b/c/D") 256 return newListDirs(t, nil, false, 257 listResults{ 258 "": {entries: fs.DirEntries{oA, da}, err: nil}, 259 "a": {entries: fs.DirEntries{oB, db}, err: nil}, 260 "a/b": {entries: fs.DirEntries{oC, dc}, err: nil}, 261 "a/b/c": {entries: fs.DirEntries{oD, dd}, err: nil}, 262 "a/b/c/d": {entries: fs.DirEntries{}, err: nil}, 263 }, 264 errorMap{ 265 "": nil, 266 "a": nil, 267 "a/b": nil, 268 "a/b/c": nil, 269 "a/b/c/d": nil, 270 }, 271 nil, 272 ).SetLevel(maxLevel) 273 } 274 func TestWalkLevels(t *testing.T) { testWalkLevels(t, -1).Walk() } 275 func TestWalkRLevels(t *testing.T) { testWalkLevels(t, -1).WalkR() } 276 func TestWalkLevelsNoRecursive10(t *testing.T) { testWalkLevels(t, 10).Walk() } 277 func TestWalkRLevelsNoRecursive10(t *testing.T) { testWalkLevels(t, 10).WalkR() } 278 279 func TestWalkNDirTree(t *testing.T) { 280 ls := testWalkLevels(t, -1) 281 entries, err := walkNDirTree(context.Background(), nil, "", ls.includeAll, ls.maxLevel, ls.ListDir) 282 require.NoError(t, err) 283 assert.Equal(t, `/ 284 A 285 a/ 286 a/ 287 B 288 b/ 289 a/b/ 290 C 291 c/ 292 a/b/c/ 293 D 294 d/ 295 a/b/c/d/ 296 `, entries.String()) 297 } 298 299 func testWalkLevelsNoRecursive(t *testing.T) *listDirs { 300 da := mockdir.New("a") 301 oA := mockobject.Object("A") 302 return newListDirs(t, nil, false, 303 listResults{ 304 "": {entries: fs.DirEntries{oA, da}, err: nil}, 305 }, 306 errorMap{ 307 "": nil, 308 }, 309 nil, 310 ).SetLevel(1) 311 } 312 func TestWalkLevelsNoRecursive(t *testing.T) { testWalkLevelsNoRecursive(t).Walk() } 313 func TestWalkRLevelsNoRecursive(t *testing.T) { testWalkLevelsNoRecursive(t).WalkR() } 314 315 func testWalkLevels2(t *testing.T) *listDirs { 316 da := mockdir.New("a") 317 oA := mockobject.Object("A") 318 db := mockdir.New("a/b") 319 oB := mockobject.Object("a/B") 320 return newListDirs(t, nil, false, 321 listResults{ 322 "": {entries: fs.DirEntries{oA, da}, err: nil}, 323 "a": {entries: fs.DirEntries{oB, db}, err: nil}, 324 }, 325 errorMap{ 326 "": nil, 327 "a": nil, 328 }, 329 nil, 330 ).SetLevel(2) 331 } 332 func TestWalkLevels2(t *testing.T) { testWalkLevels2(t).Walk() } 333 func TestWalkRLevels2(t *testing.T) { testWalkLevels2(t).WalkR() } 334 335 func testWalkSkip(t *testing.T) *listDirs { 336 da := mockdir.New("a") 337 db := mockdir.New("a/b") 338 dc := mockdir.New("a/b/c") 339 return newListDirs(t, nil, false, 340 listResults{ 341 "": {entries: fs.DirEntries{da}, err: nil}, 342 "a": {entries: fs.DirEntries{db}, err: nil}, 343 "a/b": {entries: fs.DirEntries{dc}, err: nil}, 344 }, 345 errorMap{ 346 "": nil, 347 "a": nil, 348 "a/b": ErrorSkipDir, 349 }, 350 nil, 351 ) 352 } 353 func TestWalkSkip(t *testing.T) { testWalkSkip(t).Walk() } 354 func TestWalkRSkip(t *testing.T) { testWalkSkip(t).WalkR() } 355 356 func walkErrors(t *testing.T, expectedErr error) *listDirs { 357 lr := listResults{} 358 em := errorMap{} 359 de := make(fs.DirEntries, 10) 360 for i := range de { 361 path := string('0' + i) 362 de[i] = mockdir.New(path) 363 lr[path] = listResult{entries: nil, err: fs.ErrorDirNotFound} 364 em[path] = fs.ErrorDirNotFound 365 } 366 lr[""] = listResult{entries: de, err: nil} 367 em[""] = nil 368 return newListDirs(t, nil, true, 369 lr, 370 em, 371 expectedErr, 372 ).NoCheckMaps() 373 } 374 375 func testWalkErrors(t *testing.T) *listDirs { 376 return walkErrors(t, errDirNotFound) 377 } 378 379 func testWalkRErrors(t *testing.T) *listDirs { 380 return walkErrors(t, fs.ErrorDirNotFound) 381 } 382 383 func TestWalkErrors(t *testing.T) { testWalkErrors(t).Walk() } 384 func TestWalkRErrors(t *testing.T) { testWalkRErrors(t).WalkR() } 385 386 func makeTree(level int, terminalErrors bool) (listResults, errorMap) { 387 lr := listResults{} 388 em := errorMap{} 389 var fill func(path string, level int) 390 fill = func(path string, level int) { 391 de := fs.DirEntries{} 392 if level > 0 { 393 for _, a := range "0123456789" { 394 subPath := string(a) 395 if path != "" { 396 subPath = path + "/" + subPath 397 } 398 de = append(de, mockdir.New(subPath)) 399 fill(subPath, level-1) 400 } 401 } 402 lr[path] = listResult{entries: de, err: nil} 403 em[path] = nil 404 if level == 0 && terminalErrors { 405 em[path] = errorBoom 406 } 407 } 408 fill("", level) 409 return lr, em 410 } 411 412 func testWalkMulti(t *testing.T) *listDirs { 413 lr, em := makeTree(3, false) 414 return newListDirs(t, nil, true, 415 lr, 416 em, 417 nil, 418 ) 419 } 420 func TestWalkMulti(t *testing.T) { testWalkMulti(t).Walk() } 421 func TestWalkRMulti(t *testing.T) { testWalkMulti(t).WalkR() } 422 423 func testWalkMultiErrors(t *testing.T) *listDirs { 424 lr, em := makeTree(3, true) 425 return newListDirs(t, nil, true, 426 lr, 427 em, 428 errorBoom, 429 ).NoCheckMaps() 430 } 431 func TestWalkMultiErrors(t *testing.T) { testWalkMultiErrors(t).Walk() } 432 func TestWalkRMultiErrors(t *testing.T) { testWalkMultiErrors(t).Walk() } 433 434 // a very simple listRcallback function 435 func makeListRCallback(entries fs.DirEntries, err error) fs.ListRFn { 436 return func(ctx context.Context, dir string, callback fs.ListRCallback) error { 437 if err == nil { 438 err = callback(entries) 439 } 440 return err 441 } 442 } 443 444 func TestWalkRDirTree(t *testing.T) { 445 for _, test := range []struct { 446 entries fs.DirEntries 447 want string 448 err error 449 root string 450 level int 451 }{ 452 {fs.DirEntries{}, "/\n", nil, "", -1}, 453 {fs.DirEntries{mockobject.Object("a")}, `/ 454 a 455 `, nil, "", -1}, 456 {fs.DirEntries{mockobject.Object("a/b")}, `/ 457 a/ 458 a/ 459 b 460 `, nil, "", -1}, 461 {fs.DirEntries{mockobject.Object("a/b/c/d")}, `/ 462 a/ 463 a/ 464 b/ 465 a/b/ 466 c/ 467 a/b/c/ 468 d 469 `, nil, "", -1}, 470 {fs.DirEntries{mockobject.Object("a")}, "", errorBoom, "", -1}, 471 {fs.DirEntries{ 472 mockobject.Object("0/1/2/3"), 473 mockobject.Object("4/5/6/7"), 474 mockobject.Object("8/9/a/b"), 475 mockobject.Object("c/d/e/f"), 476 mockobject.Object("g/h/i/j"), 477 mockobject.Object("k/l/m/n"), 478 mockobject.Object("o/p/q/r"), 479 mockobject.Object("s/t/u/v"), 480 mockobject.Object("w/x/y/z"), 481 }, `/ 482 0/ 483 4/ 484 8/ 485 c/ 486 g/ 487 k/ 488 o/ 489 s/ 490 w/ 491 0/ 492 1/ 493 0/1/ 494 2/ 495 0/1/2/ 496 3 497 4/ 498 5/ 499 4/5/ 500 6/ 501 4/5/6/ 502 7 503 8/ 504 9/ 505 8/9/ 506 a/ 507 8/9/a/ 508 b 509 c/ 510 d/ 511 c/d/ 512 e/ 513 c/d/e/ 514 f 515 g/ 516 h/ 517 g/h/ 518 i/ 519 g/h/i/ 520 j 521 k/ 522 l/ 523 k/l/ 524 m/ 525 k/l/m/ 526 n 527 o/ 528 p/ 529 o/p/ 530 q/ 531 o/p/q/ 532 r 533 s/ 534 t/ 535 s/t/ 536 u/ 537 s/t/u/ 538 v 539 w/ 540 x/ 541 w/x/ 542 y/ 543 w/x/y/ 544 z 545 `, nil, "", -1}, 546 {fs.DirEntries{ 547 mockobject.Object("a/b/c/d/e/f1"), 548 mockobject.Object("a/b/c/d/e/f2"), 549 mockobject.Object("a/b/c/d/e/f3"), 550 }, `a/b/c/ 551 d/ 552 a/b/c/d/ 553 e/ 554 a/b/c/d/e/ 555 f1 556 f2 557 f3 558 `, nil, "a/b/c", -1}, 559 {fs.DirEntries{ 560 mockobject.Object("A"), 561 mockobject.Object("a/B"), 562 mockobject.Object("a/b/C"), 563 mockobject.Object("a/b/c/D"), 564 mockobject.Object("a/b/c/d/E"), 565 }, `/ 566 A 567 a/ 568 a/ 569 B 570 b/ 571 `, nil, "", 2}, 572 {fs.DirEntries{ 573 mockobject.Object("a/b/c"), 574 mockobject.Object("a/b/c/d/e"), 575 }, `/ 576 a/ 577 a/ 578 b/ 579 `, nil, "", 2}, 580 } { 581 r, err := walkRDirTree(context.Background(), nil, test.root, true, test.level, makeListRCallback(test.entries, test.err)) 582 assert.Equal(t, test.err, err, fmt.Sprintf("%+v", test)) 583 assert.Equal(t, test.want, r.String(), fmt.Sprintf("%+v", test)) 584 } 585 } 586 587 func TestWalkRDirTreeExclude(t *testing.T) { 588 for _, test := range []struct { 589 entries fs.DirEntries 590 want string 591 err error 592 root string 593 level int 594 excludeFile string 595 includeAll bool 596 }{ 597 {fs.DirEntries{mockobject.Object("a"), mockobject.Object("ignore")}, "", nil, "", -1, "ignore", false}, 598 {fs.DirEntries{mockobject.Object("a")}, `/ 599 a 600 `, nil, "", -1, "ignore", false}, 601 {fs.DirEntries{ 602 mockobject.Object("a"), 603 mockobject.Object("b/b"), 604 mockobject.Object("b/.ignore"), 605 }, `/ 606 a 607 `, nil, "", -1, ".ignore", false}, 608 {fs.DirEntries{ 609 mockobject.Object("a"), 610 mockobject.Object("b/.ignore"), 611 mockobject.Object("b/b"), 612 }, `/ 613 a 614 b/ 615 b/ 616 .ignore 617 b 618 `, nil, "", -1, ".ignore", true}, 619 {fs.DirEntries{ 620 mockobject.Object("a"), 621 mockobject.Object("b/b"), 622 mockobject.Object("b/c/d/e"), 623 mockobject.Object("b/c/ign"), 624 mockobject.Object("b/c/x"), 625 }, `/ 626 a 627 b/ 628 b/ 629 b 630 `, nil, "", -1, "ign", false}, 631 {fs.DirEntries{ 632 mockobject.Object("a"), 633 mockobject.Object("b/b"), 634 mockobject.Object("b/c/d/e"), 635 mockobject.Object("b/c/ign"), 636 mockobject.Object("b/c/x"), 637 }, `/ 638 a 639 b/ 640 b/ 641 b 642 c/ 643 b/c/ 644 d/ 645 ign 646 x 647 b/c/d/ 648 e 649 `, nil, "", -1, "ign", true}, 650 } { 651 filter.Active.Opt.ExcludeFile = test.excludeFile 652 r, err := walkRDirTree(context.Background(), nil, test.root, test.includeAll, test.level, makeListRCallback(test.entries, test.err)) 653 assert.Equal(t, test.err, err, fmt.Sprintf("%+v", test)) 654 assert.Equal(t, test.want, r.String(), fmt.Sprintf("%+v", test)) 655 } 656 // Set to default value, to avoid side effects 657 filter.Active.Opt.ExcludeFile = "" 658 } 659 660 func TestListType(t *testing.T) { 661 assert.Equal(t, true, ListObjects.Objects()) 662 assert.Equal(t, false, ListObjects.Dirs()) 663 assert.Equal(t, false, ListDirs.Objects()) 664 assert.Equal(t, true, ListDirs.Dirs()) 665 assert.Equal(t, true, ListAll.Objects()) 666 assert.Equal(t, true, ListAll.Dirs()) 667 668 var ( 669 a = mockobject.Object("a") 670 b = mockobject.Object("b") 671 dir = mockdir.New("dir") 672 adir = mockobject.Object("dir/a") 673 dir2 = mockdir.New("dir2") 674 origEntries = fs.DirEntries{ 675 a, b, dir, adir, dir2, 676 } 677 dirEntries = fs.DirEntries{ 678 dir, dir2, 679 } 680 objEntries = fs.DirEntries{ 681 a, b, adir, 682 } 683 ) 684 copyOrigEntries := func() (out fs.DirEntries) { 685 out = make(fs.DirEntries, len(origEntries)) 686 copy(out, origEntries) 687 return out 688 } 689 690 got := copyOrigEntries() 691 ListAll.Filter(&got) 692 assert.Equal(t, origEntries, got) 693 694 got = copyOrigEntries() 695 ListObjects.Filter(&got) 696 assert.Equal(t, objEntries, got) 697 698 got = copyOrigEntries() 699 ListDirs.Filter(&got) 700 assert.Equal(t, dirEntries, got) 701 } 702 703 func TestListR(t *testing.T) { 704 objects := fs.DirEntries{ 705 mockobject.Object("a"), 706 mockobject.Object("b"), 707 mockdir.New("dir"), 708 mockobject.Object("dir/a"), 709 mockobject.Object("dir/b"), 710 mockobject.Object("dir/c"), 711 } 712 f := mockfs.NewFs("mock", "/") 713 var got []string 714 clearCallback := func() { 715 got = nil 716 } 717 callback := func(entries fs.DirEntries) error { 718 for _, entry := range entries { 719 got = append(got, entry.Remote()) 720 } 721 return nil 722 } 723 doListR := func(ctx context.Context, dir string, callback fs.ListRCallback) error { 724 var os fs.DirEntries 725 for _, o := range objects { 726 if dir == "" || strings.HasPrefix(o.Remote(), dir+"/") { 727 os = append(os, o) 728 } 729 } 730 return callback(os) 731 } 732 733 // Setup filter 734 oldFilter := filter.Active 735 defer func() { 736 filter.Active = oldFilter 737 }() 738 739 var err error 740 filter.Active, err = filter.NewFilter(nil) 741 require.NoError(t, err) 742 require.NoError(t, filter.Active.AddRule("+ b")) 743 require.NoError(t, filter.Active.AddRule("- *")) 744 745 // Base case 746 clearCallback() 747 err = listR(context.Background(), f, "", true, ListAll, callback, doListR, false) 748 require.NoError(t, err) 749 require.Equal(t, []string{"a", "b", "dir", "dir/a", "dir/b", "dir/c"}, got) 750 751 // Base case - with Objects 752 clearCallback() 753 err = listR(context.Background(), f, "", true, ListObjects, callback, doListR, false) 754 require.NoError(t, err) 755 require.Equal(t, []string{"a", "b", "dir/a", "dir/b", "dir/c"}, got) 756 757 // Base case - with Dirs 758 clearCallback() 759 err = listR(context.Background(), f, "", true, ListDirs, callback, doListR, false) 760 require.NoError(t, err) 761 require.Equal(t, []string{"dir"}, got) 762 763 // With filter 764 clearCallback() 765 err = listR(context.Background(), f, "", false, ListAll, callback, doListR, false) 766 require.NoError(t, err) 767 require.Equal(t, []string{"b", "dir", "dir/b"}, got) 768 769 // With filter - with Objects 770 clearCallback() 771 err = listR(context.Background(), f, "", false, ListObjects, callback, doListR, false) 772 require.NoError(t, err) 773 require.Equal(t, []string{"b", "dir/b"}, got) 774 775 // With filter - with Dir 776 clearCallback() 777 err = listR(context.Background(), f, "", false, ListDirs, callback, doListR, false) 778 require.NoError(t, err) 779 require.Equal(t, []string{"dir"}, got) 780 781 // With filter and subdir 782 clearCallback() 783 err = listR(context.Background(), f, "dir", false, ListAll, callback, doListR, false) 784 require.NoError(t, err) 785 require.Equal(t, []string{"dir/b"}, got) 786 787 // Now bucket based 788 objects = fs.DirEntries{ 789 mockobject.Object("a"), 790 mockobject.Object("b"), 791 mockobject.Object("dir/a"), 792 mockobject.Object("dir/b"), 793 mockobject.Object("dir/subdir/c"), 794 mockdir.New("dir/subdir"), 795 } 796 797 // Base case 798 clearCallback() 799 err = listR(context.Background(), f, "", true, ListAll, callback, doListR, true) 800 require.NoError(t, err) 801 require.Equal(t, []string{"a", "b", "dir/a", "dir/b", "dir/subdir/c", "dir/subdir", "dir"}, got) 802 803 // With filter 804 clearCallback() 805 err = listR(context.Background(), f, "", false, ListAll, callback, doListR, true) 806 require.NoError(t, err) 807 require.Equal(t, []string{"b", "dir/b", "dir/subdir", "dir"}, got) 808 809 // With filter and subdir 810 clearCallback() 811 err = listR(context.Background(), f, "dir", false, ListAll, callback, doListR, true) 812 require.NoError(t, err) 813 require.Equal(t, []string{"dir/b", "dir/subdir"}, got) 814 815 // With filter and subdir - with Objects 816 clearCallback() 817 err = listR(context.Background(), f, "dir", false, ListObjects, callback, doListR, true) 818 require.NoError(t, err) 819 require.Equal(t, []string{"dir/b"}, got) 820 821 // With filter and subdir - with Dirs 822 clearCallback() 823 err = listR(context.Background(), f, "dir", false, ListDirs, callback, doListR, true) 824 require.NoError(t, err) 825 require.Equal(t, []string{"dir/subdir"}, got) 826 } 827 828 func TestDirMapAdd(t *testing.T) { 829 type add struct { 830 dir string 831 sent bool 832 } 833 for i, test := range []struct { 834 root string 835 in []add 836 want map[string]bool 837 }{ 838 { 839 root: "", 840 in: []add{ 841 {"", true}, 842 }, 843 want: map[string]bool{}, 844 }, 845 { 846 root: "", 847 in: []add{ 848 {"a/b/c", true}, 849 }, 850 want: map[string]bool{ 851 "a/b/c": true, 852 "a/b": false, 853 "a": false, 854 }, 855 }, 856 { 857 root: "", 858 in: []add{ 859 {"a/b/c", true}, 860 {"a/b", true}, 861 }, 862 want: map[string]bool{ 863 "a/b/c": true, 864 "a/b": true, 865 "a": false, 866 }, 867 }, 868 { 869 root: "", 870 in: []add{ 871 {"a/b", true}, 872 {"a/b/c", false}, 873 }, 874 want: map[string]bool{ 875 "a/b/c": false, 876 "a/b": true, 877 "a": false, 878 }, 879 }, 880 { 881 root: "root", 882 in: []add{ 883 {"root/a/b", true}, 884 {"root/a/b/c", false}, 885 }, 886 want: map[string]bool{ 887 "root/a/b/c": false, 888 "root/a/b": true, 889 "root/a": false, 890 }, 891 }, 892 } { 893 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 894 dm := newDirMap(test.root) 895 for _, item := range test.in { 896 dm.add(item.dir, item.sent) 897 } 898 assert.Equal(t, test.want, dm.m) 899 }) 900 } 901 } 902 903 func TestDirMapAddEntries(t *testing.T) { 904 dm := newDirMap("") 905 entries := fs.DirEntries{ 906 mockobject.Object("dir/a"), 907 mockobject.Object("dir/b"), 908 mockdir.New("dir"), 909 mockobject.Object("dir2/a"), 910 mockobject.Object("dir2/b"), 911 } 912 require.NoError(t, dm.addEntries(entries)) 913 assert.Equal(t, map[string]bool{"dir": true, "dir2": false}, dm.m) 914 } 915 916 func TestDirMapSendEntries(t *testing.T) { 917 var got []string 918 clearCallback := func() { 919 got = nil 920 } 921 callback := func(entries fs.DirEntries) error { 922 for _, entry := range entries { 923 got = append(got, entry.Remote()) 924 } 925 return nil 926 } 927 928 // general test 929 dm := newDirMap("") 930 entries := fs.DirEntries{ 931 mockobject.Object("dir/a"), 932 mockobject.Object("dir/b"), 933 mockdir.New("dir"), 934 mockobject.Object("dir2/a"), 935 mockobject.Object("dir2/b"), 936 mockobject.Object("dir1/a"), 937 mockobject.Object("dir3/b"), 938 } 939 require.NoError(t, dm.addEntries(entries)) 940 clearCallback() 941 err := dm.sendEntries(callback) 942 require.NoError(t, err) 943 assert.Equal(t, []string{ 944 "dir1", 945 "dir2", 946 "dir3", 947 }, got) 948 949 // return error from callback 950 callback2 := func(entries fs.DirEntries) error { 951 return io.EOF 952 } 953 err = dm.sendEntries(callback2) 954 require.Equal(t, io.EOF, err) 955 956 // empty 957 dm = newDirMap("") 958 clearCallback() 959 err = dm.sendEntries(callback) 960 require.NoError(t, err) 961 assert.Equal(t, []string(nil), got) 962 }