blocks_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package scanner
  7. import (
  8. "bytes"
  9. "context"
  10. "crypto/rand"
  11. "fmt"
  12. origAdler32 "hash/adler32"
  13. mrand "math/rand"
  14. "testing"
  15. "testing/quick"
  16. rollingAdler32 "github.com/chmduquesne/rollinghash/adler32"
  17. "github.com/syncthing/syncthing/lib/protocol"
  18. "github.com/syncthing/syncthing/lib/sha256"
  19. )
  20. var blocksTestData = []struct {
  21. data []byte
  22. blocksize int
  23. hash []string
  24. weakhash []uint32
  25. }{
  26. {[]byte(""), 1024, []string{
  27. "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
  28. []uint32{0},
  29. },
  30. {[]byte("contents"), 1024, []string{
  31. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  32. []uint32{0x0f3a036f},
  33. },
  34. {[]byte("contents"), 9, []string{
  35. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  36. []uint32{0x0f3a036f},
  37. },
  38. {[]byte("contents"), 8, []string{
  39. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  40. []uint32{0x0f3a036f},
  41. },
  42. {[]byte("contents"), 7, []string{
  43. "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73",
  44. "043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89"},
  45. []uint32{0x0bcb02fc, 0x00740074},
  46. },
  47. {[]byte("contents"), 3, []string{
  48. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  49. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3",
  50. "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4"},
  51. []uint32{0x02780141, 0x02970148, 0x015d00e8},
  52. },
  53. {[]byte("conconts"), 3, []string{
  54. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  55. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  56. "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4"},
  57. []uint32{0x02780141, 0x02780141, 0x015d00e8},
  58. },
  59. {[]byte("contenten"), 3, []string{
  60. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  61. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3",
  62. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3"},
  63. []uint32{0x02780141, 0x02970148, 0x02970148},
  64. },
  65. }
  66. func TestBlocks(t *testing.T) {
  67. for testNo, test := range blocksTestData {
  68. buf := bytes.NewBuffer(test.data)
  69. blocks, err := Blocks(context.TODO(), buf, test.blocksize, -1, nil, true)
  70. if err != nil {
  71. t.Fatal(err)
  72. }
  73. if l := len(blocks); l != len(test.hash) {
  74. t.Fatalf("%d: Incorrect number of blocks %d != %d", testNo, l, len(test.hash))
  75. } else {
  76. i := 0
  77. for off := int64(0); off < int64(len(test.data)); off += int64(test.blocksize) {
  78. if blocks[i].Offset != off {
  79. t.Errorf("%d/%d: Incorrect offset %d != %d", testNo, i, blocks[i].Offset, off)
  80. }
  81. bs := test.blocksize
  82. if rem := len(test.data) - int(off); bs > rem {
  83. bs = rem
  84. }
  85. if int(blocks[i].Size) != bs {
  86. t.Errorf("%d/%d: Incorrect length %d != %d", testNo, i, blocks[i].Size, bs)
  87. }
  88. if h := fmt.Sprintf("%x", blocks[i].Hash); h != test.hash[i] {
  89. t.Errorf("%d/%d: Incorrect block hash %q != %q", testNo, i, h, test.hash[i])
  90. }
  91. if h := blocks[i].WeakHash; h != test.weakhash[i] {
  92. t.Errorf("%d/%d: Incorrect block weakhash 0x%08x != 0x%08x", testNo, i, h, test.weakhash[i])
  93. }
  94. i++
  95. }
  96. }
  97. }
  98. }
  99. func TestAdler32Variants(t *testing.T) {
  100. // Verify that the two adler32 functions give matching results for a few
  101. // different blocks of data.
  102. hf1 := origAdler32.New()
  103. hf2 := rollingAdler32.New()
  104. checkFn := func(data []byte) bool {
  105. hf1.Write(data)
  106. sum1 := hf1.Sum32()
  107. hf2.Write(data)
  108. sum2 := hf2.Sum32()
  109. hf1.Reset()
  110. hf2.Reset()
  111. // Make sure whatever we use in Validate matches too resp. this
  112. // tests gets adjusted if we ever switch the weak hash algo.
  113. return sum1 == sum2 && Validate(data, nil, sum1)
  114. }
  115. // protocol block sized data
  116. data := make([]byte, protocol.MinBlockSize)
  117. for i := 0; i < 5; i++ {
  118. rand.Read(data)
  119. if !checkFn(data) {
  120. t.Errorf("Hash mismatch on block sized data")
  121. }
  122. }
  123. // random small blocks
  124. if err := quick.Check(checkFn, nil); err != nil {
  125. t.Error(err)
  126. }
  127. // rolling should have the same result as the individual blocks
  128. // themselves.
  129. windowSize := 128
  130. hf3 := rollingAdler32.New()
  131. hf3.Write(data[:windowSize])
  132. for i := windowSize; i < len(data); i++ {
  133. if i%windowSize == 0 {
  134. // let the reference function catch up
  135. window := data[i-windowSize : i]
  136. hf1.Reset()
  137. hf1.Write(window)
  138. hf2.Reset()
  139. hf2.Write(window)
  140. // verify that they are in sync with the rolling function
  141. sum1 := hf1.Sum32()
  142. sum2 := hf2.Sum32()
  143. sum3 := hf3.Sum32()
  144. t.Logf("At i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3)
  145. if sum2 != sum3 {
  146. t.Errorf("Mismatch after roll; i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3)
  147. break
  148. }
  149. if sum1 != sum3 {
  150. t.Errorf("Mismatch after roll; i=%d, sum1=%08x, sum3=%08x", i, sum1, sum3)
  151. break
  152. }
  153. if !Validate(window, nil, sum1) {
  154. t.Errorf("Validation failure after roll; i=%d", i)
  155. }
  156. }
  157. hf3.Roll(data[i])
  158. }
  159. }
  160. func BenchmarkValidate(b *testing.B) {
  161. type block struct {
  162. data []byte
  163. hash [sha256.Size]byte
  164. weakhash uint32
  165. }
  166. var blocks []block
  167. const blocksPerType = 100
  168. r := mrand.New(mrand.NewSource(0x136bea689e851))
  169. // Valid blocks.
  170. for i := 0; i < blocksPerType; i++ {
  171. var b block
  172. b.data = make([]byte, 128<<10)
  173. r.Read(b.data)
  174. b.hash = sha256.Sum256(b.data)
  175. b.weakhash = origAdler32.Checksum(b.data)
  176. blocks = append(blocks, b)
  177. }
  178. // Blocks where the hash matches, but the weakhash doesn't.
  179. for i := 0; i < blocksPerType; i++ {
  180. var b block
  181. b.data = make([]byte, 128<<10)
  182. r.Read(b.data)
  183. b.hash = sha256.Sum256(b.data)
  184. b.weakhash = 1 // Zeros causes Validate to skip the weakhash.
  185. blocks = append(blocks, b)
  186. }
  187. b.ReportAllocs()
  188. b.ResetTimer()
  189. for i := 0; i < b.N; i++ {
  190. for _, b := range blocks {
  191. Validate(b.data, b.hash[:], b.weakhash)
  192. }
  193. }
  194. }