2016-08-31 18:29:54 +00:00
|
|
|
package restic_test
|
2016-08-01 16:31:44 +00:00
|
|
|
|
|
|
|
import (
|
2016-08-01 16:40:08 +00:00
|
|
|
"bufio"
|
2017-06-05 21:56:59 +00:00
|
|
|
"context"
|
2016-08-04 16:59:26 +00:00
|
|
|
"encoding/json"
|
2016-08-01 16:40:08 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
2016-08-01 16:31:44 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-02-22 20:21:09 +00:00
|
|
|
"github.com/restic/restic/internal/errors"
|
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"
|
2020-12-05 23:59:24 +00:00
|
|
|
"github.com/restic/restic/internal/test"
|
|
|
|
"github.com/restic/restic/internal/ui/progress"
|
2016-08-01 16:31:44 +00:00
|
|
|
)
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
func loadIDSet(t testing.TB, filename string) restic.BlobSet {
|
2016-08-01 16:40:08 +00:00
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("unable to open golden file %v: %v", filename, err)
|
2016-08-31 20:39:36 +00:00
|
|
|
return restic.NewBlobSet()
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc := bufio.NewScanner(f)
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
blobs := restic.NewBlobSet()
|
2016-08-01 16:40:08 +00:00
|
|
|
for sc.Scan() {
|
2016-08-31 20:39:36 +00:00
|
|
|
var h restic.BlobHandle
|
2016-08-04 16:59:26 +00:00
|
|
|
err := json.Unmarshal([]byte(sc.Text()), &h)
|
2016-08-01 16:40:08 +00:00
|
|
|
if err != nil {
|
2016-08-04 16:59:26 +00:00
|
|
|
t.Errorf("file %v contained invalid blob: %#v", filename, err)
|
|
|
|
continue
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 16:59:26 +00:00
|
|
|
blobs.Insert(h)
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err = f.Close(); err != nil {
|
|
|
|
t.Errorf("closing file %v failed with error %v", filename, err)
|
|
|
|
}
|
|
|
|
|
2016-08-04 16:59:26 +00:00
|
|
|
return blobs
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
func saveIDSet(t testing.TB, filename string, s restic.BlobSet) {
|
2017-03-10 21:45:26 +00:00
|
|
|
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
2016-08-01 16:40:08 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to update golden file %v: %v", filename, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
var hs restic.BlobHandles
|
2016-08-04 16:59:26 +00:00
|
|
|
for h := range s {
|
|
|
|
hs = append(hs, h)
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 16:59:26 +00:00
|
|
|
sort.Sort(hs)
|
|
|
|
|
|
|
|
enc := json.NewEncoder(f)
|
|
|
|
for _, h := range hs {
|
|
|
|
err = enc.Encode(h)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Encode() returned error: %v", err)
|
|
|
|
}
|
2016-08-01 16:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err = f.Close(); err != nil {
|
|
|
|
t.Fatalf("close file %v returned error: %v", filename, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var updateGoldenFiles = flag.Bool("update", false, "update golden files in testdata/")
|
|
|
|
|
2016-08-01 16:31:44 +00:00
|
|
|
const (
|
2016-08-01 18:44:02 +00:00
|
|
|
findTestSnapshots = 3
|
|
|
|
findTestDepth = 2
|
2016-08-01 16:31:44 +00:00
|
|
|
)
|
|
|
|
|
2016-08-01 18:44:02 +00:00
|
|
|
var findTestTime = time.Unix(1469960361, 23)
|
2016-08-01 16:31:44 +00:00
|
|
|
|
|
|
|
func TestFindUsedBlobs(t *testing.T) {
|
2022-12-11 09:41:22 +00:00
|
|
|
repo := repository.TestRepository(t)
|
2016-08-01 16:31:44 +00:00
|
|
|
|
2016-08-31 20:39:36 +00:00
|
|
|
var snapshots []*restic.Snapshot
|
2016-08-01 18:44:02 +00:00
|
|
|
for i := 0; i < findTestSnapshots; i++ {
|
2023-07-16 13:55:05 +00:00
|
|
|
sn := restic.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth)
|
2016-08-01 16:31:44 +00:00
|
|
|
t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str())
|
|
|
|
snapshots = append(snapshots, sn)
|
|
|
|
}
|
|
|
|
|
2022-12-29 11:29:46 +00:00
|
|
|
p := progress.NewCounter(time.Second, findTestSnapshots, func(value uint64, total uint64, runtime time.Duration, final bool) {})
|
2020-12-05 23:59:24 +00:00
|
|
|
defer p.Done()
|
|
|
|
|
2016-08-01 16:40:08 +00:00
|
|
|
for i, sn := range snapshots {
|
2016-08-31 20:39:36 +00:00
|
|
|
usedBlobs := restic.NewBlobSet()
|
2020-12-05 23:59:24 +00:00
|
|
|
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, usedBlobs, p)
|
2016-08-01 16:31:44 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("FindUsedBlobs returned error: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(usedBlobs) == 0 {
|
|
|
|
t.Errorf("FindUsedBlobs returned an empty set")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-10-25 05:41:44 +00:00
|
|
|
v, _ := p.Get()
|
|
|
|
test.Equals(t, v, uint64(i+1))
|
2020-12-05 23:59:24 +00:00
|
|
|
|
2016-08-01 16:40:08 +00:00
|
|
|
goldenFilename := filepath.Join("testdata", fmt.Sprintf("used_blobs_snapshot%d", i))
|
|
|
|
want := loadIDSet(t, goldenFilename)
|
|
|
|
|
|
|
|
if !want.Equals(usedBlobs) {
|
|
|
|
t.Errorf("snapshot %d: wrong list of blobs returned:\n missing blobs: %v\n extra blobs: %v",
|
|
|
|
i, want.Sub(usedBlobs), usedBlobs.Sub(want))
|
|
|
|
}
|
|
|
|
|
|
|
|
if *updateGoldenFiles {
|
|
|
|
saveIDSet(t, goldenFilename, usedBlobs)
|
|
|
|
}
|
2016-08-01 16:31:44 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-15 19:37:19 +00:00
|
|
|
|
2020-12-05 23:59:24 +00:00
|
|
|
func TestMultiFindUsedBlobs(t *testing.T) {
|
2022-12-11 09:41:22 +00:00
|
|
|
repo := repository.TestRepository(t)
|
2020-12-05 23:59:24 +00:00
|
|
|
|
|
|
|
var snapshotTrees restic.IDs
|
|
|
|
for i := 0; i < findTestSnapshots; i++ {
|
2023-07-16 13:55:05 +00:00
|
|
|
sn := restic.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth)
|
2020-12-05 23:59:24 +00:00
|
|
|
t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str())
|
|
|
|
snapshotTrees = append(snapshotTrees, *sn.Tree)
|
|
|
|
}
|
|
|
|
|
|
|
|
want := restic.NewBlobSet()
|
|
|
|
for i := range snapshotTrees {
|
|
|
|
goldenFilename := filepath.Join("testdata", fmt.Sprintf("used_blobs_snapshot%d", i))
|
|
|
|
want.Merge(loadIDSet(t, goldenFilename))
|
|
|
|
}
|
|
|
|
|
2022-12-29 11:29:46 +00:00
|
|
|
p := progress.NewCounter(time.Second, findTestSnapshots, func(value uint64, total uint64, runtime time.Duration, final bool) {})
|
2020-12-05 23:59:24 +00:00
|
|
|
defer p.Done()
|
|
|
|
|
|
|
|
// run twice to check progress bar handling of duplicate tree roots
|
|
|
|
usedBlobs := restic.NewBlobSet()
|
|
|
|
for i := 1; i < 3; i++ {
|
|
|
|
err := restic.FindUsedBlobs(context.TODO(), repo, snapshotTrees, usedBlobs, p)
|
|
|
|
test.OK(t, err)
|
2022-10-25 05:41:44 +00:00
|
|
|
v, _ := p.Get()
|
|
|
|
test.Equals(t, v, uint64(i*len(snapshotTrees)))
|
2020-12-05 23:59:24 +00:00
|
|
|
|
|
|
|
if !want.Equals(usedBlobs) {
|
|
|
|
t.Errorf("wrong list of blobs returned:\n missing blobs: %v\n extra blobs: %v",
|
|
|
|
want.Sub(usedBlobs), usedBlobs.Sub(want))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 20:21:09 +00:00
|
|
|
type ForbiddenRepo struct{}
|
|
|
|
|
2022-06-12 12:38:19 +00:00
|
|
|
func (r ForbiddenRepo) LoadBlob(context.Context, restic.BlobType, restic.ID, []byte) ([]byte, error) {
|
2020-02-22 20:21:09 +00:00
|
|
|
return nil, errors.New("should not be called")
|
|
|
|
}
|
|
|
|
|
2023-05-18 17:27:38 +00:00
|
|
|
func (r ForbiddenRepo) LookupBlobSize(_ restic.ID, _ restic.BlobType) (uint, bool) {
|
2022-02-19 11:26:09 +00:00
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
|
2021-08-07 22:38:17 +00:00
|
|
|
func (r ForbiddenRepo) Connections() uint {
|
|
|
|
return 2
|
|
|
|
}
|
|
|
|
|
2020-02-22 20:21:09 +00:00
|
|
|
func TestFindUsedBlobsSkipsSeenBlobs(t *testing.T) {
|
2022-12-11 09:41:22 +00:00
|
|
|
repo := repository.TestRepository(t)
|
2020-02-22 20:21:09 +00:00
|
|
|
|
2023-07-16 13:55:05 +00:00
|
|
|
snapshot := restic.TestCreateSnapshot(t, repo, findTestTime, findTestDepth)
|
2020-02-22 20:21:09 +00:00
|
|
|
t.Logf("snapshot %v saved, tree %v", snapshot.ID().Str(), snapshot.Tree.Str())
|
|
|
|
|
|
|
|
usedBlobs := restic.NewBlobSet()
|
2020-11-07 13:16:04 +00:00
|
|
|
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*snapshot.Tree}, usedBlobs, nil)
|
2020-02-22 20:21:09 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("FindUsedBlobs returned error: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-11-07 13:16:04 +00:00
|
|
|
err = restic.FindUsedBlobs(context.TODO(), ForbiddenRepo{}, restic.IDs{*snapshot.Tree}, usedBlobs, nil)
|
2020-02-22 20:21:09 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("FindUsedBlobs returned error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 19:37:19 +00:00
|
|
|
func BenchmarkFindUsedBlobs(b *testing.B) {
|
2022-12-11 09:41:22 +00:00
|
|
|
repo := repository.TestRepository(b)
|
2016-08-15 19:37:19 +00:00
|
|
|
|
2023-07-16 13:55:05 +00:00
|
|
|
sn := restic.TestCreateSnapshot(b, repo, findTestTime, findTestDepth)
|
2016-08-15 19:37:19 +00:00
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2016-08-31 20:39:36 +00:00
|
|
|
blobs := restic.NewBlobSet()
|
2020-11-07 13:16:04 +00:00
|
|
|
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, blobs, nil)
|
2016-08-15 19:37:19 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.Logf("found %v blobs", len(blobs))
|
|
|
|
}
|
|
|
|
}
|