From 7c107acf0bb93b146ddc1e6bf7d3eb2774f646d3 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 10 May 2015 02:41:16 +0200 Subject: [PATCH] More integration tests --- cmd/restic/cmd_list.go | 14 ++- cmd/restic/cmd_restore.go | 2 +- cmd/restic/integration_helpers_test.go | 140 +++++++++++++++++++++++++ cmd/restic/integration_test.go | 81 +++++++++++++- 4 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 cmd/restic/integration_helpers_test.go diff --git a/cmd/restic/cmd_list.go b/cmd/restic/cmd_list.go index 01201cef0..641d72c2e 100644 --- a/cmd/restic/cmd_list.go +++ b/cmd/restic/cmd_list.go @@ -3,11 +3,15 @@ package main import ( "errors" "fmt" + "io" + "os" "github.com/restic/restic/backend" ) -type CmdList struct{} +type CmdList struct { + w io.Writer +} func init() { _, err := parser.AddCommand("list", @@ -24,6 +28,10 @@ func (cmd CmdList) Usage() string { } func (cmd CmdList) Execute(args []string) error { + if cmd.w == nil { + cmd.w = os.Stdout + } + if len(args) != 1 { return fmt.Errorf("type not specified, Usage: %s", cmd.Usage()) } @@ -42,7 +50,7 @@ func (cmd CmdList) Execute(args []string) error { } for blob := range s.Index().Each(nil) { - fmt.Println(blob.ID) + fmt.Fprintln(cmd.w, blob.ID) } return nil @@ -61,7 +69,7 @@ func (cmd CmdList) Execute(args []string) error { } for id := range s.List(t, nil) { - fmt.Printf("%s\n", id) + fmt.Fprintf(cmd.w, "%s\n", id) } return nil diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 3d14b17fb..cd91228e6 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -81,7 +81,7 @@ func (cmd CmdRestore) Execute(args []string) error { } } - fmt.Printf("restoring %s to %s\n", res.Snapshot(), target) + verbosePrintf("restoring %s to %s\n", res.Snapshot(), target) err = res.RestoreTo(target) if err != nil { diff --git a/cmd/restic/integration_helpers_test.go b/cmd/restic/integration_helpers_test.go new file mode 100644 index 000000000..2f3f0ec72 --- /dev/null +++ b/cmd/restic/integration_helpers_test.go @@ -0,0 +1,140 @@ +// +build integration + +package main + +import ( + "fmt" + "os" + "path/filepath" + "syscall" +) + +type dirEntry struct { + path string + fi os.FileInfo +} + +func walkDir(dir string) <-chan *dirEntry { + ch := make(chan *dirEntry, 100) + + go func() { + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + return nil + } + + name, err := filepath.Rel(dir, path) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + return nil + } + + ch <- &dirEntry{ + path: name, + fi: info, + } + + return nil + }) + + if err != nil { + fmt.Fprintf(os.Stderr, "Walk() error: %v\n", err) + } + + close(ch) + }() + + // first element is root + _ = <-ch + + return ch +} + +func (e *dirEntry) equals(other *dirEntry) bool { + if e.path != other.path { + fmt.Printf("path does not match\n") + return false + } + + if e.fi.Mode() != other.fi.Mode() { + fmt.Printf("mode does not match\n") + return false + } + + // if e.fi.ModTime() != other.fi.ModTime() { + // fmt.Printf("%s: ModTime does not match\n", e.path) + // // TODO: Fix ModTime for directories, return false + // return true + // } + + stat, _ := e.fi.Sys().(*syscall.Stat_t) + stat2, _ := other.fi.Sys().(*syscall.Stat_t) + + if stat.Uid != stat2.Uid || stat2.Gid != stat2.Gid { + return false + } + + return true +} + +func directoriesEqualContents(dir1, dir2 string) bool { + ch1 := walkDir(dir1) + ch2 := walkDir(dir2) + + changes := false + + var a, b *dirEntry + for { + var ok bool + + if ch1 != nil && a == nil { + a, ok = <-ch1 + if !ok { + ch1 = nil + } + } + + if ch2 != nil && b == nil { + b, ok = <-ch2 + if !ok { + ch2 = nil + } + } + + if ch1 == nil && ch2 == nil { + break + } + + if ch1 == nil { + fmt.Printf("+%v\n", b.path) + changes = true + } else if ch2 == nil { + fmt.Printf("-%v\n", a.path) + changes = true + } else if !a.equals(b) { + if a.path < b.path { + fmt.Printf("-%v\n", a.path) + changes = true + a = nil + continue + } else if a.path > b.path { + fmt.Printf("+%v\n", b.path) + changes = true + b = nil + continue + } else { + fmt.Printf("%%%v\n", a.path) + changes = true + } + } + + a, b = nil, nil + } + + if changes { + return false + } + + return true +} diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index f18de86d1..4d8106974 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -3,13 +3,16 @@ package main import ( + "bufio" "flag" + "io" "io/ioutil" "os" "os/exec" "path/filepath" "testing" + "github.com/restic/restic/backend" . "github.com/restic/restic/test" ) @@ -54,6 +57,23 @@ func system(command string, args ...string) error { return cmd.Run() } +func parseIDsFromReader(t testing.TB, rd io.Reader) backend.IDs { + IDs := backend.IDs{} + sc := bufio.NewScanner(rd) + + for sc.Scan() { + id, err := backend.ParseID(sc.Text()) + if err != nil { + t.Logf("parse id %v: %v", sc.Text(), err) + continue + } + + IDs = append(IDs, id) + } + + return IDs +} + func cmdInit(t testing.TB) { cmd := &CmdInit{} OK(t, cmd.Execute(nil)) @@ -61,13 +81,43 @@ func cmdInit(t testing.TB) { t.Logf("repository initialized at %v", opts.Repo) } -func cmdBackup(t testing.TB, target []string) { +func cmdBackup(t testing.TB, target []string, parentID backend.ID) { cmd := &CmdBackup{} + cmd.Parent = parentID.String() + t.Logf("backing up %v", target) OK(t, cmd.Execute(target)) } +func cmdList(t testing.TB, tpe string) []backend.ID { + + rd, wr := io.Pipe() + + cmd := &CmdList{w: wr} + + go func() { + OK(t, cmd.Execute([]string{tpe})) + OK(t, wr.Close()) + }() + + IDs := parseIDsFromReader(t, rd) + + t.Logf("Listing %v: %v", tpe, IDs) + + return IDs +} + +func cmdRestore(t testing.TB, dir string, snapshotID backend.ID) { + cmd := &CmdRestore{} + cmd.Execute([]string{snapshotID.String(), dir}) +} + +func cmdFsck(t testing.TB) { + cmd := &CmdFsck{CheckData: true, Orphaned: true} + OK(t, cmd.Execute(nil)) +} + func TestBackup(t *testing.T) { if *TestDataFile == "" { t.Fatal("no data tar file specified, use flag '-test.datafile'") @@ -84,5 +134,32 @@ func TestBackup(t *testing.T) { setupTarTestFixture(t, datadir, *TestDataFile) - cmdBackup(t, []string{datadir}) + // first backup + cmdBackup(t, []string{datadir}, nil) + snapshotIDs := cmdList(t, "snapshots") + Assert(t, len(snapshotIDs) == 1, + "more than one snapshot ID in repo") + + // second backup, implicit incremental + cmdBackup(t, []string{datadir}, nil) + snapshotIDs = cmdList(t, "snapshots") + Assert(t, len(snapshotIDs) == 2, + "more than one snapshot ID in repo") + + // third backup, explicit incremental + cmdBackup(t, []string{datadir}, snapshotIDs[0]) + snapshotIDs = cmdList(t, "snapshots") + Assert(t, len(snapshotIDs) == 3, + "more than one snapshot ID in repo") + + // restore all backups and compare + for _, snapshotID := range snapshotIDs { + restoredir := filepath.Join(tempdir, "restore", snapshotID.String()) + t.Logf("restoring snapshot %v to %v", snapshotID.Str(), restoredir) + cmdRestore(t, restoredir, snapshotIDs[0]) + Assert(t, directoriesEqualContents(datadir, filepath.Join(restoredir, "testdata")), + "directories are not equal") + } + + cmdFsck(t) }