@@ -11,10 +11,143 @@ import (
1111 "testing"
1212 "testing/quick"
1313
14+ "github.com/stretchr/testify/require"
15+
1416 bolt "go.etcd.io/bbolt"
1517 "go.etcd.io/bbolt/internal/btesting"
1618)
1719
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+
18151// Ensure that a cursor can return a reference to the bucket that created it.
19152func TestCursor_Bucket (t * testing.T ) {
20153 db := btesting .MustCreateDB (t )
0 commit comments