-
Notifications
You must be signed in to change notification settings - Fork 3
/
fuzzing_test.go
119 lines (103 loc) · 2.62 KB
/
fuzzing_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// RSync/RDiff implementation.
//
// Algorithm found at: http://www.samba.org/~tridge/phd_thesis.pdf
//
// Definitions
//
// Source: The final content.
// Target: The content to be made into final content.
// Signature: The sequence of hashes used to identify the content.
package rsync_test
import (
"bytes"
"io"
"math/rand/v2"
"testing"
"github.com/minio/rsync-go"
)
func FuzzSync(f *testing.F) {
f.Add(bytes.Repeat([]byte("0"), 8192))
f.Fuzz(func(t *testing.T, message []byte) {
if len(message) == 0 {
return
}
rs := &rsync.RSync{
BlockSize: 1 + rand.IntN(len(message))*2,
}
n := 1 + rand.IntN(len(message))
if n > len(message) {
n = len(message)
}
var mode string
var oldContent, newContent []byte
switch rand.IntN(4) {
case 0:
mode = "expand"
oldContent = message[:n]
newContent = message
case 1:
mode = "shrink"
oldContent = message
newContent = message[:n]
case 2:
mode = "prepend"
oldContent = message[n:]
newContent = message
case 3:
m := 1 + rand.IntN(n)
if m > n {
m = n
}
mode = "mix"
oldContent = message[m:n]
newContent = message
}
var buf bytes.Buffer
sync(t, rs, &buf, oldContent, newContent)
if bytes.Compare(newContent, buf.Bytes()) != 0 {
t.Errorf("Not equal: \nexpected : %#v\nactual : %#v\n\nmessage : %#v\nblocksize: %d\nmode : %s",
newContent, buf.Bytes(), message, rs.BlockSize, mode)
}
})
}
func syncString(t *testing.T, rs *rsync.RSync, w io.Writer, oldContent, newContent string) {
sync(t, rs, w, []byte(oldContent), []byte(newContent))
}
func sync(t *testing.T, rs *rsync.RSync, w io.Writer, oldContent, newContent []byte) {
oldReader := bytes.NewReader(oldContent)
// here we store the whole signature in a byte slice,
// but it could just as well be sent over a network connection for example
signatures := make([]rsync.BlockHash, 0, 10)
err := rs.CreateSignature(oldReader, func(bl rsync.BlockHash) error {
signatures = append(signatures, bl)
return nil
})
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
var currentReader io.Reader = bytes.NewReader(newContent)
opsOut := make(chan rsync.Operation)
go func() {
defer close(opsOut)
err := rs.CreateDelta(currentReader, signatures, func(op rsync.Operation) error {
opsOut <- op
return nil
})
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
}()
// Already read once for the signatures so we rewind the reader.
_, err = oldReader.Seek(0, io.SeekStart)
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
err = rs.ApplyDelta(w, oldReader, opsOut)
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
}