github.com/mckael/restic@v0.8.3/internal/backend/test/suite.go (about)

     1  package test
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/restic/restic/internal/restic"
    10  	"github.com/restic/restic/internal/test"
    11  )
    12  
    13  // Suite implements a test suite for restic backends.
    14  type Suite struct {
    15  	// Config should be used to configure the backend.
    16  	Config interface{}
    17  
    18  	// NewConfig returns a config for a new temporary backend that will be used in tests.
    19  	NewConfig func() (interface{}, error)
    20  
    21  	// CreateFn is a function that creates a temporary repository for the tests.
    22  	Create func(cfg interface{}) (restic.Backend, error)
    23  
    24  	// OpenFn is a function that opens a previously created temporary repository.
    25  	Open func(cfg interface{}) (restic.Backend, error)
    26  
    27  	// CleanupFn removes data created during the tests.
    28  	Cleanup func(cfg interface{}) error
    29  
    30  	// MinimalData instructs the tests to not use excessive data.
    31  	MinimalData bool
    32  
    33  	// WaitForDelayedRemoval is set to a non-zero value to instruct the test
    34  	// suite to wait for this amount of time until a file that was removed
    35  	// really disappeared.
    36  	WaitForDelayedRemoval time.Duration
    37  
    38  	// ErrorHandler allows ignoring certain errors.
    39  	ErrorHandler func(testing.TB, restic.Backend, error) error
    40  }
    41  
    42  // RunTests executes all defined tests as subtests of t.
    43  func (s *Suite) RunTests(t *testing.T) {
    44  	var err error
    45  	s.Config, err = s.NewConfig()
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	// test create/open functions first
    51  	be := s.create(t)
    52  	s.close(t, be)
    53  
    54  	for _, test := range s.testFuncs(t) {
    55  		t.Run(test.Name, test.Fn)
    56  	}
    57  
    58  	if !test.TestCleanupTempDirs {
    59  		t.Logf("not cleaning up backend")
    60  		return
    61  	}
    62  
    63  	if err = s.Cleanup(s.Config); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  }
    67  
    68  type testFunction struct {
    69  	Name string
    70  	Fn   func(*testing.T)
    71  }
    72  
    73  func (s *Suite) testFuncs(t testing.TB) (funcs []testFunction) {
    74  	tpe := reflect.TypeOf(s)
    75  	v := reflect.ValueOf(s)
    76  
    77  	for i := 0; i < tpe.NumMethod(); i++ {
    78  		methodType := tpe.Method(i)
    79  		name := methodType.Name
    80  
    81  		// discard functions which do not have the right name
    82  		if !strings.HasPrefix(name, "Test") {
    83  			continue
    84  		}
    85  
    86  		iface := v.Method(i).Interface()
    87  		f, ok := iface.(func(*testing.T))
    88  		if !ok {
    89  			t.Logf("warning: function %v of *Suite has the wrong signature for a test function\nwant: func(*testing.T),\nhave: %T",
    90  				name, iface)
    91  			continue
    92  		}
    93  
    94  		funcs = append(funcs, testFunction{
    95  			Name: name,
    96  			Fn:   f,
    97  		})
    98  	}
    99  
   100  	return funcs
   101  }
   102  
   103  type benchmarkFunction struct {
   104  	Name string
   105  	Fn   func(*testing.B)
   106  }
   107  
   108  func (s *Suite) benchmarkFuncs(t testing.TB) (funcs []benchmarkFunction) {
   109  	tpe := reflect.TypeOf(s)
   110  	v := reflect.ValueOf(s)
   111  
   112  	for i := 0; i < tpe.NumMethod(); i++ {
   113  		methodType := tpe.Method(i)
   114  		name := methodType.Name
   115  
   116  		// discard functions which do not have the right name
   117  		if !strings.HasPrefix(name, "Benchmark") {
   118  			continue
   119  		}
   120  
   121  		iface := v.Method(i).Interface()
   122  		f, ok := iface.(func(*testing.B))
   123  		if !ok {
   124  			t.Logf("warning: function %v of *Suite has the wrong signature for a test function\nwant: func(*testing.T),\nhave: %T",
   125  				name, iface)
   126  			continue
   127  		}
   128  
   129  		funcs = append(funcs, benchmarkFunction{
   130  			Name: name,
   131  			Fn:   f,
   132  		})
   133  	}
   134  
   135  	return funcs
   136  }
   137  
   138  // RunBenchmarks executes all defined benchmarks as subtests of b.
   139  func (s *Suite) RunBenchmarks(b *testing.B) {
   140  	var err error
   141  	s.Config, err = s.NewConfig()
   142  	if err != nil {
   143  		b.Fatal(err)
   144  	}
   145  
   146  	// test create/open functions first
   147  	be := s.create(b)
   148  	s.close(b, be)
   149  
   150  	for _, test := range s.benchmarkFuncs(b) {
   151  		b.Run(test.Name, test.Fn)
   152  	}
   153  
   154  	if !test.TestCleanupTempDirs {
   155  		b.Logf("not cleaning up backend")
   156  		return
   157  	}
   158  
   159  	if err = s.Cleanup(s.Config); err != nil {
   160  		b.Fatal(err)
   161  	}
   162  }
   163  
   164  func (s *Suite) create(t testing.TB) restic.Backend {
   165  	be, err := s.Create(s.Config)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	return be
   170  }
   171  
   172  func (s *Suite) open(t testing.TB) restic.Backend {
   173  	be, err := s.Open(s.Config)
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	return be
   178  }
   179  
   180  func (s *Suite) close(t testing.TB, be restic.Backend) {
   181  	err := be.Close()
   182  	if err != nil {
   183  		t.Fatal(err)
   184  	}
   185  }