|
|
// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package testutil
import ( "fmt" "math/rand"
. "github.com/onsi/gomega"
"github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/util" )
type DB interface{}
type Put interface { TestPut(key []byte, value []byte) error }
type Delete interface { TestDelete(key []byte) error }
type Find interface { TestFind(key []byte) (rkey, rvalue []byte, err error) }
type Get interface { TestGet(key []byte) (value []byte, err error) }
type Has interface { TestHas(key []byte) (ret bool, err error) }
type NewIterator interface { TestNewIterator(slice *util.Range) iterator.Iterator }
type DBAct int
func (a DBAct) String() string { switch a { case DBNone: return "none" case DBPut: return "put" case DBOverwrite: return "overwrite" case DBDelete: return "delete" case DBDeleteNA: return "delete_na" } return "unknown" }
const ( DBNone DBAct = iota DBPut DBOverwrite DBDelete DBDeleteNA )
type DBTesting struct { Rand *rand.Rand DB interface { Get Put Delete } PostFn func(t *DBTesting) Deleted, Present KeyValue Act, LastAct DBAct ActKey, LastActKey []byte }
func (t *DBTesting) post() { if t.PostFn != nil { t.PostFn(t) } }
func (t *DBTesting) setAct(act DBAct, key []byte) { t.LastAct, t.Act = t.Act, act t.LastActKey, t.ActKey = t.ActKey, key }
func (t *DBTesting) text() string { return fmt.Sprintf("last action was <%v> %q, <%v> %q", t.LastAct, t.LastActKey, t.Act, t.ActKey) }
func (t *DBTesting) Text() string { return "DBTesting " + t.text() }
func (t *DBTesting) TestPresentKV(key, value []byte) { rvalue, err := t.DB.TestGet(key) Expect(err).ShouldNot(HaveOccurred(), "Get on key %q, %s", key, t.text()) Expect(rvalue).Should(Equal(value), "Value for key %q, %s", key, t.text()) }
func (t *DBTesting) TestAllPresent() { t.Present.IterateShuffled(t.Rand, func(i int, key, value []byte) { t.TestPresentKV(key, value) }) }
func (t *DBTesting) TestDeletedKey(key []byte) { _, err := t.DB.TestGet(key) Expect(err).Should(Equal(errors.ErrNotFound), "Get on deleted key %q, %s", key, t.text()) }
func (t *DBTesting) TestAllDeleted() { t.Deleted.IterateShuffled(t.Rand, func(i int, key, value []byte) { t.TestDeletedKey(key) }) }
func (t *DBTesting) TestAll() { dn := t.Deleted.Len() pn := t.Present.Len() ShuffledIndex(t.Rand, dn+pn, 1, func(i int) { if i >= dn { key, value := t.Present.Index(i - dn) t.TestPresentKV(key, value) } else { t.TestDeletedKey(t.Deleted.KeyAt(i)) } }) }
func (t *DBTesting) Put(key, value []byte) { if new := t.Present.PutU(key, value); new { t.setAct(DBPut, key) } else { t.setAct(DBOverwrite, key) } t.Deleted.Delete(key) err := t.DB.TestPut(key, value) Expect(err).ShouldNot(HaveOccurred(), t.Text()) t.TestPresentKV(key, value) t.post() }
func (t *DBTesting) PutRandom() bool { if t.Deleted.Len() > 0 { i := t.Rand.Intn(t.Deleted.Len()) key, value := t.Deleted.Index(i) t.Put(key, value) return true } return false }
func (t *DBTesting) Delete(key []byte) { if exist, value := t.Present.Delete(key); exist { t.setAct(DBDelete, key) t.Deleted.PutU(key, value) } else { t.setAct(DBDeleteNA, key) } err := t.DB.TestDelete(key) Expect(err).ShouldNot(HaveOccurred(), t.Text()) t.TestDeletedKey(key) t.post() }
func (t *DBTesting) DeleteRandom() bool { if t.Present.Len() > 0 { i := t.Rand.Intn(t.Present.Len()) t.Delete(t.Present.KeyAt(i)) return true } return false }
func (t *DBTesting) RandomAct(round int) { for i := 0; i < round; i++ { if t.Rand.Int()%2 == 0 { t.PutRandom() } else { t.DeleteRandom() } } }
func DoDBTesting(t *DBTesting) { if t.Rand == nil { t.Rand = NewRand() }
t.DeleteRandom() t.PutRandom() t.DeleteRandom() t.DeleteRandom() for i := t.Deleted.Len() / 2; i >= 0; i-- { t.PutRandom() } t.RandomAct((t.Deleted.Len() + t.Present.Len()) * 10)
// Additional iterator testing
if db, ok := t.DB.(NewIterator); ok { iter := db.TestNewIterator(nil) Expect(iter.Error()).NotTo(HaveOccurred())
it := IteratorTesting{ KeyValue: t.Present, Iter: iter, }
DoIteratorTesting(&it) iter.Release() } }
|