johnsonjh/jleveldb

View on GitHub
leveldb/testutil/kvtest.go

Summary

Maintainability
A
3 hrs
Test Coverage
// Copyright © 2014, Suryandaru Triandana <syndtr@gmail.com>
// Copyright © 2021, Jeffrey H. Johnson <trnsz@pobox.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/ginkgo"
    . "github.com/onsi/gomega"

    "github.com/johnsonjh/jleveldb/leveldb/errors"
    "github.com/johnsonjh/jleveldb/leveldb/util"
)

func TestFind(db Find, kv KeyValue) {
    ShuffledIndex(nil, kv.Len(), 1, func(i int) {
        key_, key, value := kv.IndexInexact(i)

        // Using exact key.
        rkey, rvalue, err := db.TestFind(key)
        Expect(err).ShouldNot(HaveOccurred(), "Error for exact key %q", key)
        Expect(rkey).Should(Equal(key), "Key")
        Expect(rvalue).Should(Equal(value), "Value for exact key %q", key)

        // Using inexact key.
        rkey, rvalue, err = db.TestFind(key_)
        Expect(err).ShouldNot(HaveOccurred(), "Error for inexact key %q (%q)", key_, key)
        Expect(rkey).Should(Equal(key), "Key for inexact key %q (%q)", key_, key)
        Expect(rvalue).Should(Equal(value), "Value for inexact key %q (%q)", key_, key)
    })
}

func TestFindAfterLast(db Find, kv KeyValue) {
    var key []byte
    if kv.Len() > 0 {
        key_, _ := kv.Index(kv.Len() - 1)
        key = BytesAfter(key_)
    }
    rkey, _, err := db.TestFind(key)
    Expect(err).Should(HaveOccurred(), "Find for key %q yield key %q", key, rkey)
    Expect(err).Should(Equal(errors.ErrNotFound))
}

func TestGet(db Get, kv KeyValue) {
    ShuffledIndex(nil, kv.Len(), 1, func(i int) {
        key_, key, value := kv.IndexInexact(i)

        // Using exact key.
        rvalue, err := db.TestGet(key)
        Expect(err).ShouldNot(HaveOccurred(), "Error for key %q", key)
        Expect(rvalue).Should(Equal(value), "Value for key %q", key)

        // Using inexact key.
        if len(key_) > 0 {
            _, err = db.TestGet(key_)
            Expect(err).Should(HaveOccurred(), "Error for key %q", key_)
            Expect(err).Should(Equal(errors.ErrNotFound))
        }
    })
}

func TestHas(db Has, kv KeyValue) {
    ShuffledIndex(nil, kv.Len(), 1, func(i int) {
        key_, key, _ := kv.IndexInexact(i)

        // Using exact key.
        ret, err := db.TestHas(key)
        Expect(err).ShouldNot(HaveOccurred(), "Error for key %q", key)
        Expect(ret).Should(BeTrue(), "False for key %q", key)

        // Using inexact key.
        if len(key_) > 0 {
            ret, err = db.TestHas(key_)
            Expect(err).ShouldNot(HaveOccurred(), "Error for key %q", key_)
            Expect(ret).ShouldNot(BeTrue(), "True for key %q", key)
        }
    })
}

func TestIter(db NewIterator, r *util.Range, kv KeyValue) {
    iter := db.TestNewIterator(r)
    Expect(iter.Error()).ShouldNot(HaveOccurred())

    t := IteratorTesting{
        KeyValue: kv,
        Iter:     iter,
    }

    DoIteratorTesting(&t)
    iter.Release()
}

func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB, teardown func(DB)) {
    if rnd == nil {
        rnd = NewRand()
    }

    if p == nil {
        BeforeEach(func() {
            p = setup(kv)
        })
        if teardown != nil {
            AfterEach(func() {
                teardown(p)
            })
        }
    }

    It("Should find all keys with Find", func() {
        if db, ok := p.(Find); ok {
            TestFind(db, kv)
        }
    })

    It("Should return error if Find on key after the last", func() {
        if db, ok := p.(Find); ok {
            TestFindAfterLast(db, kv)
        }
    })

    It("Should only find exact key with Get", func() {
        if db, ok := p.(Get); ok {
            TestGet(db, kv)
        }
    })

    It("Should only find present key with Has", func() {
        if db, ok := p.(Has); ok {
            TestHas(db, kv)
        }
    })

    It("Should iterates and seeks correctly", func(done Done) {
        if db, ok := p.(NewIterator); ok {
            TestIter(db, nil, kv.Clone())
        }
        done <- true
    }, 30.0)

    It("Should iterates and seeks slice correctly", func(done Done) {
        if db, ok := p.(NewIterator); ok {
            RandomIndex(rnd, kv.Len(), Min(kv.Len(), 50), func(i int) {
                type slice struct {
                    r            *util.Range
                    start, limit int
                }

                key_, _, _ := kv.IndexInexact(i)
                for _, x := range []slice{
                    {&util.Range{Start: key_, Limit: nil}, i, kv.Len()},
                    {&util.Range{Start: nil, Limit: key_}, 0, i},
                } {
                    By(fmt.Sprintf("Random index of %d .. %d", x.start, x.limit), func() {
                        TestIter(db, x.r, kv.Slice(x.start, x.limit))
                    })
                }
            })
        }
        done <- true
    }, 200.0)

    It("Should iterates and seeks slice correctly", func(done Done) {
        if db, ok := p.(NewIterator); ok {
            RandomRange(rnd, kv.Len(), Min(kv.Len(), 50), func(start, limit int) {
                By(fmt.Sprintf("Random range of %d .. %d", start, limit), func() {
                    r := kv.Range(start, limit)
                    TestIter(db, &r, kv.Slice(start, limit))
                })
            })
        }
        done <- true
    }, 200.0)
}

func AllKeyValueTesting(rnd *rand.Rand, body, setup func(KeyValue) DB, teardown func(DB)) {
    Test := func(kv *KeyValue) func() {
        return func() {
            var p DB
            if setup != nil {
                Defer("setup", func() {
                    p = setup(*kv)
                })
            }
            if teardown != nil {
                Defer("teardown", func() {
                    teardown(p)
                })
            }
            if body != nil {
                p = body(*kv)
            }
            KeyValueTesting(rnd, *kv, p, func(KeyValue) DB {
                return p
            }, nil)
        }
    }

    Describe("with no key/value (empty)", Test(&KeyValue{}))
    Describe("with empty key", Test(KeyValue_EmptyKey()))
    Describe("with empty value", Test(KeyValue_EmptyValue()))
    Describe("with one key/value", Test(KeyValue_OneKeyValue()))
    Describe("with big value", Test(KeyValue_BigValue()))
    Describe("with special key", Test(KeyValue_SpecialKey()))
    Describe("with multiple key/value", Test(KeyValue_MultipleKeyValue()))
    Describe("with generated key/value 2-incr", Test(KeyValue_Generate(nil, 120, 2, 1, 50, 10, 120)))
    Describe("with generated key/value 3-incr", Test(KeyValue_Generate(nil, 120, 3, 1, 50, 10, 120)))
}