mirror of https://github.com/restic/restic.git
checker: Test that blob types are not confused
This commit is contained in:
parent
ddf0b8cd0b
commit
9b0e718852
|
@ -413,6 +413,132 @@ func TestCheckerNoDuplicateTreeDecodes(t *testing.T) {
|
|||
test.Assert(t, !checkRepo.DuplicateTree, "detected duplicate tree loading")
|
||||
}
|
||||
|
||||
// delayRepository delays read of a specific handle.
|
||||
type delayRepository struct {
|
||||
restic.Repository
|
||||
DelayTree restic.ID
|
||||
UnblockChannel chan struct{}
|
||||
Unblocker sync.Once
|
||||
}
|
||||
|
||||
func (r *delayRepository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) {
|
||||
if id == r.DelayTree {
|
||||
<-r.UnblockChannel
|
||||
}
|
||||
return r.Repository.LoadTree(ctx, id)
|
||||
}
|
||||
|
||||
func (r *delayRepository) LookupBlobSize(id restic.ID, t restic.BlobType) (uint, bool) {
|
||||
if id == r.DelayTree && t == restic.DataBlob {
|
||||
r.Unblock()
|
||||
}
|
||||
return r.Repository.LookupBlobSize(id, t)
|
||||
}
|
||||
|
||||
func (r *delayRepository) Unblock() {
|
||||
r.Unblocker.Do(func() {
|
||||
close(r.UnblockChannel)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckerBlobTypeConfusion(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
damagedNode := &restic.Node{
|
||||
Name: "damaged",
|
||||
Type: "file",
|
||||
Mode: 0644,
|
||||
Size: 42,
|
||||
Content: restic.IDs{restic.TestParseID("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")},
|
||||
}
|
||||
damagedTree := &restic.Tree{
|
||||
Nodes: []*restic.Node{damagedNode},
|
||||
}
|
||||
|
||||
id, err := repo.SaveTree(ctx, damagedTree)
|
||||
test.OK(t, repo.Flush(ctx))
|
||||
test.OK(t, err)
|
||||
|
||||
buf, err := repo.LoadBlob(ctx, restic.TreeBlob, id, nil)
|
||||
test.OK(t, err)
|
||||
|
||||
_, _, err = repo.SaveBlob(ctx, restic.DataBlob, buf, id, false)
|
||||
test.OK(t, err)
|
||||
|
||||
malNode := &restic.Node{
|
||||
Name: "aaaaa",
|
||||
Type: "file",
|
||||
Mode: 0644,
|
||||
Size: uint64(len(buf)),
|
||||
Content: restic.IDs{id},
|
||||
}
|
||||
dirNode := &restic.Node{
|
||||
Name: "bbbbb",
|
||||
Type: "dir",
|
||||
Mode: 0755,
|
||||
Subtree: &id,
|
||||
}
|
||||
|
||||
rootTree := &restic.Tree{
|
||||
Nodes: []*restic.Node{malNode, dirNode},
|
||||
}
|
||||
|
||||
rootId, err := repo.SaveTree(ctx, rootTree)
|
||||
test.OK(t, err)
|
||||
|
||||
test.OK(t, repo.Flush(ctx))
|
||||
test.OK(t, repo.SaveIndex(ctx))
|
||||
|
||||
snapshot, err := restic.NewSnapshot([]string{"/damaged"}, []string{"test"}, "foo", time.Now())
|
||||
test.OK(t, err)
|
||||
|
||||
snapshot.Tree = &rootId
|
||||
|
||||
snapId, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, snapshot)
|
||||
test.OK(t, err)
|
||||
|
||||
t.Logf("saved snapshot %v", snapId.Str())
|
||||
|
||||
delayRepo := &delayRepository{
|
||||
Repository: repo,
|
||||
DelayTree: id,
|
||||
UnblockChannel: make(chan struct{}),
|
||||
}
|
||||
|
||||
chkr := checker.New(delayRepo)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
delayRepo.Unblock()
|
||||
}()
|
||||
|
||||
hints, errs := chkr.LoadIndex(ctx)
|
||||
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)
|
||||
}
|
||||
|
||||
errFound := false
|
||||
|
||||
for _, err := range checkStruct(chkr) {
|
||||
t.Logf("struct error: %v", err)
|
||||
errFound = true
|
||||
}
|
||||
|
||||
test.OK(t, ctx.Err())
|
||||
|
||||
if !errFound {
|
||||
t.Fatal("no error found, checker is broken")
|
||||
}
|
||||
}
|
||||
|
||||
func loadBenchRepository(t *testing.B) (*checker.Checker, restic.Repository, func()) {
|
||||
repodir, cleanup := test.Env(t, checkerTestData)
|
||||
|
||||
|
|
Loading…
Reference in New Issue