Merge pull request #360 from ptabor/20221220-btesting

Factor out (bolt)testing library.
pull/367/head
Piotr Tabor 2022-12-28 16:14:12 +01:00 committed by GitHub
commit a0def3434b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 412 additions and 502 deletions

View File

@ -15,13 +15,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
// Ensure that a bucket that gets a non-existent key returns nil.
func TestBucket_Get_NonExistent(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -39,8 +40,7 @@ func TestBucket_Get_NonExistent(t *testing.T) {
// Ensure that a bucket can read a value that is not flushed yet.
func TestBucket_Get_FromNode(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -61,8 +61,7 @@ func TestBucket_Get_FromNode(t *testing.T) {
// Ensure that a bucket retrieved via Get() returns a nil.
func TestBucket_Get_IncompatibleValue(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -87,8 +86,7 @@ func TestBucket_Get_IncompatibleValue(t *testing.T) {
//
// https://github.com/boltdb/bolt/issues/544
func TestBucket_Get_Capacity(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Write key to a bucket.
if err := db.Update(func(tx *bolt.Tx) error {
@ -125,8 +123,7 @@ func TestBucket_Get_Capacity(t *testing.T) {
// Ensure that a bucket can write a key/value.
func TestBucket_Put(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -148,8 +145,7 @@ func TestBucket_Put(t *testing.T) {
// Ensure that a bucket can rewrite a key in the same transaction.
func TestBucket_Put_Repeat(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -174,8 +170,7 @@ func TestBucket_Put_Repeat(t *testing.T) {
// Ensure that a bucket can write a bunch of large values.
func TestBucket_Put_Large(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
count, factor := 100, 200
if err := db.Update(func(tx *bolt.Tx) error {
@ -216,8 +211,7 @@ func TestDB_Put_VeryLarge(t *testing.T) {
n, batchN := 400000, 200000
ksize, vsize := 8, 500
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
for i := 0; i < n; i += batchN {
if err := db.Update(func(tx *bolt.Tx) error {
@ -241,8 +235,7 @@ func TestDB_Put_VeryLarge(t *testing.T) {
// Ensure that a setting a value on a key with a bucket value returns an error.
func TestBucket_Put_IncompatibleValue(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b0, err := tx.CreateBucket([]byte("widgets"))
@ -264,8 +257,7 @@ func TestBucket_Put_IncompatibleValue(t *testing.T) {
// Ensure that a setting a value while the transaction is closed returns an error.
func TestBucket_Put_Closed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
@ -287,8 +279,7 @@ func TestBucket_Put_Closed(t *testing.T) {
// Ensure that setting a value on a read-only bucket returns an error.
func TestBucket_Put_ReadOnly(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -312,8 +303,7 @@ func TestBucket_Put_ReadOnly(t *testing.T) {
// Ensure that a bucket can delete an existing key.
func TestBucket_Delete(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -337,8 +327,7 @@ func TestBucket_Delete(t *testing.T) {
// Ensure that deleting a large set of keys will work correctly.
func TestBucket_Delete_Large(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -388,8 +377,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
t.Skip("skipping test in short mode.")
}
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
k := make([]byte, 16)
// The bigger the pages - the more values we need to write.
@ -436,9 +424,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
}
// Free page count should be preserved on reopen.
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
db.MustClose()
db.MustReopen()
if reopenFreePages := db.Stats().FreePageN; freePages != reopenFreePages {
t.Fatalf("expected %d free pages, got %+v", freePages, db.Stats())
@ -447,8 +433,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
// Ensure that deleting of non-existing key is a no-op.
func TestBucket_Delete_NonExisting(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -480,8 +465,7 @@ func TestBucket_Delete_NonExisting(t *testing.T) {
// Ensure that accessing and updating nested buckets is ok across transactions.
func TestBucket_Nested(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
// Create a widgets bucket.
@ -567,8 +551,7 @@ func TestBucket_Nested(t *testing.T) {
// Ensure that deleting a bucket using Delete() returns an error.
func TestBucket_Delete_Bucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -588,8 +571,7 @@ func TestBucket_Delete_Bucket(t *testing.T) {
// Ensure that deleting a key on a read-only bucket returns an error.
func TestBucket_Delete_ReadOnly(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -612,8 +594,7 @@ func TestBucket_Delete_ReadOnly(t *testing.T) {
// Ensure that a deleting value while the transaction is closed returns an error.
func TestBucket_Delete_Closed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
@ -635,8 +616,7 @@ func TestBucket_Delete_Closed(t *testing.T) {
// Ensure that deleting a bucket causes nested buckets to be deleted.
func TestBucket_DeleteBucket_Nested(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -667,8 +647,7 @@ func TestBucket_DeleteBucket_Nested(t *testing.T) {
// Ensure that deleting a bucket causes nested buckets to be deleted after they have been committed.
func TestBucket_DeleteBucket_Nested2(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -734,8 +713,7 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
// Ensure that deleting a child bucket with multiple pages causes all pages to get collected.
// NOTE: Consistency check in bolt_test.DB.Close() will panic if pages not freed properly.
func TestBucket_DeleteBucket_Large(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -770,8 +748,7 @@ func TestBucket_DeleteBucket_Large(t *testing.T) {
// Ensure that a simple value retrieved via Bucket() returns a nil.
func TestBucket_Bucket_IncompatibleValue(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -793,8 +770,7 @@ func TestBucket_Bucket_IncompatibleValue(t *testing.T) {
// Ensure that creating a bucket on an existing non-bucket key returns an error.
func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -815,8 +791,7 @@ func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
// Ensure that deleting a bucket on an existing non-bucket key returns an error.
func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -837,8 +812,7 @@ func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) {
// Ensure bucket can set and update its sequence number.
func TestBucket_Sequence(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
bkt, err := tx.CreateBucket([]byte("0"))
@ -879,8 +853,7 @@ func TestBucket_Sequence(t *testing.T) {
// Ensure that a bucket can return an autoincrementing sequence.
func TestBucket_NextSequence(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
widgets, err := tx.CreateBucket([]byte("widgets"))
@ -922,8 +895,7 @@ func TestBucket_NextSequence(t *testing.T) {
// the only thing updated on the bucket.
// https://github.com/boltdb/bolt/issues/296
func TestBucket_NextSequence_Persist(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -958,8 +930,7 @@ func TestBucket_NextSequence_Persist(t *testing.T) {
// Ensure that retrieving the next sequence on a read-only bucket returns an error.
func TestBucket_NextSequence_ReadOnly(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -983,8 +954,7 @@ func TestBucket_NextSequence_ReadOnly(t *testing.T) {
// Ensure that retrieving the next sequence for a bucket on a closed database return an error.
func TestBucket_NextSequence_Closed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
@ -1003,8 +973,7 @@ func TestBucket_NextSequence_Closed(t *testing.T) {
// Ensure a user can loop over all key/value pairs in a bucket.
func TestBucket_ForEach(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
type kv struct {
k []byte
@ -1053,8 +1022,7 @@ func TestBucket_ForEach(t *testing.T) {
}
func TestBucket_ForEachBucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
expectedItems := [][]byte{
[]byte("csubbucket"),
@ -1098,8 +1066,7 @@ func TestBucket_ForEachBucket(t *testing.T) {
}
func TestBucket_ForEachBucket_NoBuckets(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
verifyReads := func(b *bolt.Bucket) {
var items [][]byte
@ -1137,8 +1104,7 @@ func TestBucket_ForEachBucket_NoBuckets(t *testing.T) {
// Ensure a database can stop iteration early.
func TestBucket_ForEach_ShortCircuit(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -1176,8 +1142,7 @@ func TestBucket_ForEach_ShortCircuit(t *testing.T) {
// Ensure that looping over a bucket on a closed database returns an error.
func TestBucket_ForEach_Closed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
@ -1200,8 +1165,7 @@ func TestBucket_ForEach_Closed(t *testing.T) {
// Ensure that an error is returned when inserting with an empty key.
func TestBucket_Put_EmptyKey(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -1222,8 +1186,7 @@ func TestBucket_Put_EmptyKey(t *testing.T) {
// Ensure that an error is returned when inserting with a key that's too large.
func TestBucket_Put_KeyTooLarge(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -1245,8 +1208,7 @@ func TestBucket_Put_ValueTooLarge(t *testing.T) {
t.Skip("not enough RAM for test")
}
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -1268,8 +1230,7 @@ func TestBucket_Stats(t *testing.T) {
t.Skip("skipping test in short mode")
}
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Add bucket with fewer keys but one big value.
bigKey := []byte("really-big-value")
@ -1361,8 +1322,7 @@ func TestBucket_Stats_RandomFill(t *testing.T) {
t.Skip("invalid page size for test")
}
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Add a set of values in random order. It will be the same random
// order so we can maintain consistency between test runs.
@ -1423,8 +1383,7 @@ func TestBucket_Stats_RandomFill(t *testing.T) {
// Ensure a bucket can calculate stats.
func TestBucket_Stats_Small(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
// Add a bucket that fits on a single root leaf.
@ -1487,8 +1446,7 @@ func TestBucket_Stats_Small(t *testing.T) {
}
func TestBucket_Stats_EmptyBucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
// Add a bucket that fits on a single root leaf.
@ -1547,8 +1505,7 @@ func TestBucket_Stats_EmptyBucket(t *testing.T) {
// Ensure a bucket can calculate stats.
func TestBucket_Stats_Nested(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("foo"))
@ -1653,8 +1610,7 @@ func TestBucket_Stats_Large(t *testing.T) {
t.Skip("skipping test in short mode.")
}
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var index int
for i := 0; i < 100; i++ {
@ -1731,7 +1687,7 @@ func TestBucket_Put_Single(t *testing.T) {
index := 0
if err := quick.Check(func(items testdata) bool {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
defer db.MustClose()
m := make(map[string][]byte)
@ -1788,7 +1744,7 @@ func TestBucket_Put_Multiple(t *testing.T) {
}
if err := quick.Check(func(items testdata) bool {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
defer db.MustClose()
// Bulk insert all values.
@ -1841,7 +1797,7 @@ func TestBucket_Delete_Quick(t *testing.T) {
}
if err := quick.Check(func(items testdata) bool {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
defer db.MustClose()
// Bulk insert all values.

View File

@ -5,6 +5,7 @@ import (
crypto "crypto/rand"
"encoding/binary"
"fmt"
"go.etcd.io/bbolt/internal/btesting"
"io"
"math/rand"
"os"
@ -18,15 +19,14 @@ import (
// Ensure the "info" command can print information about a database.
func TestInfoCommand_Run(t *testing.T) {
db := MustOpen(0666, nil)
db.DB.Close()
defer db.Close()
db := btesting.MustCreateDB(t)
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
// Run the info command.
m := NewMain()
if err := m.Run("info", db.Path); err != nil {
if err := m.Run("info", db.Path()); err != nil {
t.Fatal(err)
}
}
@ -38,11 +38,10 @@ func TestStatsCommand_Run_EmptyDatabase(t *testing.T) {
t.Skip("system does not use 4KB page size")
}
db := MustOpen(0666, nil)
defer db.Close()
db.DB.Close()
db := btesting.MustCreateDB(t)
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
// Generate expected result.
exp := "Aggregate statistics for 0 buckets\n\n" +
@ -66,7 +65,7 @@ func TestStatsCommand_Run_EmptyDatabase(t *testing.T) {
// Run the command.
m := NewMain()
if err := m.Run("stats", db.Path); err != nil {
if err := m.Run("stats", db.Path()); err != nil {
t.Fatal(err)
} else if m.Stdout.String() != exp {
t.Fatalf("unexpected stdout:\n\n%s", m.Stdout.String())
@ -80,8 +79,7 @@ func TestStatsCommand_Run(t *testing.T) {
t.Skip("system does not use 4KB page size")
}
db := MustOpen(0666, nil)
defer db.Close()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
// Create "foo" bucket.
@ -119,9 +117,9 @@ func TestStatsCommand_Run(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
db.DB.Close()
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
// Generate expected result.
exp := "Aggregate statistics for 3 buckets\n\n" +
@ -145,7 +143,7 @@ func TestStatsCommand_Run(t *testing.T) {
// Run the command.
m := NewMain()
if err := m.Run("stats", db.Path); err != nil {
if err := m.Run("stats", db.Path()); err != nil {
t.Fatal(err)
} else if m.Stdout.String() != exp {
t.Fatalf("unexpected stdout:\n\n%s", m.Stdout.String())
@ -154,8 +152,7 @@ func TestStatsCommand_Run(t *testing.T) {
// Ensure the "buckets" command can print a list of buckets.
func TestBucketsCommand_Run(t *testing.T) {
db := MustOpen(0666, nil)
defer db.Close()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
for _, name := range []string{"foo", "bar", "baz"} {
@ -168,15 +165,15 @@ func TestBucketsCommand_Run(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
db.DB.Close()
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
expected := "bar\nbaz\nfoo\n"
// Run the command.
m := NewMain()
if err := m.Run("buckets", db.Path); err != nil {
if err := m.Run("buckets", db.Path()); err != nil {
t.Fatal(err)
} else if actual := m.Stdout.String(); actual != expected {
t.Fatalf("unexpected stdout:\n\n%s", actual)
@ -185,8 +182,7 @@ func TestBucketsCommand_Run(t *testing.T) {
// Ensure the "keys" command can print a list of keys for a bucket.
func TestKeysCommand_Run(t *testing.T) {
db := MustOpen(0666, nil)
defer db.Close()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
for _, name := range []string{"foo", "bar"} {
@ -205,15 +201,15 @@ func TestKeysCommand_Run(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
db.DB.Close()
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
expected := "foo-0\nfoo-1\nfoo-2\n"
// Run the command.
m := NewMain()
if err := m.Run("keys", db.Path, "foo"); err != nil {
if err := m.Run("keys", db.Path(), "foo"); err != nil {
t.Fatal(err)
} else if actual := m.Stdout.String(); actual != expected {
t.Fatalf("unexpected stdout:\n\n%s", actual)
@ -222,8 +218,7 @@ func TestKeysCommand_Run(t *testing.T) {
// Ensure the "get" command can print the value of a key in a bucket.
func TestGetCommand_Run(t *testing.T) {
db := MustOpen(0666, nil)
defer db.Close()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
for _, name := range []string{"foo", "bar"} {
@ -243,15 +238,15 @@ func TestGetCommand_Run(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
db.DB.Close()
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path), db.Path)
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
expected := "val-foo-1\n"
// Run the command.
m := NewMain()
if err := m.Run("get", db.Path, "foo", "foo-1"); err != nil {
if err := m.Run("get", db.Path(), "foo", "foo-1"); err != nil {
t.Fatal(err)
} else if actual := m.Stdout.String(); actual != expected {
t.Fatalf("unexpected stdout:\n\n%s", actual)
@ -275,32 +270,6 @@ func NewMain() *Main {
return m
}
// MustOpen creates a Bolt database in a temporary location.
func MustOpen(mode os.FileMode, options *bolt.Options) *DB {
// Create temporary path.
f, _ := os.CreateTemp("", "bolt-")
f.Close()
os.Remove(f.Name())
db, err := bolt.Open(f.Name(), mode, options)
if err != nil {
panic(err.Error())
}
return &DB{DB: db, Path: f.Name()}
}
// DB is a test wrapper for bolt.DB.
type DB struct {
*bolt.DB
Path string
}
// Close closes and removes the database.
func (db *DB) Close() error {
defer os.Remove(db.Path)
return db.DB.Close()
}
func TestCompactCommand_Run(t *testing.T) {
var s int64
if err := binary.Read(crypto.Reader, binary.BigEndian, &s); err != nil {
@ -308,11 +277,11 @@ func TestCompactCommand_Run(t *testing.T) {
}
rand.Seed(s)
dstdb := MustOpen(0666, nil)
dstdb := btesting.MustCreateDB(t)
dstdb.Close()
// fill the db
db := MustOpen(0666, nil)
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
n := 2 + rand.Intn(5)
for i := 0; i < n; i++ {
@ -330,7 +299,6 @@ func TestCompactCommand_Run(t *testing.T) {
}
return nil
}); err != nil {
db.Close()
t.Fatal(err)
}
@ -353,7 +321,6 @@ func TestCompactCommand_Run(t *testing.T) {
}
return nil
}); err != nil {
db.Close()
t.Fatal(err)
}
if err := db.Update(func(tx *bolt.Tx) error {
@ -365,29 +332,26 @@ func TestCompactCommand_Run(t *testing.T) {
}
return tx.DeleteBucket([]byte("large_vals"))
}); err != nil {
db.Close()
t.Fatal(err)
}
db.DB.Close()
defer db.Close()
defer dstdb.Close()
db.Close()
dbChk, err := chkdb(db.Path)
dbChk, err := chkdb(db.Path())
if err != nil {
t.Fatal(err)
}
m := NewMain()
if err := m.Run("compact", "-o", dstdb.Path, db.Path); err != nil {
if err := m.Run("compact", "-o", dstdb.Path(), db.Path()); err != nil {
t.Fatal(err)
}
dbChkAfterCompact, err := chkdb(db.Path)
dbChkAfterCompact, err := chkdb(db.Path())
if err != nil {
t.Fatal(err)
}
dstdbChk, err := chkdb(dstdb.Path)
dstdbChk, err := chkdb(dstdb.Path())
if err != nil {
t.Fatal(err)
}

View File

@ -12,12 +12,12 @@ import (
"testing/quick"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
// Ensure that a cursor can return a reference to the bucket that created it.
func TestCursor_Bucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -34,8 +34,7 @@ func TestCursor_Bucket(t *testing.T) {
// Ensure that a Tx cursor can seek to the appropriate keys.
func TestCursor_Seek(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -104,8 +103,7 @@ func TestCursor_Seek(t *testing.T) {
}
func TestCursor_Delete(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
const count = 1000
@ -167,8 +165,7 @@ func TestCursor_Delete(t *testing.T) {
//
// Related: https://github.com/boltdb/bolt/pull/187
func TestCursor_Seek_Large(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var count = 10000
@ -231,8 +228,7 @@ func TestCursor_Seek_Large(t *testing.T) {
// Ensure that a cursor can iterate over an empty bucket without error.
func TestCursor_EmptyBucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
return err
@ -256,8 +252,7 @@ func TestCursor_EmptyBucket(t *testing.T) {
// Ensure that a Tx cursor can reverse iterate over an empty bucket without error.
func TestCursor_EmptyBucketReverse(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
@ -281,8 +276,7 @@ func TestCursor_EmptyBucketReverse(t *testing.T) {
// Ensure that a Tx cursor can iterate over a single root with a couple elements.
func TestCursor_Iterate_Leaf(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -352,8 +346,7 @@ func TestCursor_Iterate_Leaf(t *testing.T) {
// Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
func TestCursor_LeafRootReverse(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -416,8 +409,7 @@ func TestCursor_LeafRootReverse(t *testing.T) {
// Ensure that a Tx cursor can restart from the beginning.
func TestCursor_Restart(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -462,8 +454,7 @@ func TestCursor_Restart(t *testing.T) {
// Ensure that a cursor can skip over empty pages that have been deleted.
func TestCursor_First_EmptyPages(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Create 1000 keys in the "widgets" bucket.
if err := db.Update(func(tx *bolt.Tx) error {
@ -509,8 +500,7 @@ func TestCursor_First_EmptyPages(t *testing.T) {
// Ensure that a cursor can skip over empty pages that have been deleted.
func TestCursor_Last_EmptyPages(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Create 1000 keys in the "widgets" bucket.
if err := db.Update(func(tx *bolt.Tx) error {
@ -557,7 +547,7 @@ func TestCursor_Last_EmptyPages(t *testing.T) {
// Ensure that a Tx can iterate over all elements in a bucket.
func TestCursor_QuickCheck(t *testing.T) {
f := func(items testdata) bool {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
defer db.MustClose()
// Bulk insert all values.
@ -615,7 +605,7 @@ func TestCursor_QuickCheck(t *testing.T) {
// Ensure that a transaction can iterate over all elements in a bucket in reverse.
func TestCursor_QuickCheck_Reverse(t *testing.T) {
f := func(items testdata) bool {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
defer db.MustClose()
// Bulk insert all values.
@ -671,8 +661,7 @@ func TestCursor_QuickCheck_Reverse(t *testing.T) {
// Ensure that a Tx cursor can iterate over subbuckets.
func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -713,8 +702,7 @@ func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
// Ensure that a Tx cursor can reverse iterate over subbuckets.
func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))

View File

@ -4,23 +4,22 @@ import (
"bytes"
"encoding/binary"
"errors"
"flag"
"fmt"
"hash/fnv"
"log"
"math/rand"
"os"
"path/filepath"
"regexp"
"sync"
"testing"
"time"
"unsafe"
bolt "go.etcd.io/bbolt"
)
"github.com/stretchr/testify/require"
var statsFlag = flag.Bool("stats", false, "show performance stats")
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
// pageSize is the size of one page in the data file.
const pageSize = 4096
@ -147,12 +146,11 @@ func TestOpen_ErrVersionMismatch(t *testing.T) {
}
// Create empty database.
db := MustOpenDB()
db := btesting.MustCreateDB(t)
path := db.Path()
defer db.MustClose()
// Close database.
if err := db.DB.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
@ -184,12 +182,11 @@ func TestOpen_ErrChecksum(t *testing.T) {
}
// Create empty database.
db := MustOpenDB()
db := btesting.MustCreateDB(t)
path := db.Path()
defer db.MustClose()
// Close database.
if err := db.DB.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
@ -218,40 +215,29 @@ func TestOpen_ErrChecksum(t *testing.T) {
// https://github.com/boltdb/bolt/issues/291
func TestOpen_Size(t *testing.T) {
// Open a data file.
db := MustOpenDB()
path := db.Path()
defer db.MustClose()
db := btesting.MustCreateDB(t)
pagesize := db.Info().PageSize
// Insert until we get above the minimum 4MB size.
if err := db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists([]byte("data"))
for i := 0; i < 10000; i++ {
if err := b.Put([]byte(fmt.Sprintf("%04d", i)), make([]byte, 1000)); err != nil {
t.Fatal(err)
}
}
return nil
}); err != nil {
err := db.Fill([]byte("data"), 1, 10000,
func(tx int, k int) []byte { return []byte(fmt.Sprintf("%04d", k)) },
func(tx int, k int) []byte { return make([]byte, 1000) },
)
if err != nil {
t.Fatal(err)
}
// Close database and grab the size.
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
path := db.Path()
db.MustClose()
sz := fileSize(path)
if sz == 0 {
t.Fatalf("unexpected new file size: %d", sz)
}
// Reopen database, update, and check size again.
db0, err := bolt.Open(path, 0666, nil)
if err != nil {
t.Fatal(err)
}
if err := db0.Update(func(tx *bolt.Tx) error {
db.MustReopen()
if err := db.Update(func(tx *bolt.Tx) error {
if err := tx.Bucket([]byte("data")).Put([]byte{0}, []byte{0}); err != nil {
t.Fatal(err)
}
@ -259,7 +245,7 @@ func TestOpen_Size(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
if err := db0.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
newSz := fileSize(path)
@ -282,9 +268,8 @@ func TestOpen_Size_Large(t *testing.T) {
}
// Open a data file.
db := MustOpenDB()
db := btesting.MustCreateDB(t)
path := db.Path()
defer db.MustClose()
pagesize := db.Info().PageSize
@ -306,7 +291,7 @@ func TestOpen_Size_Large(t *testing.T) {
}
// Close database and grab the size.
if err := db.DB.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
sz := fileSize(path)
@ -464,8 +449,7 @@ func TestDB_Open_InitialMmapSize(t *testing.T) {
// TestDB_Open_ReadOnly checks a database in read only mode can read but not write.
func TestDB_Open_ReadOnly(t *testing.T) {
// Create a writable db, write k-v and close it.
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
@ -479,11 +463,11 @@ func TestDB_Open_ReadOnly(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
if err := db.DB.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
f := db.f
f := db.Path()
o := &bolt.Options{ReadOnly: true}
readOnlyDB, err := bolt.Open(f, 0666, o)
if err != nil {
@ -520,13 +504,11 @@ func TestDB_Open_ReadOnly(t *testing.T) {
func TestOpen_BigPage(t *testing.T) {
pageSize := os.Getpagesize()
db1 := MustOpenWithOption(&bolt.Options{PageSize: pageSize * 2})
defer db1.MustClose()
db1 := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize * 2})
db2 := MustOpenWithOption(&bolt.Options{PageSize: pageSize * 4})
defer db2.MustClose()
db2 := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize * 4})
if db1sz, db2sz := fileSize(db1.f), fileSize(db2.f); db1sz >= db2sz {
if db1sz, db2sz := fileSize(db1.Path()), fileSize(db2.Path()); db1sz >= db2sz {
t.Errorf("expected %d < %d", db1sz, db2sz)
}
}
@ -535,8 +517,7 @@ func TestOpen_BigPage(t *testing.T) {
// write-out after no free list sync will recover the free list
// and write it out.
func TestOpen_RecoverFreeList(t *testing.T) {
db := MustOpenWithOption(&bolt.Options{NoFreelistSync: true})
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, &bolt.Options{NoFreelistSync: true})
// Write some pages.
tx, err := db.Begin(true)
@ -575,9 +556,7 @@ func TestOpen_RecoverFreeList(t *testing.T) {
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
db.MustClose()
// Record freelist count from opening with NoFreelistSync.
db.MustReopen()
@ -585,12 +564,10 @@ func TestOpen_RecoverFreeList(t *testing.T) {
if freepages == 0 {
t.Fatalf("no free pages on NoFreelistSync reopen")
}
if err := db.DB.Close(); err != nil {
t.Fatal(err)
}
db.MustClose()
// Check free page count is reconstructed when opened with freelist sync.
db.o = &bolt.Options{}
db.SetOptions(&bolt.Options{})
db.MustReopen()
// One less free page for syncing the free list on open.
freepages--
@ -609,33 +586,22 @@ func TestDB_Begin_ErrDatabaseNotOpen(t *testing.T) {
// Ensure that a read-write transaction can be retrieved.
func TestDB_BeginRW(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
} else if tx == nil {
t.Fatal("expected tx")
}
require.NoError(t, err)
require.NotNil(t, tx, "expected tx")
defer func() { require.NoError(t, tx.Commit()) }()
if tx.DB() != db.DB {
t.Fatal("unexpected tx database")
} else if !tx.Writable() {
t.Fatal("expected writable tx")
}
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
require.True(t, tx.Writable(), "expected writable tx")
require.Same(t, db.DB, tx.DB())
}
// TestDB_Concurrent_WriteTo checks that issuing WriteTo operations concurrently
// with commits does not produce corrupted db files.
func TestDB_Concurrent_WriteTo(t *testing.T) {
o := &bolt.Options{NoFreelistSync: false}
db := MustOpenWithOption(o)
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, o)
var wg sync.WaitGroup
wtxs, rtxs := 5, 5
@ -656,8 +622,7 @@ func TestDB_Concurrent_WriteTo(t *testing.T) {
panic(err)
}
f.Close()
snap := &DB{nil, f.Name(), o}
snap.MustReopen()
snap := btesting.MustOpenDBWithOption(t, f.Name(), o)
defer snap.MustClose()
snap.MustCheck()
}
@ -708,7 +673,7 @@ func TestDB_Close_PendingTx_RO(t *testing.T) { testDB_Close_PendingTx(t, false)
// Ensure that a database cannot close while transactions are open.
func testDB_Close_PendingTx(t *testing.T, writable bool) {
db := MustOpenDB()
db := btesting.MustCreateDB(t)
// Start transaction.
tx, err := db.Begin(writable)
@ -758,8 +723,7 @@ func testDB_Close_PendingTx(t *testing.T, writable bool) {
// Ensure a database can provide a transactional block.
func TestDB_Update(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -807,8 +771,7 @@ func TestDB_Update_Closed(t *testing.T) {
// Ensure a panic occurs while trying to commit a managed transaction.
func TestDB_Update_ManualCommit(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var panicked bool
if err := db.Update(func(tx *bolt.Tx) error {
@ -833,8 +796,7 @@ func TestDB_Update_ManualCommit(t *testing.T) {
// Ensure a panic occurs while trying to rollback a managed transaction.
func TestDB_Update_ManualRollback(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var panicked bool
if err := db.Update(func(tx *bolt.Tx) error {
@ -859,8 +821,7 @@ func TestDB_Update_ManualRollback(t *testing.T) {
// Ensure a panic occurs while trying to commit a managed transaction.
func TestDB_View_ManualCommit(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var panicked bool
if err := db.View(func(tx *bolt.Tx) error {
@ -885,8 +846,7 @@ func TestDB_View_ManualCommit(t *testing.T) {
// Ensure a panic occurs while trying to rollback a managed transaction.
func TestDB_View_ManualRollback(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var panicked bool
if err := db.View(func(tx *bolt.Tx) error {
@ -911,8 +871,7 @@ func TestDB_View_ManualRollback(t *testing.T) {
// Ensure a write transaction that panics does not hold open locks.
func TestDB_Update_Panic(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Panic during update but recover.
func() {
@ -955,8 +914,7 @@ func TestDB_Update_Panic(t *testing.T) {
// Ensure a database can return an error through a read-only transactional block.
func TestDB_View_Error(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.View(func(tx *bolt.Tx) error {
return errors.New("xxx")
@ -967,8 +925,7 @@ func TestDB_View_Error(t *testing.T) {
// Ensure a read transaction that panics does not hold open locks.
func TestDB_View_Panic(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -1010,8 +967,7 @@ func TestDB_View_Panic(t *testing.T) {
// Ensure that DB stats can be returned.
func TestDB_Stats(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
return err
@ -1031,8 +987,7 @@ func TestDB_Stats(t *testing.T) {
// Ensure that database pages are in expected order and type.
func TestDB_Consistency(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
return err
@ -1117,8 +1072,7 @@ func TestDBStats_Sub(t *testing.T) {
// Ensure two functions can perform updates in a single batch.
func TestDB_Batch(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
@ -1162,8 +1116,7 @@ func TestDB_Batch(t *testing.T) {
}
func TestDB_Batch_Panic(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var sentinel int
var bork = &sentinel
@ -1193,8 +1146,7 @@ func TestDB_Batch_Panic(t *testing.T) {
}
func TestDB_BatchFull(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
return err
@ -1252,8 +1204,7 @@ func TestDB_BatchFull(t *testing.T) {
}
func TestDB_BatchTime(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets"))
return err
@ -1441,8 +1392,8 @@ func ExampleDB_Begin() {
}
func BenchmarkDBBatchAutomatic(b *testing.B) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(b)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("bench"))
return err
@ -1486,8 +1437,7 @@ func BenchmarkDBBatchAutomatic(b *testing.B) {
}
func BenchmarkDBBatchSingle(b *testing.B) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(b)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("bench"))
return err
@ -1530,8 +1480,7 @@ func BenchmarkDBBatchSingle(b *testing.B) {
}
func BenchmarkDBBatchManual10x100(b *testing.B) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(b)
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("bench"))
return err
@ -1584,7 +1533,7 @@ func BenchmarkDBBatchManual10x100(b *testing.B) {
validateBatchBench(b, db)
}
func validateBatchBench(b *testing.B, db *DB) {
func validateBatchBench(b *testing.B, db *btesting.DB) {
var rollback = errors.New("sentinel error to cause rollback")
validate := func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("bench"))
@ -1619,137 +1568,6 @@ func validateBatchBench(b *testing.B, db *DB) {
}
}
// DB is a test wrapper for bolt.DB.
type DB struct {
*bolt.DB
f string
o *bolt.Options
}
// MustOpenDB returns a new, open DB at a temporary location.
func MustOpenDB() *DB {
return MustOpenWithOption(nil)
}
// MustOpenDBWithOption returns a new, open DB at a temporary location with given options.
func MustOpenWithOption(o *bolt.Options) *DB {
f := tempfile()
if o == nil {
o = bolt.DefaultOptions
}
freelistType := bolt.FreelistArrayType
if env := os.Getenv(bolt.TestFreelistType); env == string(bolt.FreelistMapType) {
freelistType = bolt.FreelistMapType
}
o.FreelistType = freelistType
db, err := bolt.Open(f, 0666, o)
if err != nil {
panic(err)
}
return &DB{
DB: db,
f: f,
o: o,
}
}
// Close closes the database and deletes the underlying file.
func (db *DB) Close() error {
// Log statistics.
if *statsFlag {
db.PrintStats()
}
// Check database consistency after every test.
db.MustCheck()
// Close database and remove file.
defer os.Remove(db.Path())
return db.DB.Close()
}
// MustClose closes the database and deletes the underlying file. Panic on error.
func (db *DB) MustClose() {
if err := db.Close(); err != nil {
panic(err)
}
}
// MustReopen reopen the database. Panic on error.
func (db *DB) MustReopen() {
indb, err := bolt.Open(db.f, 0666, db.o)
if err != nil {
panic(err)
}
db.DB = indb
}
// PrintStats prints the database stats
func (db *DB) PrintStats() {
var stats = db.Stats()
fmt.Printf("[db] %-20s %-20s %-20s\n",
fmt.Sprintf("pg(%d/%d)", stats.TxStats.PageCount, stats.TxStats.PageAlloc),
fmt.Sprintf("cur(%d)", stats.TxStats.CursorCount),
fmt.Sprintf("node(%d/%d)", stats.TxStats.NodeCount, stats.TxStats.NodeDeref),
)
fmt.Printf(" %-20s %-20s %-20s\n",
fmt.Sprintf("rebal(%d/%v)", stats.TxStats.Rebalance, truncDuration(stats.TxStats.RebalanceTime)),
fmt.Sprintf("spill(%d/%v)", stats.TxStats.Spill, truncDuration(stats.TxStats.SpillTime)),
fmt.Sprintf("w(%d/%v)", stats.TxStats.Write, truncDuration(stats.TxStats.WriteTime)),
)
}
// MustCheck runs a consistency check on the database and panics if any errors are found.
func (db *DB) MustCheck() {
if err := db.Update(func(tx *bolt.Tx) error {
// Collect all the errors.
var errors []error
for err := range tx.Check() {
errors = append(errors, err)
if len(errors) > 10 {
break
}
}
// If errors occurred, copy the DB and print the errors.
if len(errors) > 0 {
var path = tempfile()
if err := tx.CopyFile(path, 0600); err != nil {
panic(err)
}
// Print errors.
fmt.Print("\n\n")
fmt.Printf("consistency check failed (%d errors)\n", len(errors))
for _, err := range errors {
fmt.Println(err)
}
fmt.Println("")
fmt.Println("db saved to:")
fmt.Println(path)
fmt.Print("\n\n")
os.Exit(-1)
}
return nil
}); err != nil && err != bolt.ErrDatabaseNotOpen {
panic(err)
}
}
// CopyTempFile copies a database to a temporary file.
func (db *DB) CopyTempFile() {
path := tempfile()
if err := db.View(func(tx *bolt.Tx) error {
return tx.CopyFile(path, 0600)
}); err != nil {
panic(err)
}
fmt.Println("db copied to: ", path)
}
// tempfile returns a temporary file path.
func tempfile() string {
f, err := os.CreateTemp("", "bolt-")
@ -1772,10 +1590,6 @@ func trunc(b []byte, length int) []byte {
return b
}
func truncDuration(d time.Duration) string {
return regexp.MustCompile(`^(\d+)(\.\d+)`).ReplaceAllString(d.String(), "$1")
}
func fileSize(path string) int64 {
fi, err := os.Stat(path)
if err != nil {

2
go.mod
View File

@ -10,5 +10,7 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/tools v0.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

4
go.sum
View File

@ -10,8 +10,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,205 @@
package btesting
import (
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"testing"
"time"
"github.com/stretchr/testify/require"
bolt "go.etcd.io/bbolt"
)
var statsFlag = flag.Bool("stats", false, "show performance stats")
// TestFreelistType is used as a env variable for test to indicate the backend type
const TestFreelistType = "TEST_FREELIST_TYPE"
// DB is a test wrapper for bolt.DB.
type DB struct {
*bolt.DB
f string
o *bolt.Options
t testing.TB
}
// MustCreateDB returns a new, open DB at a temporary location.
func MustCreateDB(t testing.TB) *DB {
return MustCreateDBWithOption(t, nil)
}
// MustCreateDBWithOption returns a new, open DB at a temporary location with given options.
func MustCreateDBWithOption(t testing.TB, o *bolt.Options) *DB {
f := filepath.Join(t.TempDir(), "db")
return MustOpenDBWithOption(t, f, o)
}
func MustOpenDBWithOption(t testing.TB, f string, o *bolt.Options) *DB {
t.Logf("Opening bbolt DB at: %s", f)
if o == nil {
o = bolt.DefaultOptions
}
freelistType := bolt.FreelistArrayType
if env := os.Getenv(TestFreelistType); env == string(bolt.FreelistMapType) {
freelistType = bolt.FreelistMapType
}
o.FreelistType = freelistType
db, err := bolt.Open(f, 0666, o)
require.NoError(t, err)
resDB := &DB{
DB: db,
f: f,
o: o,
t: t,
}
t.Cleanup(resDB.PostTestCleanup)
return resDB
}
func (db *DB) PostTestCleanup() {
// Check database consistency after every test.
if db.DB != nil {
db.MustCheck()
db.MustClose()
}
}
// Close closes the database but does NOT delete the underlying file.
func (db *DB) Close() error {
if db.DB != nil {
// Log statistics.
if *statsFlag {
db.PrintStats()
}
db.t.Logf("Closing bbolt DB at: %s", db.f)
err := db.DB.Close()
if err != nil {
return err
}
db.DB = nil
}
return nil
}
// MustClose closes the database but does NOT delete the underlying file.
func (db *DB) MustClose() {
err := db.Close()
require.NoError(db.t, err)
}
func (db *DB) MustDeleteFile() {
err := os.Remove(db.Path())
require.NoError(db.t, err)
}
func (db *DB) SetOptions(o *bolt.Options) {
db.o = o
}
// MustReopen reopen the database. Panic on error.
func (db *DB) MustReopen() {
if db.DB != nil {
panic("Please call Close() before MustReopen()")
}
db.t.Logf("Reopening bbolt DB at: %s", db.f)
indb, err := bolt.Open(db.Path(), 0666, db.o)
require.NoError(db.t, err)
db.DB = indb
}
// MustCheck runs a consistency check on the database and panics if any errors are found.
func (db *DB) MustCheck() {
err := db.Update(func(tx *bolt.Tx) error {
// Collect all the errors.
var errors []error
for err := range tx.Check() {
errors = append(errors, err)
if len(errors) > 10 {
break
}
}
// If errors occurred, copy the DB and print the errors.
if len(errors) > 0 {
var path = filepath.Join(db.t.TempDir(), "db.backup")
err := tx.CopyFile(path, 0600)
require.NoError(db.t, err)
// Print errors.
fmt.Print("\n\n")
fmt.Printf("consistency check failed (%d errors)\n", len(errors))
for _, err := range errors {
fmt.Println(err)
}
fmt.Println("")
fmt.Println("db saved to:")
fmt.Println(path)
fmt.Print("\n\n")
os.Exit(-1)
}
return nil
})
require.NoError(db.t, err)
}
// Fill - fills the DB using numTx transactions and numKeysPerTx.
func (db *DB) Fill(bucket []byte, numTx int, numKeysPerTx int,
keyGen func(tx int, key int) []byte,
valueGen func(tx int, key int) []byte) error {
for tr := 0; tr < numTx; tr++ {
err := db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists(bucket)
for i := 0; i < numKeysPerTx; i++ {
if err := b.Put(keyGen(tr, i), valueGen(tr, i)); err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
}
return nil
}
func (db *DB) Path() string {
return db.f
}
// CopyTempFile copies a database to a temporary file.
func (db *DB) CopyTempFile() {
path := filepath.Join(db.t.TempDir(), "db.copy")
err := db.View(func(tx *bolt.Tx) error {
return tx.CopyFile(path, 0600)
})
require.NoError(db.t, err)
fmt.Println("db copied to: ", path)
}
// PrintStats prints the database stats
func (db *DB) PrintStats() {
var stats = db.Stats()
fmt.Printf("[db] %-20s %-20s %-20s\n",
fmt.Sprintf("pg(%d/%d)", stats.TxStats.PageCount, stats.TxStats.PageAlloc),
fmt.Sprintf("cur(%d)", stats.TxStats.CursorCount),
fmt.Sprintf("node(%d/%d)", stats.TxStats.NodeCount, stats.TxStats.NodeDeref),
)
fmt.Printf(" %-20s %-20s %-20s\n",
fmt.Sprintf("rebal(%d/%v)", stats.TxStats.Rebalance, truncDuration(stats.TxStats.RebalanceTime)),
fmt.Sprintf("spill(%d/%v)", stats.TxStats.Spill, truncDuration(stats.TxStats.SpillTime)),
fmt.Sprintf("w(%d/%v)", stats.TxStats.Write, truncDuration(stats.TxStats.WriteTime)),
)
}
func truncDuration(d time.Duration) string {
return regexp.MustCompile(`^(\d+)(\.\d+)`).ReplaceAllString(d.String(), "$1")
}

View File

@ -9,6 +9,7 @@ import (
"testing"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
func TestSimulate_1op_1p(t *testing.T) { testSimulate(t, nil, 1, 1, 1) }
@ -43,8 +44,7 @@ func testSimulate(t *testing.T, openOption *bolt.Options, round, threadCount, pa
var versions = make(map[int]*QuickDB)
versions[1] = NewQuickDB()
db := MustOpenWithOption(openOption)
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, openOption)
var mutex sync.Mutex
@ -146,6 +146,9 @@ func testSimulate(t *testing.T, openOption *bolt.Options, round, threadCount, pa
}
db.MustClose()
// I have doubts the DB drop is indented here (as 'versions' is not being reset).
// But I'm preserving for now the original behavior.
db.MustDeleteFile()
db.MustReopen()
}

View File

@ -9,12 +9,12 @@ import (
"testing"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
// TestTx_Check_ReadOnly tests consistency checking on a ReadOnly database.
func TestTx_Check_ReadOnly(t *testing.T) {
db := MustOpenDB()
defer db.Close()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -27,11 +27,11 @@ func TestTx_Check_ReadOnly(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
if err := db.DB.Close(); err != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
readOnlyDB, err := bolt.Open(db.f, 0666, &bolt.Options{ReadOnly: true})
readOnlyDB, err := bolt.Open(db.Path(), 0666, &bolt.Options{ReadOnly: true})
if err != nil {
t.Fatal(err)
}
@ -65,8 +65,7 @@ func TestTx_Check_ReadOnly(t *testing.T) {
// Ensure that committing a closed transaction returns an error.
func TestTx_Commit_ErrTxClosed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
@ -87,8 +86,7 @@ func TestTx_Commit_ErrTxClosed(t *testing.T) {
// Ensure that rolling back a closed transaction returns an error.
func TestTx_Rollback_ErrTxClosed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
@ -105,8 +103,7 @@ func TestTx_Rollback_ErrTxClosed(t *testing.T) {
// Ensure that committing a read-only transaction returns an error.
func TestTx_Commit_ErrTxNotWritable(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(false)
if err != nil {
t.Fatal(err)
@ -123,8 +120,7 @@ func TestTx_Commit_ErrTxNotWritable(t *testing.T) {
// Ensure that a transaction can retrieve a cursor on the root bucket.
func TestTx_Cursor(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
t.Fatal(err)
@ -161,8 +157,7 @@ func TestTx_Cursor(t *testing.T) {
// Ensure that creating a bucket with a read-only transaction returns an error.
func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.View(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("foo"))
if err != bolt.ErrTxNotWritable {
@ -176,8 +171,7 @@ func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) {
// Ensure that creating a bucket on a closed transaction returns an error.
func TestTx_CreateBucket_ErrTxClosed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
@ -193,8 +187,7 @@ func TestTx_CreateBucket_ErrTxClosed(t *testing.T) {
// Ensure that a Tx can retrieve a bucket.
func TestTx_Bucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
t.Fatal(err)
@ -210,8 +203,7 @@ func TestTx_Bucket(t *testing.T) {
// Ensure that a Tx retrieving a non-existent key returns nil.
func TestTx_Get_NotFound(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -232,8 +224,7 @@ func TestTx_Get_NotFound(t *testing.T) {
// Ensure that a bucket can be created and retrieved.
func TestTx_CreateBucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Create a bucket.
if err := db.Update(func(tx *bolt.Tx) error {
@ -261,8 +252,7 @@ func TestTx_CreateBucket(t *testing.T) {
// Ensure that a bucket can be created if it doesn't already exist.
func TestTx_CreateBucketIfNotExists(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
// Create bucket.
if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
@ -296,8 +286,7 @@ func TestTx_CreateBucketIfNotExists(t *testing.T) {
// Ensure transaction returns an error if creating an unnamed bucket.
func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists([]byte{}); err != bolt.ErrBucketNameRequired {
t.Fatalf("unexpected error: %s", err)
@ -315,8 +304,7 @@ func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) {
// Ensure that a bucket cannot be created twice.
func TestTx_CreateBucket_ErrBucketExists(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Create a bucket.
if err := db.Update(func(tx *bolt.Tx) error {
@ -341,8 +329,7 @@ func TestTx_CreateBucket_ErrBucketExists(t *testing.T) {
// Ensure that a bucket is created with a non-blank name.
func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucket(nil); err != bolt.ErrBucketNameRequired {
t.Fatalf("unexpected error: %s", err)
@ -355,8 +342,7 @@ func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) {
// Ensure that a bucket can be deleted.
func TestTx_DeleteBucket(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
// Create a bucket and add a value.
if err := db.Update(func(tx *bolt.Tx) error {
@ -402,8 +388,7 @@ func TestTx_DeleteBucket(t *testing.T) {
// Ensure that deleting a bucket on a closed transaction returns an error.
func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
@ -418,8 +403,7 @@ func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) {
// Ensure that deleting a bucket with a read-only transaction returns an error.
func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.View(func(tx *bolt.Tx) error {
if err := tx.DeleteBucket([]byte("foo")); err != bolt.ErrTxNotWritable {
t.Fatalf("unexpected error: %s", err)
@ -432,8 +416,7 @@ func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
// Ensure that nothing happens when deleting a bucket that doesn't exist.
func TestTx_DeleteBucket_NotFound(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
if err := tx.DeleteBucket([]byte("widgets")); err != bolt.ErrBucketNotFound {
t.Fatalf("unexpected error: %s", err)
@ -447,8 +430,7 @@ func TestTx_DeleteBucket_NotFound(t *testing.T) {
// Ensure that no error is returned when a tx.ForEach function does not return
// an error.
func TestTx_ForEach_NoError(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -471,8 +453,7 @@ func TestTx_ForEach_NoError(t *testing.T) {
// Ensure that an error is returned when a tx.ForEach function returns an error.
func TestTx_ForEach_WithError(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -496,8 +477,7 @@ func TestTx_ForEach_WithError(t *testing.T) {
// Ensure that Tx commit handlers are called after a transaction successfully commits.
func TestTx_OnCommit(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var x int
if err := db.Update(func(tx *bolt.Tx) error {
@ -516,8 +496,7 @@ func TestTx_OnCommit(t *testing.T) {
// Ensure that Tx commit handlers are NOT called after a transaction rolls back.
func TestTx_OnCommit_Rollback(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
var x int
if err := db.Update(func(tx *bolt.Tx) error {
@ -536,8 +515,7 @@ func TestTx_OnCommit_Rollback(t *testing.T) {
// Ensure that the database can be copied to a file path.
func TestTx_CopyFile(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
path := tempfile()
if err := db.Update(func(tx *bolt.Tx) error {
@ -607,8 +585,7 @@ func (f *failWriter) Write(p []byte) (n int, err error) {
// Ensure that Copy handles write errors right.
func TestTx_CopyFile_Error_Meta(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -634,8 +611,7 @@ func TestTx_CopyFile_Error_Meta(t *testing.T) {
// Ensure that Copy handles write errors right.
func TestTx_CopyFile_Error_Normal(t *testing.T) {
db := MustOpenDB()
defer db.MustClose()
db := btesting.MustCreateDB(t)
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
@ -719,8 +695,7 @@ func TestTx_releaseRange(t *testing.T) {
// Set initial mmap size well beyond the limit we will hit in this
// test, since we are testing with long running read transactions
// and will deadlock if db.grow is triggered.
db := MustOpenWithOption(&bolt.Options{InitialMmapSize: os.Getpagesize() * 100})
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, &bolt.Options{InitialMmapSize: os.Getpagesize() * 100})
bucket := "bucket"

View File

@ -7,16 +7,17 @@ import (
"fmt"
"testing"
bolt "go.etcd.io/bbolt"
"golang.org/x/sys/unix"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
func TestMlock_DbOpen(t *testing.T) {
// 32KB
skipOnMemlockLimitBelow(t, 32*1024)
db := MustOpenWithOption(&bolt.Options{Mlock: true})
defer db.MustClose()
btesting.MustCreateDBWithOption(t, &bolt.Options{Mlock: true})
}
// Test change between "empty" (16KB) and "non-empty" db
@ -24,8 +25,7 @@ func TestMlock_DbCanGrow_Small(t *testing.T) {
// 32KB
skipOnMemlockLimitBelow(t, 32*1024)
db := MustOpenWithOption(&bolt.Options{Mlock: true})
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, &bolt.Options{Mlock: true})
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("bucket"))
@ -58,25 +58,24 @@ func TestMlock_DbCanGrow_Big(t *testing.T) {
chunksBefore := 64
chunksAfter := 64
db := MustOpenWithOption(&bolt.Options{Mlock: true})
defer db.MustClose()
db := btesting.MustCreateDBWithOption(t, &bolt.Options{Mlock: true})
for chunk := 0; chunk < chunksBefore; chunk++ {
insertChunk(t, db, chunk)
}
dbSize := fileSize(db.f)
dbSize := fileSize(db.Path())
for chunk := 0; chunk < chunksAfter; chunk++ {
insertChunk(t, db, chunksBefore+chunk)
}
newDbSize := fileSize(db.f)
newDbSize := fileSize(db.Path())
if newDbSize <= dbSize {
t.Errorf("db didn't grow: %v <= %v", newDbSize, dbSize)
}
}
func insertChunk(t *testing.T, db *DB, chunkId int) {
func insertChunk(t *testing.T, db *btesting.DB, chunkId int) {
chunkSize := 1024
if err := db.Update(func(tx *bolt.Tx) error {