@@ -2,8 +2,11 @@ package bbolt_test
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding/binary"
5
6
"errors"
6
7
"fmt"
8
+ "go.etcd.io/bbolt/internal/common"
9
+ "go.etcd.io/bbolt/internal/guts_cli"
7
10
"log"
8
11
"os"
9
12
"runtime"
@@ -18,6 +21,105 @@ import (
18
21
"go.etcd.io/bbolt/internal/btesting"
19
22
)
20
23
24
+ func TestTx_Check_Page (t * testing.T ) {
25
+ bucketKey := "testBucket"
26
+ db := btesting .MustCreateDBWithOption (t , & bolt.Options {PageSize : pageSize })
27
+ defer db .MustDeleteFile ()
28
+
29
+ // arrange
30
+ if err := db .Update (func (tx * bolt.Tx ) error {
31
+ b , err := tx .CreateBucketIfNotExists ([]byte (bucketKey ))
32
+ if err != nil {
33
+ t .Fatal (err )
34
+ }
35
+ // insert random data
36
+ for i := 0 ; i < 10 ; i ++ {
37
+ generateSampleDataInBucket (t , b , 3 )
38
+ }
39
+ return nil
40
+ }); err != nil {
41
+ t .Fatal (err )
42
+ }
43
+ db .MustCheck ()
44
+
45
+ // act : corrupt a leaf page
46
+ corruptLeafPageFromDB (t , db .Path ())
47
+
48
+ // assert: expected an error after check
49
+ if err := db .View (func (tx * bolt.Tx ) error {
50
+ for err := range tx .Check () {
51
+ fmt .Println (err )
52
+ }
53
+ return nil
54
+ }); err != nil {
55
+ t .Fatal (err )
56
+ }
57
+ db .MustClose ()
58
+ }
59
+
60
+ func corruptLeafPageFromDB (t testing.TB , dbPath string ) {
61
+ _ , hwm , err := guts_cli .ReadPageAndHWMSize (dbPath )
62
+ if err != nil {
63
+ t .Fatalf ("error reading HWM from dbPath %v: %v" , dbPath , err )
64
+ }
65
+
66
+ var victimPage * common.Page
67
+ var victimBuf []byte
68
+
69
+ // find first leaf page starting page after root bucket's page
70
+ for pageID := uint64 (4 ); pageID < uint64 (hwm ); {
71
+ p , buf , err := guts_cli .ReadPage (dbPath , pageID )
72
+ if err != nil {
73
+ t .Fatalf ("error reading page %v from dbPath %v: %v" , pageID , dbPath , err )
74
+ }
75
+
76
+ if p .IsLeafPage () {
77
+ victimPage = p
78
+ victimBuf = buf
79
+ break
80
+ }
81
+ }
82
+
83
+ if victimPage == nil || victimBuf == nil {
84
+ t .Fatalf ("no leaf page is found in dbPath %v" , dbPath )
85
+ }
86
+
87
+ // corrupt page's type
88
+ buf := []byte ("this is to corrupt the page" )
89
+ copy (victimBuf [17 :], buf )
90
+ // write page back to file
91
+ err = guts_cli .WritePage (dbPath , victimBuf )
92
+ if err != nil {
93
+ t .Errorf ("error writing victim page %v back to dbpath %v: %v" , victimPage .Id (), dbPath , err )
94
+ }
95
+ }
96
+
97
+ // generateSampleDataInBucket fill in sample data into given bucket to create the given
98
+ // number of leafPages. To control the number of leafPages, sample data are generated in order.
99
+ func generateSampleDataInBucket (t testing.TB , bk * bolt.Bucket , lPages int ) {
100
+ currentKey := 1
101
+ currentVal := 100
102
+ for i := 0 ; i < lPages ; i ++ {
103
+
104
+ currentSize := pageHeaderSize
105
+ for currentSize < pageSize {
106
+ err := bk .Put (convertIntIntoBytes (t , currentKey ), convertIntIntoBytes (t , currentVal ))
107
+ require .NoError (t , err )
108
+ currentSize += 16 + 4 + 4
109
+ }
110
+
111
+ currentKey ++
112
+ currentVal ++
113
+ }
114
+ }
115
+
116
+ func convertIntIntoBytes (t testing.TB , i int ) []byte {
117
+ t .Helper ()
118
+ buf := make ([]byte , 4 )
119
+ binary .BigEndian .PutUint32 (buf , uint32 (i ))
120
+ return buf
121
+ }
122
+
21
123
// TestTx_Check_ReadOnly tests consistency checking on a ReadOnly database.
22
124
func TestTx_Check_ReadOnly (t * testing.T ) {
23
125
db := btesting .MustCreateDB (t )
0 commit comments