From 13a8b5822f1115ca11f6d15e47a3a6a61e839aa5 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Thu, 8 Jun 2023 16:53:55 +0200 Subject: [PATCH] backend: Adjust tests to use the Factory to instantiate the backend This drastically reduces the amount of duplicated test code. --- internal/backend/azure/azure_test.go | 43 +------ internal/backend/b2/b2_test.go | 28 +---- internal/backend/gs/gs_test.go | 43 +------ internal/backend/local/local_test.go | 27 +---- internal/backend/mem/mem_backend.go | 20 +++ internal/backend/mem/mem_backend_test.go | 48 +------- internal/backend/rclone/backend_test.go | 29 ++--- internal/backend/rest/rest_test.go | 22 +--- internal/backend/s3/s3_test.go | 147 +++++------------------ internal/backend/sftp/sftp_test.go | 28 +---- internal/backend/swift/swift_test.go | 42 +------ internal/backend/test/suite.go | 63 +++++++--- internal/backend/test/tests.go | 2 +- 13 files changed, 119 insertions(+), 423 deletions(-) diff --git a/internal/backend/azure/azure_test.go b/internal/backend/azure/azure_test.go index 5aee96fbd..8465cc3b0 100644 --- a/internal/backend/azure/azure_test.go +++ b/internal/backend/azure/azure_test.go @@ -12,18 +12,12 @@ import ( "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/azure" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) func newAzureTestSuite(t testing.TB) *test.Suite[azure.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[azure.Config]{ // do not use excessive data MinimalData: true, @@ -40,42 +34,7 @@ func newAzureTestSuite(t testing.TB) *test.Suite[azure.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg azure.Config) (restic.Backend, error) { - ctx := context.TODO() - be, err := azure.Create(ctx, cfg, tr) - if err != nil { - return nil, err - } - - _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !be.IsNotExist(err) { - return nil, err - } - - if err == nil { - return nil, errors.New("config already exists") - } - - return be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg azure.Config) (restic.Backend, error) { - ctx := context.TODO() - return azure.Open(ctx, cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg azure.Config) error { - ctx := context.TODO() - be, err := azure.Open(ctx, cfg, tr) - if err != nil { - return err - } - - return be.Delete(context.TODO()) - }, + Factory: azure.NewFactory(), } } diff --git a/internal/backend/b2/b2_test.go b/internal/backend/b2/b2_test.go index 3a649db1e..348af9095 100644 --- a/internal/backend/b2/b2_test.go +++ b/internal/backend/b2/b2_test.go @@ -1,26 +1,18 @@ package b2_test import ( - "context" "fmt" "os" "testing" "time" - "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/b2" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) func newB2TestSuite(t testing.TB) *test.Suite[b2.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[b2.Config]{ // do not use excessive data MinimalData: true, @@ -40,25 +32,7 @@ func newB2TestSuite(t testing.TB) *test.Suite[b2.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg b2.Config) (restic.Backend, error) { - return b2.Create(context.Background(), cfg, tr) - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg b2.Config) (restic.Backend, error) { - return b2.Open(context.Background(), cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg b2.Config) error { - be, err := b2.Open(context.Background(), cfg, tr) - if err != nil { - return err - } - - return be.Delete(context.TODO()) - }, + Factory: b2.NewFactory(), } } diff --git a/internal/backend/gs/gs_test.go b/internal/backend/gs/gs_test.go index a73fe77fb..47085dc4e 100644 --- a/internal/backend/gs/gs_test.go +++ b/internal/backend/gs/gs_test.go @@ -1,26 +1,17 @@ package gs_test import ( - "context" "fmt" "os" "testing" "time" - "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/gs" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/errors" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) func newGSTestSuite(t testing.TB) *test.Suite[gs.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[gs.Config]{ // do not use excessive data MinimalData: true, @@ -37,39 +28,7 @@ func newGSTestSuite(t testing.TB) *test.Suite[gs.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg gs.Config) (restic.Backend, error) { - be, err := gs.Create(context.Background(), cfg, tr) - if err != nil { - return nil, err - } - - _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !be.IsNotExist(err) { - return nil, err - } - - if err == nil { - return nil, errors.New("config already exists") - } - - return be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg gs.Config) (restic.Backend, error) { - return gs.Open(context.TODO(), cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg gs.Config) error { - be, err := gs.Open(context.TODO(), cfg, tr) - if err != nil { - return err - } - - return be.Delete(context.TODO()) - }, + Factory: gs.NewFactory(), } } diff --git a/internal/backend/local/local_test.go b/internal/backend/local/local_test.go index ca9e3b71b..2a8b626d4 100644 --- a/internal/backend/local/local_test.go +++ b/internal/backend/local/local_test.go @@ -8,7 +8,6 @@ import ( "github.com/restic/restic/internal/backend/local" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -16,11 +15,7 @@ func newTestSuite(t testing.TB) *test.Suite[local.Config] { return &test.Suite[local.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. NewConfig: func() (*local.Config, error) { - dir, err := os.MkdirTemp(rtest.TestTempDir, "restic-test-local-") - if err != nil { - t.Fatal(err) - } - + dir := rtest.TempDir(t) t.Logf("create new backend at %v", dir) cfg := &local.Config{ @@ -30,25 +25,7 @@ func newTestSuite(t testing.TB) *test.Suite[local.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg local.Config) (restic.Backend, error) { - return local.Create(context.TODO(), cfg) - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg local.Config) (restic.Backend, error) { - return local.Open(context.TODO(), cfg) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg local.Config) error { - if !rtest.TestCleanupTempDirs { - t.Logf("leaving test backend dir at %v", cfg.Path) - } - - rtest.RemoveAll(t, cfg.Path) - return nil - }, + Factory: local.NewFactory(), } } diff --git a/internal/backend/mem/mem_backend.go b/internal/backend/mem/mem_backend.go index 618ef5752..a467d33f7 100644 --- a/internal/backend/mem/mem_backend.go +++ b/internal/backend/mem/mem_backend.go @@ -6,10 +6,12 @@ import ( "encoding/base64" "hash" "io" + "net/http" "sync" "github.com/cespare/xxhash/v2" "github.com/restic/restic/internal/backend" + "github.com/restic/restic/internal/backend/location" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" @@ -20,6 +22,24 @@ type memMap map[restic.Handle][]byte // make sure that MemoryBackend implements backend.Backend var _ restic.Backend = &MemoryBackend{} +// NewFactory creates a persistent mem backend +func NewFactory() location.Factory { + be := New() + + return location.NewHTTPBackendFactory[struct{}, *MemoryBackend]( + func(s string) (*struct{}, error) { + return &struct{}{}, nil + }, + location.NoPassword, + func(_ context.Context, _ struct{}, _ http.RoundTripper) (*MemoryBackend, error) { + return be, nil + }, + func(_ context.Context, _ struct{}, _ http.RoundTripper) (*MemoryBackend, error) { + return be, nil + }, + ) +} + var errNotFound = errors.New("not found") const connectionCount = 2 diff --git a/internal/backend/mem/mem_backend_test.go b/internal/backend/mem/mem_backend_test.go index 3dea089bc..c4dad0fb2 100644 --- a/internal/backend/mem/mem_backend_test.go +++ b/internal/backend/mem/mem_backend_test.go @@ -1,58 +1,20 @@ package mem_test import ( - "context" "testing" - "github.com/restic/restic/internal/errors" - "github.com/restic/restic/internal/restic" - "github.com/restic/restic/internal/backend/mem" "github.com/restic/restic/internal/backend/test" ) -type memConfig struct { - be restic.Backend -} - -func newTestSuite() *test.Suite[*memConfig] { - return &test.Suite[*memConfig]{ +func newTestSuite() *test.Suite[struct{}] { + return &test.Suite[struct{}]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (**memConfig, error) { - cfg := &memConfig{} - return &cfg, nil + NewConfig: func() (*struct{}, error) { + return &struct{}{}, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg *memConfig) (restic.Backend, error) { - if cfg.be != nil { - _, err := cfg.be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !cfg.be.IsNotExist(err) { - return nil, err - } - - if err == nil { - return nil, errors.New("config already exists") - } - } - - cfg.be = mem.New() - return cfg.be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg *memConfig) (restic.Backend, error) { - if cfg.be == nil { - cfg.be = mem.New() - } - return cfg.be, nil - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg *memConfig) error { - // no cleanup needed - return nil - }, + Factory: mem.NewFactory(), } } diff --git a/internal/backend/rclone/backend_test.go b/internal/backend/rclone/backend_test.go index 738462577..742031585 100644 --- a/internal/backend/rclone/backend_test.go +++ b/internal/backend/rclone/backend_test.go @@ -1,14 +1,11 @@ package rclone_test import ( - "context" "os/exec" "testing" "github.com/restic/restic/internal/backend/rclone" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/errors" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -24,23 +21,15 @@ func newTestSuite(t testing.TB) *test.Suite[rclone.Config] { return &cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg rclone.Config) (restic.Backend, error) { - t.Logf("Create()") - be, err := rclone.Create(context.TODO(), cfg, nil) - var e *exec.Error - if errors.As(err, &e) && e.Err == exec.ErrNotFound { - t.Skipf("program %q not found", e.Name) - return nil, nil - } - return be, err - }, + Factory: rclone.NewFactory(), + } +} - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg rclone.Config) (restic.Backend, error) { - t.Logf("Open()") - return rclone.Open(context.TODO(), cfg, nil) - }, +func findRclone(t testing.TB) { + // try to find a rclone binary + _, err := exec.LookPath("rclone") + if err != nil { + t.Skip(err) } } @@ -51,9 +40,11 @@ func TestBackendRclone(t *testing.T) { } }() + findRclone(t) newTestSuite(t).RunTests(t) } func BenchmarkBackendREST(t *testing.B) { + findRclone(t) newTestSuite(t).RunBenchmarks(t) } diff --git a/internal/backend/rest/rest_test.go b/internal/backend/rest/rest_test.go index 4d069e63c..60cc40afe 100644 --- a/internal/backend/rest/rest_test.go +++ b/internal/backend/rest/rest_test.go @@ -9,10 +9,8 @@ import ( "testing" "time" - "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/rest" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -68,11 +66,6 @@ func runRESTServer(ctx context.Context, t testing.TB, dir string) (*url.URL, fun } func newTestSuite(_ context.Context, t testing.TB, url *url.URL, minimalData bool) *test.Suite[rest.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[rest.Config]{ MinimalData: minimalData, @@ -83,20 +76,7 @@ func newTestSuite(_ context.Context, t testing.TB, url *url.URL, minimalData boo return &cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg rest.Config) (restic.Backend, error) { - return rest.Create(context.TODO(), cfg, tr) - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg rest.Config) (restic.Backend, error) { - return rest.Open(context.TODO(), cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg rest.Config) error { - return nil - }, + Factory: rest.NewFactory(), } } diff --git a/internal/backend/s3/s3_test.go b/internal/backend/s3/s3_test.go index 1cdc6d7e9..e645fe03e 100644 --- a/internal/backend/s3/s3_test.go +++ b/internal/backend/s3/s3_test.go @@ -4,22 +4,18 @@ import ( "context" "crypto/rand" "encoding/hex" - "errors" "fmt" "io" "net" - "net/http" "os" "os/exec" "path/filepath" "testing" "time" - "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/s3" "github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/options" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -98,84 +94,34 @@ func newRandomCredentials(t testing.TB) (key, secret string) { return key, secret } -type MinioTestConfig struct { - s3.Config - - tempdir string - stopServer func() -} - -func createS3(t testing.TB, cfg MinioTestConfig, tr http.RoundTripper) (be restic.Backend, err error) { - for i := 0; i < 10; i++ { - be, err = s3.Create(context.TODO(), cfg.Config, tr) - if err != nil { - t.Logf("s3 open: try %d: error %v", i, err) - time.Sleep(500 * time.Millisecond) - continue - } - - break - } - - return be, err -} - -func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite[MinioTestConfig] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - - return &test.Suite[MinioTestConfig]{ +func newMinioTestSuite(ctx context.Context, t testing.TB, key string, secret string) *test.Suite[s3.Config] { + return &test.Suite[s3.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (*MinioTestConfig, error) { - cfg := MinioTestConfig{} - - cfg.tempdir = rtest.TempDir(t) - key, secret := newRandomCredentials(t) - cfg.stopServer = runMinio(ctx, t, cfg.tempdir, key, secret) - - cfg.Config = s3.NewConfig() - cfg.Config.Endpoint = "localhost:9000" - cfg.Config.Bucket = "restictestbucket" - cfg.Config.Prefix = fmt.Sprintf("test-%d", time.Now().UnixNano()) - cfg.Config.UseHTTP = true - cfg.Config.KeyID = key - cfg.Config.Secret = options.NewSecretString(secret) + NewConfig: func() (*s3.Config, error) { + cfg := s3.NewConfig() + cfg.Endpoint = "localhost:9000" + cfg.Bucket = "restictestbucket" + cfg.Prefix = fmt.Sprintf("test-%d", time.Now().UnixNano()) + cfg.UseHTTP = true + cfg.KeyID = key + cfg.Secret = options.NewSecretString(secret) return &cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg MinioTestConfig) (restic.Backend, error) { - be, err := createS3(t, cfg, tr) - if err != nil { - return nil, err - } + Factory: s3.NewFactory(), + } +} - _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !be.IsNotExist(err) { - return nil, err - } +func createMinioTestSuite(t testing.TB) (*test.Suite[s3.Config], func()) { + ctx, cancel := context.WithCancel(context.Background()) - if err == nil { - return nil, errors.New("config already exists") - } + tempdir := rtest.TempDir(t) + key, secret := newRandomCredentials(t) + cleanup := runMinio(ctx, t, tempdir, key, secret) - return be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg MinioTestConfig) (restic.Backend, error) { - return s3.Open(ctx, cfg.Config, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg MinioTestConfig) error { - if cfg.stopServer != nil { - cfg.stopServer() - } - return nil - }, + return newMinioTestSuite(ctx, t, key, secret), func() { + defer cancel() + defer cleanup() } } @@ -193,10 +139,10 @@ func TestBackendMinio(t *testing.T) { return } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + suite, cleanup := createMinioTestSuite(t) + defer cleanup() - newMinioTestSuite(ctx, t).RunTests(t) + suite.RunTests(t) } func BenchmarkBackendMinio(t *testing.B) { @@ -207,18 +153,13 @@ func BenchmarkBackendMinio(t *testing.B) { return } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + suite, cleanup := createMinioTestSuite(t) + defer cleanup() - newMinioTestSuite(ctx, t).RunBenchmarks(t) + suite.RunBenchmarks(t) } func newS3TestSuite(t testing.TB) *test.Suite[s3.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[s3.Config]{ // do not use excessive data MinimalData: true, @@ -236,39 +177,7 @@ func newS3TestSuite(t testing.TB) *test.Suite[s3.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg s3.Config) (restic.Backend, error) { - be, err := s3.Create(context.TODO(), cfg, tr) - if err != nil { - return nil, err - } - - _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !be.IsNotExist(err) { - return nil, err - } - - if err == nil { - return nil, errors.New("config already exists") - } - - return be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg s3.Config) (restic.Backend, error) { - return s3.Open(context.TODO(), cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg s3.Config) error { - be, err := s3.Open(context.TODO(), cfg, tr) - if err != nil { - return err - } - - return be.Delete(context.TODO()) - }, + Factory: s3.NewFactory(), } } diff --git a/internal/backend/sftp/sftp_test.go b/internal/backend/sftp/sftp_test.go index 98175ca26..75adc0c6b 100644 --- a/internal/backend/sftp/sftp_test.go +++ b/internal/backend/sftp/sftp_test.go @@ -1,7 +1,6 @@ package sftp_test import ( - "context" "fmt" "os" "path/filepath" @@ -11,7 +10,6 @@ import ( "github.com/restic/restic/internal/backend/sftp" "github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/errors" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -33,11 +31,7 @@ func newTestSuite(t testing.TB) *test.Suite[sftp.Config] { return &test.Suite[sftp.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. NewConfig: func() (*sftp.Config, error) { - dir, err := os.MkdirTemp(rtest.TestTempDir, "restic-test-sftp-") - if err != nil { - t.Fatal(err) - } - + dir := rtest.TempDir(t) t.Logf("create new backend at %v", dir) cfg := &sftp.Config{ @@ -48,25 +42,7 @@ func newTestSuite(t testing.TB) *test.Suite[sftp.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg sftp.Config) (restic.Backend, error) { - return sftp.Create(context.TODO(), cfg) - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg sftp.Config) (restic.Backend, error) { - return sftp.Open(context.TODO(), cfg) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg sftp.Config) error { - if !rtest.TestCleanupTempDirs { - t.Logf("leaving test backend dir at %v", cfg.Path) - } - - rtest.RemoveAll(t, cfg.Path) - return nil - }, + Factory: sftp.NewFactory(), } } diff --git a/internal/backend/swift/swift_test.go b/internal/backend/swift/swift_test.go index 52278943e..98ee5b1c1 100644 --- a/internal/backend/swift/swift_test.go +++ b/internal/backend/swift/swift_test.go @@ -1,26 +1,18 @@ package swift_test import ( - "context" "fmt" "os" "testing" "time" - "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend/swift" "github.com/restic/restic/internal/backend/test" - "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) func newSwiftTestSuite(t testing.TB) *test.Suite[swift.Config] { - tr, err := backend.Transport(backend.TransportOptions{}) - if err != nil { - t.Fatalf("cannot create transport for tests: %v", err) - } - return &test.Suite[swift.Config]{ // do not use excessive data MinimalData: true, @@ -54,39 +46,7 @@ func newSwiftTestSuite(t testing.TB) *test.Suite[swift.Config] { return cfg, nil }, - // CreateFn is a function that creates a temporary repository for the tests. - Create: func(cfg swift.Config) (restic.Backend, error) { - be, err := swift.Open(context.TODO(), cfg, tr) - if err != nil { - return nil, err - } - - _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) - if err != nil && !be.IsNotExist(err) { - return nil, err - } - - if err == nil { - return nil, errors.New("config already exists") - } - - return be, nil - }, - - // OpenFn is a function that opens a previously created temporary repository. - Open: func(cfg swift.Config) (restic.Backend, error) { - return swift.Open(context.TODO(), cfg, tr) - }, - - // CleanupFn removes data created during the tests. - Cleanup: func(cfg swift.Config) error { - be, err := swift.Open(context.TODO(), cfg, tr) - if err != nil { - return err - } - - return be.Delete(context.TODO()) - }, + Factory: swift.NewFactory(), } } diff --git a/internal/backend/test/suite.go b/internal/backend/test/suite.go index 75ae0630b..bb77124d7 100644 --- a/internal/backend/test/suite.go +++ b/internal/backend/test/suite.go @@ -1,11 +1,16 @@ package test import ( + "context" + "fmt" "reflect" "strings" "testing" "time" + "github.com/restic/restic/internal/backend" + "github.com/restic/restic/internal/backend/location" + "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/test" ) @@ -18,14 +23,8 @@ type Suite[C any] struct { // NewConfig returns a config for a new temporary backend that will be used in tests. NewConfig func() (*C, error) - // CreateFn is a function that creates a temporary repository for the tests. - Create func(cfg C) (restic.Backend, error) - - // OpenFn is a function that opens a previously created temporary repository. - Open func(cfg C) (restic.Backend, error) - - // CleanupFn removes data created during the tests. - Cleanup func(cfg C) error + // Factory contains a factory that can be used to create or open a repository for the tests. + Factory location.Factory // MinimalData instructs the tests to not use excessive data. MinimalData bool @@ -60,11 +59,7 @@ func (s *Suite[C]) RunTests(t *testing.T) { return } - if s.Cleanup != nil { - if err = s.Cleanup(*s.Config); err != nil { - t.Fatal(err) - } - } + s.cleanup(t) } type testFunction struct { @@ -158,13 +153,34 @@ func (s *Suite[C]) RunBenchmarks(b *testing.B) { return } - if err = s.Cleanup(*s.Config); err != nil { - b.Fatal(err) + s.cleanup(b) +} + +func (s *Suite[C]) createOrError() (restic.Backend, error) { + tr, err := backend.Transport(backend.TransportOptions{}) + if err != nil { + return nil, fmt.Errorf("cannot create transport for tests: %v", err) } + + be, err := s.Factory.Create(context.TODO(), s.Config, tr, nil) + if err != nil { + return nil, err + } + + _, err = be.Stat(context.TODO(), restic.Handle{Type: restic.ConfigFile}) + if err != nil && !be.IsNotExist(err) { + return nil, err + } + + if err == nil { + return nil, errors.New("config already exists") + } + + return be, nil } func (s *Suite[C]) create(t testing.TB) restic.Backend { - be, err := s.Create(*s.Config) + be, err := s.createOrError() if err != nil { t.Fatal(err) } @@ -172,13 +188,26 @@ func (s *Suite[C]) create(t testing.TB) restic.Backend { } func (s *Suite[C]) open(t testing.TB) restic.Backend { - be, err := s.Open(*s.Config) + tr, err := backend.Transport(backend.TransportOptions{}) + if err != nil { + t.Fatalf("cannot create transport for tests: %v", err) + } + + be, err := s.Factory.Open(context.TODO(), s.Config, tr, nil) if err != nil { t.Fatal(err) } return be } +func (s *Suite[C]) cleanup(t testing.TB) { + be := s.open(t) + if err := be.Delete(context.TODO()); err != nil { + t.Fatal(err) + } + s.close(t, be) +} + func (s *Suite[C]) close(t testing.TB, be restic.Backend) { err := be.Close() if err != nil { diff --git a/internal/backend/test/tests.go b/internal/backend/test/tests.go index c4462495f..4a6a3f2a0 100644 --- a/internal/backend/test/tests.go +++ b/internal/backend/test/tests.go @@ -57,7 +57,7 @@ func (s *Suite[C]) TestCreateWithConfig(t *testing.T) { store(t, b, restic.ConfigFile, []byte("test config")) // now create the backend again, this must fail - _, err = s.Create(*s.Config) + _, err = s.createOrError() if err == nil { t.Fatalf("expected error not found for creating a backend with an existing config file") }