lbcd/database/internal/treap/treapiter_test.go
Dave Collins b580cdb7d3 database: Replace with new version.
This commit removes the old database package, moves the new package into
its place, and updates all imports accordingly.
2016-04-12 14:55:15 -05:00

720 lines
21 KiB
Go

// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package treap
import (
"bytes"
"encoding/binary"
"testing"
)
// TestMutableIterator ensures that the general behavior of mutable treap
// iterators is as expected including tests for first, last, ordered and reverse
// ordered iteration, limiting the range, seeking, and initially unpositioned.
func TestMutableIterator(t *testing.T) {
t.Parallel()
tests := []struct {
numKeys int
step int
startKey []byte
limitKey []byte
expectedFirst []byte
expectedLast []byte
seekKey []byte
expectedSeek []byte
}{
// No range limits. Values are the set (0, 1, 2, ..., 49).
// Seek existing value.
{
numKeys: 50,
step: 1,
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(49),
seekKey: serializeUint32(12),
expectedSeek: serializeUint32(12),
},
// Limited to range [24, end]. Values are the set
// (0, 2, 4, ..., 48). Seek value that doesn't exist and is
// greater than largest existing key.
{
numKeys: 50,
step: 2,
startKey: serializeUint32(24),
expectedFirst: serializeUint32(24),
expectedLast: serializeUint32(48),
seekKey: serializeUint32(49),
expectedSeek: nil,
},
// Limited to range [start, 25). Values are the set
// (0, 3, 6, ..., 48). Seek value that doesn't exist but is
// before an existing value within the range.
{
numKeys: 50,
step: 3,
limitKey: serializeUint32(25),
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(24),
seekKey: serializeUint32(17),
expectedSeek: serializeUint32(18),
},
// Limited to range [10, 21). Values are the set
// (0, 4, ..., 48). Seek value that exists, but is before the
// minimum allowed range.
{
numKeys: 50,
step: 4,
startKey: serializeUint32(10),
limitKey: serializeUint32(21),
expectedFirst: serializeUint32(12),
expectedLast: serializeUint32(20),
seekKey: serializeUint32(4),
expectedSeek: nil,
},
// Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}).
// Since it's a bytewise compare, {0,0,0,...} < {0,0,1}.
// Seek existing value within the allowed range.
{
numKeys: 300,
step: 1,
startKey: []byte{0x00, 0x00, 0x00},
limitKey: []byte{0x00, 0x00, 0x01},
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(255),
seekKey: serializeUint32(100),
expectedSeek: serializeUint32(100),
},
}
testLoop:
for i, test := range tests {
// Insert a bunch of keys.
testTreap := NewMutable()
for i := 0; i < test.numKeys; i += test.step {
key := serializeUint32(uint32(i))
testTreap.Put(key, key)
}
// Create new iterator limited by the test params.
iter := testTreap.Iterator(test.startKey, test.limitKey)
// Ensure the first item is accurate.
hasFirst := iter.First()
if !hasFirst && test.expectedFirst != nil {
t.Errorf("First #%d: unexpected exhausted iterator", i)
continue
}
gotKey := iter.Key()
if !bytes.Equal(gotKey, test.expectedFirst) {
t.Errorf("First.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedFirst)
continue
}
gotVal := iter.Value()
if !bytes.Equal(gotVal, test.expectedFirst) {
t.Errorf("First.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedFirst)
continue
}
// Ensure the iterator gives the expected items in order.
curNum := binary.BigEndian.Uint32(test.expectedFirst)
for iter.Next() {
curNum += uint32(test.step)
// Ensure key is as expected.
gotKey := iter.Key()
expectedKey := serializeUint32(curNum)
if !bytes.Equal(gotKey, expectedKey) {
t.Errorf("iter.Key #%d (%d): unexpected key - "+
"got %x, want %x", i, curNum, gotKey,
expectedKey)
continue testLoop
}
// Ensure value is as expected.
gotVal := iter.Value()
if !bytes.Equal(gotVal, expectedKey) {
t.Errorf("iter.Value #%d (%d): unexpected "+
"value - got %x, want %x", i, curNum,
gotVal, expectedKey)
continue testLoop
}
}
// Ensure iterator is exhausted.
if iter.Valid() {
t.Errorf("Valid #%d: iterator should be exhausted", i)
continue
}
// Ensure the last item is accurate.
hasLast := iter.Last()
if !hasLast && test.expectedLast != nil {
t.Errorf("Last #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedLast) {
t.Errorf("Last.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedLast)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedLast) {
t.Errorf("Last.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedLast)
continue
}
// Ensure the iterator gives the expected items in reverse
// order.
curNum = binary.BigEndian.Uint32(test.expectedLast)
for iter.Prev() {
curNum -= uint32(test.step)
// Ensure key is as expected.
gotKey := iter.Key()
expectedKey := serializeUint32(curNum)
if !bytes.Equal(gotKey, expectedKey) {
t.Errorf("iter.Key #%d (%d): unexpected key - "+
"got %x, want %x", i, curNum, gotKey,
expectedKey)
continue testLoop
}
// Ensure value is as expected.
gotVal := iter.Value()
if !bytes.Equal(gotVal, expectedKey) {
t.Errorf("iter.Value #%d (%d): unexpected "+
"value - got %x, want %x", i, curNum,
gotVal, expectedKey)
continue testLoop
}
}
// Ensure iterator is exhausted.
if iter.Valid() {
t.Errorf("Valid #%d: iterator should be exhausted", i)
continue
}
// Seek to the provided key.
seekValid := iter.Seek(test.seekKey)
if !seekValid && test.expectedSeek != nil {
t.Errorf("Seek #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedSeek) {
t.Errorf("Seek.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedSeek)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedSeek) {
t.Errorf("Seek.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedSeek)
continue
}
// Recreate the iterator and ensure calling Next on it before it
// has been positioned gives the first element.
iter = testTreap.Iterator(test.startKey, test.limitKey)
hasNext := iter.Next()
if !hasNext && test.expectedFirst != nil {
t.Errorf("Next #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedFirst) {
t.Errorf("Next.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedFirst)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedFirst) {
t.Errorf("Next.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedFirst)
continue
}
// Recreate the iterator and ensure calling Prev on it before it
// has been positioned gives the first element.
iter = testTreap.Iterator(test.startKey, test.limitKey)
hasPrev := iter.Prev()
if !hasPrev && test.expectedLast != nil {
t.Errorf("Prev #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedLast) {
t.Errorf("Prev.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedLast)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedLast) {
t.Errorf("Next.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedLast)
continue
}
}
}
// TestMutableEmptyIterator ensures that the various functions behave as
// expected when a mutable treap is empty.
func TestMutableEmptyIterator(t *testing.T) {
t.Parallel()
// Create iterator against empty treap.
testTreap := NewMutable()
iter := testTreap.Iterator(nil, nil)
// Ensure Valid on empty iterator reports it as exhausted.
if iter.Valid() {
t.Fatal("Valid: iterator should be exhausted")
}
// Ensure First and Last on empty iterator report it as exhausted.
if iter.First() {
t.Fatal("First: iterator should be exhausted")
}
if iter.Last() {
t.Fatal("Last: iterator should be exhausted")
}
// Ensure Next and Prev on empty iterator report it as exhausted.
if iter.Next() {
t.Fatal("Next: iterator should be exhausted")
}
if iter.Prev() {
t.Fatal("Prev: iterator should be exhausted")
}
// Ensure Key and Value on empty iterator are nil.
if gotKey := iter.Key(); gotKey != nil {
t.Fatalf("Key: should be nil - got %q", gotKey)
}
if gotVal := iter.Value(); gotVal != nil {
t.Fatalf("Value: should be nil - got %q", gotVal)
}
// Ensure Next and Prev report exhausted after forcing a reseek on an
// empty iterator.
iter.ForceReseek()
if iter.Next() {
t.Fatal("Next: iterator should be exhausted")
}
iter.ForceReseek()
if iter.Prev() {
t.Fatal("Prev: iterator should be exhausted")
}
}
// TestIteratorUpdates ensures that issuing a call to ForceReseek on an iterator
// that had the underlying mutable treap updated works as expected.
func TestIteratorUpdates(t *testing.T) {
t.Parallel()
// Create a new treap with various values inserted in no particular
// order. The resulting keys are the set (2, 4, 7, 11, 18, 25).
testTreap := NewMutable()
testTreap.Put(serializeUint32(7), nil)
testTreap.Put(serializeUint32(2), nil)
testTreap.Put(serializeUint32(18), nil)
testTreap.Put(serializeUint32(11), nil)
testTreap.Put(serializeUint32(25), nil)
testTreap.Put(serializeUint32(4), nil)
// Create an iterator against the treap with a range that excludes the
// lowest and highest entries. The limited set is then (4, 7, 11, 18)
iter := testTreap.Iterator(serializeUint32(3), serializeUint32(25))
// Delete a key from the middle of the range and notify the iterator to
// force a reseek.
testTreap.Delete(serializeUint32(11))
iter.ForceReseek()
// Ensure that calling Next on the iterator after the forced reseek
// gives the expected key. The limited set of keys at this point is
// (4, 7, 18) and the iterator has not yet been positioned.
if !iter.Next() {
t.Fatal("ForceReseek.Next: unexpected exhausted iterator")
}
wantKey := serializeUint32(4)
gotKey := iter.Key()
if !bytes.Equal(gotKey, wantKey) {
t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x",
gotKey, wantKey)
}
// Delete the key the iterator is currently position at and notify the
// iterator to force a reseek.
testTreap.Delete(serializeUint32(4))
iter.ForceReseek()
// Ensure that calling Next on the iterator after the forced reseek
// gives the expected key. The limited set of keys at this point is
// (7, 18) and the iterator is positioned at a deleted entry before 7.
if !iter.Next() {
t.Fatal("ForceReseek.Next: unexpected exhausted iterator")
}
wantKey = serializeUint32(7)
gotKey = iter.Key()
if !bytes.Equal(gotKey, wantKey) {
t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x",
gotKey, wantKey)
}
// Add a key before the current key the iterator is position at and
// notify the iterator to force a reseek.
testTreap.Put(serializeUint32(4), nil)
iter.ForceReseek()
// Ensure that calling Prev on the iterator after the forced reseek
// gives the expected key. The limited set of keys at this point is
// (4, 7, 18) and the iterator is positioned at 7.
if !iter.Prev() {
t.Fatal("ForceReseek.Prev: unexpected exhausted iterator")
}
wantKey = serializeUint32(4)
gotKey = iter.Key()
if !bytes.Equal(gotKey, wantKey) {
t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x",
gotKey, wantKey)
}
// Delete the next key the iterator would ordinarily move to then notify
// the iterator to force a reseek.
testTreap.Delete(serializeUint32(7))
iter.ForceReseek()
// Ensure that calling Next on the iterator after the forced reseek
// gives the expected key. The limited set of keys at this point is
// (4, 18) and the iterator is positioned at 4.
if !iter.Next() {
t.Fatal("ForceReseek.Next: unexpected exhausted iterator")
}
wantKey = serializeUint32(18)
gotKey = iter.Key()
if !bytes.Equal(gotKey, wantKey) {
t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x",
gotKey, wantKey)
}
}
// TestImmutableIterator ensures that the general behavior of immutable treap
// iterators is as expected including tests for first, last, ordered and reverse
// ordered iteration, limiting the range, seeking, and initially unpositioned.
func TestImmutableIterator(t *testing.T) {
t.Parallel()
tests := []struct {
numKeys int
step int
startKey []byte
limitKey []byte
expectedFirst []byte
expectedLast []byte
seekKey []byte
expectedSeek []byte
}{
// No range limits. Values are the set (0, 1, 2, ..., 49).
// Seek existing value.
{
numKeys: 50,
step: 1,
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(49),
seekKey: serializeUint32(12),
expectedSeek: serializeUint32(12),
},
// Limited to range [24, end]. Values are the set
// (0, 2, 4, ..., 48). Seek value that doesn't exist and is
// greater than largest existing key.
{
numKeys: 50,
step: 2,
startKey: serializeUint32(24),
expectedFirst: serializeUint32(24),
expectedLast: serializeUint32(48),
seekKey: serializeUint32(49),
expectedSeek: nil,
},
// Limited to range [start, 25). Values are the set
// (0, 3, 6, ..., 48). Seek value that doesn't exist but is
// before an existing value within the range.
{
numKeys: 50,
step: 3,
limitKey: serializeUint32(25),
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(24),
seekKey: serializeUint32(17),
expectedSeek: serializeUint32(18),
},
// Limited to range [10, 21). Values are the set
// (0, 4, ..., 48). Seek value that exists, but is before the
// minimum allowed range.
{
numKeys: 50,
step: 4,
startKey: serializeUint32(10),
limitKey: serializeUint32(21),
expectedFirst: serializeUint32(12),
expectedLast: serializeUint32(20),
seekKey: serializeUint32(4),
expectedSeek: nil,
},
// Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}).
// Since it's a bytewise compare, {0,0,0,...} < {0,0,1}.
// Seek existing value within the allowed range.
{
numKeys: 300,
step: 1,
startKey: []byte{0x00, 0x00, 0x00},
limitKey: []byte{0x00, 0x00, 0x01},
expectedFirst: serializeUint32(0),
expectedLast: serializeUint32(255),
seekKey: serializeUint32(100),
expectedSeek: serializeUint32(100),
},
}
testLoop:
for i, test := range tests {
// Insert a bunch of keys.
testTreap := NewImmutable()
for i := 0; i < test.numKeys; i += test.step {
key := serializeUint32(uint32(i))
testTreap = testTreap.Put(key, key)
}
// Create new iterator limited by the test params.
iter := testTreap.Iterator(test.startKey, test.limitKey)
// Ensure the first item is accurate.
hasFirst := iter.First()
if !hasFirst && test.expectedFirst != nil {
t.Errorf("First #%d: unexpected exhausted iterator", i)
continue
}
gotKey := iter.Key()
if !bytes.Equal(gotKey, test.expectedFirst) {
t.Errorf("First.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedFirst)
continue
}
gotVal := iter.Value()
if !bytes.Equal(gotVal, test.expectedFirst) {
t.Errorf("First.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedFirst)
continue
}
// Ensure the iterator gives the expected items in order.
curNum := binary.BigEndian.Uint32(test.expectedFirst)
for iter.Next() {
curNum += uint32(test.step)
// Ensure key is as expected.
gotKey := iter.Key()
expectedKey := serializeUint32(curNum)
if !bytes.Equal(gotKey, expectedKey) {
t.Errorf("iter.Key #%d (%d): unexpected key - "+
"got %x, want %x", i, curNum, gotKey,
expectedKey)
continue testLoop
}
// Ensure value is as expected.
gotVal := iter.Value()
if !bytes.Equal(gotVal, expectedKey) {
t.Errorf("iter.Value #%d (%d): unexpected "+
"value - got %x, want %x", i, curNum,
gotVal, expectedKey)
continue testLoop
}
}
// Ensure iterator is exhausted.
if iter.Valid() {
t.Errorf("Valid #%d: iterator should be exhausted", i)
continue
}
// Ensure the last item is accurate.
hasLast := iter.Last()
if !hasLast && test.expectedLast != nil {
t.Errorf("Last #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedLast) {
t.Errorf("Last.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedLast)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedLast) {
t.Errorf("Last.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedLast)
continue
}
// Ensure the iterator gives the expected items in reverse
// order.
curNum = binary.BigEndian.Uint32(test.expectedLast)
for iter.Prev() {
curNum -= uint32(test.step)
// Ensure key is as expected.
gotKey := iter.Key()
expectedKey := serializeUint32(curNum)
if !bytes.Equal(gotKey, expectedKey) {
t.Errorf("iter.Key #%d (%d): unexpected key - "+
"got %x, want %x", i, curNum, gotKey,
expectedKey)
continue testLoop
}
// Ensure value is as expected.
gotVal := iter.Value()
if !bytes.Equal(gotVal, expectedKey) {
t.Errorf("iter.Value #%d (%d): unexpected "+
"value - got %x, want %x", i, curNum,
gotVal, expectedKey)
continue testLoop
}
}
// Ensure iterator is exhausted.
if iter.Valid() {
t.Errorf("Valid #%d: iterator should be exhausted", i)
continue
}
// Seek to the provided key.
seekValid := iter.Seek(test.seekKey)
if !seekValid && test.expectedSeek != nil {
t.Errorf("Seek #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedSeek) {
t.Errorf("Seek.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedSeek)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedSeek) {
t.Errorf("Seek.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedSeek)
continue
}
// Recreate the iterator and ensure calling Next on it before it
// has been positioned gives the first element.
iter = testTreap.Iterator(test.startKey, test.limitKey)
hasNext := iter.Next()
if !hasNext && test.expectedFirst != nil {
t.Errorf("Next #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedFirst) {
t.Errorf("Next.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedFirst)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedFirst) {
t.Errorf("Next.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedFirst)
continue
}
// Recreate the iterator and ensure calling Prev on it before it
// has been positioned gives the first element.
iter = testTreap.Iterator(test.startKey, test.limitKey)
hasPrev := iter.Prev()
if !hasPrev && test.expectedLast != nil {
t.Errorf("Prev #%d: unexpected exhausted iterator", i)
continue
}
gotKey = iter.Key()
if !bytes.Equal(gotKey, test.expectedLast) {
t.Errorf("Prev.Key #%d: unexpected key - got %x, "+
"want %x", i, gotKey, test.expectedLast)
continue
}
gotVal = iter.Value()
if !bytes.Equal(gotVal, test.expectedLast) {
t.Errorf("Next.Value #%d: unexpected value - got %x, "+
"want %x", i, gotVal, test.expectedLast)
continue
}
}
}
// TestImmutableEmptyIterator ensures that the various functions behave as
// expected when an immutable treap is empty.
func TestImmutableEmptyIterator(t *testing.T) {
t.Parallel()
// Create iterator against empty treap.
testTreap := NewImmutable()
iter := testTreap.Iterator(nil, nil)
// Ensure Valid on empty iterator reports it as exhausted.
if iter.Valid() {
t.Fatal("Valid: iterator should be exhausted")
}
// Ensure First and Last on empty iterator report it as exhausted.
if iter.First() {
t.Fatal("First: iterator should be exhausted")
}
if iter.Last() {
t.Fatal("Last: iterator should be exhausted")
}
// Ensure Next and Prev on empty iterator report it as exhausted.
if iter.Next() {
t.Fatal("Next: iterator should be exhausted")
}
if iter.Prev() {
t.Fatal("Prev: iterator should be exhausted")
}
// Ensure Key and Value on empty iterator are nil.
if gotKey := iter.Key(); gotKey != nil {
t.Fatalf("Key: should be nil - got %q", gotKey)
}
if gotVal := iter.Value(); gotVal != nil {
t.Fatalf("Value: should be nil - got %q", gotVal)
}
// Ensure calling ForceReseek on an immutable treap iterator does not
// cause any issues since it only applies to mutable treap iterators.
iter.ForceReseek()
if iter.Next() {
t.Fatal("Next: iterator should be exhausted")
}
iter.ForceReseek()
if iter.Prev() {
t.Fatal("Prev: iterator should be exhausted")
}
}