// 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/iterator"
|
|
)
|
|
|
|
type IterAct int
|
|
|
|
func (a IterAct) String() string {
|
|
switch a {
|
|
case IterNone:
|
|
return "none"
|
|
case IterFirst:
|
|
return "first"
|
|
case IterLast:
|
|
return "last"
|
|
case IterPrev:
|
|
return "prev"
|
|
case IterNext:
|
|
return "next"
|
|
case IterSeek:
|
|
return "seek"
|
|
case IterSOI:
|
|
return "soi"
|
|
case IterEOI:
|
|
return "eoi"
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
const (
|
|
IterNone IterAct = iota
|
|
IterFirst
|
|
IterLast
|
|
IterPrev
|
|
IterNext
|
|
IterSeek
|
|
IterSOI
|
|
IterEOI
|
|
)
|
|
|
|
type IteratorTesting struct {
|
|
KeyValue
|
|
Iter iterator.Iterator
|
|
Rand *rand.Rand
|
|
PostFn func(t *IteratorTesting)
|
|
Pos int
|
|
Act, LastAct IterAct
|
|
|
|
once bool
|
|
}
|
|
|
|
func (t *IteratorTesting) init() {
|
|
if !t.once {
|
|
t.Pos = -1
|
|
t.once = true
|
|
}
|
|
}
|
|
|
|
func (t *IteratorTesting) post() {
|
|
if t.PostFn != nil {
|
|
t.PostFn(t)
|
|
}
|
|
}
|
|
|
|
func (t *IteratorTesting) setAct(act IterAct) {
|
|
t.LastAct, t.Act = t.Act, act
|
|
}
|
|
|
|
func (t *IteratorTesting) text() string {
|
|
return fmt.Sprintf("at pos %d and last action was <%v> -> <%v>", t.Pos, t.LastAct, t.Act)
|
|
}
|
|
|
|
func (t *IteratorTesting) Text() string {
|
|
return "IteratorTesting is " + t.text()
|
|
}
|
|
|
|
func (t *IteratorTesting) IsFirst() bool {
|
|
t.init()
|
|
return t.Len() > 0 && t.Pos == 0
|
|
}
|
|
|
|
func (t *IteratorTesting) IsLast() bool {
|
|
t.init()
|
|
return t.Len() > 0 && t.Pos == t.Len()-1
|
|
}
|
|
|
|
func (t *IteratorTesting) TestKV() {
|
|
t.init()
|
|
key, value := t.Index(t.Pos)
|
|
Expect(t.Iter.Key()).NotTo(BeNil())
|
|
Expect(t.Iter.Key()).Should(Equal(key), "Key is invalid, %s", t.text())
|
|
Expect(t.Iter.Value()).Should(Equal(value), "Value for key %q, %s", key, t.text())
|
|
}
|
|
|
|
func (t *IteratorTesting) First() {
|
|
t.init()
|
|
t.setAct(IterFirst)
|
|
|
|
ok := t.Iter.First()
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
if t.Len() > 0 {
|
|
t.Pos = 0
|
|
Expect(ok).Should(BeTrue(), t.Text())
|
|
t.TestKV()
|
|
} else {
|
|
t.Pos = -1
|
|
Expect(ok).ShouldNot(BeTrue(), t.Text())
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) Last() {
|
|
t.init()
|
|
t.setAct(IterLast)
|
|
|
|
ok := t.Iter.Last()
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
if t.Len() > 0 {
|
|
t.Pos = t.Len() - 1
|
|
Expect(ok).Should(BeTrue(), t.Text())
|
|
t.TestKV()
|
|
} else {
|
|
t.Pos = 0
|
|
Expect(ok).ShouldNot(BeTrue(), t.Text())
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) Next() {
|
|
t.init()
|
|
t.setAct(IterNext)
|
|
|
|
ok := t.Iter.Next()
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
if t.Pos < t.Len()-1 {
|
|
t.Pos++
|
|
Expect(ok).Should(BeTrue(), t.Text())
|
|
t.TestKV()
|
|
} else {
|
|
t.Pos = t.Len()
|
|
Expect(ok).ShouldNot(BeTrue(), t.Text())
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) Prev() {
|
|
t.init()
|
|
t.setAct(IterPrev)
|
|
|
|
ok := t.Iter.Prev()
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
if t.Pos > 0 {
|
|
t.Pos--
|
|
Expect(ok).Should(BeTrue(), t.Text())
|
|
t.TestKV()
|
|
} else {
|
|
t.Pos = -1
|
|
Expect(ok).ShouldNot(BeTrue(), t.Text())
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) Seek(i int) {
|
|
t.init()
|
|
t.setAct(IterSeek)
|
|
|
|
key, _ := t.Index(i)
|
|
oldKey, _ := t.IndexOrNil(t.Pos)
|
|
|
|
ok := t.Iter.Seek(key)
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
Expect(ok).Should(BeTrue(), fmt.Sprintf("Seek from key %q to %q, to pos %d, %s", oldKey, key, i, t.text()))
|
|
|
|
t.Pos = i
|
|
t.TestKV()
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) SeekInexact(i int) {
|
|
t.init()
|
|
t.setAct(IterSeek)
|
|
var key0 []byte
|
|
key1, _ := t.Index(i)
|
|
if i > 0 {
|
|
key0, _ = t.Index(i - 1)
|
|
}
|
|
key := BytesSeparator(key0, key1)
|
|
oldKey, _ := t.IndexOrNil(t.Pos)
|
|
|
|
ok := t.Iter.Seek(key)
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
Expect(ok).Should(BeTrue(), fmt.Sprintf("Seek from key %q to %q (%q), to pos %d, %s", oldKey, key, key1, i, t.text()))
|
|
|
|
t.Pos = i
|
|
t.TestKV()
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) SeekKey(key []byte) {
|
|
t.init()
|
|
t.setAct(IterSeek)
|
|
oldKey, _ := t.IndexOrNil(t.Pos)
|
|
i := t.Search(key)
|
|
|
|
ok := t.Iter.Seek(key)
|
|
Expect(t.Iter.Error()).ShouldNot(HaveOccurred())
|
|
if i < t.Len() {
|
|
key_, _ := t.Index(i)
|
|
Expect(ok).Should(BeTrue(), fmt.Sprintf("Seek from key %q to %q (%q), to pos %d, %s", oldKey, key, key_, i, t.text()))
|
|
t.Pos = i
|
|
t.TestKV()
|
|
} else {
|
|
Expect(ok).ShouldNot(BeTrue(), fmt.Sprintf("Seek from key %q to %q, %s", oldKey, key, t.text()))
|
|
}
|
|
|
|
t.Pos = i
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) SOI() {
|
|
t.init()
|
|
t.setAct(IterSOI)
|
|
Expect(t.Pos).Should(BeNumerically("<=", 0), t.Text())
|
|
for i := 0; i < 3; i++ {
|
|
t.Prev()
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) EOI() {
|
|
t.init()
|
|
t.setAct(IterEOI)
|
|
Expect(t.Pos).Should(BeNumerically(">=", t.Len()-1), t.Text())
|
|
for i := 0; i < 3; i++ {
|
|
t.Next()
|
|
}
|
|
t.post()
|
|
}
|
|
|
|
func (t *IteratorTesting) WalkPrev(fn func(t *IteratorTesting)) {
|
|
t.init()
|
|
for old := t.Pos; t.Pos > 0; old = t.Pos {
|
|
fn(t)
|
|
Expect(t.Pos).Should(BeNumerically("<", old), t.Text())
|
|
}
|
|
}
|
|
|
|
func (t *IteratorTesting) WalkNext(fn func(t *IteratorTesting)) {
|
|
t.init()
|
|
for old := t.Pos; t.Pos < t.Len()-1; old = t.Pos {
|
|
fn(t)
|
|
Expect(t.Pos).Should(BeNumerically(">", old), t.Text())
|
|
}
|
|
}
|
|
|
|
func (t *IteratorTesting) PrevAll() {
|
|
t.WalkPrev(func(t *IteratorTesting) {
|
|
t.Prev()
|
|
})
|
|
}
|
|
|
|
func (t *IteratorTesting) NextAll() {
|
|
t.WalkNext(func(t *IteratorTesting) {
|
|
t.Next()
|
|
})
|
|
}
|
|
|
|
func DoIteratorTesting(t *IteratorTesting) {
|
|
if t.Rand == nil {
|
|
t.Rand = NewRand()
|
|
}
|
|
t.SOI()
|
|
t.NextAll()
|
|
t.First()
|
|
t.SOI()
|
|
t.NextAll()
|
|
t.EOI()
|
|
t.PrevAll()
|
|
t.Last()
|
|
t.EOI()
|
|
t.PrevAll()
|
|
t.SOI()
|
|
|
|
t.NextAll()
|
|
t.PrevAll()
|
|
t.NextAll()
|
|
t.Last()
|
|
t.PrevAll()
|
|
t.First()
|
|
t.NextAll()
|
|
t.EOI()
|
|
|
|
ShuffledIndex(t.Rand, t.Len(), 1, func(i int) {
|
|
t.Seek(i)
|
|
})
|
|
|
|
ShuffledIndex(t.Rand, t.Len(), 1, func(i int) {
|
|
t.SeekInexact(i)
|
|
})
|
|
|
|
ShuffledIndex(t.Rand, t.Len(), 1, func(i int) {
|
|
t.Seek(i)
|
|
if i%2 != 0 {
|
|
t.PrevAll()
|
|
t.SOI()
|
|
} else {
|
|
t.NextAll()
|
|
t.EOI()
|
|
}
|
|
})
|
|
|
|
for _, key := range []string{"", "foo", "bar", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"} {
|
|
t.SeekKey([]byte(key))
|
|
}
|
|
}
|