Merge changes Ie33d2e05,Ie2b4509b,I5ac3a1f5

* changes:
  Fix finder on symlinks pointing to directories
  Add Stat to finder/fs
  Move finder_test filesystem helper functions to fs/test.go
This commit is contained in:
Colin Cross 2020-07-01 15:42:22 +00:00 committed by Gerrit Code Review
commit 8ec6261388
6 changed files with 496 additions and 384 deletions

View file

@ -1393,19 +1393,27 @@ func (f *Finder) listDirSync(dir *pathMap) {
for _, child := range children { for _, child := range children {
linkBits := child.Mode() & os.ModeSymlink linkBits := child.Mode() & os.ModeSymlink
isLink := linkBits != 0 isLink := linkBits != 0
if child.IsDir() { if isLink {
if !isLink { childPath := filepath.Join(path, child.Name())
childStat, err := f.filesystem.Stat(childPath)
if err != nil {
// If stat fails this is probably a broken or dangling symlink, treat it as a file.
subfiles = append(subfiles, child.Name())
} else if childStat.IsDir() {
// Skip symlink dirs. // Skip symlink dirs.
// We don't have to support symlink dirs because // We don't have to support symlink dirs because
// that would cause duplicates. // that would cause duplicates.
subdirs = append(subdirs, child.Name())
}
} else { } else {
// We do have to support symlink files because the link name might be // We do have to support symlink files because the link name might be
// different than the target name // different than the target name
// (for example, Android.bp -> build/soong/root.bp) // (for example, Android.bp -> build/soong/root.bp)
subfiles = append(subfiles, child.Name()) subfiles = append(subfiles, child.Name())
} }
} else if child.IsDir() {
subdirs = append(subdirs, child.Name())
} else {
subfiles = append(subfiles, child.Name())
}
} }
parentNode := dir parentNode := dir

File diff suppressed because it is too large Load diff

View file

@ -22,8 +22,10 @@ bootstrap_go_package {
srcs: [ srcs: [
"fs.go", "fs.go",
"readdir.go", "readdir.go",
"test.go",
], ],
testSrcs: [ testSrcs: [
"fs_test.go",
"readdir_test.go", "readdir_test.go",
], ],
darwin: { darwin: {

View file

@ -51,6 +51,7 @@ type FileSystem interface {
// getting information about files // getting information about files
Open(name string) (file io.ReadCloser, err error) Open(name string) (file io.ReadCloser, err error)
Lstat(path string) (stats os.FileInfo, err error) Lstat(path string) (stats os.FileInfo, err error)
Stat(path string) (stats os.FileInfo, err error)
ReadDir(path string) (contents []DirEntryInfo, err error) ReadDir(path string) (contents []DirEntryInfo, err error)
InodeNumber(info os.FileInfo) (number uint64, err error) InodeNumber(info os.FileInfo) (number uint64, err error)
@ -99,6 +100,10 @@ func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
return os.Lstat(path) return os.Lstat(path)
} }
func (osFs) Stat(path string) (stats os.FileInfo, err error) {
return os.Stat(path)
}
func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) { func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
entries, err := readdir(path) entries, err := readdir(path)
if err != nil { if err != nil {
@ -376,7 +381,7 @@ type mockFileInfo struct {
size int64 size int64
modTime time.Time // time at which the inode's contents were modified modTime time.Time // time at which the inode's contents were modified
permTime time.Time // time at which the inode's permissions were modified permTime time.Time // time at which the inode's permissions were modified
isDir bool mode os.FileMode
inodeNumber uint64 inodeNumber uint64
deviceNumber uint64 deviceNumber uint64
} }
@ -390,7 +395,7 @@ func (m *mockFileInfo) Size() int64 {
} }
func (m *mockFileInfo) Mode() os.FileMode { func (m *mockFileInfo) Mode() os.FileMode {
return 0 return m.mode
} }
func (m *mockFileInfo) ModTime() time.Time { func (m *mockFileInfo) ModTime() time.Time {
@ -398,7 +403,7 @@ func (m *mockFileInfo) ModTime() time.Time {
} }
func (m *mockFileInfo) IsDir() bool { func (m *mockFileInfo) IsDir() bool {
return m.isDir return m.mode&os.ModeDir != 0
} }
func (m *mockFileInfo) Sys() interface{} { func (m *mockFileInfo) Sys() interface{} {
@ -407,11 +412,11 @@ func (m *mockFileInfo) Sys() interface{} {
func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) { func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: d.modTime, modTime: d.modTime,
permTime: d.permTime, permTime: d.permTime,
isDir: true, mode: os.ModeDir,
inodeNumber: d.inodeNumber, inodeNumber: d.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@ -420,11 +425,11 @@ func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) { func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: f.modTime, modTime: f.modTime,
permTime: f.permTime, permTime: f.permTime,
isDir: false, mode: 0,
inodeNumber: f.inodeNumber, inodeNumber: f.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@ -432,11 +437,11 @@ func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) { func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: l.modTime, modTime: l.modTime,
permTime: l.permTime, permTime: l.permTime,
isDir: false, mode: os.ModeSymlink,
inodeNumber: l.inodeNumber, inodeNumber: l.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@ -485,6 +490,16 @@ func (m *MockFs) Lstat(path string) (stats os.FileInfo, err error) {
} }
} }
func (m *MockFs) Stat(path string) (stats os.FileInfo, err error) {
// resolve symlinks
path, err = m.resolve(path, true)
if err != nil {
return nil, err
}
return m.Lstat(path)
}
func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) { func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) {
mockInfo, ok := info.(*mockFileInfo) mockInfo, ok := info.(*mockFileInfo)
if ok { if ok {

76
finder/fs/fs_test.go Normal file
View file

@ -0,0 +1,76 @@
// Copyright 2020 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"os"
"testing"
)
func TestMockFs_LstatStatSymlinks(t *testing.T) {
// setup filesystem
filesystem := NewMockFs(nil)
Create(t, "/tmp/realdir/hi.txt", filesystem)
Create(t, "/tmp/realdir/ignoreme.txt", filesystem)
Link(t, "/tmp/links/dir", "../realdir", filesystem)
Link(t, "/tmp/links/file", "../realdir/hi.txt", filesystem)
Link(t, "/tmp/links/broken", "nothingHere", filesystem)
Link(t, "/tmp/links/recursive", "recursive", filesystem)
assertStat := func(t *testing.T, stat os.FileInfo, err error, wantName string, wantMode os.FileMode) {
t.Helper()
if err != nil {
t.Error(err)
return
}
if g, w := stat.Name(), wantName; g != w {
t.Errorf("want name %q, got %q", w, g)
}
if g, w := stat.Mode(), wantMode; g != w {
t.Errorf("%s: want mode %q, got %q", wantName, w, g)
}
}
assertErr := func(t *testing.T, err error, wantErr string) {
if err == nil || err.Error() != wantErr {
t.Errorf("want error %q, got %q", wantErr, err)
}
}
stat, err := filesystem.Lstat("/tmp/links/dir")
assertStat(t, stat, err, "dir", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/dir")
assertStat(t, stat, err, "realdir", os.ModeDir)
stat, err = filesystem.Lstat("/tmp/links/file")
assertStat(t, stat, err, "file", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/file")
assertStat(t, stat, err, "hi.txt", 0)
stat, err = filesystem.Lstat("/tmp/links/broken")
assertStat(t, stat, err, "broken", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/broken")
assertErr(t, err, "stat /tmp/links/nothingHere: file does not exist")
stat, err = filesystem.Lstat("/tmp/links/recursive")
assertStat(t, stat, err, "recursive", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/recursive")
assertErr(t, err, "read /tmp/links/recursive: too many levels of symbolic links")
}

146
finder/fs/test.go Normal file
View file

@ -0,0 +1,146 @@
// Copyright 2020 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"io/ioutil"
"path/filepath"
"reflect"
"sort"
"testing"
"time"
)
func Write(t *testing.T, path string, content string, filesystem *MockFs) {
parent := filepath.Dir(path)
filesystem.MkDirs(parent)
err := filesystem.WriteFile(path, []byte(content), 0777)
if err != nil {
t.Fatal(err.Error())
}
}
func Create(t *testing.T, path string, filesystem *MockFs) {
Write(t, path, "hi", filesystem)
}
func Delete(t *testing.T, path string, filesystem *MockFs) {
err := filesystem.Remove(path)
if err != nil {
t.Fatal(err.Error())
}
}
func RemoveAll(t *testing.T, path string, filesystem *MockFs) {
err := filesystem.RemoveAll(path)
if err != nil {
t.Fatal(err.Error())
}
}
func Move(t *testing.T, oldPath string, newPath string, filesystem *MockFs) {
err := filesystem.Rename(oldPath, newPath)
if err != nil {
t.Fatal(err.Error())
}
}
func Link(t *testing.T, newPath string, oldPath string, filesystem *MockFs) {
parentPath := filepath.Dir(newPath)
err := filesystem.MkDirs(parentPath)
if err != nil {
t.Fatal(err.Error())
}
err = filesystem.Symlink(oldPath, newPath)
if err != nil {
t.Fatal(err.Error())
}
}
func Read(t *testing.T, path string, filesystem *MockFs) string {
reader, err := filesystem.Open(path)
if err != nil {
t.Fatalf(err.Error())
}
bytes, err := ioutil.ReadAll(reader)
if err != nil {
t.Fatal(err.Error())
}
return string(bytes)
}
func ModTime(t *testing.T, path string, filesystem *MockFs) time.Time {
stats, err := filesystem.Lstat(path)
if err != nil {
t.Fatal(err.Error())
}
return stats.ModTime()
}
func SetReadable(t *testing.T, path string, readable bool, filesystem *MockFs) {
err := filesystem.SetReadable(path, readable)
if err != nil {
t.Fatal(err.Error())
}
}
func SetReadErr(t *testing.T, path string, readErr error, filesystem *MockFs) {
err := filesystem.SetReadErr(path, readErr)
if err != nil {
t.Fatal(err.Error())
}
}
func AssertSameResponse(t *testing.T, actual []string, expected []string) {
t.Helper()
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("Expected Finder to return these %v paths:\n %v,\ninstead returned these %v paths: %v\n",
len(expected), expected, len(actual), actual)
}
}
func AssertSameStatCalls(t *testing.T, actual []string, expected []string) {
t.Helper()
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("Finder made incorrect Stat calls.\n"+
"Actual:\n"+
"%v\n"+
"Expected:\n"+
"%v\n"+
"\n",
actual, expected)
}
}
func AssertSameReadDirCalls(t *testing.T, actual []string, expected []string) {
t.Helper()
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("Finder made incorrect ReadDir calls.\n"+
"Actual:\n"+
"%v\n"+
"Expected:\n"+
"%v\n"+
"\n",
actual, expected)
}
}