[NOD-922] Panic from cursor Next and First (#703)

* [NOD-922] Panic in Cursor First and Next if the cursor is closed.

* [NOD-922] Fix broken tests.

* [NOD-922] Fix a comment.
This commit is contained in:
stasatdaglabs 2020-04-23 16:55:25 +03:00 committed by GitHub
parent 5fe9dae557
commit 3af945692e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 25 deletions

View File

@ -3,11 +3,11 @@ package database
// Cursor iterates over database entries given some bucket.
type Cursor interface {
// Next moves the iterator to the next key/value pair. It returns whether the
// iterator is exhausted. Returns false if the cursor is closed.
// iterator is exhausted. Panics if the cursor is closed.
Next() bool
// First moves the iterator to the first key/value pair. It returns false if
// such a pair does not exist or if the cursor is closed.
// such a pair does not exist. Panics if the cursor is closed.
First() bool
// Seek moves the iterator to the first key/value pair whose key is greater

View File

@ -7,6 +7,7 @@ package database_test
import (
"bytes"
"fmt"
"github.com/kaspanet/kaspad/database"
"reflect"
"strings"
@ -23,6 +24,20 @@ func prepareCursorForTest(t *testing.T, db database.Database, testName string) d
return cursor
}
func recoverFromClosedCursorPanic(t *testing.T, testName string) {
panicErr := recover()
if panicErr == nil {
t.Fatalf("%s: cursor unexpectedly "+
"didn't panic after being closed", testName)
}
expectedPanicErr := "closed cursor"
if !strings.Contains(fmt.Sprintf("%v", panicErr), expectedPanicErr) {
t.Fatalf("%s: cursor panicked "+
"with wrong message. Want: %v, got: %s",
testName, expectedPanicErr, panicErr)
}
}
func TestCursorNext(t *testing.T) {
testForAllDatabaseTypes(t, "TestCursorNext", testCursorNext)
}
@ -67,19 +82,20 @@ func testCursorNext(t *testing.T, db database.Database, testName string) {
"not done", testName)
}
// Rewind the cursor, close it, and call Next on it again.
// This time it should return false because it's closed.
// Rewind the cursor and close it
cursor.First()
err := cursor.Close()
if err != nil {
t.Fatalf("%s: Close unexpectedly "+
"failed: %s", testName, err)
}
hasNext = cursor.Next()
if hasNext {
t.Fatalf("%s: cursor unexpectedly "+
"returned true after being closed", testName)
}
// Call Next on the cursor. This time it should panic
// because it's closed.
func() {
defer recoverFromClosedCursorPanic(t, testName)
cursor.Next()
}()
}
func TestCursorFirst(t *testing.T) {
@ -315,17 +331,15 @@ func testCursorCloseFirstAndNext(t *testing.T, db database.Database, testName st
"unexpectedly failed: %s", testName, err)
}
// We expect First to return false
result := cursor.First()
if result {
t.Fatalf("%s: First "+
"unexpectedly returned true", testName)
}
// We expect First to panic
func() {
defer recoverFromClosedCursorPanic(t, testName)
cursor.First()
}()
// We expect Next to return false
result = cursor.Next()
if result {
t.Fatalf("%s: Next "+
"unexpectedly returned true", testName)
}
// We expect Next to panic
func() {
defer recoverFromClosedCursorPanic(t, testName)
cursor.Next()
}()
}

View File

@ -27,19 +27,19 @@ func (db *LevelDB) Cursor(bucket *database.Bucket) *LevelDBCursor {
}
// Next moves the iterator to the next key/value pair. It returns whether the
// iterator is exhausted. Returns false if the cursor is closed.
// iterator is exhausted. Panics if the cursor is closed.
func (c *LevelDBCursor) Next() bool {
if c.isClosed {
return false
panic("cannot call next on a closed cursor")
}
return c.ldbIterator.Next()
}
// First moves the iterator to the first key/value pair. It returns false if
// such a pair does not exist or if the cursor is closed.
// such a pair does not exist. Panics if the cursor is closed.
func (c *LevelDBCursor) First() bool {
if c.isClosed {
return false
panic("cannot call first on a closed cursor")
}
return c.ldbIterator.First()
}