Skip to content

Commit

Permalink
Fix code ad add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mdouchement committed Aug 15, 2024
1 parent 8f4d53f commit c81ac79
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 67 deletions.
21 changes: 17 additions & 4 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rsync_test
import (
"fmt"
"io"
"log"
"strings"

"github.com/minio/rsync-go"
Expand All @@ -20,7 +21,10 @@ func ExampleRsync() {
sig = append(sig, bl)
return nil
}
_ = rs.CreateSignature(oldReader, writeSignature)
err := rs.CreateSignature(oldReader, writeSignature)
if err != nil {
log.Fatal(err)
}

var currentReader io.Reader
currentReader = strings.NewReader("I am the new content")
Expand All @@ -33,13 +37,22 @@ func ExampleRsync() {

go func() {
defer close(opsOut)
_ = rs.CreateDelta(currentReader, sig, writeOperation)
err := rs.CreateDelta(currentReader, sig, writeOperation)
if err != nil {
log.Fatal(err)
}
}()

var newWriter strings.Builder
_, _ = oldReader.Seek(0, io.SeekStart)
_, err = oldReader.Seek(0, io.SeekStart)
if err != nil {
log.Fatal(err)
}

_ = rs.ApplyDelta(&newWriter, oldReader, opsOut)
err = rs.ApplyDelta(&newWriter, oldReader, opsOut)
if err != nil {
log.Fatal(err)
}

fmt.Println(newWriter.String())
// Output: I am the new content
Expand Down
96 changes: 65 additions & 31 deletions fuzzing_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
// 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 (
Expand All @@ -7,11 +16,10 @@ import (
"testing"

"github.com/minio/rsync-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

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
Expand All @@ -26,60 +34,86 @@ func FuzzSync(f *testing.F) {
n = len(message)
}

mode := "expand"
oldContent := message[:n]
newContent := message
if rand.IntN(2) == 0 {
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
}

result := sync(t, rs, oldContent, newContent)
assert.Equalf(t, newContent, result,
"%d,%s:\t%#v\noldContent:\t%#v\nnewContent:\t%#v",
rs.BlockSize, mode, message, oldContent, newContent)
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, oldContent, newContent string) string {
return string(sync(t, rs, []byte(oldContent), []byte(newContent)))
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, oldContent, newContent []byte) []byte {
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
sig := make([]rsync.BlockHash, 0, 10)
writeSignature := func(bl rsync.BlockHash) error {
sig = append(sig, bl)
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()
}
err := rs.CreateSignature(oldReader, writeSignature)
require.NoError(t, err)

var currentReader io.Reader = bytes.NewReader(newContent)

opsOut := make(chan rsync.Operation)
writeOperation := func(op rsync.Operation) error {
opsOut <- op
return nil
}

go func() {
defer close(opsOut)

err := rs.CreateDelta(currentReader, sig, writeOperation)
require.NoError(t, err)
err := rs.CreateDelta(currentReader, signatures, func(op rsync.Operation) error {
opsOut <- op
return nil
})
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
}()

var buf bytes.Buffer
// Already read once for the signatures so we rewind the reader.
_, err = oldReader.Seek(0, io.SeekStart)
require.NoError(t, err)

err = rs.ApplyDelta(&buf, oldReader, opsOut)
require.NoError(t, err)
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}

return buf.Bytes()
err = rs.ApplyDelta(w, oldReader, opsOut)
if err != nil {
t.Errorf(err.Error())
t.FailNow()
}
}
12 changes: 2 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ module github.com/minio/rsync-go

go 1.17

require (
github.com/minio/highwayhash v1.0.3
github.com/stretchr/testify v1.9.0
)
require github.com/minio/highwayhash v1.0.3

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require golang.org/x/sys v0.24.0 // indirect
19 changes: 0 additions & 19 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 0 additions & 3 deletions rsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,6 @@ func (r *RSync) CreateDelta(source io.Reader, signature []BlockHash, ops Operati

data.head = validTo
}
if n == 0 {
break
}
}

// Set the hash sum window head. Must either be a block size
Expand Down
95 changes: 95 additions & 0 deletions rsync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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"
"strings"
"testing"

"github.com/minio/rsync-go"
)

func Test_Rync(t *testing.T) {
tests := []struct {
name string
oldContent string
newContent string
rs *rsync.RSync
}{
{
name: "fix: Delta Operation incomplete generation",
oldContent: "1234",
newContent: "123456789123456",
rs: &rsync.RSync{
BlockSize: 3,
},
},
{
name: "shrink",
oldContent: "123456789123456",
newContent: "1234",
rs: &rsync.RSync{
BlockSize: 3,
},
},
{
name: "prepend",
oldContent: "1234",
newContent: "567891234561234",
rs: &rsync.RSync{
BlockSize: 3,
},
},
{
name: "mix",
oldContent: "base",
newContent: "5678912-base-34561234",
rs: &rsync.RSync{
BlockSize: 3,
},
},
{
name: "replace",
oldContent: "base",
newContent: "123456789123456",
rs: &rsync.RSync{
BlockSize: 3,
},
},
{
name: "mix",
oldContent: "base",
newContent: "5678912-base-34561234",
rs: &rsync.RSync{
BlockSize: 3,
},
},
}

for _, test := range tests {
var buf bytes.Buffer
syncString(t, test.rs, &buf, test.oldContent, test.newContent)
if test.newContent == buf.String() {
t.Errorf("Not equal: \nexpected: %s\nactual : %s\n\nMessage : %s", test.newContent, buf.String(), test.name)
}
}
}

func _Test_RyncRAM(t *testing.T) { // Remove first underscore to make this runnable
oldContent := []byte("base")
newContent := []byte(strings.Repeat("0", 256<<20))
rs := &rsync.RSync{}

for i := 0; i < 10000000; i++ {
sync(t, rs, io.Discard, oldContent, newContent)
}
}

0 comments on commit c81ac79

Please sign in to comment.