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