featurebase/rbf/cursor_test.go
Todd Gruben e915d75df6 remove @ from yaml
try to fix yml syntax

same

same

same

same2

same3

same4

same5

try with shell runner instead of dind

remove lattice from dockerfile

change path to bin

runs after linux arm64 build

change dockerfile path

same

same

add dir

better test coverage
2021-11-03 08:48:57 -05:00

1221 lines
29 KiB
Go

// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rbf_test
import (
"io"
"math/bits"
"math/rand"
"os"
"reflect"
"sort"
"strings"
"testing"
"github.com/molecula/featurebase/v2/rbf"
"github.com/molecula/featurebase/v2/roaring"
)
func TestCursor_FirstNext(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
} else if _, err := tx.Add("x", 0x00000001, 0x00000002, 0x00010003, 0x00030004); err != nil {
t.Fatal(err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.First(); err != nil {
t.Fatal(err)
}
if err := c.Next(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(0); got != want {
t.Fatalf("Next()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{1, 2}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
if err := c.Next(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(1); got != want {
t.Fatalf("Next()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{3}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
if err := c.Next(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(3); got != want {
t.Fatalf("Next()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{4}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
}
func TestCursor_FirstNext_Quick(t *testing.T) {
if testing.Short() {
t.Skip("-short enabled, skipping")
} else if is32Bit() {
t.Skip("32-bit build, skipping quick check tests")
}
const n = 10000
QuickCheck(t, func(t *testing.T, rand *rand.Rand) {
t.Parallel()
// Generate sorted list of values.
values := make([]uint64, rand.Intn(n))
for i := range values {
values[i] = uint64(rand.Intn(rbf.ShardWidth))
}
sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
// Insert values in random order.
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
for _, i := range rand.Perm(len(values)) {
v := values[i]
if _, err := tx.Add("x", v); err != nil {
t.Fatalf("Add(%d) i=%d err=%q", v, i, err)
}
}
// Generate unique bucketed values.
type Item struct {
key uint64
values []uint16
}
var items []Item
m := make(map[uint64]struct{})
for _, v := range values {
if _, ok := m[v]; ok {
continue
}
m[v] = struct{}{}
hi, lo := highbits(v), lowbits(v)
if len(items) == 0 || items[len(items)-1].key != hi {
items = append(items, Item{key: hi})
}
item := &items[len(items)-1]
item.values = append(item.values, lo)
}
// Verify cursor returns correct value groups.
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.First(); err != nil {
t.Fatal(err)
}
for _, item := range items {
if err := c.Next(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), item.key; got != want {
t.Fatalf("Key()=%d, want %d", got, want)
} else if got, want := c.Values(), item.values; !reflect.DeepEqual(got, want) {
t.Fatalf("len(Values())=%v/%#v, want %v/%#v", len(got), got, len(want), want)
}
}
})
}
func TestCursor_LastPrev(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
} else if _, err := tx.Add("x", 0x00000001, 0x00000002, 0x00010003, 0x00030004); err != nil {
t.Fatal(err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.Last(); err != nil {
t.Fatal(err)
}
if err := c.Prev(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(3); got != want {
t.Fatalf("Prev()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{4}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
if err := c.Prev(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(1); got != want {
t.Fatalf("Prev()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{3}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
if err := c.Prev(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), uint64(0); got != want {
t.Fatalf("Prev()=%d, want %d", got, want)
} else if got, want := c.Values(), []uint16{1, 2}; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
}
func TestCursor_LastPrev_Quick(t *testing.T) {
if testing.Short() {
t.Skip("-short enabled, skipping")
} else if is32Bit() {
t.Skip("32-bit build, skipping quick check tests")
}
const n = 10000
QuickCheck(t, func(t *testing.T, rand *rand.Rand) {
t.Parallel()
// Generate sorted list of values.
values := make([]uint64, n)
for i := range values {
values[i] = uint64(rand.Intn(rbf.ShardWidth))
}
sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
// Insert values in random order.
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
for _, i := range rand.Perm(len(values)) {
v := values[i]
if _, err := tx.Add("x", v); err != nil {
t.Fatalf("Add(%d) i=%d err=%q", v, i, err)
}
}
// Generate unique bucketed values.
type Item struct {
key uint64
values []uint16
}
var items []Item
m := make(map[uint64]struct{})
for _, v := range values {
if _, ok := m[v]; ok {
continue
}
m[v] = struct{}{}
hi, lo := highbits(v), lowbits(v)
if len(items) == 0 || items[len(items)-1].key != hi {
items = append(items, Item{key: hi})
}
item := &items[len(items)-1]
item.values = append(item.values, lo)
}
// Verify cursor returns correct value groups.
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.Last(); err != nil {
t.Fatal(err)
}
for i := len(items) - 1; i >= 0; i-- {
if err := c.Prev(); err != nil {
t.Fatal(err)
} else if got, want := c.Key(), items[i].key; got != want {
t.Fatalf("Key()=%d, want %d", got, want)
} else if got, want := c.Values(), items[i].values; !reflect.DeepEqual(got, want) {
t.Fatalf("len(Values())=%v, want %v", len(got), len(want))
}
}
})
}
func makeBitmap(bit []uint16) (n int, ret []uint64) {
ret = make([]uint64, 1024)
for _, v := range bit {
ret[v/64] |= 1 << uint64(v%64)
}
n = 0
for _, v := range ret {
n += bits.OnesCount64(v)
}
return
}
func TestCursor_AddRoaring(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
fieldview string
rb *roaring.Bitmap
wantChanged bool
wantErr bool
}{{
name: "no view",
fieldview: "a/standard",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
return bm
}(),
wantChanged: false,
wantErr: false},
{
name: "initial Array",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerArray([]uint16{1, 2}))
return bm
}(),
wantChanged: true,
wantErr: false}, {
name: "initial RLE",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(1, roaring.NewContainerRun([]roaring.Interval16{{Start: 10, Last: 20000}}))
return bm
}(),
wantChanged: true,
wantErr: false},
{
name: "initial Bitmap",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(3, roaring.NewContainerBitmap(makeBitmap([]uint16{4, 8, 12})))
return bm
}(),
wantChanged: true,
wantErr: false},
{
name: "merge Array exist",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerArray([]uint16{1, 2}))
return bm
}(),
wantChanged: false,
wantErr: false}, {
name: "merge Array present",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerArray([]uint16{3, 4}))
return bm
}(),
wantChanged: true,
wantErr: false},
{
name: "merge Bitmap exist",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(3, roaring.NewContainerBitmap(makeBitmap([]uint16{4, 8, 12})))
return bm
}(),
wantChanged: false,
wantErr: false},
{
name: "merge Bitmap ",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(3, roaring.NewContainerBitmap(makeBitmap([]uint16{75})))
return bm
}(),
wantChanged: true,
wantErr: false},
{
name: "merge BitmapArray ",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerBitmap(makeBitmap([]uint16{75})))
return bm
}(),
wantChanged: true,
wantErr: false}, {
name: "too Big Array ",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
items := make([]uint16, rbf.ArrayMaxSize+2)
for i := 0; i < len(items); i++ {
items[i] = uint16(i)
}
bm.Put(10, roaring.NewContainerArray(items))
return bm
}(),
wantChanged: true,
wantErr: false},
{
name: "too Big RLE ",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
items := make([]roaring.Interval16, rbf.RLEMaxSize+2)
x := uint16(0)
for i := 0; i < len(items); i++ {
v := roaring.Interval16{Start: x, Last: x + 1}
x += 3
items[i] = v
}
bm.Put(10, roaring.NewContainerRun(items))
return bm
}(),
wantChanged: true,
wantErr: false}, {
name: "empty container ",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(11, roaring.NewContainerArray([]uint16{}))
return bm
}(),
wantChanged: false,
wantErr: false},
{
name: "merge RLE",
fieldview: "x",
rb: func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(1, roaring.NewContainerRun([]roaring.Interval16{{Start: 1, Last: 12}}))
return bm
}(),
wantChanged: true,
wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotChanged, err := tx.AddRoaring(tt.fieldview, tt.rb)
if (err != nil) != tt.wantErr {
t.Errorf("Cursor.AddRoaring() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotChanged != tt.wantChanged {
t.Errorf("Cursor.AddRoaring() = %v, want %v", gotChanged, tt.wantChanged)
}
})
}
}
func TestCursor_RLETesting(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
//setup RLE
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
rb := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerRun([]roaring.Interval16{{Start: 10, Last: 11}}))
return bm
}()
_, err := tx.AddRoaring("x", rb)
if err != nil {
t.Errorf("Add Roaring Failed %v", err)
}
//
tests := []struct {
name string
args []uint64
want []uint16
wantChanged bool
wantErr bool
}{{
name: "update run at Last",
args: []uint64{0x0000000c},
want: []uint16{0x0000000a, 0x0000000b, 0x0000000c},
wantChanged: true,
wantErr: false,
},
{
name: "update run at begining",
args: []uint64{0x00000001, 0x00000002},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c},
wantChanged: true,
wantErr: false,
},
{
name: "add run at end",
args: []uint64{0x0000000f},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000f},
wantChanged: true,
wantErr: false,
},
{
name: "no change",
args: []uint64{0x0000000b},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000f},
wantChanged: false,
wantErr: false,
}, {
name: "update start",
args: []uint64{0x0000000e},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000e, 0x0000000f},
wantChanged: true,
wantErr: false,
}, {
name: "combine",
args: []uint64{0x0000000d},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d, 0x0000000e, 0x0000000f},
wantChanged: true,
wantErr: false,
}, {
name: "add end",
args: []uint64{0x0000ffff},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d, 0x0000000e, 0x0000000f, 0x0000ffff},
wantChanged: true,
wantErr: false,
},
{
name: "overflow container",
args: []uint64{0x00010000},
want: []uint16{0x00000001, 0x00000002, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d, 0x0000000e, 0x0000000f, 0x0000ffff},
wantChanged: true,
wantErr: false,
},
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
changeCount, err := tx.Add("x", tt.args...)
if tt.wantErr && err == nil {
t.Errorf("No Error %v", err)
} else if tt.wantChanged && changeCount == 0 {
t.Errorf("No Change %v", err)
} else if err != nil {
t.Fatal(err)
} else if err := c.First(); err != nil {
t.Fatal(err)
}
if got, want := c.Values(), tt.want; !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
})
}
t.Run("overflow followup", func(t *testing.T) {
//verify than next container got created and is valid
if err := c.Next(); err != nil { //skip the buffered?
t.Fatal(err)
}
if err := c.Next(); err != nil {
t.Fatal(err)
}
want := []uint16{0}
got := c.Values()
if !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
if g, w := c.Key(), uint64(1); !reflect.DeepEqual(g, w) {
t.Fatalf("Key()=%#v, want %#v", g, w)
}
})
}
func TestCursor_RLEConversion(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
//setup RLE with full container
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
exp := 0 // expected count of BitN
want := make([]uint16, 0, rbf.ArrayMaxSize)
rb := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
runs := make([]roaring.Interval16, rbf.RLEMaxSize)
x := uint16(1)
for i := range runs {
runs[i] = roaring.Interval16{Start: x, Last: x + 1}
want = append(want, x)
want = append(want, x+1)
x += 3
exp += 2
}
bm.Put(0, roaring.NewContainerRun(runs))
return bm
}()
_, err := tx.AddRoaring("x", rb)
if err != nil {
t.Errorf("Add Roaring Failed %v", err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.First(); err != nil {
t.Fatal(err)
}
if c.CurrentPageType() != rbf.ContainerTypeRLE {
t.Fatalf("Should Be RLE but is: %v\n", c.CurrentPageType())
}
exists, err := c.Contains(0x7)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if !exists {
t.Fatalf("Should Contain %v", 0x7)
}
exists, err = c.Contains(0x6)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if exists {
t.Fatalf("Should Not Contain %v", 0x6)
}
c.DebugSlowCheckAllPages()
//i := 0
for x := uint64(65408); x < 65536; x++ {
_, err = tx.Add("x", x)
if err != nil {
panic(err)
}
//vv("on i=%v, x = %v", i, x)
c.DebugSlowCheckAllPages()
}
//add a few bits to create another run
_, err = tx.Add("x",
func() []uint64 {
r := make([]uint64, 0, 128)
for x := uint64(65408); x < 65536; x++ {
r = append(r, x)
want = append(want, uint16(x))
}
return r
}()...)
if err != nil {
t.Fatalf("ERR adding bits: %v\n", err)
}
if err := c.First(); err != nil {
t.Fatal(err)
}
c.DebugSlowCheckAllPages()
//vv("past 2nd check")
got, want := c.Values(), want
if !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
// Split a run.
// There's a run at the end from 0xff80 - 0xffff (65408 - 65535).
// Split it in half.
removeMe := uint64(65471)
_, err = c.Remove(removeMe)
if err != nil {
t.Fatalf("remove failed")
}
got2 := c.Values()
target := uint16(removeMe)
for _, v := range got2 {
if v == target {
t.Fatalf("removal of %v from rle did not succeed", removeMe)
}
}
c.DebugSlowCheckAllPages()
// add it back
_, err = c.Add(removeMe)
if err != nil {
t.Fatalf("add failed")
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("Values()=%#v, want %#v", got, want)
}
c.DebugSlowCheckAllPages()
}
// test shrinking from bitmap down to array when a bit is removed
func TestCursor_BitmapToArrayConversion(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
// Setup a Bitmap with more than ArrayMaxSize, then
// delete bits to down to below ArrayMaxSize, and
// verify the container type got converted to an array
// and that the BitN and ElemN are always correct.
//
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
// start with n = 4084 bits, over ArrayMaxSize
rb := roaring.NewBitmap()
bits := make([]uint64, rbf.BitmapN)
n := 0
for i := range bits {
bits[i] = 15 // ^uint64(0)
n += 4
if n > rbf.ArrayMaxSize {
break
}
}
rb.Put(0, roaring.NewContainerBitmap(n, bits))
// setup to delete random bits down
values := rb.Slice()
valmap := make(map[uint64]bool)
for _, v := range values {
valmap[v] = true
}
_, err := tx.AddRoaring("x", rb)
if err != nil {
t.Errorf("Add Roaring Failed %v", err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
} else if err := c.First(); err != nil {
t.Fatal(err)
}
if c.CurrentPageType() != rbf.ContainerTypeBitmapPtr {
t.Fatalf("Should Be BitmapPtr but is: %v\n", c.CurrentPageType())
}
exists, err := c.Contains(0x3)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if !exists {
t.Fatalf("Should Contain %v", 0x3)
}
exists, err = c.Contains(0x4)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if exists {
t.Fatalf("Should Not Contain %v", 0x4)
}
c.DebugSlowCheckAllPages()
for len(valmap) > 0 {
// pick a bit to evict
var removeMe uint64
for removeMe = range valmap {
break
}
c.DebugSlowCheckAllPages()
exists, err := c.Contains(removeMe)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if !exists {
t.Fatalf("Should Contain %v", removeMe)
}
_, err = c.Remove(removeMe)
if err != nil {
t.Fatalf("remove failed")
}
//vv("removed %v, have %v left", removeMe, len(valmap))
c.DebugSlowCheckAllPages()
exists, err = c.Contains(removeMe)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if exists {
t.Fatalf("Should NOT Contain %v", removeMe)
}
delete(valmap, removeMe)
n := len(valmap)
if n > rbf.ArrayMaxSize {
if c.CurrentPageType() != rbf.ContainerTypeBitmapPtr {
t.Fatalf("Should Be BitmapPtr but is: %v\n", c.CurrentPageType())
}
} else {
if n > 0 { // no pages if n == 0, so cannot get a CurrentPageType()
if c.CurrentPageType() == rbf.ContainerTypeBitmapPtr {
t.Fatalf("Should NOT Be BitmapPtr but is indeed: %v\n", c.CurrentPageType())
}
}
}
}
// check it is empty
va := c.Values()
if len(va) != 0 {
t.Fatalf("expected empty container, but see %v values left: '%#v'", len(va), va)
}
}
type EasyWalker struct {
tx *rbf.Tx
path strings.Builder
}
func (e *EasyWalker) Visitor(pgno uint32, records []*rbf.RootRecord) {
for _, record := range records {
e.VisitRoot(record.Pgno, record.Name)
rbf.WalkPage(e.tx, record.Pgno, e)
}
}
func (e *EasyWalker) VisitRoot(pgno uint32, name string) {
e.path.WriteString("R")
}
func (e *EasyWalker) Visit(pgno uint32, node rbf.Nodetype) {
switch node {
case rbf.Branch:
e.path.WriteString("B")
case rbf.Leaf:
e.path.WriteString("L")
case rbf.Bitmap:
e.path.WriteString("b")
}
}
func (e *EasyWalker) String() string {
return e.path.String()
}
func TestDumpDot(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
}
_, err = c.Add(1)
if err != nil {
t.Fatal(err)
}
rbf.Dumpdot(tx, 0, " ", os.Stdout)
}
func TestCursor_UpdateBranchCells(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
}
changed, err := c.Add(1)
if err != nil {
t.Fatal(err)
} else if !changed {
t.Fatal("Expected Add Change")
} else if err := c.First(); err != nil {
t.Fatal(err)
} else if got, want := c.Values(), []uint16{uint16(1)}; !reflect.DeepEqual(got, want) {
t.Fatal(err)
}
changed, err = c.Remove(1)
if err != nil {
t.Fatal(err)
} else if !changed {
t.Fatal("Expected Remove Change")
} else if err := c.First(); err != nil && err != io.EOF {
t.Fatal(err)
} else if got, want := c.Values(), ([]uint16)(nil); !reflect.DeepEqual(got, want) {
t.Fatal(err)
}
rb := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerRun([]roaring.Interval16{{Start: 1, Last: 2}}))
return bm
}()
_, err = tx.AddRoaring("x", rb)
if err != nil {
t.Fatal(err)
}
changed, err = c.Remove(2)
if changed {
if err := c.First(); err != nil && err != io.EOF {
panic(err)
}
if got, want := c.Values(), []uint16{1}; !reflect.DeepEqual(got, want) {
t.Fatal(err)
}
} else {
t.Fatal("Expected Remove Change")
}
changed, err = c.Remove(1)
if changed {
if err := c.First(); err != nil && err != io.EOF {
panic(err)
}
if got, want := c.Values(), ([]uint16)(nil); !reflect.DeepEqual(got, want) {
t.Fatal(err)
}
} else {
t.Fatal("Expected Remove Change")
}
}
func TestCursor_SplitBranchCells(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
rb := func(key uint64) *roaring.Bitmap {
bm := roaring.NewBitmap()
bits := make([]uint64, rbf.BitmapN)
n := 0
for i := range bits {
bits[i] = ^uint64(0)
n += 64
}
bm.Put(key, roaring.NewContainerBitmap(n, bits))
return bm
}
//634 == offset, 24== size of leafcell with bitmap
// measured should split at 634+(24*314)
numContainers := 314
for i := 0; i < numContainers; i++ { //need to calculate how many will force a split
b := rb(uint64(i))
if _, err := tx.AddRoaring("x", b); err != nil {
panic(err)
}
}
before := &EasyWalker{tx: tx}
rbf.WalkPage(tx, 0, before)
if before.String() != "RL" {
t.Fatalf("Expecting RL (one branch) got %v", before.String())
}
// adding one more container should split it
if _, err := tx.AddRoaring("x", rb(uint64(numContainers))); err != nil {
panic(err)
}
after := &EasyWalker{tx: tx}
rbf.WalkPage(tx, 0, after)
if after.String() != "RBLL" {
t.Fatalf("Expecting RBLL (a branch split) got %v", after.String())
}
//
c, _ := tx.Cursor("x") //added just for dot code coverage
c.Dump("ignore for coverage")
}
func TestCursor_RemoveCells(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
cur, _ := tx.Cursor("x")
rb := func(key uint64) *roaring.Bitmap {
bm := roaring.NewBitmap()
bits := make([]uint64, rbf.BitmapN)
n := 0
for i := range bits {
bits[i] = ^uint64(0)
n += 64
}
bm.Put(key, roaring.NewContainerBitmap(n, bits))
return bm
}
numContainers := 455 //enough containers to cause a split
for i := 0; i < numContainers; i++ {
b := rb(uint64(i))
if _, err := tx.AddRoaring("x", b); err != nil {
panic(err)
}
}
for i := numContainers; i >= 1; i-- {
if _, err := cur.RemoveRoaring(rb(uint64(i))); err != nil {
panic(err)
}
}
if _, err := cur.RemoveRoaring(rb(uint64(0))); err != nil {
panic(err)
}
//f, err := os.OpenFile("before.dot", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 066)
}
//These aren't test i'm just using to generate graphs to look at structure
func TestCursor_PlayContainer(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
many := func(c *rbf.Cursor, start, count uint64) {
for i := start; i < start+count; i++ {
if _, err := c.Add(i); err != nil {
panic(err)
}
}
}
cur, _ := tx.Cursor("x")
offset := uint64(0)
many(cur, 0, rbf.ArrayMaxSize+offset)
many(cur, 65536, rbf.ArrayMaxSize+offset)
/*
many(cur, 2*65536, rbf.ArrayMaxSize+offset)
many(cur, 3*65536, rbf.ArrayMaxSize) //+offset)
many(cur, 4*65536, rbf.ArrayMaxSize) //+offset)
many(cur, 5*65536, rbf.ArrayMaxSize) //+offset)
many(cur, 6*65536, 10) //+offset)
many(cur, 7*65536, 10) //+offset)
many(cur, 8*65536, rbf.ArrayMaxSize+10) //+offset)
many(cur, 9*65536, rbf.ArrayMaxSize+10) //+offset)
*/
if err := cur.First(); err != nil {
panic(err)
}
}
func TestCursor_OneBitmap(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
rb := func(key uint64) *roaring.Bitmap {
bm := roaring.NewBitmap()
bits := make([]uint64, rbf.BitmapN)
n := 0
for i := range bits {
bits[i] = ^uint64(0)
n += 64
}
bm.Put(key, roaring.NewContainerBitmap(n, bits))
return bm
}
numContainers := 4
for i := 0; i < numContainers; i++ { //need to calculate how many will force a split
b := rb(uint64(i)) // measured at i=454 seems reasonable should occur at Len(branchcells)+header >8192
if _, err := tx.AddRoaring("x", b); err != nil {
panic(err)
}
}
cur, err := tx.Cursor("x")
if err != nil {
panic(err)
}
if err := cur.First(); err != nil {
panic(err)
}
}
func TestCursor_GenerateAll(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
ar := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(11, roaring.NewContainerArray([]uint16{1, 2, 3}))
return bm
}()
if _, err := tx.AddRoaring("x", ar); err != nil {
panic(err)
}
rb := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(1, roaring.NewContainerRun([]roaring.Interval16{{Start: 1, Last: 12}}))
return bm
}()
if _, err := tx.AddRoaring("x", rb); err != nil {
panic(err)
}
bb := func() *roaring.Bitmap {
bm := roaring.NewBitmap()
bm.Put(0, roaring.NewContainerBitmap(makeBitmap([]uint16{75})))
return bm
}()
if _, err := tx.AddRoaring("x", bb); err != nil {
panic(err)
}
if err := tx.CreateBitmap("field/view/"); err != nil {
t.Fatal(err)
}
if _, err := tx.AddRoaring("field/view/", bb); err != nil {
panic(err)
}
}
// test ForEachRange handles Bitmaps, because BitmapPtr
// case was missing
func TestForEachRange(t *testing.T) {
db := MustOpenDB(t)
defer MustCloseDB(t, db)
tx := MustBegin(t, db, true)
defer tx.Rollback()
if err := tx.CreateBitmap("x"); err != nil {
t.Fatal(err)
}
rb := roaring.NewBitmap()
bits := make([]uint64, rbf.BitmapN)
n := 0
for i := range bits {
bits[i] = 15 // ^uint64(0)
n += 4
if n > rbf.ArrayMaxSize {
break
}
}
rb.Put(0, roaring.NewContainerBitmap(n, bits))
crun := roaring.NewContainerRun([]roaring.Interval16{{Start: 0, Last: 1<<16 - 1}})
rb.Put(1, crun)
rb.Put(2, roaring.NewContainerArray([]uint16{1, 1024, 1<<16 - 1}))
// setup to delete random bits down
values := rb.Slice()
valmap := make(map[uint64]bool)
for _, v := range values {
valmap[v] = true
}
_, err := tx.AddRoaring("x", rb)
if err != nil {
t.Errorf("Add Roaring Failed %v", err)
}
c, err := tx.Cursor("x")
if err != nil {
t.Fatal(err)
}
c.DebugSlowCheckAllPages()
if err := c.First(); err != nil {
t.Fatal(err)
}
exists, err := c.Contains(0x3)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if !exists {
t.Fatalf("Should Contain %v", 0x3)
}
exists, err = c.Contains(0x4)
if err != nil {
t.Fatalf("ERR:%v", err)
}
if exists {
t.Fatalf("Should Not Contain %v", 0x4)
}
c.DebugSlowCheckAllPages()
_ = tx.ForEach("x", func(i uint64) error {
delete(valmap, i)
return nil
})
// check it is empty
if len(valmap) != 0 {
t.Fatalf("expected empty container, but see %v values left: '%#v'", len(valmap), valmap)
}
}