1// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
2// All rights reserved.
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7package table
8
9import (
10	"encoding/binary"
11	"fmt"
12
13	. "github.com/onsi/ginkgo"
14	. "github.com/onsi/gomega"
15
16	"github.com/syndtr/goleveldb/leveldb/comparer"
17	"github.com/syndtr/goleveldb/leveldb/iterator"
18	"github.com/syndtr/goleveldb/leveldb/testutil"
19	"github.com/syndtr/goleveldb/leveldb/util"
20)
21
22type blockTesting struct {
23	tr *Reader
24	b  *block
25}
26
27func (t *blockTesting) TestNewIterator(slice *util.Range) iterator.Iterator {
28	return t.tr.newBlockIter(t.b, nil, slice, false)
29}
30
31var _ = testutil.Defer(func() {
32	Describe("Block", func() {
33		Build := func(kv *testutil.KeyValue, restartInterval int) *blockTesting {
34			// Building the block.
35			bw := &blockWriter{
36				restartInterval: restartInterval,
37				scratch:         make([]byte, 30),
38			}
39			kv.Iterate(func(i int, key, value []byte) {
40				bw.append(key, value)
41			})
42			bw.finish()
43
44			// Opening the block.
45			data := bw.buf.Bytes()
46			restartsLen := int(binary.LittleEndian.Uint32(data[len(data)-4:]))
47			return &blockTesting{
48				tr: &Reader{cmp: comparer.DefaultComparer},
49				b: &block{
50					data:           data,
51					restartsLen:    restartsLen,
52					restartsOffset: len(data) - (restartsLen+1)*4,
53				},
54			}
55		}
56
57		Describe("read test", func() {
58			for restartInterval := 1; restartInterval <= 5; restartInterval++ {
59				Describe(fmt.Sprintf("with restart interval of %d", restartInterval), func() {
60					kv := &testutil.KeyValue{}
61					Text := func() string {
62						return fmt.Sprintf("and %d keys", kv.Len())
63					}
64
65					Test := func() {
66						// Make block.
67						br := Build(kv, restartInterval)
68						// Do testing.
69						testutil.KeyValueTesting(nil, kv.Clone(), br, nil, nil)
70					}
71
72					Describe(Text(), Test)
73
74					kv.PutString("", "empty")
75					Describe(Text(), Test)
76
77					kv.PutString("a1", "foo")
78					Describe(Text(), Test)
79
80					kv.PutString("a2", "v")
81					Describe(Text(), Test)
82
83					kv.PutString("a3qqwrkks", "hello")
84					Describe(Text(), Test)
85
86					kv.PutString("a4", "bar")
87					Describe(Text(), Test)
88
89					kv.PutString("a5111111", "v5")
90					kv.PutString("a6", "")
91					kv.PutString("a7", "v7")
92					kv.PutString("a8", "vvvvvvvvvvvvvvvvvvvvvv8")
93					kv.PutString("b", "v9")
94					kv.PutString("c9", "v9")
95					kv.PutString("c91", "v9")
96					kv.PutString("d0", "v9")
97					Describe(Text(), Test)
98				})
99			}
100		})
101
102		Describe("out-of-bound slice test", func() {
103			kv := &testutil.KeyValue{}
104			kv.PutString("k1", "v1")
105			kv.PutString("k2", "v2")
106			kv.PutString("k3abcdefgg", "v3")
107			kv.PutString("k4", "v4")
108			kv.PutString("k5", "v5")
109			for restartInterval := 1; restartInterval <= 5; restartInterval++ {
110				Describe(fmt.Sprintf("with restart interval of %d", restartInterval), func() {
111					// Make block.
112					bt := Build(kv, restartInterval)
113
114					Test := func(r *util.Range) func(done Done) {
115						return func(done Done) {
116							iter := bt.TestNewIterator(r)
117							Expect(iter.Error()).ShouldNot(HaveOccurred())
118
119							t := testutil.IteratorTesting{
120								KeyValue: kv.Clone(),
121								Iter:     iter,
122							}
123
124							testutil.DoIteratorTesting(&t)
125							iter.Release()
126							done <- true
127						}
128					}
129
130					It("Should do iterations and seeks correctly #0",
131						Test(&util.Range{Start: []byte("k0"), Limit: []byte("k6")}), 2.0)
132
133					It("Should do iterations and seeks correctly #1",
134						Test(&util.Range{Start: []byte(""), Limit: []byte("zzzzzzz")}), 2.0)
135				})
136			}
137		})
138	})
139})
140