etcd/storage/key_index_test.go
2015-05-14 17:35:12 -07:00

365 lines
6.8 KiB
Go

package storage
import (
"reflect"
"testing"
)
func TestKeyIndexGet(t *testing.T) {
// key: "foo"
// index: 12
// generations:
// {empty}
// {8[1], 10[2], 12(t)[3]}
// {4[2], 6(t)[3]}
ki := newTestKeyIndex()
ki.compact(4, make(map[uint64]struct{}))
tests := []struct {
index uint64
windex uint64
werr error
}{
// expected not exist on an index that is greater than the last tombstone
{13, 0, ErrIndexNotFound},
{13, 0, ErrIndexNotFound},
// get on generation 2
{12, 12, nil},
{11, 10, nil},
{10, 10, nil},
{9, 8, nil},
{8, 8, nil},
{7, 0, ErrIndexNotFound},
// get on generation 1
{6, 6, nil},
{5, 4, nil},
{4, 4, nil},
}
for i, tt := range tests {
index, err := ki.get(tt.index)
if err != tt.werr {
t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
}
if index != tt.windex {
t.Errorf("#%d: index = %d, want %d", i, index, tt.index)
}
}
}
func TestKeyIndexPut(t *testing.T) {
ki := &keyIndex{key: []byte("foo")}
ki.put(5)
wki := &keyIndex{
key: []byte("foo"),
index: 5,
generations: []generation{{ver: 1, cont: []uint64{5}}},
}
if !reflect.DeepEqual(ki, wki) {
t.Errorf("ki = %+v, want %+v", ki, wki)
}
ki.put(7)
wki = &keyIndex{
key: []byte("foo"),
index: 7,
generations: []generation{{ver: 2, cont: []uint64{5, 7}}},
}
if !reflect.DeepEqual(ki, wki) {
t.Errorf("ki = %+v, want %+v", ki, wki)
}
}
func TestKeyIndexTombstone(t *testing.T) {
ki := &keyIndex{key: []byte("foo")}
ki.put(5)
ki.tombstone(7)
wki := &keyIndex{
key: []byte("foo"),
index: 7,
generations: []generation{{ver: 2, cont: []uint64{5, 7}}, {}},
}
if !reflect.DeepEqual(ki, wki) {
t.Errorf("ki = %+v, want %+v", ki, wki)
}
ki.put(8)
ki.put(9)
ki.tombstone(15)
wki = &keyIndex{
key: []byte("foo"),
index: 15,
generations: []generation{{ver: 2, cont: []uint64{5, 7}}, {ver: 3, cont: []uint64{8, 9, 15}}, {}},
}
if !reflect.DeepEqual(ki, wki) {
t.Errorf("ki = %+v, want %+v", ki, wki)
}
}
func TestKeyIndexCompact(t *testing.T) {
tests := []struct {
compact uint64
wki *keyIndex
wam map[uint64]struct{}
}{
{
1,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{2, 4, 6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
2: struct{}{}, 4: struct{}{}, 6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
2,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{2, 4, 6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
2: struct{}{}, 4: struct{}{}, 6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
3,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{2, 4, 6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
2: struct{}{}, 4: struct{}{}, 6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
4,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{4, 6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
4: struct{}{}, 6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
5,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{4, 6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
4: struct{}{}, 6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
6,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{6}},
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
6: struct{}{},
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
7,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
8,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
9,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{8, 10, 12}},
{},
},
},
map[uint64]struct{}{
8: struct{}{}, 10: struct{}{}, 12: struct{}{},
},
},
{
10,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{10, 12}},
{},
},
},
map[uint64]struct{}{
10: struct{}{}, 12: struct{}{},
},
},
{
11,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{10, 12}},
{},
},
},
map[uint64]struct{}{
10: struct{}{}, 12: struct{}{},
},
},
{
12,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{ver: 3, cont: []uint64{12}},
{},
},
},
map[uint64]struct{}{
12: struct{}{},
},
},
{
13,
&keyIndex{
key: []byte("foo"),
index: 12,
generations: []generation{
{},
},
},
map[uint64]struct{}{},
},
}
// Continous Compaction
ki := newTestKeyIndex()
for i, tt := range tests {
am := make(map[uint64]struct{})
ki.compact(tt.compact, am)
if !reflect.DeepEqual(ki, tt.wki) {
t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
}
if !reflect.DeepEqual(am, tt.wam) {
t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
}
}
// Jump Compaction
for i, tt := range tests {
if (i%2 == 0 && i < 6) && (i%2 == 1 && i > 6) {
am := make(map[uint64]struct{})
ki.compact(tt.compact, am)
if !reflect.DeepEqual(ki, tt.wki) {
t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
}
if !reflect.DeepEqual(am, tt.wam) {
t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
}
}
}
// OnceCompaction
for i, tt := range tests {
ki := newTestKeyIndex()
am := make(map[uint64]struct{})
ki.compact(tt.compact, am)
if !reflect.DeepEqual(ki, tt.wki) {
t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
}
if !reflect.DeepEqual(am, tt.wam) {
t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
}
}
}
func newTestKeyIndex() *keyIndex {
// key: "foo"
// index: 12
// generations:
// {empty}
// {8[1], 10[2], 12(t)[3]}
// {2[1], 4[2], 6(t)[3]}
ki := &keyIndex{key: []byte("foo")}
ki.put(2)
ki.put(4)
ki.tombstone(6)
ki.put(8)
ki.put(10)
ki.tombstone(12)
return ki
}