trie: improve error reporting of random test

This commit is contained in:
Felix Lange 2017-06-13 13:12:25 +02:00
parent 1a008e5511
commit d36e4a4176

View file

@ -19,6 +19,7 @@ package trie
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
@ -34,7 +35,7 @@ import (
func init() { func init() {
spew.Config.Indent = " " spew.Config.Indent = " "
spew.Config.DisableMethods = true spew.Config.DisableMethods = false
} }
// Used for testing // Used for testing
@ -357,6 +358,7 @@ type randTestStep struct {
op int op int
key []byte // for opUpdate, opDelete, opGet key []byte // for opUpdate, opDelete, opGet
value []byte // for opUpdate value []byte // for opUpdate
err error // for debugging
} }
const ( const (
@ -406,7 +408,7 @@ func runRandTest(rt randTest) bool {
tr, _ := New(common.Hash{}, db) tr, _ := New(common.Hash{}, db)
values := make(map[string]string) // tracks content of the trie values := make(map[string]string) // tracks content of the trie
for _, step := range rt { for i, step := range rt {
switch step.op { switch step.op {
case opUpdate: case opUpdate:
tr.Update(step.key, step.value) tr.Update(step.key, step.value)
@ -418,23 +420,22 @@ func runRandTest(rt randTest) bool {
v := tr.Get(step.key) v := tr.Get(step.key)
want := values[string(step.key)] want := values[string(step.key)]
if string(v) != want { if string(v) != want {
fmt.Printf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want) rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
return false
} }
case opCommit: case opCommit:
if _, err := tr.Commit(); err != nil { _, rt[i].err = tr.Commit()
panic(err)
}
case opHash: case opHash:
tr.Hash() tr.Hash()
case opReset: case opReset:
hash, err := tr.Commit() hash, err := tr.Commit()
if err != nil { if err != nil {
panic(err) rt[i].err = err
return false
} }
newtr, err := New(hash, db) newtr, err := New(hash, db)
if err != nil { if err != nil {
panic(err) rt[i].err = err
return false
} }
tr = newtr tr = newtr
case opItercheckhash: case opItercheckhash:
@ -444,17 +445,20 @@ func runRandTest(rt randTest) bool {
checktr.Update(it.Key, it.Value) checktr.Update(it.Key, it.Value)
} }
if tr.Hash() != checktr.Hash() { if tr.Hash() != checktr.Hash() {
fmt.Println("hashes not equal") rt[i].err = fmt.Errorf("hash mismatch in opItercheckhash")
return false
} }
case opCheckCacheInvariant: case opCheckCacheInvariant:
return checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0) rt[i].err = checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0)
}
// Abort the test on error.
if rt[i].err != nil {
return false
} }
} }
return true return true
} }
func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) bool { func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) error {
var children []node var children []node
var flag nodeFlag var flag nodeFlag
switch n := n.(type) { switch n := n.(type) {
@ -465,33 +469,34 @@ func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool
flag = n.flags flag = n.flags
children = n.Children[:] children = n.Children[:]
default: default:
return true return nil
} }
showerror := func() { errorf := func(format string, args ...interface{}) error {
fmt.Printf("at depth %d node %s", depth, spew.Sdump(n)) msg := fmt.Sprintf(format, args...)
fmt.Printf("parent: %s", spew.Sdump(parent)) msg += fmt.Sprintf("\nat depth %d node %s", depth, spew.Sdump(n))
msg += fmt.Sprintf("parent: %s", spew.Sdump(parent))
return errors.New(msg)
} }
if flag.gen > parentCachegen { if flag.gen > parentCachegen {
fmt.Printf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen) return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen)
showerror()
return false
} }
if depth > 0 && !parentDirty && flag.dirty { if depth > 0 && !parentDirty && flag.dirty {
fmt.Printf("cache invariant violation: child is dirty but parent isn't\n") return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen)
showerror()
return false
} }
for _, child := range children { for _, child := range children {
if !checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1) { if err := checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1); err != nil {
return false return err
} }
} }
return true return nil
} }
func TestRandom(t *testing.T) { func TestRandom(t *testing.T) {
if err := quick.Check(runRandTest, nil); err != nil { if err := quick.Check(runRandTest, nil); err != nil {
if cerr, ok := err.(*quick.CheckError); ok {
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
}
t.Fatal(err) t.Fatal(err)
} }
} }