147 lines
2.3 KiB
Go
147 lines
2.3 KiB
Go
|
package repository
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/rand"
|
||
|
"errors"
|
||
|
"io"
|
||
|
mrand "math/rand"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/restic/restic/backend"
|
||
|
"github.com/restic/restic/pack"
|
||
|
)
|
||
|
|
||
|
const parallelSaves = 20
|
||
|
const saveIndexTime = 100 * time.Millisecond
|
||
|
const testTimeout = 1 * time.Second
|
||
|
|
||
|
var DupID backend.ID
|
||
|
|
||
|
func randomID() backend.ID {
|
||
|
if mrand.Float32() < 0.5 {
|
||
|
return DupID
|
||
|
}
|
||
|
|
||
|
id := backend.ID{}
|
||
|
_, err := io.ReadFull(rand.Reader, id[:])
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
|
||
|
// forgetfulBackend returns a backend that forgets everything.
|
||
|
func forgetfulBackend() backend.Backend {
|
||
|
be := &backend.MockBackend{}
|
||
|
|
||
|
be.TestFn = func(t backend.Type, name string) (bool, error) {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
be.LoadFn = func(h backend.Handle, p []byte, off int64) (int, error) {
|
||
|
return 0, errors.New("not found")
|
||
|
}
|
||
|
|
||
|
be.SaveFn = func(h backend.Handle, p []byte) error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
be.StatFn = func(h backend.Handle) (backend.BlobInfo, error) {
|
||
|
return backend.BlobInfo{}, errors.New("not found")
|
||
|
}
|
||
|
|
||
|
be.RemoveFn = func(t backend.Type, name string) error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
be.ListFn = func(t backend.Type, done <-chan struct{}) <-chan string {
|
||
|
ch := make(chan string)
|
||
|
close(ch)
|
||
|
return ch
|
||
|
}
|
||
|
|
||
|
be.DeleteFn = func() error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return be
|
||
|
}
|
||
|
|
||
|
func testMasterIndex(t *testing.T) {
|
||
|
_, err := io.ReadFull(rand.Reader, DupID[:])
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
repo := New(forgetfulBackend())
|
||
|
err = repo.Init("foo")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
|
||
|
if repo.Index().Has(id) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
buf := make([]byte, 50)
|
||
|
|
||
|
err := repo.SaveFrom(pack.Data, &id, uint(len(buf)), bytes.NewReader(buf))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
saveIndexes := func() {
|
||
|
defer wg.Done()
|
||
|
|
||
|
ticker := time.NewTicker(saveIndexTime)
|
||
|
defer ticker.Stop()
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-done:
|
||
|
return
|
||
|
case <-ticker.C:
|
||
|
err := repo.SaveFullIndex()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wg.Add(1)
|
||
|
go saveIndexes()
|
||
|
|
||
|
<-time.After(testTimeout)
|
||
|
close(done)
|
||
|
|
||
|
wg.Wait()
|
||
|
}
|
||
|
|
||
|
func TestMasterIndex(t *testing.T) {
|
||
|
for i := 0; i < 5; i++ {
|
||
|
testMasterIndex(t)
|
||
|
}
|
||
|
}
|