github.com/artpar/rclone@v1.67.3/backend/hidrive/hidrivehash/hidrivehash_test.go (about)

     1  package hidrivehash_test
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"io"
     9  	"testing"
    10  
    11  	"github.com/artpar/rclone/backend/hidrive/hidrivehash"
    12  	"github.com/artpar/rclone/backend/hidrive/hidrivehash/internal"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  // helper functions to set up test-tables
    17  
    18  func sha1ArrayAsSlice(sum [sha1.Size]byte) []byte {
    19  	return sum[:]
    20  }
    21  
    22  func mustDecode(hexstring string) []byte {
    23  	result, err := hex.DecodeString(hexstring)
    24  	if err != nil {
    25  		panic(err)
    26  	}
    27  	return result
    28  }
    29  
    30  // ------------------------------------------------------------
    31  
    32  var testTableLevelPositionEmbedded = []struct {
    33  	ins  [][]byte
    34  	outs [][]byte
    35  	name string
    36  }{
    37  	{
    38  		[][]byte{
    39  			sha1ArrayAsSlice([20]byte{245, 202, 195, 223, 121, 198, 189, 112, 138, 202, 222, 2, 146, 156, 127, 16, 208, 233, 98, 88}),
    40  			sha1ArrayAsSlice([20]byte{78, 188, 156, 219, 173, 54, 81, 55, 47, 220, 222, 207, 201, 21, 57, 252, 255, 239, 251, 186}),
    41  		},
    42  		[][]byte{
    43  			sha1ArrayAsSlice([20]byte{245, 202, 195, 223, 121, 198, 189, 112, 138, 202, 222, 2, 146, 156, 127, 16, 208, 233, 98, 88}),
    44  			sha1ArrayAsSlice([20]byte{68, 135, 96, 187, 38, 253, 14, 167, 186, 167, 188, 210, 91, 177, 185, 13, 208, 217, 94, 18}),
    45  		},
    46  		"documentation-v3.2rev27-example L0 (position-embedded)",
    47  	},
    48  	{
    49  		[][]byte{
    50  			sha1ArrayAsSlice([20]byte{68, 254, 92, 166, 52, 37, 104, 180, 22, 123, 249, 144, 182, 78, 64, 74, 57, 117, 225, 195}),
    51  			sha1ArrayAsSlice([20]byte{75, 211, 153, 190, 125, 179, 67, 49, 60, 149, 98, 246, 142, 20, 11, 254, 159, 162, 129, 237}),
    52  			sha1ArrayAsSlice([20]byte{150, 2, 9, 153, 97, 153, 189, 104, 147, 14, 77, 203, 244, 243, 25, 212, 67, 48, 111, 107}),
    53  		},
    54  		[][]byte{
    55  			sha1ArrayAsSlice([20]byte{68, 254, 92, 166, 52, 37, 104, 180, 22, 123, 249, 144, 182, 78, 64, 74, 57, 117, 225, 195}),
    56  			sha1ArrayAsSlice([20]byte{144, 209, 246, 100, 177, 216, 171, 229, 83, 17, 92, 135, 68, 98, 76, 72, 217, 24, 99, 176}),
    57  			sha1ArrayAsSlice([20]byte{38, 211, 255, 254, 19, 114, 105, 77, 230, 31, 170, 83, 57, 85, 102, 29, 28, 72, 211, 27}),
    58  		},
    59  		"documentation-example L0 (position-embedded)",
    60  	},
    61  	{
    62  		[][]byte{
    63  			sha1ArrayAsSlice([20]byte{173, 123, 132, 245, 176, 172, 43, 183, 121, 40, 66, 252, 101, 249, 188, 193, 160, 189, 2, 116}),
    64  			sha1ArrayAsSlice([20]byte{40, 34, 8, 238, 37, 5, 237, 184, 79, 105, 10, 167, 171, 254, 13, 229, 132, 112, 254, 8}),
    65  			sha1ArrayAsSlice([20]byte{39, 112, 26, 86, 190, 35, 100, 101, 28, 131, 122, 191, 254, 144, 239, 107, 253, 124, 104, 203}),
    66  		},
    67  		[][]byte{
    68  			sha1ArrayAsSlice([20]byte{173, 123, 132, 245, 176, 172, 43, 183, 121, 40, 66, 252, 101, 249, 188, 193, 160, 189, 2, 116}),
    69  			sha1ArrayAsSlice([20]byte{213, 157, 141, 227, 213, 178, 25, 111, 200, 145, 77, 164, 17, 247, 202, 167, 37, 46, 0, 124}),
    70  			sha1ArrayAsSlice([20]byte{253, 13, 168, 58, 147, 213, 125, 212, 229, 20, 200, 100, 16, 136, 186, 19, 34, 170, 105, 71}),
    71  		},
    72  		"documentation-example L1 (position-embedded)",
    73  	},
    74  }
    75  
    76  var testTableLevel = []struct {
    77  	ins  [][]byte
    78  	outs [][]byte
    79  	name string
    80  }{
    81  	{
    82  		[][]byte{
    83  			mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
    84  			mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
    85  			mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
    86  		},
    87  		[][]byte{
    88  			mustDecode("44fe5ca6342568b4167bf990b64e404a3975e1c3"),
    89  			mustDecode("90d1f664b1d8abe553115c8744624c48d91863b0"),
    90  			mustDecode("26d3fffe1372694de61faa533955661d1c48d31b"),
    91  		},
    92  		"documentation-example L0",
    93  	},
    94  	{
    95  		[][]byte{
    96  			mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
    97  			mustDecode("daedc425199501b1e86b5eaba5649cbde205e6ae"),
    98  			mustDecode("286ac5283f99c4e0f11683900a3e39661c375dd6"),
    99  		},
   100  		[][]byte{
   101  			mustDecode("ad7b84f5b0ac2bb7792842fc65f9bcc1a0bd0274"),
   102  			mustDecode("d59d8de3d5b2196fc8914da411f7caa7252e007c"),
   103  			mustDecode("fd0da83a93d57dd4e514c8641088ba1322aa6947"),
   104  		},
   105  		"documentation-example L1",
   106  	},
   107  	{
   108  		[][]byte{
   109  			mustDecode("0000000000000000000000000000000000000000"),
   110  			mustDecode("0000000000000000000000000000000000000000"),
   111  			mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
   112  			mustDecode("0000000000000000000000000000000000000000"),
   113  			mustDecode("daedc425199501b1e86b5eaba5649cbde205e6ae"),
   114  			mustDecode("0000000000000000000000000000000000000000"),
   115  			mustDecode("0000000000000000000000000000000000000000"),
   116  			mustDecode("0000000000000000000000000000000000000000"),
   117  			mustDecode("286ac5283f99c4e0f11683900a3e39661c375dd6"),
   118  			mustDecode("0000000000000000000000000000000000000000"),
   119  		},
   120  		[][]byte{
   121  			mustDecode("0000000000000000000000000000000000000000"),
   122  			mustDecode("0000000000000000000000000000000000000000"),
   123  			mustDecode("a197464ec19f2b2b2bc6b21f6c939c7e57772843"),
   124  			mustDecode("a197464ec19f2b2b2bc6b21f6c939c7e57772843"),
   125  			mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
   126  			mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
   127  			mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
   128  			mustDecode("b04769357aa4eb4b52cd5bec6935bc8f977fa3a1"),
   129  			mustDecode("8f56351897b4e1d100646fa122c924347721b2f5"),
   130  			mustDecode("8f56351897b4e1d100646fa122c924347721b2f5"),
   131  		},
   132  		"mixed-with-empties",
   133  	},
   134  }
   135  
   136  var testTable = []struct {
   137  	data []byte
   138  	// pattern describes how to use data to construct the hash-input.
   139  	// For every entry n at even indices this repeats the data n times.
   140  	// For every entry m at odd indices this repeats a null-byte m times.
   141  	// The input-data is constructed by concatenating the results in order.
   142  	pattern []int64
   143  	out     []byte
   144  	name    string
   145  }{
   146  	{
   147  		[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
   148  		[]int64{64},
   149  		mustDecode("09f077820a8a41f34a639f2172f1133b1eafe4e6"),
   150  		"documentation-example L0",
   151  	},
   152  	{
   153  		[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
   154  		[]int64{64 * 256},
   155  		mustDecode("75a9f88fb219ef1dd31adf41c93e2efaac8d0245"),
   156  		"documentation-example L1",
   157  	},
   158  	{
   159  		[]byte("#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"),
   160  		[]int64{64 * 256, 0, 64 * 128, 4096 * 128, 64*2 + 32},
   161  		mustDecode("fd0da83a93d57dd4e514c8641088ba1322aa6947"),
   162  		"documentation-example L2",
   163  	},
   164  	{
   165  		[]byte("hello rclone\n"),
   166  		[]int64{316},
   167  		mustDecode("72370f9c18a2c20b31d71f3f4cee7a3cd2703737"),
   168  		"not-block-aligned",
   169  	},
   170  	{
   171  		[]byte("hello rclone\n"),
   172  		[]int64{13, 4096 * 3, 4},
   173  		mustDecode("a6990b81791f0d2db750b38f046df321c975aa60"),
   174  		"not-block-aligned-with-null-bytes",
   175  	},
   176  	{
   177  		[]byte{},
   178  		[]int64{},
   179  		mustDecode("0000000000000000000000000000000000000000"),
   180  		"empty",
   181  	},
   182  	{
   183  		[]byte{},
   184  		[]int64{0, 4096 * 256 * 256},
   185  		mustDecode("0000000000000000000000000000000000000000"),
   186  		"null-bytes",
   187  	},
   188  }
   189  
   190  // ------------------------------------------------------------
   191  
   192  func TestLevelAdd(t *testing.T) {
   193  	for _, test := range testTableLevelPositionEmbedded {
   194  		l := hidrivehash.NewLevel().(internal.LevelHash)
   195  		t.Run(test.name, func(t *testing.T) {
   196  			for i := range test.ins {
   197  				l.Add(test.ins[i])
   198  				assert.Equal(t, test.outs[i], l.Sum(nil))
   199  			}
   200  		})
   201  	}
   202  }
   203  
   204  func TestLevelWrite(t *testing.T) {
   205  	for _, test := range testTableLevel {
   206  		l := hidrivehash.NewLevel()
   207  		t.Run(test.name, func(t *testing.T) {
   208  			for i := range test.ins {
   209  				l.Write(test.ins[i])
   210  				assert.Equal(t, test.outs[i], l.Sum(nil))
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func TestLevelIsFull(t *testing.T) {
   217  	content := [hidrivehash.Size]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
   218  	l := hidrivehash.NewLevel()
   219  	for i := 0; i < 256; i++ {
   220  		assert.False(t, l.(internal.LevelHash).IsFull())
   221  		written, err := l.Write(content[:])
   222  		assert.Equal(t, len(content), written)
   223  		if !assert.NoError(t, err) {
   224  			t.FailNow()
   225  		}
   226  	}
   227  	assert.True(t, l.(internal.LevelHash).IsFull())
   228  	written, err := l.Write(content[:])
   229  	assert.True(t, l.(internal.LevelHash).IsFull())
   230  	assert.Equal(t, 0, written)
   231  	assert.ErrorIs(t, err, hidrivehash.ErrorHashFull)
   232  }
   233  
   234  func TestLevelReset(t *testing.T) {
   235  	l := hidrivehash.NewLevel()
   236  	zeroHash := l.Sum(nil)
   237  	_, err := l.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19})
   238  	if assert.NoError(t, err) {
   239  		assert.NotEqual(t, zeroHash, l.Sum(nil))
   240  		l.Reset()
   241  		assert.Equal(t, zeroHash, l.Sum(nil))
   242  	}
   243  }
   244  
   245  func TestLevelSize(t *testing.T) {
   246  	l := hidrivehash.NewLevel()
   247  	assert.Equal(t, 20, l.Size())
   248  }
   249  
   250  func TestLevelBlockSize(t *testing.T) {
   251  	l := hidrivehash.NewLevel()
   252  	assert.Equal(t, 20, l.BlockSize())
   253  }
   254  
   255  func TestLevelBinaryMarshaler(t *testing.T) {
   256  	content := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
   257  	l := hidrivehash.NewLevel().(internal.LevelHash)
   258  	l.Write(content[:10])
   259  	encoded, err := l.MarshalBinary()
   260  	if assert.NoError(t, err) {
   261  		d := hidrivehash.NewLevel().(internal.LevelHash)
   262  		err = d.UnmarshalBinary(encoded)
   263  		if assert.NoError(t, err) {
   264  			assert.Equal(t, l.Sum(nil), d.Sum(nil))
   265  			l.Write(content[10:])
   266  			d.Write(content[10:])
   267  			assert.Equal(t, l.Sum(nil), d.Sum(nil))
   268  		}
   269  	}
   270  }
   271  
   272  func TestLevelInvalidEncoding(t *testing.T) {
   273  	l := hidrivehash.NewLevel().(internal.LevelHash)
   274  	err := l.UnmarshalBinary([]byte{})
   275  	assert.ErrorIs(t, err, hidrivehash.ErrorInvalidEncoding)
   276  }
   277  
   278  // ------------------------------------------------------------
   279  
   280  type infiniteReader struct {
   281  	source []byte
   282  	offset int
   283  }
   284  
   285  func (m *infiniteReader) Read(b []byte) (int, error) {
   286  	count := copy(b, m.source[m.offset:])
   287  	m.offset += count
   288  	m.offset %= len(m.source)
   289  	return count, nil
   290  }
   291  
   292  func writeInChunks(writer io.Writer, chunkSize int64, data []byte, pattern []int64) error {
   293  	readers := make([]io.Reader, len(pattern))
   294  	nullBytes := [4096]byte{}
   295  	for i, n := range pattern {
   296  		if i%2 == 0 {
   297  			readers[i] = io.LimitReader(&infiniteReader{data, 0}, n*int64(len(data)))
   298  		} else {
   299  			readers[i] = io.LimitReader(&infiniteReader{nullBytes[:], 0}, n)
   300  		}
   301  	}
   302  	reader := io.MultiReader(readers...)
   303  	for {
   304  		_, err := io.CopyN(writer, reader, chunkSize)
   305  		if err != nil {
   306  			if err == io.EOF {
   307  				err = nil
   308  			}
   309  			return err
   310  		}
   311  	}
   312  }
   313  
   314  func TestWrite(t *testing.T) {
   315  	for _, test := range testTable {
   316  		t.Run(test.name, func(t *testing.T) {
   317  			h := hidrivehash.New()
   318  			err := writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern)
   319  			if assert.NoError(t, err) {
   320  				normalSum := h.Sum(nil)
   321  				assert.Equal(t, test.out, normalSum)
   322  				// Test if different block-sizes produce differing results.
   323  				for _, blockSize := range []int64{397, 512, 4091, 8192, 10000} {
   324  					t.Run(fmt.Sprintf("block-size %v", blockSize), func(t *testing.T) {
   325  						h := hidrivehash.New()
   326  						err := writeInChunks(h, blockSize, test.data, test.pattern)
   327  						if assert.NoError(t, err) {
   328  							assert.Equal(t, normalSum, h.Sum(nil))
   329  						}
   330  					})
   331  				}
   332  			}
   333  		})
   334  	}
   335  }
   336  
   337  func TestReset(t *testing.T) {
   338  	h := hidrivehash.New()
   339  	zeroHash := h.Sum(nil)
   340  	_, err := h.Write([]byte{1})
   341  	if assert.NoError(t, err) {
   342  		assert.NotEqual(t, zeroHash, h.Sum(nil))
   343  		h.Reset()
   344  		assert.Equal(t, zeroHash, h.Sum(nil))
   345  	}
   346  }
   347  
   348  func TestSize(t *testing.T) {
   349  	h := hidrivehash.New()
   350  	assert.Equal(t, 20, h.Size())
   351  }
   352  
   353  func TestBlockSize(t *testing.T) {
   354  	h := hidrivehash.New()
   355  	assert.Equal(t, 4096, h.BlockSize())
   356  }
   357  
   358  func TestBinaryMarshaler(t *testing.T) {
   359  	for _, test := range testTable {
   360  		h := hidrivehash.New()
   361  		d := hidrivehash.New()
   362  		half := len(test.pattern) / 2
   363  		t.Run(test.name, func(t *testing.T) {
   364  			err := writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern[:half])
   365  			assert.NoError(t, err)
   366  			encoded, err := h.(encoding.BinaryMarshaler).MarshalBinary()
   367  			if assert.NoError(t, err) {
   368  				err = d.(encoding.BinaryUnmarshaler).UnmarshalBinary(encoded)
   369  				if assert.NoError(t, err) {
   370  					assert.Equal(t, h.Sum(nil), d.Sum(nil))
   371  					err = writeInChunks(h, int64(h.BlockSize()), test.data, test.pattern[half:])
   372  					assert.NoError(t, err)
   373  					err = writeInChunks(d, int64(d.BlockSize()), test.data, test.pattern[half:])
   374  					assert.NoError(t, err)
   375  					assert.Equal(t, h.Sum(nil), d.Sum(nil))
   376  				}
   377  			}
   378  		})
   379  	}
   380  }
   381  
   382  func TestInvalidEncoding(t *testing.T) {
   383  	h := hidrivehash.New()
   384  	err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte{})
   385  	assert.ErrorIs(t, err, hidrivehash.ErrorInvalidEncoding)
   386  }
   387  
   388  func TestSum(t *testing.T) {
   389  	assert.Equal(t, [hidrivehash.Size]byte{}, hidrivehash.Sum([]byte{}))
   390  	content := []byte{1}
   391  	h := hidrivehash.New()
   392  	h.Write(content)
   393  	sum := hidrivehash.Sum(content)
   394  	assert.Equal(t, h.Sum(nil), sum[:])
   395  }