2016-08-31 22:39:36 +02:00
|
|
|
package archiver_test
|
2016-02-01 23:26:57 +01:00
|
|
|
|
|
|
|
import (
|
2017-06-05 23:56:59 +02:00
|
|
|
"context"
|
2016-02-01 23:26:57 +01:00
|
|
|
"crypto/rand"
|
|
|
|
"io"
|
|
|
|
mrand "math/rand"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2017-07-23 14:21:03 +02:00
|
|
|
"github.com/restic/restic/internal/errors"
|
2017-07-24 17:42:25 +02:00
|
|
|
"github.com/restic/restic/internal/restic"
|
2016-08-21 17:46:23 +02:00
|
|
|
|
2017-07-23 14:21:03 +02:00
|
|
|
"github.com/restic/restic/internal/archiver"
|
|
|
|
"github.com/restic/restic/internal/mock"
|
|
|
|
"github.com/restic/restic/internal/repository"
|
2016-02-01 23:26:57 +01:00
|
|
|
)
|
|
|
|
|
2016-02-01 23:48:36 +01:00
|
|
|
const parallelSaves = 50
|
|
|
|
const testSaveIndexTime = 100 * time.Millisecond
|
|
|
|
const testTimeout = 2 * time.Second
|
2016-02-01 23:26:57 +01:00
|
|
|
|
2016-08-31 23:07:50 +02:00
|
|
|
var DupID restic.ID
|
2016-02-01 23:26:57 +01:00
|
|
|
|
2016-08-31 23:07:50 +02:00
|
|
|
func randomID() restic.ID {
|
2016-02-01 23:26:57 +01:00
|
|
|
if mrand.Float32() < 0.5 {
|
|
|
|
return DupID
|
|
|
|
}
|
|
|
|
|
2016-08-31 23:07:50 +02:00
|
|
|
id := restic.ID{}
|
2016-02-01 23:26:57 +01:00
|
|
|
_, err := io.ReadFull(rand.Reader, id[:])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
|
|
|
// forgetfulBackend returns a backend that forgets everything.
|
2016-08-31 19:10:10 +02:00
|
|
|
func forgetfulBackend() restic.Backend {
|
2018-01-17 05:59:16 +01:00
|
|
|
be := mock.NewBackend()
|
2016-02-01 23:26:57 +01:00
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
be.TestFn = func(ctx context.Context, h restic.Handle) (bool, error) {
|
2016-02-01 23:26:57 +01:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2018-01-17 05:59:16 +01:00
|
|
|
be.OpenReaderFn = func(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
|
2017-01-23 17:20:08 +01:00
|
|
|
return nil, errors.New("not found")
|
2016-02-01 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2018-03-03 14:20:54 +01:00
|
|
|
be.SaveFn = func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
2016-02-01 23:26:57 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
be.StatFn = func(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
|
2016-08-31 23:07:50 +02:00
|
|
|
return restic.FileInfo{}, errors.New("not found")
|
2016-02-01 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
be.RemoveFn = func(ctx context.Context, h restic.Handle) error {
|
2016-02-01 23:26:57 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-21 17:25:36 +01:00
|
|
|
be.ListFn = func(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error {
|
|
|
|
return nil
|
2016-02-01 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
be.DeleteFn = func(ctx context.Context) error {
|
2016-02-01 23:26:57 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return be
|
|
|
|
}
|
|
|
|
|
2016-02-01 23:48:36 +01:00
|
|
|
func testArchiverDuplication(t *testing.T) {
|
2016-02-01 23:26:57 +01:00
|
|
|
_, err := io.ReadFull(rand.Reader, DupID[:])
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-03-06 13:14:06 +01:00
|
|
|
repo := repository.New(forgetfulBackend())
|
2016-03-06 12:34:23 +01:00
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
err = repo.Init(context.TODO(), "foo")
|
2016-02-01 23:26:57 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-08-31 23:07:50 +02:00
|
|
|
arch := archiver.New(repo)
|
2016-02-01 23:48:36 +01:00
|
|
|
|
2016-02-01 23:26:57 +01:00
|
|
|
wg := &sync.WaitGroup{}
|
|
|
|
done := make(chan struct{})
|
|
|
|
for i := 0; i < parallelSaves; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
id := randomID()
|
|
|
|
|
2016-08-31 22:39:36 +02:00
|
|
|
if repo.Index().Has(id, restic.DataBlob) {
|
2016-02-01 23:26:57 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := make([]byte, 50)
|
|
|
|
|
2017-06-05 23:56:59 +02:00
|
|
|
err := arch.Save(context.TODO(), restic.DataBlob, buf, id)
|
2016-02-01 23:26:57 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2016-02-01 23:48:36 +01:00
|
|
|
saveIndex := func() {
|
2016-02-01 23:26:57 +01:00
|
|
|
defer wg.Done()
|
|
|
|
|
2016-02-01 23:48:36 +01:00
|
|
|
ticker := time.NewTicker(testSaveIndexTime)
|
2016-02-01 23:26:57 +01:00
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-ticker.C:
|
2017-06-05 23:56:59 +02:00
|
|
|
err := repo.SaveFullIndex(context.TODO())
|
2016-02-01 23:26:57 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Add(1)
|
2016-02-01 23:48:36 +01:00
|
|
|
go saveIndex()
|
2016-02-01 23:26:57 +01:00
|
|
|
|
|
|
|
<-time.After(testTimeout)
|
|
|
|
close(done)
|
|
|
|
|
|
|
|
wg.Wait()
|
2017-01-23 17:24:34 +01:00
|
|
|
|
2017-11-22 12:27:29 +01:00
|
|
|
err = repo.Flush(context.Background())
|
2017-01-23 17:24:34 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-02-01 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2016-02-01 23:48:36 +01:00
|
|
|
func TestArchiverDuplication(t *testing.T) {
|
2016-02-01 23:26:57 +01:00
|
|
|
for i := 0; i < 5; i++ {
|
2016-02-01 23:48:36 +01:00
|
|
|
testArchiverDuplication(t)
|
2016-02-01 23:26:57 +01:00
|
|
|
}
|
|
|
|
}
|