From f3442ce8a5337f15ecf9c5dd53c763d38b87f487 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 3 Jan 2021 17:42:06 +0100 Subject: [PATCH] Test that WriteTo of a backend's Load remains accessible --- cmd/restic/global.go | 19 +++++++++---- cmd/restic/integration_test.go | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 6ac07c6db..76a0e33cb 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -71,7 +71,7 @@ type GlobalOptions struct { stdout io.Writer stderr io.Writer - backendTestHook backendWrapper + backendTestHook, backendInnerTestHook backendWrapper // verbosity is set as follows: // 0 means: don't print any messages except errors, this is used when --quiet is specified @@ -695,12 +695,8 @@ func open(s string, gopts GlobalOptions, opts options.Options) (restic.Backend, switch loc.Scheme { case "local": be, err = local.Open(globalOptions.ctx, cfg.(local.Config)) - // wrap the backend in a LimitBackend so that the throughput is limited - be = limiter.LimitBackend(be, lim) case "sftp": be, err = sftp.Open(globalOptions.ctx, cfg.(sftp.Config)) - // wrap the backend in a LimitBackend so that the throughput is limited - be = limiter.LimitBackend(be, lim) case "s3": be, err = s3.Open(globalOptions.ctx, cfg.(s3.Config), rt) case "gs": @@ -724,6 +720,19 @@ func open(s string, gopts GlobalOptions, opts options.Options) (restic.Backend, return nil, errors.Fatalf("unable to open repo at %v: %v", location.StripPassword(s), err) } + // wrap backend if a test specified an inner hook + if gopts.backendInnerTestHook != nil { + be, err = gopts.backendInnerTestHook(be) + if err != nil { + return nil, err + } + } + + if loc.Scheme == "local" || loc.Scheme == "sftp" { + // wrap the backend in a LimitBackend so that the throughput is limited + be = limiter.LimitBackend(be, lim) + } + // check if config is there fi, err := be.Stat(globalOptions.ctx, restic.Handle{Type: restic.ConfigFile}) if err != nil { diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index dad398522..d17602d0b 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -1829,3 +1829,53 @@ func TestDiff(t *testing.T) { rtest.Assert(t, r.MatchString(out), "expected pattern %v in output, got\n%v", pattern, out) } } + +type writeToOnly struct { + rd io.Reader +} + +func (r *writeToOnly) Read(p []byte) (n int, err error) { + return 0, fmt.Errorf("should have called WriteTo instead") +} + +func (r *writeToOnly) WriteTo(w io.Writer) (int64, error) { + return io.Copy(w, r.rd) +} + +type onlyLoadWithWriteToBackend struct { + restic.Backend +} + +func (be *onlyLoadWithWriteToBackend) Load(ctx context.Context, h restic.Handle, + length int, offset int64, fn func(rd io.Reader) error) error { + + return be.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error { + return fn(&writeToOnly{rd: rd}) + }) +} + +func TestBackendLoadWriteTo(t *testing.T) { + env, cleanup := withTestEnvironment(t) + defer cleanup() + + // setup backend which only works if it's WriteTo method is correctly propagated upwards + env.gopts.backendInnerTestHook = func(r restic.Backend) (restic.Backend, error) { + return &onlyLoadWithWriteToBackend{Backend: r}, nil + } + + testSetupBackupData(t, env) + + // add some data, but make sure that it isn't cached during upload + opts := BackupOptions{} + env.gopts.NoCache = true + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9")}, opts, env.gopts) + + // loading snapshots must still work + env.gopts.NoCache = false + firstSnapshot := testRunList(t, "snapshots", env.gopts) + rtest.Assert(t, len(firstSnapshot) == 1, + "expected one snapshot, got %v", firstSnapshot) + + // test readData using the hashing.Reader + testRunCheck(t, env.gopts) +}