2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-23 03:18:55 +00:00

tests: Add BenchmarkLoadFile

This commit is contained in:
Alexander Neumann 2017-05-13 21:37:07 +02:00
parent f142b1c22f
commit 77ebb95d3d
6 changed files with 202 additions and 124 deletions

View File

@ -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")
}
}
}

View File

@ -8,7 +8,7 @@ import (
var testFunctions = []struct { var testFunctions = []struct {
Name string Name string
Fn func(t testing.TB, suite *Suite) Fn func(testing.TB, *Suite)
}{ }{
{"CreateWithConfig", BackendTestCreateWithConfig}, {"CreateWithConfig", BackendTestCreateWithConfig},
{"Location", BackendTestLocation}, {"Location", BackendTestLocation},
@ -22,5 +22,7 @@ var testFunctions = []struct {
var benchmarkFunctions = []struct { var benchmarkFunctions = []struct {
Name string Name string
Fn func(t testing.TB, suite *Suite) Fn func(*testing.B, *Suite)
}{} }{
{"LoadFile", BackendBenchmarkLoadFile},
}

View File

@ -13,6 +13,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"text/template" "text/template"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@ -35,7 +36,7 @@ import (
var testFunctions = []struct { var testFunctions = []struct {
Name string Name string
Fn func(t testing.TB, suite *Suite) Fn func(testing.TB, *Suite)
}{ }{
{{ range $f := .TestFuncs -}} {{ range $f := .TestFuncs -}}
{"{{ $f }}", BackendTest{{ $f }},}, {"{{ $f }}", BackendTest{{ $f }},},
@ -44,7 +45,7 @@ var testFunctions = []struct {
var benchmarkFunctions = []struct { var benchmarkFunctions = []struct {
Name string Name string
Fn func(t testing.TB, suite *Suite) Fn func(*testing.B, *Suite)
}{ }{
{{ range $f := .BenchmarkFuncs -}} {{ range $f := .BenchmarkFuncs -}}
{"{{ $f }}", BackendBenchmark{{ $f }},}, {"{{ $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 outputFile = flag.String("output", "funcs.go", "output file to write generated code to")
var packageName = flag.String("package", "", "the package name to use") var packageName = flag.String("package", "", "the package name to use")
var prefix = flag.String("prefix", "", "test function prefix") 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*\(`) var benchmarkFuncRegex = regexp.MustCompile(`^func\s+BackendBenchmark(.+)\s*\(`)
func findFunctions() (testFuncs, benchmarkFuncs []string) { func findFunctions() (testFuncs, benchmarkFuncs []string) {
f, err := os.Open(*testFile) for _, filename := range strings.Split(*testFiles, ",") {
errx(err) f, err := os.Open(filename)
errx(err)
sc := bufio.NewScanner(f) sc := bufio.NewScanner(f)
for sc.Scan() { for sc.Scan() {
match := testFuncRegex.FindStringSubmatch(sc.Text()) match := testFuncRegex.FindStringSubmatch(sc.Text())
if len(match) > 0 { if len(match) > 0 {
testFuncs = append(testFuncs, match[1]) 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 err := sc.Err(); err != nil {
if len(match) > 0 { log.Fatalf("Error scanning file: %v", err)
benchmarkFuncs = append(benchmarkFuncs, match[1])
} }
errx(f.Close())
} }
if err := sc.Err(); err != nil {
log.Fatalf("Error scanning file: %v", err)
}
errx(f.Close())
return testFuncs, benchmarkFuncs return testFuncs, benchmarkFuncs
} }

View File

@ -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)
}
}

View File

@ -20,105 +20,6 @@ import (
"restic/backend" "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 // BackendTestCreateWithConfig tests that creating a backend in a location which already
// has a config file fails. // has a config file fails.
func BackendTestCreateWithConfig(t testing.TB, s *Suite) { func BackendTestCreateWithConfig(t testing.TB, s *Suite) {

View File

@ -15,8 +15,8 @@ type memConfig struct {
be restic.Backend be restic.Backend
} }
func TestSuiteBackendMem(t *testing.T) { func newTestSuite(t testing.TB) *test.Suite {
suite := test.Suite{ return &test.Suite{
// NewConfig returns a config for a new temporary backend that will be used in tests. // NewConfig returns a config for a new temporary backend that will be used in tests.
NewConfig: func() (interface{}, error) { NewConfig: func() (interface{}, error) {
return &memConfig{}, nil return &memConfig{}, nil
@ -55,6 +55,12 @@ func TestSuiteBackendMem(t *testing.T) {
return nil return nil
}, },
} }
}
suite.RunTests(t)
func TestSuiteBackendMem(t *testing.T) {
newTestSuite(t).RunTests(t)
}
func BenchmarkSuiteBackendMem(b *testing.B) {
newTestSuite(b).RunBenchmarks(b)
} }