lib/ignore, lib/scanner: Catch included items below ignored ones (#4811)
This commit is contained in:
parent
22b0bd8e13
commit
d59aecba31
@ -29,10 +29,6 @@ func (f *BasicFilesystem) Watch(name string, ignore Matcher, ctx context.Context
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
absShouldIgnore := func(absPath string) bool {
|
|
||||||
return ignore.ShouldIgnore(f.unrootedChecked(absPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
outChan := make(chan Event)
|
outChan := make(chan Event)
|
||||||
backendChan := make(chan notify.EventInfo, backendBuffer)
|
backendChan := make(chan notify.EventInfo, backendBuffer)
|
||||||
|
|
||||||
@ -41,7 +37,15 @@ func (f *BasicFilesystem) Watch(name string, ignore Matcher, ctx context.Context
|
|||||||
eventMask |= permEventMask
|
eventMask |= permEventMask
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := notify.WatchWithFilter(filepath.Join(absName, "..."), backendChan, absShouldIgnore, eventMask); err != nil {
|
if ignore.SkipIgnoredDirs() {
|
||||||
|
absShouldIgnore := func(absPath string) bool {
|
||||||
|
return ignore.ShouldIgnore(f.unrootedChecked(absPath))
|
||||||
|
}
|
||||||
|
err = notify.WatchWithFilter(filepath.Join(absName, "..."), backendChan, absShouldIgnore, eventMask)
|
||||||
|
} else {
|
||||||
|
err = notify.Watch(filepath.Join(absName, "..."), backendChan, eventMask)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
notify.Stop(backendChan)
|
notify.Stop(backendChan)
|
||||||
if reachedMaxUserWatches(err) {
|
if reachedMaxUserWatches(err) {
|
||||||
err = errors.New("failed to setup inotify handler. Please increase inotify limits, see https://docs.syncthing.net/users/faq.html#inotify-limits")
|
err = errors.New("failed to setup inotify handler. Please increase inotify limits, see https://docs.syncthing.net/users/faq.html#inotify-limits")
|
||||||
|
|||||||
@ -41,15 +41,17 @@ func TestMain(m *testing.M) {
|
|||||||
testFs = NewFilesystem(FilesystemTypeBasic, testDirAbs)
|
testFs = NewFilesystem(FilesystemTypeBasic, testDirAbs)
|
||||||
|
|
||||||
backendBuffer = 10
|
backendBuffer = 10
|
||||||
defer func() {
|
|
||||||
backendBuffer = 500
|
exitCode := m.Run()
|
||||||
os.RemoveAll(testDir)
|
|
||||||
}()
|
backendBuffer = 500
|
||||||
os.Exit(m.Run())
|
os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testDir = "temporary_test_root"
|
testDir = "testdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -75,7 +77,31 @@ func TestWatchIgnore(t *testing.T) {
|
|||||||
{name, NonRemove},
|
{name, NonRemove},
|
||||||
}
|
}
|
||||||
|
|
||||||
testScenario(t, name, testCase, expectedEvents, allowedEvents, ignored)
|
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), skipIgnoredDirs: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWatchInclude(t *testing.T) {
|
||||||
|
name := "include"
|
||||||
|
|
||||||
|
file := "file"
|
||||||
|
ignored := "ignored"
|
||||||
|
testFs.MkdirAll(filepath.Join(name, ignored), 0777)
|
||||||
|
included := filepath.Join(ignored, "included")
|
||||||
|
|
||||||
|
testCase := func() {
|
||||||
|
createTestFile(name, file)
|
||||||
|
createTestFile(name, included)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedEvents := []Event{
|
||||||
|
{file, NonRemove},
|
||||||
|
{included, NonRemove},
|
||||||
|
}
|
||||||
|
allowedEvents := []Event{
|
||||||
|
{name, NonRemove},
|
||||||
|
}
|
||||||
|
|
||||||
|
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), include: filepath.Join(name, included)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchRename(t *testing.T) {
|
func TestWatchRename(t *testing.T) {
|
||||||
@ -104,7 +130,7 @@ func TestWatchRename(t *testing.T) {
|
|||||||
|
|
||||||
// set the "allow others" flag because we might get the create of
|
// set the "allow others" flag because we might get the create of
|
||||||
// "oldfile" initially
|
// "oldfile" initially
|
||||||
testScenario(t, name, testCase, expectedEvents, allowedEvents, "")
|
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestWatchOutside checks that no changes from outside the folder make it in
|
// TestWatchOutside checks that no changes from outside the folder make it in
|
||||||
@ -120,7 +146,11 @@ func TestWatchOutside(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if recover() == nil {
|
if recover() == nil {
|
||||||
t.Fatalf("Watch did not panic on receiving event outside of folder")
|
select {
|
||||||
|
case <-ctx.Done(): // timed out
|
||||||
|
default:
|
||||||
|
t.Fatalf("Watch did not panic on receiving event outside of folder")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
@ -128,6 +158,13 @@ func TestWatchOutside(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
backendChan <- fakeEventInfo(filepath.Join(filepath.Dir(testDirAbs), "outside"))
|
backendChan <- fakeEventInfo(filepath.Join(filepath.Dir(testDirAbs), "outside"))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
cancel()
|
||||||
|
t.Errorf("Timed out before panicing")
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchSubpath(t *testing.T) {
|
func TestWatchSubpath(t *testing.T) {
|
||||||
@ -178,7 +215,7 @@ func TestWatchOverflow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testScenario(t, name, testCase, expectedEvents, allowedEvents, "")
|
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchErrorLinuxInterpretation(t *testing.T) {
|
func TestWatchErrorLinuxInterpretation(t *testing.T) {
|
||||||
@ -283,7 +320,7 @@ func TestWatchIssue4877(t *testing.T) {
|
|||||||
testFs = origTestFs
|
testFs = origTestFs
|
||||||
}()
|
}()
|
||||||
|
|
||||||
testScenario(t, name, testCase, expectedEvents, allowedEvents, "")
|
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// path relative to folder root, also creates parent dirs if necessary
|
// path relative to folder root, also creates parent dirs if necessary
|
||||||
@ -312,7 +349,7 @@ func sleepMs(ms int) {
|
|||||||
time.Sleep(time.Duration(ms) * time.Millisecond)
|
time.Sleep(time.Duration(ms) * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, ignored string) {
|
func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, fm fakeMatcher) {
|
||||||
if err := testFs.MkdirAll(name, 0755); err != nil {
|
if err := testFs.MkdirAll(name, 0755); err != nil {
|
||||||
panic(fmt.Sprintf("Failed to create directory %s: %s", name, err))
|
panic(fmt.Sprintf("Failed to create directory %s: %s", name, err))
|
||||||
}
|
}
|
||||||
@ -321,11 +358,7 @@ func testScenario(t *testing.T, name string, testCase func(), expectedEvents, al
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if ignored != "" {
|
eventChan, err := testFs.Watch(name, fm, ctx, false)
|
||||||
ignored = filepath.Join(name, ignored)
|
|
||||||
}
|
|
||||||
|
|
||||||
eventChan, err := testFs.Watch(name, fakeMatcher{ignored}, ctx, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -335,7 +368,7 @@ func testScenario(t *testing.T, name string, testCase func(), expectedEvents, al
|
|||||||
testCase()
|
testCase()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.NewTimer(time.Minute).C:
|
case <-time.After(time.Minute):
|
||||||
t.Errorf("Timed out before receiving all expected events")
|
t.Errorf("Timed out before receiving all expected events")
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@ -382,10 +415,19 @@ func testWatchOutput(t *testing.T, name string, in <-chan Event, expectedEvents,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeMatcher struct{ match string }
|
// Matches are done via direct comparison against both ignore and include
|
||||||
|
type fakeMatcher struct {
|
||||||
|
ignore string
|
||||||
|
include string
|
||||||
|
skipIgnoredDirs bool
|
||||||
|
}
|
||||||
|
|
||||||
func (fm fakeMatcher) ShouldIgnore(name string) bool {
|
func (fm fakeMatcher) ShouldIgnore(name string) bool {
|
||||||
return name == fm.match
|
return name != fm.include && name == fm.ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm fakeMatcher) SkipIgnoredDirs() bool {
|
||||||
|
return fm.skipIgnoredDirs
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeEventInfo string
|
type fakeEventInfo string
|
||||||
|
|||||||
@ -87,6 +87,7 @@ type Usage struct {
|
|||||||
|
|
||||||
type Matcher interface {
|
type Matcher interface {
|
||||||
ShouldIgnore(name string) bool
|
ShouldIgnore(name string) bool
|
||||||
|
SkipIgnoredDirs() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchResult interface {
|
type MatchResult interface {
|
||||||
|
|||||||
@ -77,15 +77,16 @@ type ChangeDetector interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Matcher struct {
|
type Matcher struct {
|
||||||
fs fs.Filesystem
|
fs fs.Filesystem
|
||||||
lines []string // exact lines read from .stignore
|
lines []string // exact lines read from .stignore
|
||||||
patterns []Pattern // patterns including those from included files
|
patterns []Pattern // patterns including those from included files
|
||||||
withCache bool
|
withCache bool
|
||||||
matches *cache
|
matches *cache
|
||||||
curHash string
|
curHash string
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
changeDetector ChangeDetector
|
changeDetector ChangeDetector
|
||||||
mut sync.Mutex
|
skipIgnoredDirs bool
|
||||||
|
mut sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Option can be passed to New()
|
// An Option can be passed to New()
|
||||||
@ -108,9 +109,10 @@ func WithChangeDetector(cd ChangeDetector) Option {
|
|||||||
|
|
||||||
func New(fs fs.Filesystem, opts ...Option) *Matcher {
|
func New(fs fs.Filesystem, opts ...Option) *Matcher {
|
||||||
m := &Matcher{
|
m := &Matcher{
|
||||||
fs: fs,
|
fs: fs,
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
mut: sync.NewMutex(),
|
mut: sync.NewMutex(),
|
||||||
|
skipIgnoredDirs: true,
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(m)
|
opt(m)
|
||||||
@ -169,6 +171,14 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.skipIgnoredDirs = true
|
||||||
|
for _, p := range patterns {
|
||||||
|
if p.result&resultInclude == resultInclude {
|
||||||
|
m.skipIgnoredDirs = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m.curHash = newHash
|
m.curHash = newHash
|
||||||
m.patterns = patterns
|
m.patterns = patterns
|
||||||
if m.withCache {
|
if m.withCache {
|
||||||
@ -291,6 +301,12 @@ func (m *Matcher) ShouldIgnore(filename string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) SkipIgnoredDirs() bool {
|
||||||
|
m.mut.Lock()
|
||||||
|
defer m.mut.Unlock()
|
||||||
|
return m.skipIgnoredDirs
|
||||||
|
}
|
||||||
|
|
||||||
func hashPatterns(patterns []Pattern) string {
|
func hashPatterns(patterns []Pattern) string {
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
for _, pat := range patterns {
|
for _, pat := range patterns {
|
||||||
|
|||||||
1
lib/scanner/testdata/.stignore
vendored
1
lib/scanner/testdata/.stignore
vendored
@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
bfile
|
bfile
|
||||||
dir1/cfile
|
dir1/cfile
|
||||||
|
/dir2/dir21
|
||||||
|
|||||||
0
lib/scanner/testdata/dir2/dir21/dir22/dir23/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dir22/dir23/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dir22/efile/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dir22/efile/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dira/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dira/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dira/ffile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/dira/ffile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/efile/ign/efile
vendored
Normal file
0
lib/scanner/testdata/dir2/dir21/efile/ign/efile
vendored
Normal file
@ -8,7 +8,9 @@ package scanner
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@ -196,6 +198,8 @@ func (w *walker) walk(ctx context.Context) chan protocol.FileInfo {
|
|||||||
|
|
||||||
func (w *walker) walkAndHashFiles(ctx context.Context, fchan, dchan chan protocol.FileInfo) fs.WalkFunc {
|
func (w *walker) walkAndHashFiles(ctx context.Context, fchan, dchan chan protocol.FileInfo) fs.WalkFunc {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
ignoredParent := ""
|
||||||
|
|
||||||
return func(path string, info fs.FileInfo, err error) error {
|
return func(path string, info fs.FileInfo, err error) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@ -220,12 +224,6 @@ func (w *walker) walkAndHashFiles(ctx context.Context, fchan, dchan chan protoco
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err = w.Filesystem.Lstat(path)
|
|
||||||
// An error here would be weird as we've already gotten to this point, but act on it nonetheless
|
|
||||||
if err != nil {
|
|
||||||
return skip
|
|
||||||
}
|
|
||||||
|
|
||||||
if fs.IsTemporary(path) {
|
if fs.IsTemporary(path) {
|
||||||
l.Debugln("temporary:", path)
|
l.Debugln("temporary:", path)
|
||||||
if info.IsRegular() && info.ModTime().Add(w.TempLifetime).Before(now) {
|
if info.IsRegular() && info.ModTime().Add(w.TempLifetime).Before(now) {
|
||||||
@ -240,43 +238,88 @@ func (w *walker) walkAndHashFiles(ctx context.Context, fchan, dchan chan protoco
|
|||||||
return skip
|
return skip
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.Matcher.Match(path).IsIgnored() {
|
|
||||||
l.Debugln("ignored (patterns):", path)
|
|
||||||
return skip
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utf8.ValidString(path) {
|
if !utf8.ValidString(path) {
|
||||||
l.Warnf("File name %q is not in UTF8 encoding; skipping.", path)
|
l.Warnf("File name %q is not in UTF8 encoding; skipping.", path)
|
||||||
return skip
|
return skip
|
||||||
}
|
}
|
||||||
|
|
||||||
path, shouldSkip := w.normalizePath(path, info)
|
if w.Matcher.Match(path).IsIgnored() {
|
||||||
if shouldSkip {
|
l.Debugln("ignored (patterns):", path)
|
||||||
return skip
|
// Only descend if matcher says so and the current file is not a symlink.
|
||||||
}
|
if w.Matcher.SkipIgnoredDirs() || (info.IsSymlink() && info.IsDir()) {
|
||||||
|
|
||||||
switch {
|
|
||||||
case info.IsSymlink():
|
|
||||||
if err := w.walkSymlink(ctx, path, dchan); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
// under no circumstances shall we descend into a symlink
|
|
||||||
return fs.SkipDir
|
return fs.SkipDir
|
||||||
}
|
}
|
||||||
|
// If the parent wasn't ignored already, set this path as the "highest" ignored parent
|
||||||
|
if info.IsDir() && (ignoredParent == "" || !strings.HasPrefix(path, ignoredParent+string(fs.PathSeparator))) {
|
||||||
|
ignoredParent = path
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case info.IsDir():
|
|
||||||
err = w.walkDir(ctx, path, info, dchan)
|
|
||||||
|
|
||||||
case info.IsRegular():
|
|
||||||
err = w.walkRegular(ctx, path, info, fchan)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
if ignoredParent == "" {
|
||||||
|
// parent isn't ignored, nothing special
|
||||||
|
return w.handleItem(ctx, path, fchan, dchan, skip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part of current path below the ignored (potential) parent
|
||||||
|
rel := strings.TrimPrefix(path, ignoredParent+string(fs.PathSeparator))
|
||||||
|
|
||||||
|
// ignored path isn't actually a parent of the current path
|
||||||
|
if rel == path {
|
||||||
|
ignoredParent = ""
|
||||||
|
return w.handleItem(ctx, path, fchan, dchan, skip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The previously ignored parent directories of the current, not
|
||||||
|
// ignored path need to be handled as well.
|
||||||
|
if err = w.handleItem(ctx, ignoredParent, fchan, dchan, skip); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range strings.Split(rel, string(fs.PathSeparator)) {
|
||||||
|
ignoredParent = filepath.Join(ignoredParent, name)
|
||||||
|
if err = w.handleItem(ctx, ignoredParent, fchan, dchan, skip); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ignoredParent = ""
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *walker) handleItem(ctx context.Context, path string, fchan, dchan chan protocol.FileInfo, skip error) error {
|
||||||
|
info, err := w.Filesystem.Lstat(path)
|
||||||
|
// An error here would be weird as we've already gotten to this point, but act on it nonetheless
|
||||||
|
if err != nil {
|
||||||
|
return skip
|
||||||
|
}
|
||||||
|
|
||||||
|
path, shouldSkip := w.normalizePath(path, info)
|
||||||
|
if shouldSkip {
|
||||||
|
return skip
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case info.IsSymlink():
|
||||||
|
if err := w.walkSymlink(ctx, path, dchan); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
// under no circumstances shall we descend into a symlink
|
||||||
|
return fs.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case info.IsDir():
|
||||||
|
err = w.walkDir(ctx, path, info, dchan)
|
||||||
|
|
||||||
|
case info.IsRegular():
|
||||||
|
err = w.walkRegular(ctx, path, info, fchan)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileInfo, fchan chan protocol.FileInfo) error {
|
func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileInfo, fchan chan protocol.FileInfo) error {
|
||||||
curMode := uint32(info.Mode())
|
curMode := uint32(info.Mode())
|
||||||
if runtime.GOOS == "windows" && osutil.IsWindowsExecutable(relPath) {
|
if runtime.GOOS == "windows" && osutil.IsWindowsExecutable(relPath) {
|
||||||
|
|||||||
@ -38,6 +38,8 @@ type testfile struct {
|
|||||||
|
|
||||||
type testfileList []testfile
|
type testfileList []testfile
|
||||||
|
|
||||||
|
var testFs fs.Filesystem
|
||||||
|
|
||||||
var testdata = testfileList{
|
var testdata = testfileList{
|
||||||
{"afile", 4, "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
|
{"afile", 4, "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
|
||||||
{"dir1", 128, ""},
|
{"dir1", 128, ""},
|
||||||
@ -53,17 +55,19 @@ func init() {
|
|||||||
// Limit the stack size to 10 megs to crash early in that case instead of
|
// Limit the stack size to 10 megs to crash early in that case instead of
|
||||||
// potentially taking down the box...
|
// potentially taking down the box...
|
||||||
rdebug.SetMaxStack(10 * 1 << 20)
|
rdebug.SetMaxStack(10 * 1 << 20)
|
||||||
|
|
||||||
|
testFs = fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWalkSub(t *testing.T) {
|
func TestWalkSub(t *testing.T) {
|
||||||
ignores := ignore.New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."))
|
ignores := ignore.New(testFs)
|
||||||
err := ignores.Load("testdata/.stignore")
|
err := ignores.Load(".stignore")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fchan := Walk(context.TODO(), Config{
|
fchan := Walk(context.TODO(), Config{
|
||||||
Filesystem: fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"),
|
Filesystem: testFs,
|
||||||
Subs: []string{"dir2"},
|
Subs: []string{"dir2"},
|
||||||
Matcher: ignores,
|
Matcher: ignores,
|
||||||
Hashers: 2,
|
Hashers: 2,
|
||||||
@ -88,15 +92,15 @@ func TestWalkSub(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWalk(t *testing.T) {
|
func TestWalk(t *testing.T) {
|
||||||
ignores := ignore.New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."))
|
ignores := ignore.New(testFs)
|
||||||
err := ignores.Load("testdata/.stignore")
|
err := ignores.Load(".stignore")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Log(ignores)
|
t.Log(ignores)
|
||||||
|
|
||||||
fchan := Walk(context.TODO(), Config{
|
fchan := Walk(context.TODO(), Config{
|
||||||
Filesystem: fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"),
|
Filesystem: testFs,
|
||||||
Matcher: ignores,
|
Matcher: ignores,
|
||||||
Hashers: 2,
|
Hashers: 2,
|
||||||
})
|
})
|
||||||
@ -192,11 +196,9 @@ func TestNormalization(t *testing.T) {
|
|||||||
|
|
||||||
numValid := len(tests) - numInvalid
|
numValid := len(tests) - numInvalid
|
||||||
|
|
||||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, ".")
|
|
||||||
|
|
||||||
for _, s1 := range tests {
|
for _, s1 := range tests {
|
||||||
// Create a directory for each of the interesting strings above
|
// Create a directory for each of the interesting strings above
|
||||||
if err := fs.MkdirAll(filepath.Join("testdata/normalization", s1), 0755); err != nil {
|
if err := testFs.MkdirAll(filepath.Join("normalization", s1), 0755); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +207,7 @@ func TestNormalization(t *testing.T) {
|
|||||||
// file names. Ensure that the file doesn't exist when it's
|
// file names. Ensure that the file doesn't exist when it's
|
||||||
// created. This detects and fails if there's file name
|
// created. This detects and fails if there's file name
|
||||||
// normalization stuff at the filesystem level.
|
// normalization stuff at the filesystem level.
|
||||||
if fd, err := fs.OpenFile(filepath.Join("testdata/normalization", s1, s2), os.O_CREATE|os.O_EXCL, 0644); err != nil {
|
if fd, err := testFs.OpenFile(filepath.Join("normalization", s1, s2), os.O_CREATE|os.O_EXCL, 0644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
fd.Write([]byte("test"))
|
fd.Write([]byte("test"))
|
||||||
@ -219,14 +221,8 @@ func TestNormalization(t *testing.T) {
|
|||||||
// make sure it all gets done. In production, things will be correct
|
// make sure it all gets done. In production, things will be correct
|
||||||
// eventually...
|
// eventually...
|
||||||
|
|
||||||
_, err := walkDir(fs, "testdata/normalization", nil)
|
walkDir(testFs, "normalization", nil, nil)
|
||||||
if err != nil {
|
tmp := walkDir(testFs, "normalization", nil, nil)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
tmp, err := walkDir(fs, "testdata/normalization", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
files := fileList(tmp).testfiles()
|
files := fileList(tmp).testfiles()
|
||||||
|
|
||||||
@ -268,9 +264,10 @@ func TestWalkSymlinkUnix(t *testing.T) {
|
|||||||
defer os.RemoveAll("_symlinks")
|
defer os.RemoveAll("_symlinks")
|
||||||
os.Symlink("../testdata", "_symlinks/link")
|
os.Symlink("../testdata", "_symlinks/link")
|
||||||
|
|
||||||
|
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, "_symlinks")
|
||||||
for _, path := range []string{".", "link"} {
|
for _, path := range []string{".", "link"} {
|
||||||
// Scan it
|
// Scan it
|
||||||
files, _ := walkDir(fs.NewFilesystem(fs.FilesystemTypeBasic, "_symlinks"), path, nil)
|
files := walkDir(fs, path, nil, nil)
|
||||||
|
|
||||||
// Verify that we got one symlink and with the correct attributes
|
// Verify that we got one symlink and with the correct attributes
|
||||||
if len(files) != 1 {
|
if len(files) != 1 {
|
||||||
@ -291,9 +288,11 @@ func TestWalkSymlinkWindows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a folder with a symlink in it
|
// Create a folder with a symlink in it
|
||||||
os.RemoveAll("_symlinks")
|
name := "_symlinks-win"
|
||||||
os.Mkdir("_symlinks", 0755)
|
os.RemoveAll(name)
|
||||||
defer os.RemoveAll("_symlinks")
|
os.Mkdir(name, 0755)
|
||||||
|
defer os.RemoveAll(name)
|
||||||
|
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, name)
|
||||||
if err := osutil.DebugSymlinkForTestsOnly("../testdata", "_symlinks/link"); err != nil {
|
if err := osutil.DebugSymlinkForTestsOnly("../testdata", "_symlinks/link"); err != nil {
|
||||||
// Probably we require permissions we don't have.
|
// Probably we require permissions we don't have.
|
||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
@ -301,7 +300,7 @@ func TestWalkSymlinkWindows(t *testing.T) {
|
|||||||
|
|
||||||
for _, path := range []string{".", "link"} {
|
for _, path := range []string{".", "link"} {
|
||||||
// Scan it
|
// Scan it
|
||||||
files, _ := walkDir(fs.NewFilesystem(fs.FilesystemTypeBasic, "_symlinks"), path, nil)
|
files := walkDir(fs, path, nil, nil)
|
||||||
|
|
||||||
// Verify that we got zero symlinks
|
// Verify that we got zero symlinks
|
||||||
if len(files) != 0 {
|
if len(files) != 0 {
|
||||||
@ -330,10 +329,8 @@ func TestWalkRootSymlink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scan it
|
// Scan it
|
||||||
files, err := walkDir(fs.NewFilesystem(fs.FilesystemTypeBasic, link), ".", nil)
|
files := walkDir(fs.NewFilesystem(fs.FilesystemTypeBasic, link), ".", nil, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error when root folder path is provided via a symlink: " + err.Error())
|
|
||||||
}
|
|
||||||
// Verify that we got two files
|
// Verify that we got two files
|
||||||
if len(files) != 2 {
|
if len(files) != 2 {
|
||||||
t.Errorf("expected two files, not %d", len(files))
|
t.Errorf("expected two files, not %d", len(files))
|
||||||
@ -352,10 +349,7 @@ func TestBlocksizeHysteresis(t *testing.T) {
|
|||||||
current := make(fakeCurrentFiler)
|
current := make(fakeCurrentFiler)
|
||||||
|
|
||||||
runTest := func(expectedBlockSize int) {
|
runTest := func(expectedBlockSize int) {
|
||||||
files, err := walkDir(sf, ".", current)
|
files := walkDir(sf, ".", current, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(files) != 1 {
|
if len(files) != 1 {
|
||||||
t.Fatalf("expected one file, not %d", len(files))
|
t.Fatalf("expected one file, not %d", len(files))
|
||||||
}
|
}
|
||||||
@ -409,7 +403,7 @@ func TestBlocksizeHysteresis(t *testing.T) {
|
|||||||
runTest(512 << 10)
|
runTest(512 << 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler) ([]protocol.FileInfo, error) {
|
func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore.Matcher) []protocol.FileInfo {
|
||||||
fchan := Walk(context.TODO(), Config{
|
fchan := Walk(context.TODO(), Config{
|
||||||
Filesystem: fs,
|
Filesystem: fs,
|
||||||
Subs: []string{dir},
|
Subs: []string{dir},
|
||||||
@ -417,6 +411,7 @@ func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler) ([]protocol.File
|
|||||||
Hashers: 2,
|
Hashers: 2,
|
||||||
UseLargeBlocks: true,
|
UseLargeBlocks: true,
|
||||||
CurrentFiler: cfiler,
|
CurrentFiler: cfiler,
|
||||||
|
Matcher: matcher,
|
||||||
})
|
})
|
||||||
|
|
||||||
var tmp []protocol.FileInfo
|
var tmp []protocol.FileInfo
|
||||||
@ -425,7 +420,7 @@ func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler) ([]protocol.File
|
|||||||
}
|
}
|
||||||
sort.Sort(fileList(tmp))
|
sort.Sort(fileList(tmp))
|
||||||
|
|
||||||
return tmp, nil
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileList []protocol.FileInfo
|
type fileList []protocol.FileInfo
|
||||||
@ -580,15 +575,53 @@ func TestIssue4799(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fd.Close()
|
fd.Close()
|
||||||
|
|
||||||
files, err := walkDir(fs, "/foo", nil)
|
files := walkDir(fs, "/foo", nil, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(files) != 1 || files[0].Name != "foo" {
|
if len(files) != 1 || files[0].Name != "foo" {
|
||||||
t.Error(`Received unexpected file infos when walking "/foo"`, files)
|
t.Error(`Received unexpected file infos when walking "/foo"`, files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRecurseInclude(t *testing.T) {
|
||||||
|
stignore := `
|
||||||
|
!/dir1/cfile
|
||||||
|
!efile
|
||||||
|
!ffile
|
||||||
|
*
|
||||||
|
`
|
||||||
|
ignores := ignore.New(testFs, ignore.WithCache(true))
|
||||||
|
if err := ignores.Parse(bytes.NewBufferString(stignore), ".stignore"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
files := walkDir(testFs, ".", nil, ignores)
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
filepath.Join("dir1"),
|
||||||
|
filepath.Join("dir1", "cfile"),
|
||||||
|
filepath.Join("dir2"),
|
||||||
|
filepath.Join("dir2", "dir21"),
|
||||||
|
filepath.Join("dir2", "dir21", "dir22"),
|
||||||
|
filepath.Join("dir2", "dir21", "dir22", "dir23"),
|
||||||
|
filepath.Join("dir2", "dir21", "dir22", "dir23", "efile"),
|
||||||
|
filepath.Join("dir2", "dir21", "dir22", "efile"),
|
||||||
|
filepath.Join("dir2", "dir21", "dir22", "efile", "efile"),
|
||||||
|
filepath.Join("dir2", "dir21", "dira"),
|
||||||
|
filepath.Join("dir2", "dir21", "dira", "efile"),
|
||||||
|
filepath.Join("dir2", "dir21", "dira", "ffile"),
|
||||||
|
filepath.Join("dir2", "dir21", "efile"),
|
||||||
|
filepath.Join("dir2", "dir21", "efile", "ign"),
|
||||||
|
filepath.Join("dir2", "dir21", "efile", "ign", "efile"),
|
||||||
|
}
|
||||||
|
if len(files) != len(expected) {
|
||||||
|
t.Fatalf("Got %d files %v, expected %d files at %v", len(files), files, len(expected), expected)
|
||||||
|
}
|
||||||
|
for i := range files {
|
||||||
|
if files[i].Name != expected[i] {
|
||||||
|
t.Errorf("Got %v, expected file at %v", files[i], expected[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIssue4841(t *testing.T) {
|
func TestIssue4841(t *testing.T) {
|
||||||
tmp, err := ioutil.TempDir("", "")
|
tmp, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user