forked from TrueCloudLab/restic
ff7ef5007e
The ioutil functions are deprecated since Go 1.17 and only wrap another library function. Thus directly call the underlying function. This commit only mechanically replaces the function calls.
210 lines
4.1 KiB
Go
210 lines
4.1 KiB
Go
package cache
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"math/rand"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/restic/restic/internal/backend"
|
|
"github.com/restic/restic/internal/backend/mem"
|
|
"github.com/restic/restic/internal/restic"
|
|
"github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
func loadAndCompare(t testing.TB, be restic.Backend, h restic.Handle, data []byte) {
|
|
buf, err := backend.LoadAll(context.TODO(), nil, be, h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(buf) != len(data) {
|
|
t.Fatalf("wrong number of bytes read, want %v, got %v", len(data), len(buf))
|
|
}
|
|
|
|
if !bytes.Equal(buf, data) {
|
|
t.Fatalf("wrong data returned, want:\n %02x\ngot:\n %02x", data[:16], buf[:16])
|
|
}
|
|
}
|
|
|
|
func save(t testing.TB, be restic.Backend, h restic.Handle, data []byte) {
|
|
err := be.Save(context.TODO(), h, restic.NewByteReader(data, be.Hasher()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func remove(t testing.TB, be restic.Backend, h restic.Handle) {
|
|
err := be.Remove(context.TODO(), h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func randomData(n int) (restic.Handle, []byte) {
|
|
data := test.Random(rand.Int(), n)
|
|
id := restic.Hash(data)
|
|
h := restic.Handle{
|
|
Type: restic.IndexFile,
|
|
Name: id.String(),
|
|
}
|
|
return h, data
|
|
}
|
|
|
|
func TestBackend(t *testing.T) {
|
|
be := mem.New()
|
|
|
|
c, cleanup := TestNewCache(t)
|
|
defer cleanup()
|
|
|
|
wbe := c.Wrap(be)
|
|
|
|
h, data := randomData(5234142)
|
|
|
|
// save directly in backend
|
|
save(t, be, h, data)
|
|
if c.Has(h) {
|
|
t.Errorf("cache has file too early")
|
|
}
|
|
|
|
// load data via cache
|
|
loadAndCompare(t, wbe, h, data)
|
|
if !c.Has(h) {
|
|
t.Errorf("cache doesn't have file after load")
|
|
}
|
|
|
|
// remove via cache
|
|
remove(t, wbe, h)
|
|
if c.Has(h) {
|
|
t.Errorf("cache has file after remove")
|
|
}
|
|
|
|
// save via cache
|
|
save(t, wbe, h, data)
|
|
if !c.Has(h) {
|
|
t.Errorf("cache doesn't have file after load")
|
|
}
|
|
|
|
// load data directly from backend
|
|
loadAndCompare(t, be, h, data)
|
|
|
|
// load data via cache
|
|
loadAndCompare(t, be, h, data)
|
|
|
|
// remove directly
|
|
remove(t, be, h)
|
|
if !c.Has(h) {
|
|
t.Errorf("file not in cache any more")
|
|
}
|
|
|
|
// run stat
|
|
_, err := wbe.Stat(context.TODO(), h)
|
|
if err == nil {
|
|
t.Errorf("expected error for removed file not found, got nil")
|
|
}
|
|
|
|
if !wbe.IsNotExist(err) {
|
|
t.Errorf("Stat() returned error that does not match IsNotExist(): %v", err)
|
|
}
|
|
|
|
if c.Has(h) {
|
|
t.Errorf("removed file still in cache after stat")
|
|
}
|
|
}
|
|
|
|
type loadErrorBackend struct {
|
|
restic.Backend
|
|
loadError error
|
|
}
|
|
|
|
func (be loadErrorBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
|
|
time.Sleep(10 * time.Millisecond)
|
|
return be.loadError
|
|
}
|
|
|
|
func TestErrorBackend(t *testing.T) {
|
|
be := mem.New()
|
|
|
|
c, cleanup := TestNewCache(t)
|
|
defer cleanup()
|
|
|
|
h, data := randomData(5234142)
|
|
|
|
// save directly in backend
|
|
save(t, be, h, data)
|
|
|
|
testErr := errors.New("test error")
|
|
errBackend := loadErrorBackend{
|
|
Backend: be,
|
|
loadError: testErr,
|
|
}
|
|
|
|
loadTest := func(wg *sync.WaitGroup, be restic.Backend) {
|
|
defer wg.Done()
|
|
|
|
buf, err := backend.LoadAll(context.TODO(), nil, be, h)
|
|
if err == testErr {
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
if !bytes.Equal(buf, data) {
|
|
t.Errorf("data does not match")
|
|
}
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
|
|
wrappedBE := c.Wrap(errBackend)
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 5; i++ {
|
|
wg.Add(1)
|
|
go loadTest(&wg, wrappedBE)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestBackendRemoveBroken(t *testing.T) {
|
|
be := mem.New()
|
|
|
|
c, cleanup := TestNewCache(t)
|
|
defer cleanup()
|
|
|
|
h, data := randomData(5234142)
|
|
// save directly in backend
|
|
save(t, be, h, data)
|
|
|
|
// prime cache with broken copy
|
|
broken := append([]byte{}, data...)
|
|
broken[0] ^= 0xff
|
|
err := c.Save(h, bytes.NewReader(broken))
|
|
test.OK(t, err)
|
|
|
|
// loadall retries if broken data was returned
|
|
buf, err := backend.LoadAll(context.TODO(), nil, c.Wrap(be), h)
|
|
test.OK(t, err)
|
|
|
|
if !bytes.Equal(buf, data) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
|
|
// check that the cache now contains the correct data
|
|
rd, err := c.load(h, 0, 0)
|
|
defer func() {
|
|
_ = rd.Close()
|
|
}()
|
|
test.OK(t, err)
|
|
cached, err := io.ReadAll(rd)
|
|
test.OK(t, err)
|
|
if !bytes.Equal(cached, data) {
|
|
t.Fatalf("wrong data cache")
|
|
}
|
|
}
|