2015-05-09 21:52:03 +00:00
|
|
|
package repository_test
|
2015-02-16 18:32:36 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-06-05 21:56:59 +00:00
|
|
|
"context"
|
2015-02-16 18:32:36 +00:00
|
|
|
"crypto/sha256"
|
2015-02-16 19:00:23 +00:00
|
|
|
"io"
|
2017-01-13 11:20:37 +00:00
|
|
|
"math/rand"
|
2015-07-04 14:52:17 +00:00
|
|
|
"path/filepath"
|
2015-02-16 18:32:36 +00:00
|
|
|
"testing"
|
2017-01-13 11:20:37 +00:00
|
|
|
"time"
|
2015-02-16 18:32:36 +00:00
|
|
|
|
2017-07-23 12:21:03 +00:00
|
|
|
"github.com/restic/restic/internal/archiver"
|
2018-10-28 20:12:15 +00:00
|
|
|
"github.com/restic/restic/internal/errors"
|
|
|
|
"github.com/restic/restic/internal/fs"
|
2017-07-23 12:21:03 +00:00
|
|
|
"github.com/restic/restic/internal/repository"
|
2017-07-24 15:42:25 +00:00
|
|
|
"github.com/restic/restic/internal/restic"
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest "github.com/restic/restic/internal/test"
|
2015-02-16 18:32:36 +00:00
|
|
|
)
|
|
|
|
|
2015-02-16 19:00:23 +00:00
|
|
|
var testSizes = []int{5, 23, 2<<18 + 23, 1 << 20}
|
|
|
|
|
2017-01-13 11:20:37 +00:00
|
|
|
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
|
2015-04-26 15:44:38 +00:00
|
|
|
func TestSave(t *testing.T) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-04-26 15:44:38 +00:00
|
|
|
|
|
|
|
for _, size := range testSizes {
|
|
|
|
data := make([]byte, size)
|
2017-01-13 11:20:37 +00:00
|
|
|
_, err := io.ReadFull(rnd, data)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2016-08-31 18:29:54 +00:00
|
|
|
id := restic.Hash(data)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
|
|
|
// save
|
2017-06-05 21:56:59 +00:00
|
|
|
sid, err := repo.SaveBlob(context.TODO(), restic.DataBlob, data, restic.ID{})
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Equals(t, id, sid)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2017-10-02 13:06:39 +00:00
|
|
|
// rtest.OK(t, repo.SaveIndex())
|
2015-04-26 15:44:38 +00:00
|
|
|
|
|
|
|
// read back
|
2017-01-13 11:57:05 +00:00
|
|
|
buf := restic.NewBlobBuffer(size)
|
2017-06-05 21:56:59 +00:00
|
|
|
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
|
|
|
rtest.Equals(t, len(buf), n)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Assert(t, len(buf) == len(data),
|
2015-04-26 15:44:38 +00:00
|
|
|
"number of bytes read back does not match: expected %d, got %d",
|
|
|
|
len(data), len(buf))
|
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Assert(t, bytes.Equal(buf, data),
|
2015-04-26 15:44:38 +00:00
|
|
|
"data does not match: expected %02x, got %02x",
|
|
|
|
data, buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-16 19:00:23 +00:00
|
|
|
func TestSaveFrom(t *testing.T) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-02-16 19:00:23 +00:00
|
|
|
|
|
|
|
for _, size := range testSizes {
|
|
|
|
data := make([]byte, size)
|
2017-01-13 11:20:37 +00:00
|
|
|
_, err := io.ReadFull(rnd, data)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-02-16 19:00:23 +00:00
|
|
|
|
2016-08-31 18:29:54 +00:00
|
|
|
id := restic.Hash(data)
|
2015-02-16 19:00:23 +00:00
|
|
|
|
|
|
|
// save
|
2017-06-05 21:56:59 +00:00
|
|
|
id2, err := repo.SaveBlob(context.TODO(), restic.DataBlob, data, id)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
|
|
|
rtest.Equals(t, id, id2)
|
2015-02-16 19:00:23 +00:00
|
|
|
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2015-02-16 19:00:23 +00:00
|
|
|
// read back
|
2017-01-13 11:57:05 +00:00
|
|
|
buf := restic.NewBlobBuffer(size)
|
2017-06-05 21:56:59 +00:00
|
|
|
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
|
|
|
rtest.Equals(t, len(buf), n)
|
2015-02-16 19:00:23 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Assert(t, len(buf) == len(data),
|
2015-02-16 19:00:23 +00:00
|
|
|
"number of bytes read back does not match: expected %d, got %d",
|
|
|
|
len(data), len(buf))
|
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Assert(t, bytes.Equal(buf, data),
|
2015-02-16 19:00:23 +00:00
|
|
|
"data does not match: expected %02x, got %02x",
|
|
|
|
data, buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-08 11:13:29 +00:00
|
|
|
func BenchmarkSaveAndEncrypt(t *testing.B) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-02-16 19:00:23 +00:00
|
|
|
|
|
|
|
size := 4 << 20 // 4MiB
|
|
|
|
|
|
|
|
data := make([]byte, size)
|
2017-01-13 11:20:37 +00:00
|
|
|
_, err := io.ReadFull(rnd, data)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-02-16 19:00:23 +00:00
|
|
|
|
2016-08-31 18:29:54 +00:00
|
|
|
id := restic.ID(sha256.Sum256(data))
|
2015-02-16 19:00:23 +00:00
|
|
|
|
|
|
|
t.ResetTimer()
|
|
|
|
t.SetBytes(int64(size))
|
|
|
|
|
|
|
|
for i := 0; i < t.N; i++ {
|
|
|
|
// save
|
2017-06-05 21:56:59 +00:00
|
|
|
_, err = repo.SaveBlob(context.TODO(), restic.DataBlob, data, id)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-02-16 19:00:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-17 22:05:23 +00:00
|
|
|
|
2016-09-03 11:34:04 +00:00
|
|
|
func TestLoadTree(t *testing.T) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-06-26 20:12:04 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
if rtest.BenchArchiveDirectory == "" {
|
2015-05-09 11:32:52 +00:00
|
|
|
t.Skip("benchdir not set, skipping")
|
2015-02-18 21:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// archive a few files
|
2017-10-02 13:06:39 +00:00
|
|
|
sn := archiver.TestSnapshot(t, repo, rtest.BenchArchiveDirectory, nil)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2015-03-28 14:07:08 +00:00
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
_, err := repo.LoadTree(context.TODO(), *sn.Tree)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-02-18 21:46:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-03 11:34:04 +00:00
|
|
|
func BenchmarkLoadTree(t *testing.B) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-10-26 19:47:48 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
if rtest.BenchArchiveDirectory == "" {
|
2015-10-26 19:47:48 +00:00
|
|
|
t.Skip("benchdir not set, skipping")
|
|
|
|
}
|
|
|
|
|
|
|
|
// archive a few files
|
2017-10-02 13:06:39 +00:00
|
|
|
sn := archiver.TestSnapshot(t, repo, rtest.BenchArchiveDirectory, nil)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2015-10-26 19:47:48 +00:00
|
|
|
|
|
|
|
t.ResetTimer()
|
|
|
|
|
|
|
|
for i := 0; i < t.N; i++ {
|
2017-06-05 21:56:59 +00:00
|
|
|
_, err := repo.LoadTree(context.TODO(), *sn.Tree)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-10-26 19:47:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-24 10:25:33 +00:00
|
|
|
func TestLoadBlob(t *testing.T) {
|
|
|
|
repo, cleanup := repository.TestRepository(t)
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
length := 1000000
|
|
|
|
buf := restic.NewBlobBuffer(length)
|
|
|
|
_, err := io.ReadFull(rnd, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2017-01-24 10:25:33 +00:00
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
id, err := repo.SaveBlob(context.TODO(), restic.DataBlob, buf, restic.ID{})
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2017-01-24 10:25:33 +00:00
|
|
|
|
|
|
|
// first, test with buffers that are too small
|
|
|
|
for _, testlength := range []int{length - 20, length, restic.CiphertextLength(length) - 1} {
|
|
|
|
buf = make([]byte, 0, testlength)
|
2017-06-05 21:56:59 +00:00
|
|
|
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
2017-01-24 10:25:33 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Errorf("LoadBlob() did not return an error for a buffer that is too small to hold the blob")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if n != 0 {
|
|
|
|
t.Errorf("LoadBlob() returned an error and n > 0")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// then use buffers that are large enough
|
|
|
|
base := restic.CiphertextLength(length)
|
|
|
|
for _, testlength := range []int{base, base + 7, base + 15, base + 1000} {
|
|
|
|
buf = make([]byte, 0, testlength)
|
2017-06-05 21:56:59 +00:00
|
|
|
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
2017-01-24 10:25:33 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("LoadBlob() returned an error for buffer size %v: %v", testlength, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if n != length {
|
|
|
|
t.Errorf("LoadBlob() returned the wrong number of bytes: want %v, got %v", length, n)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-13 11:20:37 +00:00
|
|
|
func BenchmarkLoadBlob(b *testing.B) {
|
|
|
|
repo, cleanup := repository.TestRepository(b)
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
length := 1000000
|
2017-01-13 11:57:05 +00:00
|
|
|
buf := restic.NewBlobBuffer(length)
|
2017-01-13 11:20:37 +00:00
|
|
|
_, err := io.ReadFull(rnd, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 11:20:37 +00:00
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
id, err := repo.SaveBlob(context.TODO(), restic.DataBlob, buf, restic.ID{})
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(b, repo.Flush(context.Background()))
|
2017-01-13 11:20:37 +00:00
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
b.SetBytes(int64(length))
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2017-06-05 21:56:59 +00:00
|
|
|
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 11:20:37 +00:00
|
|
|
if n != length {
|
|
|
|
b.Errorf("wanted %d bytes, got %d", length, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
id2 := restic.Hash(buf[:n])
|
|
|
|
if !id.Equal(id2) {
|
|
|
|
b.Errorf("wrong data returned, wanted %v, got %v", id.Str(), id2.Str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-13 19:56:50 +00:00
|
|
|
func BenchmarkLoadAndDecrypt(b *testing.B) {
|
|
|
|
repo, cleanup := repository.TestRepository(b)
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
length := 1000000
|
|
|
|
buf := restic.NewBlobBuffer(length)
|
|
|
|
_, err := io.ReadFull(rnd, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 19:56:50 +00:00
|
|
|
|
|
|
|
dataID := restic.Hash(buf)
|
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
storageID, err := repo.SaveUnpacked(context.TODO(), restic.DataFile, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
|
|
|
// rtest.OK(b, repo.Flush())
|
2017-01-13 19:56:50 +00:00
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
b.SetBytes(int64(length))
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2019-03-24 20:59:14 +00:00
|
|
|
data, err := repo.LoadAndDecrypt(context.TODO(), nil, restic.DataFile, storageID)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 19:56:50 +00:00
|
|
|
if len(data) != length {
|
|
|
|
b.Errorf("wanted %d bytes, got %d", length, len(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
id2 := restic.Hash(data)
|
|
|
|
if !dataID.Equal(id2) {
|
|
|
|
b.Errorf("wrong data returned, wanted %v, got %v", storageID.Str(), id2.Str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-04 18:40:17 +00:00
|
|
|
func TestLoadJSONUnpacked(t *testing.T) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-06-26 20:12:04 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
if rtest.BenchArchiveDirectory == "" {
|
2015-05-09 11:32:52 +00:00
|
|
|
t.Skip("benchdir not set, skipping")
|
2015-02-18 21:46:09 +00:00
|
|
|
}
|
|
|
|
|
2015-04-26 15:44:38 +00:00
|
|
|
// archive a snapshot
|
|
|
|
sn := restic.Snapshot{}
|
|
|
|
sn.Hostname = "foobar"
|
|
|
|
sn.Username = "test!"
|
2015-02-18 21:46:09 +00:00
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
id, err := repo.SaveJSONUnpacked(context.TODO(), restic.SnapshotFile, &sn)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-02-18 21:46:09 +00:00
|
|
|
|
2015-04-26 15:44:38 +00:00
|
|
|
var sn2 restic.Snapshot
|
|
|
|
|
|
|
|
// restore
|
2017-06-05 21:56:59 +00:00
|
|
|
err = repo.LoadJSONUnpacked(context.TODO(), restic.SnapshotFile, id, &sn2)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-04-26 15:44:38 +00:00
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.Equals(t, sn.Hostname, sn2.Hostname)
|
|
|
|
rtest.Equals(t, sn.Username, sn2.Username)
|
2015-02-18 21:46:09 +00:00
|
|
|
}
|
2015-07-04 14:52:17 +00:00
|
|
|
|
|
|
|
var repoFixture = filepath.Join("testdata", "test-repo.tar.gz")
|
|
|
|
|
2015-08-08 10:22:17 +00:00
|
|
|
func TestRepositoryLoadIndex(t *testing.T) {
|
2017-10-02 13:06:39 +00:00
|
|
|
repodir, cleanup := rtest.Env(t, repoFixture)
|
2016-09-04 12:29:04 +00:00
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
repo := repository.TestOpenLocal(t, repodir)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, repo.LoadIndex(context.TODO()))
|
2015-07-04 14:52:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkLoadIndex(b *testing.B) {
|
2017-01-13 20:39:40 +00:00
|
|
|
repository.TestUseLowSecurityKDFParameters(b)
|
|
|
|
|
|
|
|
repo, cleanup := repository.TestRepository(b)
|
2016-09-04 12:29:04 +00:00
|
|
|
defer cleanup()
|
|
|
|
|
2017-01-13 20:39:40 +00:00
|
|
|
idx := repository.NewIndex()
|
|
|
|
|
|
|
|
for i := 0; i < 5000; i++ {
|
|
|
|
idx.Store(restic.PackedBlob{
|
|
|
|
Blob: restic.Blob{
|
|
|
|
Type: restic.DataBlob,
|
|
|
|
Length: 1234,
|
|
|
|
ID: restic.NewRandomID(),
|
|
|
|
Offset: 1235,
|
|
|
|
},
|
|
|
|
PackID: restic.NewRandomID(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
id, err := repository.SaveIndex(context.TODO(), repo, idx)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 20:39:40 +00:00
|
|
|
|
|
|
|
b.Logf("index saved as %v (%v entries)", id.Str(), idx.Count(restic.DataBlob))
|
2017-06-05 21:56:59 +00:00
|
|
|
fi, err := repo.Backend().Stat(context.TODO(), restic.Handle{Type: restic.IndexFile, Name: id.String()})
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2017-01-13 20:39:40 +00:00
|
|
|
b.Logf("filesize is %v", fi.Size)
|
|
|
|
|
2016-09-04 12:29:04 +00:00
|
|
|
b.ResetTimer()
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2017-06-05 21:56:59 +00:00
|
|
|
_, err := repository.LoadIndex(context.TODO(), repo, id)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(b, err)
|
2016-09-04 12:29:04 +00:00
|
|
|
}
|
2015-07-04 14:52:17 +00:00
|
|
|
}
|
2015-10-25 14:35:33 +00:00
|
|
|
|
|
|
|
// saveRandomDataBlobs generates random data blobs and saves them to the repository.
|
2016-09-03 11:34:04 +00:00
|
|
|
func saveRandomDataBlobs(t testing.TB, repo restic.Repository, num int, sizeMax int) {
|
2015-10-25 14:35:33 +00:00
|
|
|
for i := 0; i < num; i++ {
|
2017-01-13 11:20:37 +00:00
|
|
|
size := rand.Int() % sizeMax
|
2015-10-25 14:35:33 +00:00
|
|
|
|
|
|
|
buf := make([]byte, size)
|
2017-01-13 11:20:37 +00:00
|
|
|
_, err := io.ReadFull(rnd, buf)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-10-25 14:35:33 +00:00
|
|
|
|
2017-06-05 21:56:59 +00:00
|
|
|
_, err = repo.SaveBlob(context.TODO(), restic.DataBlob, buf, restic.ID{})
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRepositoryIncrementalIndex(t *testing.T) {
|
2016-09-04 11:24:51 +00:00
|
|
|
repo, cleanup := repository.TestRepository(t)
|
2016-09-04 10:52:43 +00:00
|
|
|
defer cleanup()
|
2015-10-25 14:35:33 +00:00
|
|
|
|
|
|
|
repository.IndexFull = func(*repository.Index) bool { return true }
|
|
|
|
|
|
|
|
// add 15 packs
|
|
|
|
for j := 0; j < 5; j++ {
|
|
|
|
// add 3 packs, write intermediate index
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
saveRandomDataBlobs(t, repo, 5, 1<<15)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, repo.SaveFullIndex(context.TODO()))
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// add another 5 packs
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
saveRandomDataBlobs(t, repo, 5, 1<<15)
|
2017-11-22 11:27:29 +00:00
|
|
|
rtest.OK(t, repo.Flush(context.Background()))
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// save final index
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, repo.SaveIndex(context.TODO()))
|
2015-10-25 14:35:33 +00:00
|
|
|
|
2016-08-31 18:29:54 +00:00
|
|
|
packEntries := make(map[restic.ID]map[restic.ID]struct{})
|
2015-10-25 14:35:33 +00:00
|
|
|
|
2018-01-21 16:25:36 +00:00
|
|
|
err := repo.List(context.TODO(), restic.IndexFile, func(id restic.ID, size int64) error {
|
2017-06-05 21:56:59 +00:00
|
|
|
idx, err := repository.LoadIndex(context.TODO(), repo, id)
|
2017-10-02 13:06:39 +00:00
|
|
|
rtest.OK(t, err)
|
2015-10-25 14:35:33 +00:00
|
|
|
|
2017-06-18 12:45:02 +00:00
|
|
|
for pb := range idx.Each(context.TODO()) {
|
2015-10-25 14:35:33 +00:00
|
|
|
if _, ok := packEntries[pb.PackID]; !ok {
|
2016-08-31 18:29:54 +00:00
|
|
|
packEntries[pb.PackID] = make(map[restic.ID]struct{})
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
packEntries[pb.PackID][id] = struct{}{}
|
|
|
|
}
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2015-10-25 14:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for packID, ids := range packEntries {
|
|
|
|
if len(ids) > 1 {
|
|
|
|
t.Errorf("pack %v listed in %d indexes\n", packID, len(ids))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-28 20:12:15 +00:00
|
|
|
|
|
|
|
type backend struct {
|
|
|
|
rd io.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (be backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
|
|
|
|
return fn(be.rd)
|
|
|
|
}
|
|
|
|
|
|
|
|
type retryBackend struct {
|
|
|
|
buf []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (be retryBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
|
|
|
|
err := fn(bytes.NewReader(be.buf[:len(be.buf)/2]))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return fn(bytes.NewReader(be.buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDownloadAndHash(t *testing.T) {
|
|
|
|
buf := make([]byte, 5*1024*1024+881)
|
|
|
|
_, err := io.ReadFull(rnd, buf)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var tests = []struct {
|
|
|
|
be repository.Loader
|
|
|
|
want []byte
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
be: backend{rd: bytes.NewReader(buf)},
|
|
|
|
want: buf,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
be: retryBackend{buf: buf},
|
|
|
|
want: buf,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run("", func(t *testing.T) {
|
|
|
|
f, id, size, err := repository.DownloadAndHash(context.TODO(), test.be, restic.Handle{})
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
want := restic.Hash(test.want)
|
|
|
|
if !want.Equal(id) {
|
|
|
|
t.Errorf("wrong hash returned, want %v, got %v", want.Str(), id.Str())
|
|
|
|
}
|
|
|
|
|
|
|
|
if size != int64(len(test.want)) {
|
|
|
|
t.Errorf("wrong size returned, want %v, got %v", test.want, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = fs.RemoveIfExists(f.Name())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type errorReader struct {
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (er errorReader) Read(p []byte) (n int, err error) {
|
|
|
|
return 0, er.err
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDownloadAndHashErrors(t *testing.T) {
|
|
|
|
var tests = []struct {
|
|
|
|
be repository.Loader
|
|
|
|
err string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
be: backend{rd: errorReader{errors.New("test error 1")}},
|
|
|
|
err: "test error 1",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run("", func(t *testing.T) {
|
|
|
|
_, _, _, err := repository.DownloadAndHash(context.TODO(), test.be, restic.Handle{})
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("wanted error %q, got nil", test.err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if errors.Cause(err).Error() != test.err {
|
|
|
|
t.Fatalf("wanted error %q, got %q", test.err, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|