From 3316a5ee54e81d2bddc47c7a2f785cf69fc4a7b4 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 27 Sep 2018 15:49:45 -0700 Subject: [PATCH] Add pathtools.Filesystem.Stat for soong_zip Stat is used by soong_zip. Add it to the FileSystem interface and add tests for it. Test: fs_test.go Change-Id: I1baa2b27398846a4e55bcf4fa291c62f507a4e9d --- pathtools/fs.go | 49 ++++++++++++++++++++------ pathtools/fs_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 12 deletions(-) diff --git a/pathtools/fs.go b/pathtools/fs.go index 47b58c3..4217487 100644 --- a/pathtools/fs.go +++ b/pathtools/fs.go @@ -106,6 +106,9 @@ type FileSystem interface { // Lstat returns info on a file without following symlinks. Lstat(name string) (os.FileInfo, error) + // Lstat returns info on a file. + Stat(name string) (os.FileInfo, error) + // ListDirsRecursive returns a list of all the directories in a path, following symlinks if requested. ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) @@ -159,6 +162,10 @@ func (osFs) Lstat(path string) (stats os.FileInfo, err error) { return os.Lstat(path) } +func (osFs) Stat(path string) (stats os.FileInfo, err error) { + return os.Stat(path) +} + // Returns a list of all directories under dir func (osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) { return listDirsRecursive(OsFs, name, follow) @@ -349,24 +356,46 @@ func (ms *mockStat) ModTime() time.Time { return time.Time{} } func (ms *mockStat) Sys() interface{} { return nil } func (m *mockFs) Lstat(name string) (os.FileInfo, error) { - name = filepath.Clean(name) + dir, file := saneSplit(name) + dir = m.followSymlinks(dir) + name = filepath.Join(dir, file) ms := mockStat{ - name: name, - size: int64(len(m.files[name])), + name: file, } - if isSymlink, err := m.IsSymlink(name); err != nil { - // IsSymlink handles ErrNotExist - return nil, err - } else if isSymlink { + if symlink, isSymlink := m.symlinks[name]; isSymlink { ms.mode = os.ModeSymlink - } else if isDir, err := m.IsDir(name); err != nil { - return nil, err - } else if isDir { + ms.size = int64(len(symlink)) + } else if _, isDir := m.dirs[name]; isDir { ms.mode = os.ModeDir + } else if _, isFile := m.files[name]; isFile { + ms.mode = 0 + ms.size = int64(len(m.files[name])) } else { + return nil, os.ErrNotExist + } + + return &ms, nil +} + +func (m *mockFs) Stat(name string) (os.FileInfo, error) { + name = filepath.Clean(name) + origName := name + name = m.followSymlinks(name) + + ms := mockStat{ + name: filepath.Base(origName), + size: int64(len(m.files[name])), + } + + if _, isDir := m.dirs[name]; isDir { + ms.mode = os.ModeDir + } else if _, isFile := m.files[name]; isFile { ms.mode = 0 + ms.size = int64(len(m.files[name])) + } else { + return nil, os.ErrNotExist } return &ms, nil diff --git a/pathtools/fs_test.go b/pathtools/fs_test.go index 3bcce2f..1b5c458 100644 --- a/pathtools/fs_test.go +++ b/pathtools/fs_test.go @@ -409,11 +409,89 @@ func TestFs_Lstat(t *testing.T) { return } if got.Mode()&os.ModeType != test.mode { - t.Errorf("fs.Readlink(%q).Mode()&os.ModeType want: %x, got %x", + t.Errorf("fs.Lstat(%q).Mode()&os.ModeType want: %x, got %x", test.name, test.mode, got.Mode()&os.ModeType) } if test.mode == 0 && got.Size() != test.size { - t.Errorf("fs.Readlink(%q).Size() want: %d, got %d", test.name, test.size, got.Size()) + t.Errorf("fs.Lstat(%q).Size() want: %d, got %d", test.name, test.size, got.Size()) + } + }) + } + }) + } +} + +func TestFs_Stat(t *testing.T) { + testCases := []struct { + name string + mode os.FileMode + size int64 + err error + }{ + {".", os.ModeDir, 0, nil}, + {"/", os.ModeDir, 0, nil}, + + {"a", os.ModeDir, 0, nil}, + {"a/a", os.ModeDir, 0, nil}, + {"a/a/a", 0, 0, nil}, + {"a/a/f", 0, 0, nil}, + + {"b", os.ModeDir, 0, nil}, + {"b/a", os.ModeDir, 0, nil}, + {"b/a/a", 0, 0, nil}, + {"b/a/f", 0, 0, nil}, + + {"c", os.ModeDir, 0, nil}, + {"c/a", 0, 0, nil}, + {"c/f", 0, 0, nil}, + + {"d/a", 0, 0, nil}, + {"d/f", 0, 0, nil}, + + {"e", 0, 0, nil}, + + {"f", 0, 0, nil}, + + {"dangling", 0, 0, os.ErrNotExist}, + + {"a/missing", 0, 0, os.ErrNotExist}, + {"b/missing", 0, 0, os.ErrNotExist}, + {"c/missing", 0, 0, os.ErrNotExist}, + {"d/missing", 0, 0, os.ErrNotExist}, + {"e/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing", 0, 0, os.ErrNotExist}, + + {"a/missing/missing", 0, 0, os.ErrNotExist}, + {"b/missing/missing", 0, 0, os.ErrNotExist}, + {"c/missing/missing", 0, 0, os.ErrNotExist}, + {"d/missing/missing", 0, 0, os.ErrNotExist}, + {"e/missing/missing", 0, 0, os.ErrNotExist}, + {"dangling/missing/missing", 0, 0, os.ErrNotExist}, + } + + mock := symlinkMockFs() + fsList := []FileSystem{mock, OsFs} + names := []string{"mock", "os"} + + os.Chdir("testdata/dangling") + defer os.Chdir("../..") + + for i, fs := range fsList { + t.Run(names[i], func(t *testing.T) { + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := fs.Stat(test.name) + checkErr(t, test.err, err) + if err != nil { + return + } + if got.Mode()&os.ModeType != test.mode { + t.Errorf("fs.Stat(%q).Mode()&os.ModeType want: %x, got %x", + test.name, test.mode, got.Mode()&os.ModeType) + } + if test.mode == 0 && got.Size() != test.size { + t.Errorf("fs.Stat(%q).Size() want: %d, got %d", test.name, test.size, got.Size()) } }) } -- GitLab