package fs import ( "sync" "testing" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) type ( listResult struct { entries DirEntries err error } listResults map[string]listResult errorMap map[string]error listDirs struct { mu sync.Mutex t *testing.T fs Fs includeAll bool results listResults walkResults listResults walkErrors errorMap finalError error checkMaps bool maxLevel int } ) func newListDirs(t *testing.T, f Fs, includeAll bool, results listResults, walkErrors errorMap, finalError error) *listDirs { return &listDirs{ t: t, fs: f, includeAll: includeAll, results: results, walkErrors: walkErrors, walkResults: listResults{}, finalError: finalError, checkMaps: true, maxLevel: -1, } } // NoCheckMaps marks the maps as to be ignored at the end func (ls *listDirs) NoCheckMaps() *listDirs { ls.checkMaps = false return ls } // SetLevel(1) turns off recursion func (ls *listDirs) SetLevel(maxLevel int) *listDirs { ls.maxLevel = maxLevel return ls } // ListDir returns the expected listing for the directory func (ls *listDirs) ListDir(f Fs, includeAll bool, dir string) (entries DirEntries, err error) { ls.mu.Lock() defer ls.mu.Unlock() assert.Equal(ls.t, ls.fs, f) assert.Equal(ls.t, ls.includeAll, includeAll) // Fetch results for this path result, ok := ls.results[dir] if !ok { ls.t.Errorf("Unexpected list of %q", dir) return nil, errors.New("unexpected list") } delete(ls.results, dir) // Put expected results for call of WalkFn ls.walkResults[dir] = result return result.entries, result.err } // IsFinished checks everything expected was used up func (ls *listDirs) IsFinished() { if ls.checkMaps { assert.Equal(ls.t, errorMap{}, ls.walkErrors) assert.Equal(ls.t, listResults{}, ls.results) assert.Equal(ls.t, listResults{}, ls.walkResults) } } // WalkFn is called by the walk to test the expectations func (ls *listDirs) WalkFn(dir string, entries DirEntries, err error) error { ls.mu.Lock() defer ls.mu.Unlock() // Fetch expected entries and err result, ok := ls.walkResults[dir] if !ok { ls.t.Errorf("Unexpected walk of %q (result not found)", dir) return errors.New("result not found") } delete(ls.walkResults, dir) // Check arguments are as expected assert.Equal(ls.t, result.entries, entries) assert.Equal(ls.t, result.err, err) // Fetch return value returnErr, ok := ls.walkErrors[dir] if !ok { ls.t.Errorf("Unexpected walk of %q (error not found)", dir) return errors.New("error not found") } delete(ls.walkErrors, dir) return returnErr } // Walk does the walk and tests the expectations func (ls *listDirs) Walk() { err := walk(nil, "", ls.includeAll, ls.maxLevel, ls.WalkFn, ls.ListDir) assert.Equal(ls.t, ls.finalError, err) ls.IsFinished() } func newDir(name string) *Dir { return &Dir{Name: name} } func TestWalkEmpty(t *testing.T) { newListDirs(t, nil, false, listResults{ "": {entries: DirEntries{}, err: nil}, }, errorMap{ "": nil, }, nil, ).Walk() } func TestWalkEmptySkip(t *testing.T) { newListDirs(t, nil, true, listResults{ "": {entries: DirEntries{}, err: nil}, }, errorMap{ "": ErrorSkipDir, }, nil, ).Walk() } func TestWalkNotFound(t *testing.T) { newListDirs(t, nil, true, listResults{ "": {err: ErrorDirNotFound}, }, errorMap{ "": ErrorDirNotFound, }, ErrorDirNotFound, ).Walk() } func TestWalkNotFoundMaskError(t *testing.T) { newListDirs(t, nil, true, listResults{ "": {err: ErrorDirNotFound}, }, errorMap{ "": nil, }, nil, ).Walk() } func TestWalkNotFoundSkipkError(t *testing.T) { newListDirs(t, nil, true, listResults{ "": {err: ErrorDirNotFound}, }, errorMap{ "": ErrorSkipDir, }, nil, ).Walk() } func testWalkLevels(t *testing.T, maxLevel int) { da := newDir("a") db := newDir("a/b") dc := newDir("a/b/c") dd := newDir("a/b/c/d") newListDirs(t, nil, false, listResults{ "": {entries: DirEntries{da}, err: nil}, "a": {entries: DirEntries{db}, err: nil}, "a/b": {entries: DirEntries{dc}, err: nil}, "a/b/c": {entries: DirEntries{dd}, err: nil}, "a/b/c/d": {entries: DirEntries{}, err: nil}, }, errorMap{ "": nil, "a": nil, "a/b": nil, "a/b/c": nil, "a/b/c/d": nil, }, nil, ).SetLevel(maxLevel).Walk() } func TestWalkLevels(t *testing.T) { testWalkLevels(t, -1) } func TestWalkLevelsNoRecursive10(t *testing.T) { testWalkLevels(t, 10) } func TestWalkLevelsNoRecursive(t *testing.T) { da := newDir("a") newListDirs(t, nil, false, listResults{ "": {entries: DirEntries{da}, err: nil}, }, errorMap{ "": nil, }, nil, ).SetLevel(1).Walk() } func TestWalkLevels2(t *testing.T) { da := newDir("a") db := newDir("a/b") newListDirs(t, nil, false, listResults{ "": {entries: DirEntries{da}, err: nil}, "a": {entries: DirEntries{db}, err: nil}, }, errorMap{ "": nil, "a": nil, }, nil, ).SetLevel(2).Walk() } func TestWalkSkip(t *testing.T) { da := newDir("a") db := newDir("a/b") dc := newDir("a/b/c") newListDirs(t, nil, false, listResults{ "": {entries: DirEntries{da}, err: nil}, "a": {entries: DirEntries{db}, err: nil}, "a/b": {entries: DirEntries{dc}, err: nil}, }, errorMap{ "": nil, "a": nil, "a/b": ErrorSkipDir, }, nil, ).Walk() } func TestWalkErrors(t *testing.T) { lr := listResults{} em := errorMap{} de := make(DirEntries, 10) for i := range de { path := string('0' + i) de[i] = newDir(path) lr[path] = listResult{entries: nil, err: ErrorDirNotFound} em[path] = ErrorDirNotFound } lr[""] = listResult{entries: de, err: nil} em[""] = nil newListDirs(t, nil, true, lr, em, ErrorDirNotFound, ).NoCheckMaps().Walk() } var errorBoom = errors.New("boom") func makeTree(level int, terminalErrors bool) (listResults, errorMap) { lr := listResults{} em := errorMap{} var fill func(path string, level int) fill = func(path string, level int) { de := DirEntries{} if level > 0 { for _, a := range "0123456789" { subPath := string(a) if path != "" { subPath = path + "/" + subPath } de = append(de, newDir(subPath)) fill(subPath, level-1) } } lr[path] = listResult{entries: de, err: nil} em[path] = nil if level == 0 && terminalErrors { em[path] = errorBoom } } fill("", level) return lr, em } func TestWalkMulti(t *testing.T) { lr, em := makeTree(3, false) newListDirs(t, nil, true, lr, em, nil, ).Walk() } func TestWalkMultiErrors(t *testing.T) { lr, em := makeTree(3, true) newListDirs(t, nil, true, lr, em, errorBoom, ).NoCheckMaps().Walk() }