This enables the backends to request the calculation of a backend-specific hash. For the currently supported backends this will always be MD5. The hash calculation happens as early as possible, for pack files this is during assembly of the pack file. That way the hash would even capture corruptions of the temporary pack file on disk.
175 lines
3.5 KiB
Go
175 lines
3.5 KiB
Go
package restic
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"hash"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
func TestByteReader(t *testing.T) {
|
|
buf := []byte("foobar")
|
|
for _, hasher := range []hash.Hash{nil, md5.New()} {
|
|
fn := func() RewindReader {
|
|
return NewByteReader(buf, hasher)
|
|
}
|
|
testRewindReader(t, fn, buf)
|
|
}
|
|
}
|
|
|
|
func TestFileReader(t *testing.T) {
|
|
buf := []byte("foobar")
|
|
|
|
d, cleanup := test.TempDir(t)
|
|
defer cleanup()
|
|
|
|
filename := filepath.Join(d, "file-reader-test")
|
|
err := ioutil.WriteFile(filename, buf, 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
err := f.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
for _, hasher := range []hash.Hash{nil, md5.New()} {
|
|
fn := func() RewindReader {
|
|
var hash []byte
|
|
if hasher != nil {
|
|
// must never fail according to interface
|
|
_, _ = hasher.Write(buf)
|
|
hash = hasher.Sum(nil)
|
|
}
|
|
rd, err := NewFileReader(f, hash)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return rd
|
|
}
|
|
|
|
testRewindReader(t, fn, buf)
|
|
}
|
|
}
|
|
|
|
func testRewindReader(t *testing.T, fn func() RewindReader, data []byte) {
|
|
seed := time.Now().UnixNano()
|
|
t.Logf("seed is %d", seed)
|
|
rnd := rand.New(rand.NewSource(seed))
|
|
|
|
type ReaderTestFunc func(t testing.TB, r RewindReader, data []byte)
|
|
var tests = []ReaderTestFunc{
|
|
func(t testing.TB, rd RewindReader, data []byte) {
|
|
if rd.Length() != int64(len(data)) {
|
|
t.Fatalf("wrong length returned, want %d, got %d", int64(len(data)), rd.Length())
|
|
}
|
|
|
|
buf := make([]byte, len(data))
|
|
_, err := io.ReadFull(rd, buf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(buf, data) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
|
|
if rd.Length() != int64(len(data)) {
|
|
t.Fatalf("wrong length returned, want %d, got %d", int64(len(data)), rd.Length())
|
|
}
|
|
|
|
err = rd.Rewind()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if rd.Length() != int64(len(data)) {
|
|
t.Fatalf("wrong length returned, want %d, got %d", int64(len(data)), rd.Length())
|
|
}
|
|
|
|
buf2 := make([]byte, int64(len(data)))
|
|
_, err = io.ReadFull(rd, buf2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(buf2, data) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
|
|
if rd.Length() != int64(len(data)) {
|
|
t.Fatalf("wrong length returned, want %d, got %d", int64(len(data)), rd.Length())
|
|
}
|
|
|
|
if rd.Hash() != nil {
|
|
hasher := md5.New()
|
|
// must never fail according to interface
|
|
_, _ = hasher.Write(buf2)
|
|
if !bytes.Equal(rd.Hash(), hasher.Sum(nil)) {
|
|
t.Fatal("hash does not match data")
|
|
}
|
|
}
|
|
},
|
|
func(t testing.TB, rd RewindReader, data []byte) {
|
|
// read first bytes
|
|
buf := make([]byte, rnd.Intn(len(data)))
|
|
_, err := io.ReadFull(rd, buf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(buf, data[:len(buf)]) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
|
|
err = rd.Rewind()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
buf2 := make([]byte, rnd.Intn(len(data)))
|
|
_, err = io.ReadFull(rd, buf2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(buf2, data[:len(buf2)]) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
|
|
// read remainder
|
|
buf3 := make([]byte, len(data)-len(buf2))
|
|
_, err = io.ReadFull(rd, buf3)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(buf3, data[len(buf2):]) {
|
|
t.Fatalf("wrong data returned")
|
|
}
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
rd := fn()
|
|
test(t, rd, data)
|
|
})
|
|
}
|
|
}
|