diff options
Diffstat (limited to 'c/cursor_test.go')
-rw-r--r-- | c/cursor_test.go | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/c/cursor_test.go b/c/cursor_test.go new file mode 100644 index 0000000..9e7cb1b --- /dev/null +++ b/c/cursor_test.go @@ -0,0 +1,220 @@ +package c_test + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/boltdb/bolt" + . "github.com/boltdb/bolt/c" + "github.com/stretchr/testify/assert" +) + +// Ensure that the C cursor can +func TestCursor_First(t *testing.T) { + withDB(func(db *bolt.DB) { + db.Update(func(tx *bolt.Tx) error { + b, _ := tx.CreateBucket([]byte("widgets")) + return b.Put([]byte("foo"), []byte("barz")) + }) + db.View(func(tx *bolt.Tx) error { + c := NewCursor(tx.Bucket([]byte("widgets"))) + key, value := c.First() + assert.Equal(t, []byte("foo"), key) + assert.Equal(t, []byte("barz"), value) + return nil + }) + }) +} + +// Ensure that a C cursor can seek to the appropriate keys. +func TestCursor_Seek(t *testing.T) { + withDB(func(db *bolt.DB) { + db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucket([]byte("widgets")) + assert.NoError(t, err) + assert.NoError(t, b.Put([]byte("foo"), []byte("0001"))) + assert.NoError(t, b.Put([]byte("bar"), []byte("0002"))) + assert.NoError(t, b.Put([]byte("baz"), []byte("0003"))) + _, err = b.CreateBucket([]byte("bkt")) + assert.NoError(t, err) + return nil + }) + db.View(func(tx *bolt.Tx) error { + c := NewCursor(tx.Bucket([]byte("widgets"))) + + // Exact match should go to the key. + k, v, flags := c.Seek([]byte("bar")) + assert.Equal(t, "bar", string(k)) + assert.Equal(t, "0002", string(v)) + assert.Equal(t, 0, flags) + + // Inexact match should go to the next key. + k, v, flags = c.Seek([]byte("bas")) + assert.Equal(t, "baz", string(k)) + assert.Equal(t, "0003", string(v)) + assert.Equal(t, 0, flags) + + // Low key should go to the first key. + k, v, flags = c.Seek([]byte("")) + assert.Equal(t, "bar", string(k)) + assert.Equal(t, "0002", string(v)) + assert.Equal(t, 0, flags) + + // High key should return no key. + k, v, flags = c.Seek([]byte("zzz")) + assert.Equal(t, "", string(k)) + assert.Equal(t, "", string(v)) + assert.Equal(t, 0, flags) + + // Buckets should return their key but no value. + k, v, flags = c.Seek([]byte("bkt")) + assert.Equal(t, []byte("bkt"), k) + assert.True(t, len(v) > 0) + assert.Equal(t, 1, flags) // bucketLeafFlag + + return nil + }) + }) +} + +// Ensure that a C cursor can iterate over a single root with a couple elements. +func TestCursor_Iterate_Leaf(t *testing.T) { + withDB(func(db *bolt.DB) { + db.Update(func(tx *bolt.Tx) error { + tx.CreateBucket([]byte("widgets")) + tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte{}) + tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{0}) + tx.Bucket([]byte("widgets")).Put([]byte("bar"), []byte{1}) + return nil + }) + db.View(func(tx *bolt.Tx) error { + c := NewCursor(tx.Bucket([]byte("widgets"))) + + k, v := c.First() + assert.Equal(t, string(k), "bar") + assert.Equal(t, []byte{1}, v) + + k, v = c.Next() + assert.Equal(t, string(k), "baz") + assert.Equal(t, []byte{}, v) + + k, v = c.Next() + assert.Equal(t, string(k), "foo") + assert.Equal(t, []byte{0}, v) + + k, v = c.Next() + assert.Equal(t, []byte{}, k) + assert.Equal(t, []byte{}, v) + + k, v = c.Next() + assert.Equal(t, []byte{}, k) + assert.Equal(t, []byte{}, v) + return nil + }) + }) +} + +// Ensure that a C cursor can iterate over branches and leafs. +func TestCursor_Iterate_Large(t *testing.T) { + withDB(func(db *bolt.DB) { + db.Update(func(tx *bolt.Tx) error { + b, _ := tx.CreateBucket([]byte("widgets")) + for i := 0; i < 1000; i++ { + b.Put([]byte(fmt.Sprintf("%05d", i)), []byte(fmt.Sprintf("%020d", i))) + } + return nil + }) + db.View(func(tx *bolt.Tx) error { + var index int + c := NewCursor(tx.Bucket([]byte("widgets"))) + for k, v := c.First(); len(k) > 0; k, v = c.Next() { + assert.Equal(t, fmt.Sprintf("%05d", index), string(k)) + assert.Equal(t, fmt.Sprintf("%020d", index), string(v)) + index++ + } + assert.Equal(t, 1000, index) + return nil + }) + }) +} + +// Ensure that a C cursor can seek over branches and leafs. +func TestCursor_Seek_Large(t *testing.T) { + withDB(func(db *bolt.DB) { + db.Update(func(tx *bolt.Tx) error { + b, _ := tx.CreateBucket([]byte("widgets")) + for i := 1; i < 1000; i++ { + b.Put([]byte(fmt.Sprintf("%05d\000", i*10)), []byte(fmt.Sprintf("%020d", i*10))) + } + return nil + }) + db.View(func(tx *bolt.Tx) error { + c := NewCursor(tx.Bucket([]byte("widgets"))) + + // Exact match should go to the key. + k, v, _ := c.Seek([]byte("05000\000")) + assert.Equal(t, "05000\000", string(k)) + assert.Equal(t, fmt.Sprintf("%020d", 5000), string(v)) + + // Inexact match should go to the next key. + k, v, _ = c.Seek([]byte("07495\000")) + assert.Equal(t, "07500\000", string(k)) + assert.Equal(t, fmt.Sprintf("%020d", 7500), string(v)) + + // Low key should go to the first key. + k, v, _ = c.Seek([]byte("00000\000")) + assert.Equal(t, "00010\000", string(k)) + assert.Equal(t, fmt.Sprintf("%020d", 10), string(v)) + + // High key should return no key. + k, v, _ = c.Seek([]byte("40000\000")) + assert.Equal(t, "", string(k)) + assert.Equal(t, "", string(v)) + + return nil + }) + }) +} + +// tempfile returns a temporary path. +func tempfile() string { + f, _ := ioutil.TempFile("", "bolt-c-") + f.Close() + os.Remove(f.Name()) + return f.Name() +} + +// withDB executes a function with an already opened database. +func withDB(fn func(*bolt.DB)) { + path := tempfile() + db, err := bolt.Open(path, 0666) + if err != nil { + panic("cannot open db: " + err.Error()) + } + defer os.Remove(path) + defer db.Close() + + fn(db) + + // Check database consistency after every test. + mustCheck(db) +} + +// mustCheck runs a consistency check on the database and panics if any errors are found. +func mustCheck(db *bolt.DB) { + if err := db.Check(); err != nil { + // Copy db off first. + var path = tempfile() + db.CopyFile(path, 0600) + + if errors, ok := err.(bolt.ErrorList); ok { + for _, err := range errors { + fmt.Println(err) + } + } + fmt.Println(err) + panic("check failure: " + path) + } +} |