diff --git a/src/restic/backend/test/benchmarks.go b/src/restic/backend/test/benchmarks.go new file mode 100644 index 000000000..19cfc468f --- /dev/null +++ b/src/restic/backend/test/benchmarks.go @@ -0,0 +1,59 @@ +package test + +import ( + "bytes" + "io" + "restic" + "restic/test" + "testing" +) + +// BackendBenchmarkLoad benchmarks the backend's Load function. +func BackendBenchmarkLoadFile(t *testing.B, s *Suite) { + be := s.open(t) + defer s.close(t, be) + + length := 1<<24 + 2123 + data := test.Random(23, length) + id := restic.Hash(data) + handle := restic.Handle{Type: restic.DataFile, Name: id.String()} + if err := be.Save(handle, bytes.NewReader(data)); err != nil { + t.Fatalf("Save() error: %+v", err) + } + + defer func() { + if err := be.Remove(handle); err != nil { + t.Fatalf("Remove() returned error: %v", err) + } + }() + + buf := make([]byte, length) + + t.SetBytes(int64(length)) + t.ResetTimer() + + for i := 0; i < t.N; i++ { + rd, err := be.Load(handle, 0, 0) + if err != nil { + t.Fatal(err) + } + + n, err := io.ReadFull(rd, buf) + if err != nil { + t.Fatal(err) + } + + if err = rd.Close(); err != nil { + t.Fatalf("Close() returned error: %v", err) + } + + if n != length { + t.Fatalf("wrong number of bytes read: want %v, got %v", length, n) + } + + if !bytes.Equal(data, buf) { + t.Fatalf("wrong bytes returned") + } + + } +} diff --git a/src/restic/backend/test/funcs.go b/src/restic/backend/test/funcs.go index 9cfbbe707..b3f2f7bd3 100644 --- a/src/restic/backend/test/funcs.go +++ b/src/restic/backend/test/funcs.go @@ -8,7 +8,7 @@ import ( var testFunctions = []struct { Name string - Fn func(t testing.TB, suite *Suite) + Fn func(testing.TB, *Suite) }{ {"CreateWithConfig", BackendTestCreateWithConfig}, {"Location", BackendTestLocation}, @@ -22,5 +22,7 @@ var testFunctions = []struct { var benchmarkFunctions = []struct { Name string - Fn func(t testing.TB, suite *Suite) -}{} + Fn func(*testing.B, *Suite) +}{ + {"LoadFile", BackendBenchmarkLoadFile}, +} diff --git a/src/restic/backend/test/generate_test_list.go b/src/restic/backend/test/generate_test_list.go index 5bdd43515..98ae06fe4 100644 --- a/src/restic/backend/test/generate_test_list.go +++ b/src/restic/backend/test/generate_test_list.go @@ -13,6 +13,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "text/template" "unicode" "unicode/utf8" @@ -35,7 +36,7 @@ import ( var testFunctions = []struct { Name string - Fn func(t testing.TB, suite *Suite) + Fn func(testing.TB, *Suite) }{ {{ range $f := .TestFuncs -}} {"{{ $f }}", BackendTest{{ $f }},}, @@ -44,7 +45,7 @@ var testFunctions = []struct { var benchmarkFunctions = []struct { Name string - Fn func(t testing.TB, suite *Suite) + Fn func(*testing.B, *Suite) }{ {{ range $f := .BenchmarkFuncs -}} {"{{ $f }}", BackendBenchmark{{ $f }},}, @@ -52,7 +53,7 @@ var benchmarkFunctions = []struct { } ` -var testFile = flag.String("testfile", "tests.go", "file to search test functions in") +var testFiles = flag.String("testfiles", "tests.go,benchmarks.go", "files to search test functions in (comma separated)") var outputFile = flag.String("output", "funcs.go", "output file to write generated code to") var packageName = flag.String("package", "", "the package name to use") var prefix = flag.String("prefix", "", "test function prefix") @@ -71,27 +72,30 @@ var testFuncRegex = regexp.MustCompile(`^func\s+BackendTest(.+)\s*\(`) var benchmarkFuncRegex = regexp.MustCompile(`^func\s+BackendBenchmark(.+)\s*\(`) func findFunctions() (testFuncs, benchmarkFuncs []string) { - f, err := os.Open(*testFile) - errx(err) + for _, filename := range strings.Split(*testFiles, ",") { + f, err := os.Open(filename) + errx(err) - sc := bufio.NewScanner(f) - for sc.Scan() { - match := testFuncRegex.FindStringSubmatch(sc.Text()) - if len(match) > 0 { - testFuncs = append(testFuncs, match[1]) + sc := bufio.NewScanner(f) + for sc.Scan() { + match := testFuncRegex.FindStringSubmatch(sc.Text()) + if len(match) > 0 { + testFuncs = append(testFuncs, match[1]) + } + + match = benchmarkFuncRegex.FindStringSubmatch(sc.Text()) + if len(match) > 0 { + benchmarkFuncs = append(benchmarkFuncs, match[1]) + } } - match = benchmarkFuncRegex.FindStringSubmatch(sc.Text()) - if len(match) > 0 { - benchmarkFuncs = append(benchmarkFuncs, match[1]) + if err := sc.Err(); err != nil { + log.Fatalf("Error scanning file: %v", err) } + + errx(f.Close()) } - if err := sc.Err(); err != nil { - log.Fatalf("Error scanning file: %v", err) - } - - errx(f.Close()) return testFuncs, benchmarkFuncs } diff --git a/src/restic/backend/test/suite.go b/src/restic/backend/test/suite.go new file mode 100644 index 000000000..f7409aa4c --- /dev/null +++ b/src/restic/backend/test/suite.go @@ -0,0 +1,106 @@ +package test + +import ( + "restic" + "restic/test" + "testing" +) + +// Suite implements a test suite for restic backends. +type Suite struct { + Config interface{} + + // NewConfig returns a config for a new temporary backend that will be used in tests. + NewConfig func() (interface{}, error) + + // CreateFn is a function that creates a temporary repository for the tests. + Create func(cfg interface{}) (restic.Backend, error) + + // OpenFn is a function that opens a previously created temporary repository. + Open func(cfg interface{}) (restic.Backend, error) + + // CleanupFn removes data created during the tests. + Cleanup func(cfg interface{}) error + + // MinimalData instructs the tests to not use excessive data. + MinimalData bool +} + +// RunTests executes all defined tests as subtests of t. +func (s *Suite) RunTests(t *testing.T) { + var err error + s.Config, err = s.NewConfig() + if err != nil { + t.Fatal(err) + } + + // test create/open functions first + be := s.create(t) + s.close(t, be) + + for _, test := range testFunctions { + t.Run(test.Name, func(t *testing.T) { + test.Fn(t, s) + }) + } + + if !test.TestCleanupTempDirs { + t.Logf("not cleaning up backend") + return + } + + if err = s.Cleanup(s.Config); err != nil { + t.Fatal(err) + } +} + +// RunBenchmarks executes all defined benchmarks as subtests of b. +func (s *Suite) RunBenchmarks(b *testing.B) { + var err error + s.Config, err = s.NewConfig() + if err != nil { + b.Fatal(err) + } + + // test create/open functions first + be := s.create(b) + s.close(b, be) + + for _, test := range benchmarkFunctions { + b.Run(test.Name, func(b *testing.B) { + test.Fn(b, s) + }) + } + + if !test.TestCleanupTempDirs { + b.Logf("not cleaning up backend") + return + } + + if err = s.Cleanup(s.Config); err != nil { + b.Fatal(err) + } +} + +func (s *Suite) create(t testing.TB) restic.Backend { + be, err := s.Create(s.Config) + if err != nil { + t.Fatal(err) + } + return be +} + +func (s *Suite) open(t testing.TB) restic.Backend { + be, err := s.Open(s.Config) + if err != nil { + t.Fatal(err) + } + return be +} + +func (s *Suite) close(t testing.TB, be restic.Backend) { + err := be.Close() + if err != nil { + t.Fatal(err) + } +} diff --git a/src/restic/backend/test/tests.go b/src/restic/backend/test/tests.go index 51d7ec215..ff7d77549 100644 --- a/src/restic/backend/test/tests.go +++ b/src/restic/backend/test/tests.go @@ -20,105 +20,6 @@ import ( "restic/backend" ) -// Suite implements a test suite for restic backends. -type Suite struct { - Config interface{} - - // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig func() (interface{}, error) - - // CreateFn is a function that creates a temporary repository for the tests. - Create func(cfg interface{}) (restic.Backend, error) - - // OpenFn is a function that opens a previously created temporary repository. - Open func(cfg interface{}) (restic.Backend, error) - - // CleanupFn removes data created during the tests. - Cleanup func(cfg interface{}) error - - // MinimalData instructs the tests to not use excessive data. - MinimalData bool -} - -// RunTests executes all defined tests as subtests of t. -func (s *Suite) RunTests(t *testing.T) { - var err error - s.Config, err = s.NewConfig() - if err != nil { - t.Fatal(err) - } - - // test create/open functions first - be := s.create(t) - s.close(t, be) - - for _, test := range testFunctions { - t.Run(test.Name, func(t *testing.T) { - test.Fn(t, s) - }) - } - - if !test.TestCleanupTempDirs { - t.Logf("not cleaning up backend") - return - } - - if err = s.Cleanup(s.Config); err != nil { - t.Fatal(err) - } -} - -// RunBenchmarks executes all defined benchmarks as subtests of b. -func (s *Suite) RunBenchmarks(b *testing.B) { - var err error - s.Config, err = s.NewConfig() - if err != nil { - b.Fatal(err) - } - - // test create/open functions first - be := s.create(b) - s.close(b, be) - - for _, test := range benchmarkFunctions { - b.Run(test.Name, func(b *testing.B) { - test.Fn(b, s) - }) - } - - if !test.TestCleanupTempDirs { - b.Logf("not cleaning up backend") - return - } - - if err = s.Cleanup(s.Config); err != nil { - b.Fatal(err) - } -} - -func (s *Suite) create(t testing.TB) restic.Backend { - be, err := s.Create(s.Config) - if err != nil { - t.Fatal(err) - } - return be -} - -func (s *Suite) open(t testing.TB) restic.Backend { - be, err := s.Open(s.Config) - if err != nil { - t.Fatal(err) - } - return be -} - -func (s *Suite) close(t testing.TB, be restic.Backend) { - err := be.Close() - if err != nil { - t.Fatal(err) - } -} - // BackendTestCreateWithConfig tests that creating a backend in a location which already // has a config file fails. func BackendTestCreateWithConfig(t testing.TB, s *Suite) { diff --git a/src/restic/backend/test/tests_test.go b/src/restic/backend/test/tests_test.go index 514cd9ef9..010fcfe6e 100644 --- a/src/restic/backend/test/tests_test.go +++ b/src/restic/backend/test/tests_test.go @@ -15,8 +15,8 @@ type memConfig struct { be restic.Backend } -func TestSuiteBackendMem(t *testing.T) { - suite := test.Suite{ +func newTestSuite(t testing.TB) *test.Suite { + return &test.Suite{ // NewConfig returns a config for a new temporary backend that will be used in tests. NewConfig: func() (interface{}, error) { return &memConfig{}, nil @@ -55,6 +55,12 @@ func TestSuiteBackendMem(t *testing.T) { return nil }, } - - suite.RunTests(t) +} + +func TestSuiteBackendMem(t *testing.T) { + newTestSuite(t).RunTests(t) +} + +func BenchmarkSuiteBackendMem(b *testing.B) { + newTestSuite(b).RunBenchmarks(b) }