| Index: impl/memory/gkvlite_iter_test.go
|
| diff --git a/impl/memory/gkvlite_iter_test.go b/impl/memory/gkvlite_iter_test.go
|
| index aa99b2de939c6ae6081d3feaec4a36e34c8c9c82..166bc08fff6d179473796bcbace0ffe86afe2049 100644
|
| --- a/impl/memory/gkvlite_iter_test.go
|
| +++ b/impl/memory/gkvlite_iter_test.go
|
| @@ -6,7 +6,6 @@ package memory
|
|
|
| import (
|
| "bytes"
|
| - "fmt"
|
| "testing"
|
|
|
| "github.com/luci/gkvlite"
|
| @@ -14,25 +13,23 @@ import (
|
| . "github.com/smartystreets/goconvey/convey"
|
| )
|
|
|
| -func TestIterator(t *testing.T) {
|
| - t.Parallel()
|
| +func mkNum(n int64) []byte {
|
| + buf := &bytes.Buffer{}
|
| + _, err := cmpbin.WriteInt(buf, n)
|
| + memoryCorruption(err)
|
|
|
| - mkNum := func(n int64) []byte {
|
| - buf := &bytes.Buffer{}
|
| - _, err := cmpbin.WriteInt(buf, n)
|
| - if err != nil {
|
| - panic(fmt.Errorf("your RAM is busted: %s", err))
|
| - }
|
| - return buf.Bytes()
|
| - }
|
| + return buf.Bytes()
|
| +}
|
|
|
| - getNum := func(data []byte) int64 {
|
| - ret, _, err := cmpbin.ReadInt(bytes.NewBuffer(data))
|
| - if err != nil {
|
| - panic(fmt.Errorf("your RAM is (probably) busted: %s", err))
|
| - }
|
| - return ret
|
| - }
|
| +func readNum(data []byte) int64 {
|
| + ret, _, err := cmpbin.ReadInt(bytes.NewBuffer(data))
|
| + memoryCorruption(err)
|
| +
|
| + return ret
|
| +}
|
| +
|
| +func TestIterator(t *testing.T) {
|
| + t.Parallel()
|
|
|
| s := newMemStore()
|
| c := s.SetCollection("zup", nil)
|
| @@ -43,65 +40,88 @@ func TestIterator(t *testing.T) {
|
| prev = data
|
| }
|
|
|
| - get := func(c C, t *iterator) int64 {
|
| - ret := int64(0)
|
| + get := func(c C, t *iterator) interface{} {
|
| + ret := interface{}(nil)
|
| t.next(nil, func(i *gkvlite.Item) {
|
| - c.So(i, ShouldNotBeNil)
|
| - ret = getNum(i.Key)
|
| + if i != nil {
|
| + ret = readNum(i.Key)
|
| + }
|
| })
|
| return ret
|
| }
|
|
|
| - skipGet := func(c C, t *iterator, skipTo int64) int64 {
|
| - ret := int64(0)
|
| + skipGet := func(c C, t *iterator, skipTo int64) interface{} {
|
| + ret := interface{}(nil)
|
| t.next(mkNum(skipTo), func(i *gkvlite.Item) {
|
| - c.So(i, ShouldNotBeNil)
|
| - ret = getNum(i.Key)
|
| + if i != nil {
|
| + ret = readNum(i.Key)
|
| + }
|
| })
|
| return ret
|
| }
|
|
|
| Convey("Test iterator", t, func() {
|
| Convey("start at nil", func(ctx C) {
|
| - t := newIterable(c, nil)
|
| + t := (&iterDefinition{c: c}).mkIter()
|
| + defer t.stop()
|
| So(get(ctx, t), ShouldEqual, 5)
|
| So(get(ctx, t), ShouldEqual, 6)
|
| So(get(ctx, t), ShouldEqual, 7)
|
|
|
| - Convey("And can skip", func() {
|
| + Convey("And can skip", func(ctx C) {
|
| So(skipGet(ctx, t, 10), ShouldEqual, 10)
|
| So(get(ctx, t), ShouldEqual, 11)
|
|
|
| - Convey("But not forever", func(c C) {
|
| + Convey("But not forever", func(ctx C) {
|
| t.next(mkNum(200), func(i *gkvlite.Item) {
|
| - c.So(i, ShouldBeNil)
|
| + ctx.So(i, ShouldBeNil)
|
| })
|
| t.next(nil, func(i *gkvlite.Item) {
|
| - c.So(i, ShouldBeNil)
|
| + ctx.So(i, ShouldBeNil)
|
| })
|
| })
|
| })
|
|
|
| - Convey("Can stop", func(c C) {
|
| + Convey("Can iterate explicitly", func(ctx C) {
|
| + So(skipGet(ctx, t, 7), ShouldEqual, 8)
|
| + So(skipGet(ctx, t, 8), ShouldEqual, 9)
|
| +
|
| + // Giving the immediately next key doesn't cause an internal reset.
|
| + So(skipGet(ctx, t, 10), ShouldEqual, 10)
|
| + })
|
| +
|
| + Convey("Can stop", func(ctx C) {
|
| t.stop()
|
| t.next(mkNum(200), func(i *gkvlite.Item) {
|
| - c.So(i, ShouldBeNil)
|
| + ctx.So(i, ShouldBeNil)
|
| })
|
| t.next(nil, func(i *gkvlite.Item) {
|
| - c.So(i, ShouldBeNil)
|
| + ctx.So(i, ShouldBeNil)
|
| })
|
| So(t.stop, ShouldNotPanic)
|
| })
|
|
|
| - Convey("Going backwards is fine", func(c C) {
|
| - So(skipGet(ctx, t, 3), ShouldEqual, 5)
|
| - So(get(ctx, t), ShouldEqual, 6)
|
| + Convey("Going backwards is ignored", func(ctx C) {
|
| + So(skipGet(ctx, t, 3), ShouldEqual, 8)
|
| + So(get(ctx, t), ShouldEqual, 9)
|
| + So(skipGet(ctx, t, 20), ShouldEqual, 20)
|
| + So(get(ctx, t), ShouldEqual, 21)
|
| + })
|
| +
|
| + Convey("will stop at the end of the list", func(ctx C) {
|
| + So(skipGet(ctx, t, 95), ShouldEqual, 95)
|
| + So(get(ctx, t), ShouldEqual, 96)
|
| + So(get(ctx, t), ShouldEqual, 97)
|
| + So(get(ctx, t), ShouldEqual, 98)
|
| + So(get(ctx, t), ShouldEqual, 99)
|
| + So(get(ctx, t), ShouldBeNil)
|
| + So(get(ctx, t), ShouldBeNil)
|
| })
|
| })
|
|
|
| Convey("can have caps on both sides", func(ctx C) {
|
| - t := newIterable(c, mkNum(25))
|
| - So(skipGet(ctx, t, 20), ShouldEqual, 20)
|
| + t := (&iterDefinition{c: c, start: mkNum(20), end: mkNum(25)}).mkIter()
|
| + So(get(ctx, t), ShouldEqual, 20)
|
| So(get(ctx, t), ShouldEqual, 21)
|
| So(get(ctx, t), ShouldEqual, 22)
|
| So(get(ctx, t), ShouldEqual, 23)
|
| @@ -110,5 +130,149 @@ func TestIterator(t *testing.T) {
|
| ctx.So(i, ShouldBeNil)
|
| })
|
| })
|
| +
|
| + Convey("can skip over starting cap", func(ctx C) {
|
| + t := (&iterDefinition{c: c, start: mkNum(20), end: mkNum(25)}).mkIter()
|
| + So(skipGet(ctx, t, 22), ShouldEqual, 22)
|
| + So(get(ctx, t), ShouldEqual, 23)
|
| + So(get(ctx, t), ShouldEqual, 24)
|
| + t.next(nil, func(i *gkvlite.Item) {
|
| + ctx.So(i, ShouldBeNil)
|
| + })
|
| + })
|
| +
|
| + })
|
| +}
|
| +
|
| +func TestMultiIteratorSimple(t *testing.T) {
|
| + t.Parallel()
|
| +
|
| + // Simulate an index with 2 columns (int and int).
|
| + vals := [][]int64{
|
| + {1, 0},
|
| + {1, 2},
|
| + {1, 4},
|
| + {1, 7},
|
| + {1, 9},
|
| + {3, 10},
|
| + {3, 11},
|
| + }
|
| +
|
| + valBytes := make([][]byte, len(vals))
|
| + for i, nms := range vals {
|
| + numbs := make([][]byte, len(nms))
|
| + for j, n := range nms {
|
| + numbs[j] = mkNum(n)
|
| + }
|
| + valBytes[i] = bjoin(numbs...)
|
| + }
|
| +
|
| + otherVals := [][]int64{
|
| + {3, 0},
|
| + {4, 10},
|
| + {19, 7},
|
| + {20, 2},
|
| + {20, 3},
|
| + {20, 4},
|
| + {20, 8},
|
| + {20, 11},
|
| + }
|
| +
|
| + otherValBytes := make([][]byte, len(otherVals))
|
| + for i, nms := range otherVals {
|
| + numbs := make([][]byte, len(nms))
|
| + for i, n := range nms {
|
| + numbs[i] = mkNum(n)
|
| + }
|
| + otherValBytes[i] = bjoin(numbs...)
|
| + }
|
| +
|
| + Convey("Test MultiIterator", t, func() {
|
| + s := newMemStore()
|
| + c := s.SetCollection("zup1", nil)
|
| + for _, row := range valBytes {
|
| + c.Set(row, []byte{})
|
| + }
|
| + c2 := s.SetCollection("zup2", nil)
|
| + for _, row := range otherValBytes {
|
| + c2.Set(row, []byte{})
|
| + }
|
| +
|
| + Convey("can join the same collection twice", func() {
|
| + // get just the (1, *)
|
| + // starting at (1, 2) (i.e. >= 2)
|
| + // ending at (1, 4) (i.e. < 7)
|
| + defs := []*iterDefinition{
|
| + {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1)), start: mkNum(2), end: mkNum(7)},
|
| + {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1)), start: mkNum(2), end: mkNum(7)},
|
| + }
|
| +
|
| + i := 1
|
| + multiIterate(defs, func(suffix []byte) bool {
|
| + So(readNum(suffix), ShouldEqual, vals[i][1])
|
| + i++
|
| + return true
|
| + })
|
| +
|
| + So(i, ShouldEqual, 3)
|
| + })
|
| +
|
| + Convey("can make empty iteration", func() {
|
| + // get just the (20, *) (doesn't exist)
|
| + defs := []*iterDefinition{
|
| + {c: c, prefix: mkNum(20)},
|
| + {c: c, prefix: mkNum(20)},
|
| + }
|
| +
|
| + i := 0
|
| + multiIterate(defs, func(suffix []byte) bool {
|
| + panic("never")
|
| + })
|
| +
|
| + So(i, ShouldEqual, 0)
|
| + })
|
| +
|
| + Convey("can join (other, val, val)", func() {
|
| + // 'other' must start with 20, 'vals' must start with 1
|
| + // no range constraints
|
| + defs := []*iterDefinition{
|
| + {c: c2, prefix: mkNum(20)},
|
| + {c: c, prefix: mkNum(1)},
|
| + {c: c, prefix: mkNum(1)},
|
| + }
|
| +
|
| + expect := []int64{2, 4}
|
| + i := 0
|
| + multiIterate(defs, func(suffix []byte) bool {
|
| + So(readNum(suffix), ShouldEqual, expect[i])
|
| + i++
|
| + return true
|
| + })
|
| + })
|
| +
|
| + Convey("Can stop early", func() {
|
| + defs := []*iterDefinition{
|
| + {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1))},
|
| + {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1))},
|
| + }
|
| +
|
| + i := 0
|
| + multiIterate(defs, func(suffix []byte) bool {
|
| + So(readNum(suffix), ShouldEqual, vals[i][1])
|
| + i++
|
| + return true
|
| + })
|
| + So(i, ShouldEqual, 5)
|
| +
|
| + i = 0
|
| + multiIterate(defs, func(suffix []byte) bool {
|
| + So(readNum(suffix), ShouldEqual, vals[i][1])
|
| + i++
|
| + return false
|
| + })
|
| + So(i, ShouldEqual, 1)
|
| + })
|
| +
|
| })
|
| +
|
| }
|
|
|