@@ -11,10 +11,143 @@ import (
11
11
"testing"
12
12
"testing/quick"
13
13
14
+ "github.com/stretchr/testify/require"
15
+
14
16
bolt "go.etcd.io/bbolt"
15
17
"go.etcd.io/bbolt/internal/btesting"
16
18
)
17
19
20
+ // TestCursor_RepeatOperations verifies that a cursor can continue to
21
+ // iterate over all elements in reverse direction when it has already
22
+ // reached to the end or beginning.
23
+ // Refer to https://github.com/etcd-io/bbolt/issues/733
24
+ func TestCursor_RepeatOperations (t * testing.T ) {
25
+ testCases := []struct {
26
+ name string
27
+ testFunc func (t2 * testing.T , bucket * bolt.Bucket )
28
+ }{
29
+ {
30
+ name : "Repeat NextPrevNext" ,
31
+ testFunc : testRepeatCursorOperations_NextPrevNext ,
32
+ },
33
+ {
34
+ name : "Repeat PrevNextPrev" ,
35
+ testFunc : testRepeatCursorOperations_PrevNextPrev ,
36
+ },
37
+ }
38
+
39
+ for _ , tc := range testCases {
40
+ t .Run (tc .name , func (t * testing.T ) {
41
+ db := btesting .MustCreateDBWithOption (t , & bolt.Options {PageSize : 4096 })
42
+
43
+ bucketName := []byte ("data" )
44
+
45
+ _ = db .Update (func (tx * bolt.Tx ) error {
46
+ b , _ := tx .CreateBucketIfNotExists (bucketName )
47
+ testCursorRepeatOperations_PrepareData (t , b )
48
+ return nil
49
+ })
50
+
51
+ _ = db .View (func (tx * bolt.Tx ) error {
52
+ b := tx .Bucket (bucketName )
53
+ tc .testFunc (t , b )
54
+ return nil
55
+ })
56
+ })
57
+ }
58
+ }
59
+
60
+ func testCursorRepeatOperations_PrepareData (t * testing.T , b * bolt.Bucket ) {
61
+ // ensure we have at least one branch page.
62
+ for i := 0 ; i < 1000 ; i ++ {
63
+ k := []byte (fmt .Sprintf ("%05d" , i ))
64
+ err := b .Put (k , k )
65
+ require .NoError (t , err )
66
+ }
67
+ }
68
+
69
+ func testRepeatCursorOperations_NextPrevNext (t * testing.T , b * bolt.Bucket ) {
70
+ c := b .Cursor ()
71
+ c .First ()
72
+ startKey := []byte (fmt .Sprintf ("%05d" , 2 ))
73
+ returnedKey , _ := c .Seek (startKey )
74
+ require .Equal (t , startKey , returnedKey )
75
+
76
+ // Step 1: verify next
77
+ for i := 3 ; i < 1000 ; i ++ {
78
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
79
+ actualKey , _ := c .Next ()
80
+ require .Equal (t , expectedKey , actualKey )
81
+ }
82
+
83
+ // Once we've reached the end, it should always return nil no matter how many times we call `Next`.
84
+ for i := 0 ; i < 10 ; i ++ {
85
+ k , _ := c .Next ()
86
+ require .Equal (t , []byte (nil ), k )
87
+ }
88
+
89
+ // Step 2: verify prev
90
+ for i := 998 ; i >= 0 ; i -- {
91
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
92
+ actualKey , _ := c .Prev ()
93
+ require .Equal (t , expectedKey , actualKey )
94
+ }
95
+
96
+ // Once we've reached the beginning, it should always return nil no matter how many times we call `Prev`.
97
+ for i := 0 ; i < 10 ; i ++ {
98
+ k , _ := c .Prev ()
99
+ require .Equal (t , []byte (nil ), k )
100
+ }
101
+
102
+ // Step 3: verify next again
103
+ for i := 1 ; i < 1000 ; i ++ {
104
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
105
+ actualKey , _ := c .Next ()
106
+ require .Equal (t , expectedKey , actualKey )
107
+ }
108
+ }
109
+
110
+ func testRepeatCursorOperations_PrevNextPrev (t * testing.T , b * bolt.Bucket ) {
111
+ c := b .Cursor ()
112
+
113
+ startKey := []byte (fmt .Sprintf ("%05d" , 998 ))
114
+ returnedKey , _ := c .Seek (startKey )
115
+ require .Equal (t , startKey , returnedKey )
116
+
117
+ // Step 1: verify prev
118
+ for i := 997 ; i >= 0 ; i -- {
119
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
120
+ actualKey , _ := c .Prev ()
121
+ require .Equal (t , expectedKey , actualKey )
122
+ }
123
+
124
+ // Once we've reached the beginning, it should always return nil no matter how many times we call `Prev`.
125
+ for i := 0 ; i < 10 ; i ++ {
126
+ k , _ := c .Prev ()
127
+ require .Equal (t , []byte (nil ), k )
128
+ }
129
+
130
+ // Step 2: verify next
131
+ for i := 1 ; i < 1000 ; i ++ {
132
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
133
+ actualKey , _ := c .Next ()
134
+ require .Equal (t , expectedKey , actualKey )
135
+ }
136
+
137
+ // Once we've reached the end, it should always return nil no matter how many times we call `Next`.
138
+ for i := 0 ; i < 10 ; i ++ {
139
+ k , _ := c .Next ()
140
+ require .Equal (t , []byte (nil ), k )
141
+ }
142
+
143
+ // Step 3: verify prev again
144
+ for i := 998 ; i >= 0 ; i -- {
145
+ expectedKey := []byte (fmt .Sprintf ("%05d" , i ))
146
+ actualKey , _ := c .Prev ()
147
+ require .Equal (t , expectedKey , actualKey )
148
+ }
149
+ }
150
+
18
151
// Ensure that a cursor can return a reference to the bucket that created it.
19
152
func TestCursor_Bucket (t * testing.T ) {
20
153
db := btesting .MustCreateDB (t )
0 commit comments