mirror of
https://github.com/kopia/kopia.git
synced 2026-01-25 23:08:01 -05:00
This helps recycle buffers more efficiently during snapshots. Also, improved memory tracking, enabled profiling flags and added pprof by default.
180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
package blobtesting
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/kopia/kopia/internal/gather"
|
|
"github.com/kopia/kopia/repo/blob"
|
|
)
|
|
|
|
const maxTimeDiffBetweenGetAndList = time.Minute
|
|
|
|
// AssertGetBlob asserts that the specified BLOB has correct content.
|
|
func AssertGetBlob(ctx context.Context, t *testing.T, s blob.Storage, blobID blob.ID, expected []byte) {
|
|
t.Helper()
|
|
|
|
var b gather.WriteBuffer
|
|
defer b.Close()
|
|
|
|
err := s.GetBlob(ctx, blobID, 0, -1, &b)
|
|
require.NoErrorf(t, err, "GetBlob(%v)", blobID)
|
|
|
|
if v := b.ToByteSlice(); !bytes.Equal(v, expected) {
|
|
t.Fatalf("GetBlob(%v) returned %x, but expected %x", blobID, v, expected)
|
|
}
|
|
|
|
half := int64(len(expected) / 2)
|
|
if half == 0 {
|
|
return
|
|
}
|
|
|
|
err = s.GetBlob(ctx, blobID, 0, 0, &b)
|
|
if err != nil {
|
|
t.Fatalf("GetBlob(%v) returned error %v, expected data: %v", blobID, err, expected)
|
|
return
|
|
}
|
|
|
|
if b.Length() != 0 {
|
|
t.Fatalf("GetBlob(%v) returned non-zero length: %v", blobID, b.Length())
|
|
return
|
|
}
|
|
|
|
err = s.GetBlob(ctx, blobID, 0, half, &b)
|
|
if err != nil {
|
|
t.Fatalf("GetBlob(%v) returned error %v, expected data: %v", blobID, err, expected)
|
|
return
|
|
}
|
|
|
|
if v := b.ToByteSlice(); !bytes.Equal(v, expected[0:half]) {
|
|
t.Fatalf("GetBlob(%v) returned %x, but expected %x", blobID, v, expected[0:half])
|
|
}
|
|
|
|
err = s.GetBlob(ctx, blobID, half, int64(len(expected))-half, &b)
|
|
if err != nil {
|
|
t.Fatalf("GetBlob(%v) returned error %v, expected data: %v", blobID, err, expected)
|
|
return
|
|
}
|
|
|
|
if v := b.ToByteSlice(); !bytes.Equal(v, expected[len(expected)-int(half):]) {
|
|
t.Fatalf("GetBlob(%v) returned %x, but expected %x", blobID, v, expected[len(expected)-int(half):])
|
|
}
|
|
|
|
AssertInvalidOffsetLength(ctx, t, s, blobID, -3, 1)
|
|
AssertInvalidOffsetLength(ctx, t, s, blobID, int64(len(expected)), 3)
|
|
AssertInvalidOffsetLength(ctx, t, s, blobID, int64(len(expected)-1), 3)
|
|
AssertInvalidOffsetLength(ctx, t, s, blobID, int64(len(expected)+1), 3)
|
|
}
|
|
|
|
// AssertInvalidOffsetLength verifies that the given combination of (offset,length) fails on GetBlob().
|
|
func AssertInvalidOffsetLength(ctx context.Context, t *testing.T, s blob.Storage, blobID blob.ID, offset, length int64) {
|
|
t.Helper()
|
|
|
|
var tmp gather.WriteBuffer
|
|
defer tmp.Close()
|
|
|
|
if err := s.GetBlob(ctx, blobID, offset, length, &tmp); err == nil {
|
|
t.Fatalf("GetBlob(%v,%v,%v) did not return error for invalid offset/length", blobID, offset, length)
|
|
}
|
|
}
|
|
|
|
// AssertGetBlobNotFound asserts that GetBlob() for specified blobID returns ErrNotFound.
|
|
func AssertGetBlobNotFound(ctx context.Context, t *testing.T, s blob.Storage, blobID blob.ID) {
|
|
t.Helper()
|
|
|
|
var b gather.WriteBuffer
|
|
defer b.Close()
|
|
|
|
err := s.GetBlob(ctx, blobID, 0, -1, &b)
|
|
if !errors.Is(err, blob.ErrBlobNotFound) || b.Length() != 0 {
|
|
t.Fatalf("GetBlob(%v) returned %v, %v but expected ErrNotFound", blobID, b.Length(), err)
|
|
}
|
|
}
|
|
|
|
// AssertGetMetadataNotFound asserts that GetMetadata() for specified blobID returns ErrNotFound.
|
|
func AssertGetMetadataNotFound(ctx context.Context, t *testing.T, s blob.Storage, blobID blob.ID) {
|
|
t.Helper()
|
|
|
|
_, err := s.GetMetadata(ctx, blobID)
|
|
if !errors.Is(err, blob.ErrBlobNotFound) {
|
|
t.Fatalf("GetMetadata(%v) returned %v but expected ErrNotFound", blobID, err)
|
|
}
|
|
}
|
|
|
|
// AssertListResults asserts that the list results with given prefix return the specified list of names in order.
|
|
func AssertListResults(ctx context.Context, t *testing.T, s blob.Storage, prefix blob.ID, want ...blob.ID) {
|
|
t.Helper()
|
|
|
|
var names []blob.ID
|
|
|
|
if err := s.ListBlobs(ctx, prefix, func(m blob.Metadata) error {
|
|
names = append(names, m.BlobID)
|
|
|
|
m2, err := s.GetMetadata(ctx, m.BlobID)
|
|
if err != nil {
|
|
t.Fatalf("GetMetadata() failed: %v", err)
|
|
}
|
|
|
|
if got, want := m2.BlobID, m.BlobID; got != want {
|
|
t.Fatalf("invalid blob ID on %v: %v, want %v", m.BlobID, got, want)
|
|
}
|
|
|
|
if got, want := m2.Length, m.Length; got != want {
|
|
t.Fatalf("invalid length on %v: %v, want %v", m.BlobID, got, want)
|
|
}
|
|
|
|
timeDiff := m2.Timestamp.Sub(m.Timestamp)
|
|
if timeDiff < 0 {
|
|
timeDiff = -timeDiff
|
|
}
|
|
|
|
// truncated time comparison, because some providers return different precision of time in list vs get
|
|
if timeDiff > maxTimeDiffBetweenGetAndList {
|
|
t.Fatalf("invalid timestamp on %v: getmetadata returned %v, list returned %v", m.BlobID, m2.Timestamp, m.Timestamp)
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
names = sorted(names)
|
|
want = sorted(want)
|
|
|
|
if !reflect.DeepEqual(names, want) {
|
|
t.Fatalf("ListBlobs(%v) returned %v, but wanted %v", prefix, names, want)
|
|
}
|
|
}
|
|
|
|
// AssertListResultsIDs asserts that the list results with given prefix return the specified list of names.
|
|
func AssertListResultsIDs(ctx context.Context, t *testing.T, s blob.Storage, prefix blob.ID, want ...blob.ID) {
|
|
t.Helper()
|
|
|
|
var names []blob.ID
|
|
|
|
if err := s.ListBlobs(ctx, prefix, func(m blob.Metadata) error {
|
|
names = append(names, m.BlobID)
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
require.ElementsMatch(t, names, want)
|
|
}
|
|
|
|
func sorted(s []blob.ID) []blob.ID {
|
|
x := append([]blob.ID(nil), s...)
|
|
sort.Slice(x, func(i, j int) bool {
|
|
return x[i] < x[j]
|
|
})
|
|
|
|
return x
|
|
}
|