mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 23:06:32 +00:00
469 lines
12 KiB
Go
469 lines
12 KiB
Go
package archiver
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/restic/restic/internal/fs"
|
|
restictest "github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
// debug.Log requires Tree.String.
|
|
var _ fmt.Stringer = Tree{}
|
|
|
|
func TestPathComponents(t *testing.T) {
|
|
var tests = []struct {
|
|
p string
|
|
c []string
|
|
virtual bool
|
|
rel bool
|
|
win bool
|
|
}{
|
|
{
|
|
p: "/foo/bar/baz",
|
|
c: []string{"foo", "bar", "baz"},
|
|
},
|
|
{
|
|
p: "/foo/bar/baz",
|
|
c: []string{"foo", "bar", "baz"},
|
|
rel: true,
|
|
},
|
|
{
|
|
p: "foo/bar/baz",
|
|
c: []string{"foo", "bar", "baz"},
|
|
},
|
|
{
|
|
p: "foo/bar/baz",
|
|
c: []string{"foo", "bar", "baz"},
|
|
rel: true,
|
|
},
|
|
{
|
|
p: "../foo/bar/baz",
|
|
c: []string{"foo", "bar", "baz"},
|
|
},
|
|
{
|
|
p: "../foo/bar/baz",
|
|
c: []string{"..", "foo", "bar", "baz"},
|
|
rel: true,
|
|
},
|
|
{
|
|
p: "c:/foo/bar/baz",
|
|
c: []string{"c", "foo", "bar", "baz"},
|
|
virtual: true,
|
|
rel: true,
|
|
win: true,
|
|
},
|
|
{
|
|
p: "c:/foo/../bar/baz",
|
|
c: []string{"c", "bar", "baz"},
|
|
virtual: true,
|
|
win: true,
|
|
},
|
|
{
|
|
p: `c:\foo\..\bar\baz`,
|
|
c: []string{"c", "bar", "baz"},
|
|
virtual: true,
|
|
win: true,
|
|
},
|
|
{
|
|
p: "c:/foo/../bar/baz",
|
|
c: []string{"c", "bar", "baz"},
|
|
virtual: true,
|
|
rel: true,
|
|
win: true,
|
|
},
|
|
{
|
|
p: `c:\foo\..\bar\baz`,
|
|
c: []string{"c", "bar", "baz"},
|
|
virtual: true,
|
|
rel: true,
|
|
win: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
if test.win && runtime.GOOS != "windows" {
|
|
t.Skip("skip test on unix")
|
|
}
|
|
|
|
c, v := pathComponents(fs.Local{}, filepath.FromSlash(test.p), test.rel)
|
|
if !cmp.Equal(test.c, c) {
|
|
t.Error(test.c, c)
|
|
}
|
|
|
|
if v != test.virtual {
|
|
t.Errorf("unexpected virtual prefix count returned, want %v, got %v", test.virtual, v)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRootDirectory(t *testing.T) {
|
|
var tests = []struct {
|
|
target string
|
|
root string
|
|
unix bool
|
|
win bool
|
|
}{
|
|
{target: ".", root: "."},
|
|
{target: "foo/bar/baz", root: "."},
|
|
{target: "../foo/bar/baz", root: ".."},
|
|
{target: "..", root: ".."},
|
|
{target: "../../..", root: "../../.."},
|
|
{target: "/home/foo", root: "/", unix: true},
|
|
{target: "c:/home/foo", root: "c:/", win: true},
|
|
{target: `c:\home\foo`, root: `c:\`, win: true},
|
|
{target: "//host/share/foo", root: "//host/share/", win: true},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
if test.unix && runtime.GOOS == "windows" {
|
|
t.Skip("skip test on windows")
|
|
}
|
|
if test.win && runtime.GOOS != "windows" {
|
|
t.Skip("skip test on unix")
|
|
}
|
|
|
|
root := rootDirectory(fs.Local{}, filepath.FromSlash(test.target))
|
|
want := filepath.FromSlash(test.root)
|
|
if root != want {
|
|
t.Fatalf("wrong root directory, want %v, got %v", want, root)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTree(t *testing.T) {
|
|
var tests = []struct {
|
|
targets []string
|
|
src TestDir
|
|
want Tree
|
|
unix bool
|
|
win bool
|
|
mustError bool
|
|
}{
|
|
{
|
|
targets: []string{"foo"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Path: "foo", Root: "."},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo", "bar", "baz"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Path: "foo", Root: "."},
|
|
"bar": Tree{Path: "bar", Root: "."},
|
|
"baz": Tree{Path: "baz", Root: "."},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/user1", "foo/user2", "foo/other"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/user2")},
|
|
"other": Tree{Path: filepath.FromSlash("foo/other")},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/work/user1", "foo/work/user2"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/work/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/work/user2")},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/user1", "bar/user1", "foo/other"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/user1")},
|
|
"other": Tree{Path: filepath.FromSlash("foo/other")},
|
|
}},
|
|
"bar": Tree{Root: ".", FileInfoPath: "bar", Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("bar/user1")},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"../work"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"work": Tree{Root: "..", Path: filepath.FromSlash("../work")},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"../work/other"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"work": Tree{Root: "..", FileInfoPath: filepath.FromSlash("../work"), Nodes: map[string]Tree{
|
|
"other": Tree{Path: filepath.FromSlash("../work/other")},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/user1", "../work/other", "foo/user2"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/user2")},
|
|
}},
|
|
"work": Tree{Root: "..", FileInfoPath: filepath.FromSlash("../work"), Nodes: map[string]Tree{
|
|
"other": Tree{Path: filepath.FromSlash("../work/other")},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/user1", "../foo/other", "foo/user2"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/user2")},
|
|
}},
|
|
"foo-1": Tree{Root: "..", FileInfoPath: filepath.FromSlash("../foo"), Nodes: map[string]Tree{
|
|
"other": Tree{Path: filepath.FromSlash("../foo/other")},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"foo": TestDir{
|
|
"file": TestFile{Content: "file content"},
|
|
"work": TestFile{Content: "work file content"},
|
|
},
|
|
},
|
|
targets: []string{"foo", "foo/work"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{
|
|
Root: ".",
|
|
FileInfoPath: "foo",
|
|
Nodes: map[string]Tree{
|
|
"file": Tree{Path: filepath.FromSlash("foo/file")},
|
|
"work": Tree{Path: filepath.FromSlash("foo/work")},
|
|
},
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"foo": TestDir{
|
|
"file": TestFile{Content: "file content"},
|
|
"work": TestDir{
|
|
"other": TestFile{Content: "other file content"},
|
|
},
|
|
},
|
|
},
|
|
targets: []string{"foo/work", "foo"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{
|
|
Root: ".",
|
|
FileInfoPath: "foo",
|
|
Nodes: map[string]Tree{
|
|
"file": Tree{Path: filepath.FromSlash("foo/file")},
|
|
"work": Tree{Path: filepath.FromSlash("foo/work")},
|
|
},
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"foo": TestDir{
|
|
"work": TestDir{
|
|
"user1": TestFile{Content: "file content"},
|
|
"user2": TestFile{Content: "other file content"},
|
|
},
|
|
},
|
|
},
|
|
targets: []string{"foo/work", "foo/work/user2"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"work": Tree{
|
|
FileInfoPath: filepath.FromSlash("foo/work"),
|
|
Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/work/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/work/user2")},
|
|
},
|
|
},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"foo": TestDir{
|
|
"work": TestDir{
|
|
"user1": TestFile{Content: "file content"},
|
|
"user2": TestFile{Content: "other file content"},
|
|
},
|
|
},
|
|
},
|
|
targets: []string{"foo/work/user2", "foo/work"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("foo/work"),
|
|
Nodes: map[string]Tree{
|
|
"user1": Tree{Path: filepath.FromSlash("foo/work/user1")},
|
|
"user2": Tree{Path: filepath.FromSlash("foo/work/user2")},
|
|
},
|
|
},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"foo": TestDir{
|
|
"other": TestFile{Content: "file content"},
|
|
"work": TestDir{
|
|
"user2": TestDir{
|
|
"data": TestDir{
|
|
"secret": TestFile{Content: "secret file content"},
|
|
},
|
|
},
|
|
"user3": TestDir{
|
|
"important.txt": TestFile{Content: "important work"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
targets: []string{"foo/work/user2/data/secret", "foo"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"other": Tree{Path: filepath.FromSlash("foo/other")},
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]Tree{
|
|
"user2": Tree{FileInfoPath: filepath.FromSlash("foo/work/user2"), Nodes: map[string]Tree{
|
|
"data": Tree{FileInfoPath: filepath.FromSlash("foo/work/user2/data"), Nodes: map[string]Tree{
|
|
"secret": Tree{
|
|
Path: filepath.FromSlash("foo/work/user2/data/secret"),
|
|
},
|
|
}},
|
|
}},
|
|
"user3": Tree{Path: filepath.FromSlash("foo/work/user3")},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
src: TestDir{
|
|
"mnt": TestDir{
|
|
"driveA": TestDir{
|
|
"work": TestDir{
|
|
"driveB": TestDir{
|
|
"secret": TestFile{Content: "secret file content"},
|
|
},
|
|
"test1": TestDir{
|
|
"important.txt": TestFile{Content: "important work"},
|
|
},
|
|
},
|
|
"test2": TestDir{
|
|
"important.txt": TestFile{Content: "other important work"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
unix: true,
|
|
targets: []string{"mnt/driveA", "mnt/driveA/work/driveB"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"mnt": Tree{Root: ".", FileInfoPath: filepath.FromSlash("mnt"), Nodes: map[string]Tree{
|
|
"driveA": Tree{FileInfoPath: filepath.FromSlash("mnt/driveA"), Nodes: map[string]Tree{
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("mnt/driveA/work"), Nodes: map[string]Tree{
|
|
"driveB": Tree{
|
|
Path: filepath.FromSlash("mnt/driveA/work/driveB"),
|
|
},
|
|
"test1": Tree{Path: filepath.FromSlash("mnt/driveA/work/test1")},
|
|
}},
|
|
"test2": Tree{Path: filepath.FromSlash("mnt/driveA/test2")},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"foo/work/user", "foo/work/user"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]Tree{
|
|
"user": Tree{Path: filepath.FromSlash("foo/work/user")},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"./foo/work/user", "foo/work/user"},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"foo": Tree{Root: ".", FileInfoPath: "foo", Nodes: map[string]Tree{
|
|
"work": Tree{FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]Tree{
|
|
"user": Tree{Path: filepath.FromSlash("foo/work/user")},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
win: true,
|
|
targets: []string{`c:\users\foobar\temp`},
|
|
want: Tree{Nodes: map[string]Tree{
|
|
"c": Tree{Root: `c:\`, FileInfoPath: `c:\`, Nodes: map[string]Tree{
|
|
"users": Tree{FileInfoPath: `c:\users`, Nodes: map[string]Tree{
|
|
"foobar": Tree{FileInfoPath: `c:\users\foobar`, Nodes: map[string]Tree{
|
|
"temp": Tree{Path: `c:\users\foobar\temp`},
|
|
}},
|
|
}},
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
targets: []string{"."},
|
|
mustError: true,
|
|
},
|
|
{
|
|
targets: []string{".."},
|
|
mustError: true,
|
|
},
|
|
{
|
|
targets: []string{"../.."},
|
|
mustError: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
if test.unix && runtime.GOOS == "windows" {
|
|
t.Skip("skip test on windows")
|
|
}
|
|
|
|
if test.win && runtime.GOOS != "windows" {
|
|
t.Skip("skip test on unix")
|
|
}
|
|
|
|
tempdir, cleanup := restictest.TempDir(t)
|
|
defer cleanup()
|
|
|
|
TestCreateFiles(t, tempdir, test.src)
|
|
|
|
back := restictest.Chdir(t, tempdir)
|
|
defer back()
|
|
|
|
tree, err := NewTree(fs.Local{}, test.targets)
|
|
if test.mustError {
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
t.Logf("found expected error: %v", err)
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !cmp.Equal(&test.want, tree) {
|
|
t.Error(cmp.Diff(&test.want, tree))
|
|
}
|
|
})
|
|
}
|
|
}
|