mirror of
https://github.com/restic/restic.git
synced 2024-12-27 10:17:58 +00:00
1140950d7b
When the scanner is slower than the actual backup, the tomb cancels the context passed to Scan(), which then returns ctx.Err(). In the end, the main function prints an error message that is not helpful ("Context cancelled") and exits with an error code although no error occurred. The code now ignores the error in the context and just uses it for cancellation. The scanner is not supposed to return an error anyway. Closes #1978
329 lines
7.8 KiB
Go
329 lines
7.8 KiB
Go
package archiver
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/restic/restic/internal/fs"
|
|
restictest "github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
func TestScanner(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
src TestDir
|
|
want map[string]ScanStats
|
|
selFn SelectFunc
|
|
}{
|
|
{
|
|
name: "include-all",
|
|
src: TestDir{
|
|
"other": TestFile{Content: "another file"},
|
|
"work": TestDir{
|
|
"foo": TestFile{Content: "foo"},
|
|
"foo.txt": TestFile{Content: "foo text file"},
|
|
"subdir": TestDir{
|
|
"other": TestFile{Content: "other in subdir"},
|
|
"bar.txt": TestFile{Content: "bar.txt in subdir"},
|
|
},
|
|
},
|
|
},
|
|
want: map[string]ScanStats{
|
|
filepath.FromSlash("other"): ScanStats{Files: 1, Bytes: 12},
|
|
filepath.FromSlash("work/foo"): ScanStats{Files: 2, Bytes: 15},
|
|
filepath.FromSlash("work/foo.txt"): ScanStats{Files: 3, Bytes: 28},
|
|
filepath.FromSlash("work/subdir/bar.txt"): ScanStats{Files: 4, Bytes: 45},
|
|
filepath.FromSlash("work/subdir/other"): ScanStats{Files: 5, Bytes: 60},
|
|
filepath.FromSlash("work/subdir"): ScanStats{Files: 5, Dirs: 1, Bytes: 60},
|
|
filepath.FromSlash("work"): ScanStats{Files: 5, Dirs: 2, Bytes: 60},
|
|
filepath.FromSlash("."): ScanStats{Files: 5, Dirs: 3, Bytes: 60},
|
|
filepath.FromSlash(""): ScanStats{Files: 5, Dirs: 3, Bytes: 60},
|
|
},
|
|
},
|
|
{
|
|
name: "select-txt",
|
|
src: TestDir{
|
|
"other": TestFile{Content: "another file"},
|
|
"work": TestDir{
|
|
"foo": TestFile{Content: "foo"},
|
|
"foo.txt": TestFile{Content: "foo text file"},
|
|
"subdir": TestDir{
|
|
"other": TestFile{Content: "other in subdir"},
|
|
"bar.txt": TestFile{Content: "bar.txt in subdir"},
|
|
},
|
|
},
|
|
},
|
|
selFn: func(item string, fi os.FileInfo) bool {
|
|
if fi.IsDir() {
|
|
return true
|
|
}
|
|
|
|
if filepath.Ext(item) == ".txt" {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
want: map[string]ScanStats{
|
|
filepath.FromSlash("work/foo.txt"): ScanStats{Files: 1, Bytes: 13},
|
|
filepath.FromSlash("work/subdir/bar.txt"): ScanStats{Files: 2, Bytes: 30},
|
|
filepath.FromSlash("work/subdir"): ScanStats{Files: 2, Dirs: 1, Bytes: 30},
|
|
filepath.FromSlash("work"): ScanStats{Files: 2, Dirs: 2, Bytes: 30},
|
|
filepath.FromSlash("."): ScanStats{Files: 2, Dirs: 3, Bytes: 30},
|
|
filepath.FromSlash(""): ScanStats{Files: 2, Dirs: 3, Bytes: 30},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tempdir, cleanup := restictest.TempDir(t)
|
|
defer cleanup()
|
|
|
|
TestCreateFiles(t, tempdir, test.src)
|
|
|
|
back := fs.TestChdir(t, tempdir)
|
|
defer back()
|
|
|
|
cur, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
|
if test.selFn != nil {
|
|
sc.Select = test.selFn
|
|
}
|
|
|
|
results := make(map[string]ScanStats)
|
|
sc.Result = func(item string, s ScanStats) {
|
|
var p string
|
|
var err error
|
|
|
|
if item != "" {
|
|
p, err = filepath.Rel(cur, item)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
results[p] = s
|
|
}
|
|
|
|
err = sc.Scan(ctx, []string{"."})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !cmp.Equal(test.want, results) {
|
|
t.Error(cmp.Diff(test.want, results))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScannerError(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
unix bool
|
|
src TestDir
|
|
result ScanStats
|
|
selFn SelectFunc
|
|
errFn func(t testing.TB, item string, fi os.FileInfo, err error) error
|
|
resFn func(t testing.TB, item string, s ScanStats)
|
|
prepare func(t testing.TB)
|
|
}{
|
|
{
|
|
name: "no-error",
|
|
src: TestDir{
|
|
"other": TestFile{Content: "another file"},
|
|
"work": TestDir{
|
|
"foo": TestFile{Content: "foo"},
|
|
"foo.txt": TestFile{Content: "foo text file"},
|
|
"subdir": TestDir{
|
|
"other": TestFile{Content: "other in subdir"},
|
|
"bar.txt": TestFile{Content: "bar.txt in subdir"},
|
|
},
|
|
},
|
|
},
|
|
result: ScanStats{Files: 5, Dirs: 3, Bytes: 60},
|
|
},
|
|
{
|
|
name: "unreadable-dir",
|
|
unix: true,
|
|
src: TestDir{
|
|
"other": TestFile{Content: "another file"},
|
|
"work": TestDir{
|
|
"foo": TestFile{Content: "foo"},
|
|
"foo.txt": TestFile{Content: "foo text file"},
|
|
"subdir": TestDir{
|
|
"other": TestFile{Content: "other in subdir"},
|
|
"bar.txt": TestFile{Content: "bar.txt in subdir"},
|
|
},
|
|
},
|
|
},
|
|
result: ScanStats{Files: 3, Dirs: 2, Bytes: 28},
|
|
prepare: func(t testing.TB) {
|
|
err := os.Chmod(filepath.Join("work", "subdir"), 0000)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
},
|
|
errFn: func(t testing.TB, item string, fi os.FileInfo, err error) error {
|
|
if item == filepath.FromSlash("work/subdir") {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "removed-item",
|
|
src: TestDir{
|
|
"bar": TestFile{Content: "bar"},
|
|
"baz": TestFile{Content: "baz"},
|
|
"foo": TestFile{Content: "foo"},
|
|
"other": TestFile{Content: "other"},
|
|
},
|
|
result: ScanStats{Files: 3, Dirs: 1, Bytes: 11},
|
|
resFn: func(t testing.TB, item string, s ScanStats) {
|
|
if item == "bar" {
|
|
err := os.Remove("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
},
|
|
errFn: func(t testing.TB, item string, fi os.FileInfo, err error) error {
|
|
if item == "foo" {
|
|
t.Logf("ignoring error for %v: %v", item, err)
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.unix && runtime.GOOS == "windows" {
|
|
t.Skipf("skip on windows")
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tempdir, cleanup := restictest.TempDir(t)
|
|
defer cleanup()
|
|
|
|
TestCreateFiles(t, tempdir, test.src)
|
|
|
|
back := fs.TestChdir(t, tempdir)
|
|
defer back()
|
|
|
|
cur, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if test.prepare != nil {
|
|
test.prepare(t)
|
|
}
|
|
|
|
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
|
if test.selFn != nil {
|
|
sc.Select = test.selFn
|
|
}
|
|
|
|
var stats ScanStats
|
|
|
|
sc.Result = func(item string, s ScanStats) {
|
|
if item == "" {
|
|
stats = s
|
|
return
|
|
}
|
|
|
|
if test.resFn != nil {
|
|
p, relErr := filepath.Rel(cur, item)
|
|
if relErr != nil {
|
|
panic(relErr)
|
|
}
|
|
test.resFn(t, p, s)
|
|
}
|
|
}
|
|
if test.errFn != nil {
|
|
sc.Error = func(item string, fi os.FileInfo, err error) error {
|
|
p, relErr := filepath.Rel(cur, item)
|
|
if relErr != nil {
|
|
panic(relErr)
|
|
}
|
|
|
|
return test.errFn(t, p, fi, err)
|
|
}
|
|
}
|
|
|
|
err = sc.Scan(ctx, []string{"."})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if stats != test.result {
|
|
t.Errorf("wrong final result, want\n %#v\ngot:\n %#v", test.result, stats)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScannerCancel(t *testing.T) {
|
|
src := TestDir{
|
|
"bar": TestFile{Content: "bar"},
|
|
"baz": TestFile{Content: "baz"},
|
|
"foo": TestFile{Content: "foo"},
|
|
"other": TestFile{Content: "other"},
|
|
}
|
|
|
|
result := ScanStats{Files: 2, Dirs: 1, Bytes: 6}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tempdir, cleanup := restictest.TempDir(t)
|
|
defer cleanup()
|
|
|
|
TestCreateFiles(t, tempdir, src)
|
|
|
|
back := fs.TestChdir(t, tempdir)
|
|
defer back()
|
|
|
|
cur, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
|
var lastStats ScanStats
|
|
sc.Result = func(item string, s ScanStats) {
|
|
lastStats = s
|
|
|
|
if item == filepath.Join(cur, "baz") {
|
|
t.Logf("found baz")
|
|
cancel()
|
|
}
|
|
}
|
|
|
|
err = sc.Scan(ctx, []string{"."})
|
|
if err != nil {
|
|
t.Errorf("unexpected error %v found", err)
|
|
}
|
|
|
|
if lastStats != result {
|
|
t.Errorf("wrong final result, want\n %#v\ngot:\n %#v", result, lastStats)
|
|
}
|
|
}
|