mirror of
https://github.com/rclone/rclone.git
synced 2024-12-30 17:03:38 +08:00
396 lines
12 KiB
Go
396 lines
12 KiB
Go
package hidrivehash_test
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"encoding"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/rclone/rclone/backend/hidrive/hidrivehash"
|
|
"github.com/rclone/rclone/backend/hidrive/hidrivehash/internal"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// helper functions to set up test-tables
|
|
|
|
func sha1ArrayAsSlice(sum [sha1.Size]byte) []byte {
|
|
return sum[:]
|
|
}
|
|
|
|
func mustDecode(hexstring string) []byte {
|
|
result, err := hex.DecodeString(hexstring)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
var testTableLevelPositionEmbedded = []struct {
|
|
ins [][]byte
|
|
outs [][]byte
|
|
name string
|
|
}{
|
|
{
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{245, 202, 195, 223, 121, 198, 189, 112, 138, 202, 222, 2, 146, 156, 127, 16, 208, 233, 98, 88}),
|
|
sha1ArrayAsSlice([20]byte{78, 188, 156, 219, 173, 54, 81, 55, 47, 220, 222, 207, 201, 21, 57, 252, 255, 239, 251, 186}),
|
|
},
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{245, 202, 195, 223, 121, 198, 189, 112, 138, 202, 222, 2, 146, 156, 127, 16, 208, 233, 98, 88}),
|
|
sha1ArrayAsSlice([20]byte{68, 135, 96, 187, 38, 253, 14, 167, 186, 167, 188, 210, 91, 177, 185, 13, 208, 217, 94, 18}),
|
|
},
|
|
"documentation-v3.2rev27-example L0 (position-embedded)",
|
|
},
|
|
{
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{68, 254, 92, 166, 52, 37, 104, 180, 22, 123, 249, 144, 182, 78, 64, 74, 57, 117, 225, 195}),
|
|
sha1ArrayAsSlice([20]byte{75, 211, 153, 190, 125, 179, 67, 49, 60, 149, 98, 246, 142, 20, 11, 254, 159, 162, 129, 237}),
|
|
sha1ArrayAsSlice([20]byte{150, 2, 9, 153, 97, 153, 189, 104, 147, 14, 77, 203, 244, 243, 25, 212, 67, 48, 111, 107}),
|
|
},
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{68, 254, 92, 166, 52, 37, 104, 180, 22, 123, 249, 144, 182, 78, 64, 74, 57, 117, 225, 195}),
|
|
sha1ArrayAsSlice([20]byte{144, 209, 246, 100, 177, 216, 171, 229, 83, 17, 92, 135, 68, 98, 76, 72, 217, 24, 99, 176}),
|
|
sha1ArrayAsSlice([20]byte{38, 211, 255, 254, 19, 114, 105, 77, 230, 31, 170, 83, 57, 85, 102, 29, 28, 72, 211, 27}),
|
|
},
|
|
"documentation-example L0 (position-embedded)",
|
|
},
|
|
{
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{173, 123, 132, 245, 176, 172, 43, 183, 121, 40, 66, 252, 101, 249, 188, 193, 160, 189, 2, 116}),
|
|
sha1ArrayAsSlice([20]byte{40, 34, 8, 238, 37, 5, 237, 184, 79, 105, 10, 167, 171, 254, 13, 229, 132, 112, 254, 8}),
|
|
sha1ArrayAsSlice([20]byte{39, 112, 26, 86, 190, 35, 100, 101, 28, 131, 122, 191, 254, 144, 239, 107, 253, 124, 104, 203}),
|
|
},
|
|
[][]byte{
|
|
sha1ArrayAsSlice([20]byte{173, 123, 132, 245, 176, 172, 43, 183, 121, 40, 66, 252, 101, 249, 188, 193, 160, 189, 2, 116}),
|
|
sha1ArrayAsSlice([20]byte{213, 157, 141, 227, 213, 178, 25, 111, 200, 145, 77, 164, 17, 247, 202, 167, 37, 46, 0, 124}),
|
|
sha1ArrayAsSlice([20]byte{253, 13, 168, 58, 147, 213, 125, 212, 229, 20, 200, 100, 16, 136, 186, 19, 34, 170, 105, 71}),
|
|
},
|
|
"documentation-example L1 (position-embedded)",
|
|
},
|
|
}
|
|
|
|
var testTableLevel = []struct {
|
|
ins [][]byte
|
|
outs [][]byte
|
|
name string
|
|
}{
|
|
{
|
|
[][]byte{
|
|
mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
|
|
mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
|
|
mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
|
|
},
|
|
[][]byte{
|
|
mustDecode("44fe5ca6342568b4167bf990b64e404a3975e1c3"),
|
|
mustDecode("90d1f664b1d8abe553115c8744624c48d91863b0"),
|
|
mustDecode("26d3fffe1372694de61faa533955661d1c48d31b"),
|
|
},
|
|
"documentation-example L0",
|
|
},
|
|
{
|
|
[][]byte{
|
|
mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
|
|
mustDecode("daedc425199501b1e86b5eaba5649cbde205e6ae"),
|
|
mustDecode("286ac5283f99c4e0f11683900a3e39661c375dd6"),
|
|
},
|
|
[][]byte{
|
|
mustDecode("ad7b84f5b0ac2bb7792842fc65f9bcc1a0bd0274"),
|
|
mustDecode("d59d8de3d5b2196fc8914da411f7caa7252e007c"),
|
|
mustDecode("fd0da83a93d57dd4e514c8641088ba1322aa6947"),
|
|
},
|
|
"documentation-example L1",
|
|
},
|
|
{
|
|
[][]byte{
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("daedc425199501b1e86b5eaba5649cbde205e6ae"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("286ac5283f99c4e0f11683900a3e39661c375dd6"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
},
|
|
[][]byte{
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
mustDecode("a197464ec19f2b2b2bc6b21f6c939c7e57772843"),
|
|
mustDecode("a197464ec19f2b2b2bc6b21f6c939c7e57772843"),
|
|
mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
|
|
mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
|
|
mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
|
|
mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
|
|
mustDecode("8f56351897b4e1d100646fa122c924347721b2f5"),
|
|
mustDecode("8f56351897b4e1d100646fa122c924347721b2f5"),
|
|
},
|
|
"mixed-with-empties",
|
|
},
|
|
}
|
|
|
|
var testTable = []struct {
|
|
data []byte
|
|
// pattern describes how to use data to construct the hash-input.
|
|
// For every entry n at even indices this repeats the data n times.
|
|
// For every entry m at odd indices this repeats a null-byte m times.
|
|
// The input-data is constructed by concatinating the results in order.
|
|
pattern []int64
|
|
out []byte
|
|
name string
|
|
}{
|
|
{
|
|
[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
|
|
[]int64{64},
|
|
mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
|
|
"documentation-example L0",
|
|
},
|
|
{
|
|
[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
|
|
[]int64{64 * 256},
|
|
mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
|
|
"documentation-example L1",
|
|
},
|
|
{
|
|
[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
|
|
[]int64{64 * 256, 0, 64 * 128, 4096 * 128, 64*2 + 32},
|
|
mustDecode("fd0da83a93d57dd4e514c8641088ba1322aa6947"),
|
|
"documentation-example L2",
|
|
},
|
|
{
|
|
[]byte("hello rclone\n"),
|
|
[]int64{316},
|
|
mustDecode("72370f9c18a2c20b31d71f3f4cee7a3cd2703737"),
|
|
"not-block-aligned",
|
|
},
|
|
{
|
|
[]byte("hello rclone\n"),
|
|
[]int64{13, 4096 * 3, 4},
|
|
mustDecode("a6990b81791f0d2db750b38f046df321c975aa60"),
|
|
"not-block-aligned-with-null-bytes",
|
|
},
|
|
{
|
|
[]byte{},
|
|
[]int64{},
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
"empty",
|
|
},
|
|
{
|
|
[]byte{},
|
|
[]int64{0, 4096 * 256 * 256},
|
|
mustDecode("0000000000000000000000000000000000000000"),
|
|
"null-bytes",
|
|
},
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
func TestLevelAdd(t *testing.T) {
|
|
for _, test := range testTableLevelPositionEmbedded {
|
|
l := hidrivehash.NewLevel().(internal.LevelHash)
|
|
t.Run(test.name, func(t *testing.T) {
|
|
for i := range test.ins {
|
|
l.Add(test.ins[i])
|
|
assert.Equal(t, test.outs[i], l.Sum(nil))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLevelWrite(t *testing.T) {
|
|
for _, test := range testTableLevel {
|
|
l := hidrivehash.NewLevel()
|
|
t.Run(test.name, func(t *testing.T) {
|
|
for i := range test.ins {
|
|
l.Write(test.ins[i])
|
|
assert.Equal(t, test.outs[i], l.Sum(nil))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLevelIsFull(t *testing.T) {
|
|
content := [hidrivehash.Size]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
|
l := hidrivehash.NewLevel()
|
|
for i := 0; i < 256; i++ {
|
|
assert.False(t, l.(internal.LevelHash).IsFull())
|
|
written, err := l.Write(content[:])
|
|
assert.Equal(t, len(content), written)
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
}
|
|
assert.True(t, l.(internal.LevelHash).IsFull())
|
|
written, err := l.Write(content[:])
|
|
assert.True(t, l.(internal.LevelHash).IsFull())
|
|
assert.Equal(t, 0, written)
|
|
assert.ErrorIs(t, err, hidrivehash.ErrorHashFull)
|
|
}
|
|
|
|
func TestLevelReset(t *testing.T) {
|
|
l := hidrivehash.NewLevel()
|
|
zeroHash := l.Sum(nil)
|
|
_, err := l.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19})
|
|
if assert.NoError(t, err) {
|
|
assert.NotEqual(t, zeroHash, l.Sum(nil))
|
|
l.Reset()
|
|
assert.Equal(t, zeroHash, l.Sum(nil))
|
|
}
|
|
}
|
|
|
|
func TestLevelSize(t *testing.T) {
|
|
l := hidrivehash.NewLevel()
|
|
assert.Equal(t, 20, l.Size())
|
|
}
|
|
|
|
func TestLevelBlockSize(t *testing.T) {
|
|
l := hidrivehash.NewLevel()
|
|
assert.Equal(t, 20, l.BlockSize())
|
|
}
|
|
|
|
func TestLevelBinaryMarshaler(t *testing.T) {
|
|
content := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
|
l := hidrivehash.NewLevel().(internal.LevelHash)
|
|
l.Write(content[:10])
|
|
encoded, err := l.MarshalBinary()
|
|
if assert.NoError(t, err) {
|
|
d := hidrivehash.NewLevel().(internal.LevelHash)
|
|
err = d.UnmarshalBinary(encoded)
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, l.Sum(nil), d.Sum(nil))
|
|
l.Write(content[10:])
|
|
d.Write(content[10:])
|
|
assert.Equal(t, l.Sum(nil), d.Sum(nil))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLevelInvalidEncoding(t *testing.T) {
|
|
l := hidrivehash.NewLevel().(internal.LevelHash)
|
|
err := l.UnmarshalBinary([]byte{})
|
|
assert.ErrorIs(t, err, hidrivehash.ErrorInvalidEncoding)
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
type infiniteReader struct {
|
|
source []byte
|
|
offset int
|
|
}
|
|
|
|
func (m *infiniteReader) Read(b []byte) (int, error) {
|
|
count := copy(b, m.source[m.offset:])
|
|
m.offset += count
|
|
m.offset %= len(m.source)
|
|
return count, nil
|
|
}
|
|
|
|
func writeInChunks(writer io.Writer, chunkSize int64, data []byte, pattern []int64) error {
|
|
readers := make([]io.Reader, len(pattern))
|
|
nullBytes := [4096]byte{}
|
|
for i, n := range pattern {
|
|
if i%2 == 0 {
|
|
readers[i] = io.LimitReader(&infiniteReader{data, 0}, n*int64(len(data)))
|
|
} else {
|
|
readers[i] = io.LimitReader(&infiniteReader{nullBytes[:], 0}, n)
|
|
}
|
|
}
|
|
reader := io.MultiReader(readers...)
|
|
for {
|
|
_, err := io.CopyN(writer, reader, chunkSize)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWrite(t *testing.T) {
|
|
for _, test := range testTable {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
err := writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern)
|
|
if assert.NoError(t, err) {
|
|
normalSum := h.Sum(nil)
|
|
assert.Equal(t, test.out, normalSum)
|
|
// Test if different block-sizes produce differing results.
|
|
for _, blockSize := range []int64{397, 512, 4091, 8192, 10000} {
|
|
t.Run(fmt.Sprintf("block-size %v", blockSize), func(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
err := writeInChunks(h, blockSize, test.data, test.pattern)
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, normalSum, h.Sum(nil))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReset(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
zeroHash := h.Sum(nil)
|
|
_, err := h.Write([]byte{1})
|
|
if assert.NoError(t, err) {
|
|
assert.NotEqual(t, zeroHash, h.Sum(nil))
|
|
h.Reset()
|
|
assert.Equal(t, zeroHash, h.Sum(nil))
|
|
}
|
|
}
|
|
|
|
func TestSize(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
assert.Equal(t, 20, h.Size())
|
|
}
|
|
|
|
func TestBlockSize(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
assert.Equal(t, 4096, h.BlockSize())
|
|
}
|
|
|
|
func TestBinaryMarshaler(t *testing.T) {
|
|
for _, test := range testTable {
|
|
h := hidrivehash.New()
|
|
d := hidrivehash.New()
|
|
half := len(test.pattern) / 2
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern[:half])
|
|
assert.NoError(t, err)
|
|
encoded, err := h.(encoding.BinaryMarshaler).MarshalBinary()
|
|
if assert.NoError(t, err) {
|
|
err = d.(encoding.BinaryUnmarshaler).UnmarshalBinary(encoded)
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, h.Sum(nil), d.Sum(nil))
|
|
err = writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern[half:])
|
|
assert.NoError(t, err)
|
|
err = writeInChunks(d, int64(d.BlockSize()), test.data, test.pattern[half:])
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, h.Sum(nil), d.Sum(nil))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInvalidEncoding(t *testing.T) {
|
|
h := hidrivehash.New()
|
|
err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte{})
|
|
assert.ErrorIs(t, err, hidrivehash.ErrorInvalidEncoding)
|
|
}
|
|
|
|
func TestSum(t *testing.T) {
|
|
assert.Equal(t, [hidrivehash.Size]byte{}, hidrivehash.Sum([]byte{}))
|
|
content := []byte{1}
|
|
h := hidrivehash.New()
|
|
h.Write(content)
|
|
sum := hidrivehash.Sum(content)
|
|
assert.Equal(t, h.Sum(nil), sum[:])
|
|
}
|