blocks_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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 http://mozilla.org/MPL/2.0/.
  6. package scanner
  7. import (
  8. "bytes"
  9. "crypto/rand"
  10. "fmt"
  11. origAdler32 "hash/adler32"
  12. "testing"
  13. "testing/quick"
  14. rollingAdler32 "github.com/chmduquesne/rollinghash/adler32"
  15. "github.com/syncthing/syncthing/lib/protocol"
  16. )
  17. var blocksTestData = []struct {
  18. data []byte
  19. blocksize int
  20. hash []string
  21. weakhash []uint32
  22. }{
  23. {[]byte(""), 1024, []string{
  24. "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
  25. []uint32{0},
  26. },
  27. {[]byte("contents"), 1024, []string{
  28. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  29. []uint32{0x0f3a036f},
  30. },
  31. {[]byte("contents"), 9, []string{
  32. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  33. []uint32{0x0f3a036f},
  34. },
  35. {[]byte("contents"), 8, []string{
  36. "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8"},
  37. []uint32{0x0f3a036f},
  38. },
  39. {[]byte("contents"), 7, []string{
  40. "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73",
  41. "043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89"},
  42. []uint32{0x0bcb02fc, 0x00740074},
  43. },
  44. {[]byte("contents"), 3, []string{
  45. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  46. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3",
  47. "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4"},
  48. []uint32{0x02780141, 0x02970148, 0x015d00e8},
  49. },
  50. {[]byte("conconts"), 3, []string{
  51. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  52. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  53. "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4"},
  54. []uint32{0x02780141, 0x02780141, 0x015d00e8},
  55. },
  56. {[]byte("contenten"), 3, []string{
  57. "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952",
  58. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3",
  59. "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3"},
  60. []uint32{0x02780141, 0x02970148, 0x02970148},
  61. },
  62. }
  63. func TestBlocks(t *testing.T) {
  64. for testNo, test := range blocksTestData {
  65. buf := bytes.NewBuffer(test.data)
  66. blocks, err := Blocks(buf, test.blocksize, -1, nil, true)
  67. if err != nil {
  68. t.Fatal(err)
  69. }
  70. if l := len(blocks); l != len(test.hash) {
  71. t.Fatalf("%d: Incorrect number of blocks %d != %d", testNo, l, len(test.hash))
  72. } else {
  73. i := 0
  74. for off := int64(0); off < int64(len(test.data)); off += int64(test.blocksize) {
  75. if blocks[i].Offset != off {
  76. t.Errorf("%d/%d: Incorrect offset %d != %d", testNo, i, blocks[i].Offset, off)
  77. }
  78. bs := test.blocksize
  79. if rem := len(test.data) - int(off); bs > rem {
  80. bs = rem
  81. }
  82. if int(blocks[i].Size) != bs {
  83. t.Errorf("%d/%d: Incorrect length %d != %d", testNo, i, blocks[i].Size, bs)
  84. }
  85. if h := fmt.Sprintf("%x", blocks[i].Hash); h != test.hash[i] {
  86. t.Errorf("%d/%d: Incorrect block hash %q != %q", testNo, i, h, test.hash[i])
  87. }
  88. if h := blocks[i].WeakHash; h != test.weakhash[i] {
  89. t.Errorf("%d/%d: Incorrect block weakhash 0x%08x != 0x%08x", testNo, i, h, test.weakhash[i])
  90. }
  91. i++
  92. }
  93. }
  94. }
  95. }
  96. var diffTestData = []struct {
  97. a string
  98. b string
  99. s int
  100. d []protocol.BlockInfo
  101. }{
  102. {"contents", "contents", 1024, []protocol.BlockInfo{}},
  103. {"", "", 1024, []protocol.BlockInfo{}},
  104. {"contents", "contents", 3, []protocol.BlockInfo{}},
  105. {"contents", "cantents", 3, []protocol.BlockInfo{{Offset: 0, Size: 3}}},
  106. {"contents", "contants", 3, []protocol.BlockInfo{{Offset: 3, Size: 3}}},
  107. {"contents", "cantants", 3, []protocol.BlockInfo{{Offset: 0, Size: 3}, {Offset: 3, Size: 3}}},
  108. {"contents", "", 3, []protocol.BlockInfo{{Offset: 0, Size: 0}}},
  109. {"", "contents", 3, []protocol.BlockInfo{{Offset: 0, Size: 3}, {Offset: 3, Size: 3}, {Offset: 6, Size: 2}}},
  110. {"con", "contents", 3, []protocol.BlockInfo{{Offset: 3, Size: 3}, {Offset: 6, Size: 2}}},
  111. {"contents", "con", 3, nil},
  112. {"contents", "cont", 3, []protocol.BlockInfo{{Offset: 3, Size: 1}}},
  113. {"cont", "contents", 3, []protocol.BlockInfo{{Offset: 3, Size: 3}, {Offset: 6, Size: 2}}},
  114. }
  115. func TestDiff(t *testing.T) {
  116. for i, test := range diffTestData {
  117. a, _ := Blocks(bytes.NewBufferString(test.a), test.s, -1, nil, false)
  118. b, _ := Blocks(bytes.NewBufferString(test.b), test.s, -1, nil, false)
  119. _, d := BlockDiff(a, b)
  120. if len(d) != len(test.d) {
  121. t.Fatalf("Incorrect length for diff %d; %d != %d", i, len(d), len(test.d))
  122. } else {
  123. for j := range test.d {
  124. if d[j].Offset != test.d[j].Offset {
  125. t.Errorf("Incorrect offset for diff %d block %d; %d != %d", i, j, d[j].Offset, test.d[j].Offset)
  126. }
  127. if d[j].Size != test.d[j].Size {
  128. t.Errorf("Incorrect length for diff %d block %d; %d != %d", i, j, d[j].Size, test.d[j].Size)
  129. }
  130. }
  131. }
  132. }
  133. }
  134. func TestDiffEmpty(t *testing.T) {
  135. emptyCases := []struct {
  136. a []protocol.BlockInfo
  137. b []protocol.BlockInfo
  138. need int
  139. have int
  140. }{
  141. {nil, nil, 0, 0},
  142. {[]protocol.BlockInfo{{Offset: 3, Size: 1}}, nil, 0, 0},
  143. {nil, []protocol.BlockInfo{{Offset: 3, Size: 1}}, 1, 0},
  144. }
  145. for _, emptyCase := range emptyCases {
  146. h, n := BlockDiff(emptyCase.a, emptyCase.b)
  147. if len(h) != emptyCase.have {
  148. t.Errorf("incorrect have: %d != %d", len(h), emptyCase.have)
  149. }
  150. if len(n) != emptyCase.need {
  151. t.Errorf("incorrect have: %d != %d", len(h), emptyCase.have)
  152. }
  153. }
  154. }
  155. func TestAdler32Variants(t *testing.T) {
  156. // Verify that the two adler32 functions give matching results for a few
  157. // different blocks of data.
  158. hf1 := origAdler32.New()
  159. hf2 := rollingAdler32.New()
  160. checkFn := func(data []byte) bool {
  161. hf1.Write(data)
  162. sum1 := hf1.Sum32()
  163. hf2.Write(data)
  164. sum2 := hf2.Sum32()
  165. hf1.Reset()
  166. hf2.Reset()
  167. return sum1 == sum2
  168. }
  169. // protocol block sized data
  170. data := make([]byte, protocol.BlockSize)
  171. for i := 0; i < 5; i++ {
  172. rand.Read(data)
  173. if !checkFn(data) {
  174. t.Errorf("Hash mismatch on block sized data")
  175. }
  176. }
  177. // random small blocks
  178. if err := quick.Check(checkFn, nil); err != nil {
  179. t.Error(err)
  180. }
  181. // rolling should have the same result as the individual blocks
  182. // themselves. Which is not the same as the original non-rollind adler32
  183. // blocks.
  184. windowSize := 128
  185. hf2.Reset()
  186. hf3 := rollingAdler32.New()
  187. hf3.Write(data[:windowSize])
  188. for i := windowSize; i < len(data); i++ {
  189. if i%windowSize == 0 {
  190. // let the reference function catch up
  191. hf2.Write(data[i-windowSize : i])
  192. // verify that they are in sync with the rolling function
  193. sum2 := hf2.Sum32()
  194. sum3 := hf3.Sum32()
  195. t.Logf("At i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3)
  196. if sum2 != sum3 {
  197. t.Errorf("Mismatch after roll; i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3)
  198. break
  199. }
  200. }
  201. hf3.Roll(data[i])
  202. }
  203. }