bep_fileinfo_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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 protocol
  7. import (
  8. "crypto/sha256"
  9. "testing"
  10. "github.com/syncthing/syncthing/lib/build"
  11. )
  12. func TestLocalFlagBits(t *testing.T) {
  13. var f FileInfo
  14. if f.IsIgnored() || f.MustRescan() || f.IsInvalid() {
  15. t.Error("file should have no weird bits set by default")
  16. }
  17. f.SetIgnored()
  18. if !f.IsIgnored() || f.MustRescan() || !f.IsInvalid() {
  19. t.Error("file should be ignored and invalid")
  20. }
  21. f.SetMustRescan()
  22. if f.IsIgnored() || !f.MustRescan() || !f.IsInvalid() {
  23. t.Error("file should be must-rescan and invalid")
  24. }
  25. f.SetUnsupported()
  26. if f.IsIgnored() || f.MustRescan() || !f.IsInvalid() {
  27. t.Error("file should be invalid")
  28. }
  29. }
  30. func TestIsEquivalent(t *testing.T) {
  31. b := func(v bool) *bool {
  32. return &v
  33. }
  34. type testCase struct {
  35. a FileInfo
  36. b FileInfo
  37. ignPerms *bool // nil means should not matter, we'll test both variants
  38. ignBlocks *bool
  39. ignFlags FlagLocal
  40. eq bool
  41. }
  42. cases := []testCase{
  43. // Empty FileInfos are equivalent
  44. {eq: true},
  45. // Various basic attributes, all of which cause inequality when
  46. // they differ
  47. {
  48. a: FileInfo{Name: "foo"},
  49. b: FileInfo{Name: "bar"},
  50. eq: false,
  51. },
  52. {
  53. a: FileInfo{Type: FileInfoTypeFile},
  54. b: FileInfo{Type: FileInfoTypeDirectory},
  55. eq: false,
  56. },
  57. {
  58. a: FileInfo{Size: 1234},
  59. b: FileInfo{Size: 2345},
  60. eq: false,
  61. },
  62. {
  63. a: FileInfo{Deleted: false},
  64. b: FileInfo{Deleted: true},
  65. eq: false,
  66. },
  67. {
  68. a: FileInfo{LocalFlags: 0},
  69. b: FileInfo{LocalFlags: FlagLocalRemoteInvalid},
  70. eq: false,
  71. },
  72. {
  73. a: FileInfo{ModifiedS: 1234},
  74. b: FileInfo{ModifiedS: 2345},
  75. eq: false,
  76. },
  77. {
  78. a: FileInfo{ModifiedNs: 1234},
  79. b: FileInfo{ModifiedNs: 2345},
  80. eq: false,
  81. },
  82. // Special handling of local flags and invalidity. "MustRescan"
  83. // files are never equivalent to each other. Otherwise, equivalence
  84. // is based just on whether the file becomes IsInvalid() or not, not
  85. // the specific reason or flag bits.
  86. {
  87. a: FileInfo{LocalFlags: FlagLocalMustRescan},
  88. b: FileInfo{LocalFlags: FlagLocalMustRescan},
  89. eq: false,
  90. },
  91. {
  92. a: FileInfo{LocalFlags: FlagLocalRemoteInvalid},
  93. b: FileInfo{LocalFlags: FlagLocalRemoteInvalid},
  94. eq: true,
  95. },
  96. {
  97. a: FileInfo{LocalFlags: FlagLocalUnsupported},
  98. b: FileInfo{LocalFlags: FlagLocalUnsupported},
  99. eq: true,
  100. },
  101. {
  102. a: FileInfo{LocalFlags: FlagLocalRemoteInvalid},
  103. b: FileInfo{LocalFlags: FlagLocalUnsupported},
  104. eq: true,
  105. },
  106. {
  107. a: FileInfo{LocalFlags: 0},
  108. b: FileInfo{LocalFlags: FlagLocalReceiveOnly},
  109. eq: false,
  110. },
  111. {
  112. a: FileInfo{LocalFlags: 0},
  113. b: FileInfo{LocalFlags: FlagLocalReceiveOnly},
  114. ignFlags: FlagLocalReceiveOnly,
  115. eq: true,
  116. },
  117. // Difference in blocks is not OK
  118. {
  119. a: FileInfo{Blocks: []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}},
  120. b: FileInfo{Blocks: []BlockInfo{{Hash: []byte{2, 3, 4, 5}}}},
  121. ignBlocks: b(false),
  122. eq: false,
  123. },
  124. // ... unless we say it is
  125. {
  126. a: FileInfo{Blocks: []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}},
  127. b: FileInfo{Blocks: []BlockInfo{{Hash: []byte{2, 3, 4, 5}}}},
  128. ignBlocks: b(true),
  129. eq: true,
  130. },
  131. // Difference in permissions is not OK.
  132. {
  133. a: FileInfo{Permissions: 0o444},
  134. b: FileInfo{Permissions: 0o666},
  135. ignPerms: b(false),
  136. eq: false,
  137. },
  138. // ... unless we say it is
  139. {
  140. a: FileInfo{Permissions: 0o666},
  141. b: FileInfo{Permissions: 0o444},
  142. ignPerms: b(true),
  143. eq: true,
  144. },
  145. // These attributes are not checked at all
  146. {
  147. a: FileInfo{NoPermissions: false},
  148. b: FileInfo{NoPermissions: true},
  149. eq: true,
  150. },
  151. {
  152. a: FileInfo{Version: Vector{Counters: []Counter{{ID: 1, Value: 42}}}},
  153. b: FileInfo{Version: Vector{Counters: []Counter{{ID: 42, Value: 1}}}},
  154. eq: true,
  155. },
  156. {
  157. a: FileInfo{Sequence: 1},
  158. b: FileInfo{Sequence: 2},
  159. eq: true,
  160. },
  161. // The block size is not checked (but this would fail the blocks
  162. // check in real world)
  163. {
  164. a: FileInfo{RawBlockSize: 1},
  165. b: FileInfo{RawBlockSize: 2},
  166. eq: true,
  167. },
  168. // The symlink target is checked for symlinks
  169. {
  170. a: FileInfo{Type: FileInfoTypeSymlink, SymlinkTarget: []byte("a")},
  171. b: FileInfo{Type: FileInfoTypeSymlink, SymlinkTarget: []byte("b")},
  172. eq: false,
  173. },
  174. // ... but not for non-symlinks
  175. {
  176. a: FileInfo{Type: FileInfoTypeFile, SymlinkTarget: []byte("a")},
  177. b: FileInfo{Type: FileInfoTypeFile, SymlinkTarget: []byte("b")},
  178. eq: true,
  179. },
  180. }
  181. if build.IsWindows {
  182. // On windows we only check the user writable bit of the permission
  183. // set, so these are equivalent.
  184. cases = append(cases, testCase{
  185. a: FileInfo{Permissions: 0o777},
  186. b: FileInfo{Permissions: 0o600},
  187. ignPerms: b(false),
  188. eq: true,
  189. })
  190. }
  191. for i, tc := range cases {
  192. // Check the standard attributes with all permutations of the
  193. // special ignore flags, unless the value of those flags are given
  194. // in the tests.
  195. for _, ignPerms := range []bool{true, false} {
  196. for _, ignBlocks := range []bool{true, false} {
  197. if tc.ignPerms != nil && *tc.ignPerms != ignPerms {
  198. continue
  199. }
  200. if tc.ignBlocks != nil && *tc.ignBlocks != ignBlocks {
  201. continue
  202. }
  203. if res := tc.a.isEquivalent(tc.b, FileInfoComparison{IgnorePerms: ignPerms, IgnoreBlocks: ignBlocks, IgnoreFlags: tc.ignFlags}); res != tc.eq {
  204. t.Errorf("Case %d:\na: %v\nb: %v\na.IsEquivalent(b, %v, %v) => %v, expected %v", i, tc.a, tc.b, ignPerms, ignBlocks, res, tc.eq)
  205. }
  206. if res := tc.b.isEquivalent(tc.a, FileInfoComparison{IgnorePerms: ignPerms, IgnoreBlocks: ignBlocks, IgnoreFlags: tc.ignFlags}); res != tc.eq {
  207. t.Errorf("Case %d:\na: %v\nb: %v\nb.IsEquivalent(a, %v, %v) => %v, expected %v", i, tc.a, tc.b, ignPerms, ignBlocks, res, tc.eq)
  208. }
  209. }
  210. }
  211. }
  212. }
  213. func TestSha256OfEmptyBlock(t *testing.T) {
  214. // every block size should have a correct entry in sha256OfEmptyBlock
  215. for blockSize := MinBlockSize; blockSize <= MaxBlockSize; blockSize *= 2 {
  216. expected := sha256.Sum256(make([]byte, blockSize))
  217. if sha256OfEmptyBlock[blockSize] != expected {
  218. t.Error("missing or wrong hash for block of size", blockSize)
  219. }
  220. }
  221. }
  222. func TestBlocksEqual(t *testing.T) {
  223. blocksOne := []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}
  224. blocksTwo := []BlockInfo{{Hash: []byte{5, 6, 7, 8}}}
  225. hashOne := []byte{42, 42, 42, 42}
  226. hashTwo := []byte{29, 29, 29, 29}
  227. cases := []struct {
  228. b1 []BlockInfo
  229. h1 []byte
  230. b2 []BlockInfo
  231. h2 []byte
  232. eq bool
  233. }{
  234. {blocksOne, hashOne, blocksOne, hashOne, true}, // everything equal
  235. {blocksOne, hashOne, blocksTwo, hashTwo, false}, // nothing equal
  236. {blocksOne, hashOne, blocksOne, nil, true}, // blocks compared
  237. {blocksOne, nil, blocksOne, nil, true}, // blocks compared
  238. {blocksOne, nil, blocksTwo, nil, false}, // blocks compared
  239. {blocksOne, hashOne, blocksTwo, hashOne, true}, // hashes equal, blocks not looked at
  240. {blocksOne, hashOne, blocksOne, hashTwo, true}, // hashes different, blocks compared
  241. {blocksOne, hashOne, blocksTwo, hashTwo, false}, // hashes different, blocks compared
  242. {blocksOne, hashOne, nil, nil, false}, // blocks is different from no blocks
  243. {blocksOne, nil, nil, nil, false}, // blocks is different from no blocks
  244. {nil, hashOne, nil, nil, true}, // nil blocks are equal, even of one side has a hash
  245. }
  246. for _, tc := range cases {
  247. f1 := FileInfo{Blocks: tc.b1, BlocksHash: tc.h1}
  248. f2 := FileInfo{Blocks: tc.b2, BlocksHash: tc.h2}
  249. if !f1.BlocksEqual(f1) {
  250. t.Error("f1 is always equal to itself", f1)
  251. }
  252. if !f2.BlocksEqual(f2) {
  253. t.Error("f2 is always equal to itself", f2)
  254. }
  255. if res := f1.BlocksEqual(f2); res != tc.eq {
  256. t.Log("f1", f1.BlocksHash, f1.Blocks)
  257. t.Log("f2", f2.BlocksHash, f2.Blocks)
  258. t.Errorf("f1.BlocksEqual(f2) == %v but should be %v", res, tc.eq)
  259. }
  260. if res := f2.BlocksEqual(f1); res != tc.eq {
  261. t.Log("f1", f1.BlocksHash, f1.Blocks)
  262. t.Log("f2", f2.BlocksHash, f2.Blocks)
  263. t.Errorf("f2.BlocksEqual(f1) == %v but should be %v", res, tc.eq)
  264. }
  265. }
  266. }