Skip to content

Commit 29d1e3d

Browse files
committed
refactor/simplify the test case TestTx_Check_CorruptPage
Signed-off-by: Benjamin Wang <benjamin.ahrtr@gmail.com>
1 parent 20e9611 commit 29d1e3d

File tree

1 file changed

+38
-73
lines changed

1 file changed

+38
-73
lines changed

tx_check_test.go

+38-73
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package bbolt_test
22

33
import (
4-
"bytes"
5-
"encoding/binary"
64
"fmt"
75
"math/rand"
86
"testing"
9-
"unsafe"
107

118
"github.com/stretchr/testify/require"
129

@@ -17,26 +14,19 @@ import (
1714
)
1815

1916
func TestTx_Check_CorruptPage(t *testing.T) {
20-
bucketKey := "testBucket"
21-
2217
t.Log("Creating db file.")
23-
db := btesting.MustCreateDBWithOption(t, &bbolt.Options{PageSize: pageSize})
24-
defer func() {
25-
require.NoError(t, db.Close())
26-
}()
27-
28-
uErr := db.Update(func(tx *bbolt.Tx) error {
29-
t.Logf("Creating bucket '%v'.", bucketKey)
30-
b, bErr := tx.CreateBucketIfNotExists([]byte(bucketKey))
31-
require.NoError(t, bErr)
32-
t.Logf("Generating random data in bucket '%v'.", bucketKey)
33-
generateSampleDataInBucket(t, b, pageSize, 3)
34-
return nil
35-
})
36-
require.NoError(t, uErr)
18+
db := btesting.MustCreateDBWithOption(t, &bbolt.Options{PageSize: 4096})
19+
20+
// Each page can hold roughly 20 key/values pair, so 100 such
21+
// key/value pairs will consume about 5 leaf pages.
22+
err := db.Fill([]byte("data"), 1, 100,
23+
func(tx int, k int) []byte { return []byte(fmt.Sprintf("%04d", k)) },
24+
func(tx int, k int) []byte { return make([]byte, 100) },
25+
)
26+
require.NoError(t, err)
3727

38-
t.Logf("Corrupting random leaf page in bucket '%v'.", bucketKey)
39-
victimPageId, validPageIds := corruptLeafPage(t, db.DB)
28+
t.Log("Corrupting random leaf page.")
29+
victimPageId, validPageIds := corruptRandomLeafPage(t, db.DB)
4030

4131
t.Log("Running consistency check.")
4232
vErr := db.View(func(tx *bbolt.Tx) error {
@@ -61,45 +51,53 @@ func TestTx_Check_CorruptPage(t *testing.T) {
6151
return nil
6252
})
6353
require.NoError(t, vErr)
54+
t.Log("All check passed")
55+
56+
// Manually close the db, otherwise the PostTestCleanup will
57+
// check the db again and accordingly fail the test.
58+
db.MustClose()
6459
}
6560

66-
// corruptLeafPage write an invalid leafPageElement into the victim page.
67-
func corruptLeafPage(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
68-
t.Helper()
69-
victimPageId, validPageIds = findVictimPageId(t, db)
61+
// corruptRandomLeafPage corrupts one random leaf page.
62+
func corruptRandomLeafPage(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
63+
victimPageId, validPageIds = pickupRandomLeafPage(t, db)
7064
victimPage, victimBuf, err := guts_cli.ReadPage(db.Path(), uint64(victimPageId))
7165
require.NoError(t, err)
7266
require.True(t, victimPage.IsLeafPage())
73-
require.True(t, victimPage.Count() > 0)
74-
// Dumping random bytes in victim page for corruption.
75-
copy(victimBuf[32:], generateCorruptionBytes(t))
67+
require.True(t, victimPage.Count() > 1)
68+
69+
// intentionally make the second key < the first key.
70+
element := victimPage.LeafPageElement(1)
71+
key := element.Key()
72+
key[0] = 0
73+
7674
// Write the corrupt page to db file.
7775
err = guts_cli.WritePage(db.Path(), victimBuf)
7876
require.NoError(t, err)
7977
return victimPageId, validPageIds
8078
}
8179

82-
// findVictimPageId finds all the leaf pages of a bucket and picks a random leaf page as a victim to be corrupted.
83-
func findVictimPageId(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
84-
t.Helper()
85-
// Read DB's RootPage.
80+
// pickupRandomLeafPage picks up a random leaf page.
81+
func pickupRandomLeafPage(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, validPageIds []common.Pgid) {
82+
// Read DB's RootPage, which should be a leaf page.
8683
rootPageId, _, err := guts_cli.GetRootPage(db.Path())
8784
require.NoError(t, err)
8885
rootPage, _, err := guts_cli.ReadPage(db.Path(), uint64(rootPageId))
8986
require.NoError(t, err)
9087
require.True(t, rootPage.IsLeafPage())
91-
require.Equal(t, 1, len(rootPage.LeafPageElements()))
92-
// Find Bucket's RootPage.
88+
89+
// The leaf page contains only one item, namely the bucket
90+
require.Equal(t, uint16(1), rootPage.Count())
9391
lpe := rootPage.LeafPageElement(uint16(0))
94-
require.Equal(t, uint32(common.BranchPageFlag), lpe.Flags())
95-
k := lpe.Key()
96-
require.Equal(t, "testBucket", string(k))
92+
require.True(t, lpe.IsBucketEntry())
93+
94+
// The bucket should be pointing to a branch page
9795
bucketRootPageId := lpe.Bucket().RootPage()
98-
// Read Bucket's RootPage.
9996
bucketRootPage, _, err := guts_cli.ReadPage(db.Path(), uint64(bucketRootPageId))
10097
require.NoError(t, err)
101-
require.Equal(t, uint16(common.BranchPageFlag), bucketRootPage.Flags())
102-
// Retrieve Bucket's PageIds
98+
require.True(t, bucketRootPage.IsBranchPage())
99+
100+
// Retrieve all the leaf pages included in the branch page, and pick up random one from them.
103101
var bucketPageIds []common.Pgid
104102
for _, bpe := range bucketRootPage.BranchPageElements() {
105103
bucketPageIds = append(bucketPageIds, bpe.Pgid())
@@ -109,36 +107,3 @@ func findVictimPageId(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, val
109107
validPageIds = append(bucketPageIds[:randomIdx], bucketPageIds[randomIdx+1:]...)
110108
return victimPageId, validPageIds
111109
}
112-
113-
// generateSampleDataInBucket fill in sample data into given bucket to create the given
114-
// number of leafPages. To control the number of leafPages, sample data are generated in order.
115-
func generateSampleDataInBucket(t testing.TB, bk *bbolt.Bucket, pageSize int, lPages int) {
116-
t.Helper()
117-
maxBytesInPage := int(bk.FillPercent * float64(pageSize))
118-
currentKey := 1
119-
currentVal := 100
120-
for i := 0; i < lPages; i++ {
121-
currentSize := common.PageHeaderSize
122-
for {
123-
err := bk.Put([]byte(fmt.Sprintf("key_%d", currentKey)), []byte(fmt.Sprintf("val_%d", currentVal)))
124-
require.NoError(t, err)
125-
currentSize += common.LeafPageElementSize + unsafe.Sizeof(currentKey) + unsafe.Sizeof(currentVal)
126-
if int(currentSize) >= maxBytesInPage {
127-
break
128-
}
129-
currentKey++
130-
currentVal++
131-
}
132-
}
133-
}
134-
135-
// generateCorruptionBytes returns random bytes to corrupt a page.
136-
// It inserts a page element which violates the btree key order if no panic is expected.
137-
func generateCorruptionBytes(t testing.TB) []byte {
138-
t.Helper()
139-
invalidLPE := common.NewLeafPageElement(0, 0, 0, 0)
140-
var buf bytes.Buffer
141-
err := binary.Write(&buf, binary.BigEndian, invalidLPE)
142-
require.NoError(t, err)
143-
return buf.Bytes()
144-
}

0 commit comments

Comments
 (0)