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 }