func TestMissingPack(t *testing.T) { repodir, cleanup := test.Env(t, checkerTestData) defer cleanup() repo := repository.TestOpenLocal(t, repodir) packID := "657f7fb64f6a854fff6fe9279998ee09034901eded4e6db9bcee0e59745bbce6" test.OK(t, repo.Backend().Remove(restic.DataFile, packID)) chkr := checker.New(repo) hints, errs := chkr.LoadIndex() if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } if len(hints) > 0 { t.Errorf("expected no hints, got %v: %v", len(hints), hints) } errs = checkPacks(chkr) test.Assert(t, len(errs) == 1, "expected exactly one error, got %v", len(errs)) if err, ok := errs[0].(checker.PackError); ok { test.Equals(t, packID, err.ID.String()) } else { t.Errorf("expected error returned by checker.Packs() to be PackError, got %v", err) } }
func TestUnreferencedPack(t *testing.T) { repodir, cleanup := test.Env(t, checkerTestData) defer cleanup() repo := repository.TestOpenLocal(t, repodir) // index 3f1a only references pack 60e0 indexID := "3f1abfcb79c6f7d0a3be517d2c83c8562fba64ef2c8e9a3544b4edaf8b5e3b44" packID := "60e0438dcb978ec6860cc1f8c43da648170ee9129af8f650f876bad19f8f788e" test.OK(t, repo.Backend().Remove(restic.IndexFile, indexID)) chkr := checker.New(repo) hints, errs := chkr.LoadIndex() if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } if len(hints) > 0 { t.Errorf("expected no hints, got %v: %v", len(hints), hints) } errs = checkPacks(chkr) test.Assert(t, len(errs) == 1, "expected exactly one error, got %v", len(errs)) if err, ok := errs[0].(checker.PackError); ok { test.Equals(t, packID, err.ID.String()) } else { t.Errorf("expected error returned by checker.Packs() to be PackError, got %v", err) } }
func TestCheckerModifiedData(t *testing.T) { be := mem.New() repository.TestUseLowSecurityKDFParameters(t) repo := repository.New(be) test.OK(t, repo.Init(test.TestPassword)) arch := archiver.New(repo) _, id, err := arch.Snapshot(nil, []string{"."}, nil, nil) test.OK(t, err) t.Logf("archived as %v", id.Str()) beError := &errorBackend{Backend: be} checkRepo := repository.New(beError) test.OK(t, checkRepo.SearchKey(test.TestPassword, 5)) chkr := checker.New(checkRepo) hints, errs := chkr.LoadIndex() if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } if len(hints) > 0 { t.Errorf("expected no hints, got %v: %v", len(hints), hints) } beError.ProduceErrors = true errFound := false for _, err := range checkPacks(chkr) { t.Logf("pack error: %v", err) } for _, err := range checkStruct(chkr) { t.Logf("struct error: %v", err) } for _, err := range checkData(chkr) { t.Logf("struct error: %v", err) errFound = true } if !errFound { t.Fatal("no error found, checker is broken") } }
// TestLoadNegativeOffset tests the backend's Load function with negative offsets. func TestLoadNegativeOffset(t testing.TB) { b := open(t) defer close(t) length := rand.Intn(1<<24) + 2000 data := test.Random(23, length) id := restic.Hash(data) handle := restic.Handle{Type: restic.DataFile, Name: id.String()} err := b.Save(handle, data) if err != nil { t.Fatalf("Save() error: %v", err) } // test normal reads for i := 0; i < 50; i++ { l := rand.Intn(length + 2000) o := -rand.Intn(length + 2000) buf := make([]byte, l) n, err := b.Load(handle, buf, int64(o)) // if we requested data beyond the end of the file, require // ErrUnexpectedEOF error if len(buf) > -o { if errors.Cause(err) != io.ErrUnexpectedEOF { t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), o) continue } err = nil buf = buf[:-o] } if err != nil { t.Errorf("Load(%d, %d) returned error: %v", len(buf), o, err) continue } if n != len(buf) { t.Errorf("Load(%d, %d) returned short read, only got %d bytes", len(buf), o, n) continue } p := len(data) + o if !bytes.Equal(buf, data[p:p+len(buf)]) { t.Errorf("Load(%d, %d) returned wrong bytes", len(buf), o) continue } } test.OK(t, b.Remove(restic.DataFile, id.String())) }
// TestSave tests saving data in the backend. func TestSave(t testing.TB) { b := open(t) defer close(t) var id restic.ID for i := 0; i < 10; i++ { length := rand.Intn(1<<23) + 200000 data := test.Random(23, length) // use the first 32 byte as the ID copy(id[:], data) h := restic.Handle{ Type: restic.DataFile, Name: fmt.Sprintf("%s-%d", id, i), } err := b.Save(h, data) test.OK(t, err) buf, err := backend.LoadAll(b, h, nil) test.OK(t, err) if len(buf) != len(data) { t.Fatalf("number of bytes does not match, want %v, got %v", len(data), len(buf)) } if !bytes.Equal(buf, data) { t.Fatalf("data not equal") } fi, err := b.Stat(h) test.OK(t, err) if fi.Size != int64(len(data)) { t.Fatalf("Stat() returned different size, want %q, got %d", len(data), fi.Size) } err = b.Remove(h.Type, h.Name) if err != nil { t.Fatalf("error removing item: %v", err) } } }
func TestUnreferencedBlobs(t *testing.T) { repodir, cleanup := test.Env(t, checkerTestData) defer cleanup() repo := repository.TestOpenLocal(t, repodir) snID := "51d249d28815200d59e4be7b3f21a157b864dc343353df9d8e498220c2499b02" test.OK(t, repo.Backend().Remove(restic.SnapshotFile, snID)) unusedBlobsBySnapshot := restic.IDs{ restic.TestParseID("58c748bbe2929fdf30c73262bd8313fe828f8925b05d1d4a87fe109082acb849"), restic.TestParseID("988a272ab9768182abfd1fe7d7a7b68967825f0b861d3b36156795832c772235"), restic.TestParseID("c01952de4d91da1b1b80bc6e06eaa4ec21523f4853b69dc8231708b9b7ec62d8"), restic.TestParseID("bec3a53d7dc737f9a9bee68b107ec9e8ad722019f649b34d474b9982c3a3fec7"), restic.TestParseID("2a6f01e5e92d8343c4c6b78b51c5a4dc9c39d42c04e26088c7614b13d8d0559d"), restic.TestParseID("18b51b327df9391732ba7aaf841a4885f350d8a557b2da8352c9acf8898e3f10"), } sort.Sort(unusedBlobsBySnapshot) chkr := checker.New(repo) hints, errs := chkr.LoadIndex() if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } if len(hints) > 0 { t.Errorf("expected no hints, got %v: %v", len(hints), hints) } test.OKs(t, checkPacks(chkr)) test.OKs(t, checkStruct(chkr)) blobs := chkr.UnusedBlobs() sort.Sort(blobs) test.Equals(t, unusedBlobsBySnapshot, blobs) }
// TestBackend tests all functions of the backend. func TestBackend(t testing.TB) { b := open(t) defer close(t) for _, tpe := range []restic.FileType{ restic.DataFile, restic.KeyFile, restic.LockFile, restic.SnapshotFile, restic.IndexFile, } { // detect non-existing files for _, ts := range testStrings { id, err := restic.ParseID(ts.id) test.OK(t, err) // test if blob is already in repository ret, err := b.Test(tpe, id.String()) test.OK(t, err) test.Assert(t, !ret, "blob was found to exist before creating") // try to stat a not existing blob h := restic.Handle{Type: tpe, Name: id.String()} _, err = b.Stat(h) test.Assert(t, err != nil, "blob data could be extracted before creation") // try to read not existing blob _, err = b.Load(h, nil, 0) test.Assert(t, err != nil, "blob reader could be obtained before creation") // try to get string out, should fail ret, err = b.Test(tpe, id.String()) test.OK(t, err) test.Assert(t, !ret, "id %q was found (but should not have)", ts.id) } // add files for _, ts := range testStrings { store(t, b, tpe, []byte(ts.data)) // test Load() h := restic.Handle{Type: tpe, Name: ts.id} buf, err := backend.LoadAll(b, h, nil) test.OK(t, err) test.Equals(t, ts.data, string(buf)) // try to read it out with an offset and a length start := 1 end := len(ts.data) - 2 length := end - start buf2 := make([]byte, length) n, err := b.Load(h, buf2, int64(start)) test.OK(t, err) test.Equals(t, length, n) test.Equals(t, ts.data[start:end], string(buf2)) } // test adding the first file again ts := testStrings[0] // create blob err := b.Save(restic.Handle{Type: tpe, Name: ts.id}, []byte(ts.data)) test.Assert(t, err != nil, "expected error, got %v", err) // remove and recreate err = b.Remove(tpe, ts.id) test.OK(t, err) // test that the blob is gone ok, err := b.Test(tpe, ts.id) test.OK(t, err) test.Assert(t, ok == false, "removed blob still present") // create blob err = b.Save(restic.Handle{Type: tpe, Name: ts.id}, []byte(ts.data)) test.OK(t, err) // list items IDs := restic.IDs{} for _, ts := range testStrings { id, err := restic.ParseID(ts.id) test.OK(t, err) IDs = append(IDs, id) } list := restic.IDs{} for s := range b.List(tpe, nil) { list = append(list, restic.TestParseID(s)) } if len(IDs) != len(list) { t.Fatalf("wrong number of IDs returned: want %d, got %d", len(IDs), len(list)) } sort.Sort(IDs) sort.Sort(list) if !reflect.DeepEqual(IDs, list) { t.Fatalf("lists aren't equal, want:\n %v\n got:\n%v\n", IDs, list) } // remove content if requested if test.TestCleanupTempDirs { for _, ts := range testStrings { id, err := restic.ParseID(ts.id) test.OK(t, err) found, err := b.Test(tpe, id.String()) test.OK(t, err) test.OK(t, b.Remove(tpe, id.String())) found, err = b.Test(tpe, id.String()) test.OK(t, err) test.Assert(t, !found, fmt.Sprintf("id %q not found after removal", id)) } } } }
func store(t testing.TB, b restic.Backend, tpe restic.FileType, data []byte) { id := restic.Hash(data) err := b.Save(restic.Handle{Name: id.String(), Type: tpe}, data) test.OK(t, err) }
// TestLoad tests the backend's Load function. func TestLoad(t testing.TB) { b := open(t) defer close(t) _, err := b.Load(restic.Handle{}, nil, 0) if err == nil { t.Fatalf("Load() did not return an error for invalid handle") } _, err = b.Load(restic.Handle{Type: restic.DataFile, Name: "foobar"}, nil, 0) if err == nil { t.Fatalf("Load() did not return an error for non-existing blob") } length := rand.Intn(1<<24) + 2000 data := test.Random(23, length) id := restic.Hash(data) handle := restic.Handle{Type: restic.DataFile, Name: id.String()} err = b.Save(handle, data) if err != nil { t.Fatalf("Save() error: %v", err) } for i := 0; i < 50; i++ { l := rand.Intn(length + 2000) o := rand.Intn(length + 2000) d := data if o < len(d) { d = d[o:] } else { o = len(d) d = d[:0] } if l > 0 && l < len(d) { d = d[:l] } buf := make([]byte, l) n, err := b.Load(handle, buf, int64(o)) // if we requested data beyond the end of the file, require // ErrUnexpectedEOF error if l > len(d) { if errors.Cause(err) != io.ErrUnexpectedEOF { t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o)) } err = nil buf = buf[:len(d)] } if err != nil { t.Errorf("Load(%d, %d): unexpected error: %v", len(buf), int64(o), err) continue } if n != len(buf) { t.Errorf("Load(%d, %d): wrong length returned, want %d, got %d", len(buf), int64(o), len(buf), n) continue } buf = buf[:n] if !bytes.Equal(buf, d) { t.Errorf("Load(%d, %d) returned wrong bytes", len(buf), int64(o)) continue } } // test with negative offset for i := 0; i < 50; i++ { l := rand.Intn(length + 2000) o := rand.Intn(length + 2000) d := data if o < len(d) { d = d[len(d)-o:] } else { o = 0 } if l > 0 && l < len(d) { d = d[:l] } buf := make([]byte, l) n, err := b.Load(handle, buf, -int64(o)) // if we requested data beyond the end of the file, require // ErrUnexpectedEOF error if l > len(d) { if errors.Cause(err) != io.ErrUnexpectedEOF { t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o)) continue } err = nil buf = buf[:len(d)] } if err != nil { t.Errorf("Load(%d, %d): unexpected error: %v", len(buf), int64(o), err) continue } if n != len(buf) { t.Errorf("Load(%d, %d): wrong length returned, want %d, got %d", len(buf), int64(o), len(buf), n) continue } buf = buf[:n] if !bytes.Equal(buf, d) { t.Errorf("Load(%d, %d) returned wrong bytes", len(buf), int64(o)) continue } } // load with a too-large buffer, this should return io.ErrUnexpectedEOF buf := make([]byte, length+100) n, err := b.Load(handle, buf, 0) if n != length { t.Errorf("wrong length for larger buffer returned, want %d, got %d", length, n) } if errors.Cause(err) != io.ErrUnexpectedEOF { t.Errorf("wrong error returned for larger buffer: want io.ErrUnexpectedEOF, got %#v", err) } test.OK(t, b.Remove(restic.DataFile, id.String())) }