|
|
@@ -21,8 +21,11 @@ import (
|
|
|
"encoding/binary"
|
|
|
"fmt"
|
|
|
"io/ioutil"
|
|
|
+ "math/rand"
|
|
|
"os"
|
|
|
+ "reflect"
|
|
|
"testing"
|
|
|
+ "testing/quick"
|
|
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
@@ -297,41 +300,6 @@ func TestReplication(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func paranoiaCheck(t1 *Trie) (bool, *Trie) {
|
|
|
- t2 := new(Trie)
|
|
|
- it := NewIterator(t1)
|
|
|
- for it.Next() {
|
|
|
- t2.Update(it.Key, it.Value)
|
|
|
- }
|
|
|
- return t2.Hash() == t1.Hash(), t2
|
|
|
-}
|
|
|
-
|
|
|
-func TestParanoia(t *testing.T) {
|
|
|
- t.Skip()
|
|
|
- trie := newEmpty()
|
|
|
-
|
|
|
- vals := []struct{ k, v string }{
|
|
|
- {"do", "verb"},
|
|
|
- {"ether", "wookiedoo"},
|
|
|
- {"horse", "stallion"},
|
|
|
- {"shaman", "horse"},
|
|
|
- {"doge", "coin"},
|
|
|
- {"ether", ""},
|
|
|
- {"dog", "puppy"},
|
|
|
- {"shaman", ""},
|
|
|
- {"somethingveryoddindeedthis is", "myothernodedata"},
|
|
|
- }
|
|
|
- for _, val := range vals {
|
|
|
- updateString(trie, val.k, val.v)
|
|
|
- }
|
|
|
- trie.Commit()
|
|
|
-
|
|
|
- ok, t2 := paranoiaCheck(trie)
|
|
|
- if !ok {
|
|
|
- t.Errorf("trie paranoia check failed %x %x", trie.Hash(), t2.Hash())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
// Not an actual test
|
|
|
func TestOutput(t *testing.T) {
|
|
|
t.Skip()
|
|
|
@@ -356,7 +324,128 @@ func TestLargeValue(t *testing.T) {
|
|
|
trie.Update([]byte("key1"), []byte{99, 99, 99, 99})
|
|
|
trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32))
|
|
|
trie.Hash()
|
|
|
+}
|
|
|
+
|
|
|
+type randTestStep struct {
|
|
|
+ op int
|
|
|
+ key []byte // for opUpdate, opDelete, opGet
|
|
|
+ value []byte // for opUpdate
|
|
|
+}
|
|
|
+
|
|
|
+type randTest []randTestStep
|
|
|
+
|
|
|
+const (
|
|
|
+ opUpdate = iota
|
|
|
+ opDelete
|
|
|
+ opGet
|
|
|
+ opCommit
|
|
|
+ opHash
|
|
|
+ opReset
|
|
|
+ opItercheckhash
|
|
|
+ opMax // boundary value, not an actual op
|
|
|
+)
|
|
|
+
|
|
|
+func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
|
|
|
+ var allKeys [][]byte
|
|
|
+ genKey := func() []byte {
|
|
|
+ if len(allKeys) < 2 || r.Intn(100) < 10 {
|
|
|
+ // new key
|
|
|
+ key := make([]byte, r.Intn(50))
|
|
|
+ randRead(r, key)
|
|
|
+ allKeys = append(allKeys, key)
|
|
|
+ return key
|
|
|
+ }
|
|
|
+ // use existing key
|
|
|
+ return allKeys[r.Intn(len(allKeys))]
|
|
|
+ }
|
|
|
+
|
|
|
+ var steps randTest
|
|
|
+ for i := 0; i < size; i++ {
|
|
|
+ step := randTestStep{op: r.Intn(opMax)}
|
|
|
+ switch step.op {
|
|
|
+ case opUpdate:
|
|
|
+ step.key = genKey()
|
|
|
+ step.value = make([]byte, 8)
|
|
|
+ binary.BigEndian.PutUint64(step.value, uint64(i))
|
|
|
+ case opGet, opDelete:
|
|
|
+ step.key = genKey()
|
|
|
+ }
|
|
|
+ steps = append(steps, step)
|
|
|
+ }
|
|
|
+ return reflect.ValueOf(steps)
|
|
|
+}
|
|
|
+
|
|
|
+// rand.Rand provides a Read method in Go 1.7 and later, but
|
|
|
+// we can't use it yet.
|
|
|
+func randRead(r *rand.Rand, b []byte) {
|
|
|
+ pos := 0
|
|
|
+ val := 0
|
|
|
+ for n := 0; n < len(b); n++ {
|
|
|
+ if pos == 0 {
|
|
|
+ val = r.Int()
|
|
|
+ pos = 7
|
|
|
+ }
|
|
|
+ b[n] = byte(val)
|
|
|
+ val >>= 8
|
|
|
+ pos--
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
+func runRandTest(rt randTest) bool {
|
|
|
+ db, _ := ethdb.NewMemDatabase()
|
|
|
+ tr, _ := New(common.Hash{}, db)
|
|
|
+ values := make(map[string]string) // tracks content of the trie
|
|
|
+
|
|
|
+ for _, step := range rt {
|
|
|
+ switch step.op {
|
|
|
+ case opUpdate:
|
|
|
+ tr.Update(step.key, step.value)
|
|
|
+ values[string(step.key)] = string(step.value)
|
|
|
+ case opDelete:
|
|
|
+ tr.Delete(step.key)
|
|
|
+ delete(values, string(step.key))
|
|
|
+ case opGet:
|
|
|
+ v := tr.Get(step.key)
|
|
|
+ want := values[string(step.key)]
|
|
|
+ if string(v) != want {
|
|
|
+ fmt.Printf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ case opCommit:
|
|
|
+ if _, err := tr.Commit(); err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ case opHash:
|
|
|
+ tr.Hash()
|
|
|
+ case opReset:
|
|
|
+ hash, err := tr.Commit()
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ newtr, err := New(hash, db)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ tr = newtr
|
|
|
+ case opItercheckhash:
|
|
|
+ checktr, _ := New(common.Hash{}, nil)
|
|
|
+ it := tr.Iterator()
|
|
|
+ for it.Next() {
|
|
|
+ checktr.Update(it.Key, it.Value)
|
|
|
+ }
|
|
|
+ if tr.Hash() != checktr.Hash() {
|
|
|
+ fmt.Println("hashes not equal")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func TestRandom(t *testing.T) {
|
|
|
+ if err := quick.Check(runRandTest, nil); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func BenchmarkGet(b *testing.B) { benchGet(b, false) }
|