bep_fileinfo_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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. // Unix Ownership should be the same
  181. {
  182. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  183. b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  184. eq: true,
  185. },
  186. // ... but matching ID is enough
  187. {
  188. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  189. b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1000, GID: 1000}}},
  190. eq: true,
  191. },
  192. // ... or matching name
  193. {
  194. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  195. b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1001, GID: 1001}}},
  196. eq: true,
  197. },
  198. // ... or empty name
  199. {
  200. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  201. b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "", GroupName: "", UID: 1000, GID: 1000}}},
  202. eq: true,
  203. },
  204. // ... but not different ownership
  205. {
  206. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  207. b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1001, GID: 1001}}},
  208. eq: false,
  209. },
  210. // or missing ownership
  211. {
  212. a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
  213. b: FileInfo{Platform: PlatformData{}},
  214. eq: false,
  215. },
  216. }
  217. if build.IsWindows {
  218. // On windows we only check the user writable bit of the permission
  219. // set, so these are equivalent.
  220. cases = append(cases, testCase{
  221. a: FileInfo{Permissions: 0o777},
  222. b: FileInfo{Permissions: 0o600},
  223. ignPerms: b(false),
  224. eq: true,
  225. })
  226. }
  227. for i, tc := range cases {
  228. // Check the standard attributes with all permutations of the
  229. // special ignore flags, unless the value of those flags are given
  230. // in the tests.
  231. for _, ignPerms := range []bool{true, false} {
  232. for _, ignBlocks := range []bool{true, false} {
  233. if tc.ignPerms != nil && *tc.ignPerms != ignPerms {
  234. continue
  235. }
  236. if tc.ignBlocks != nil && *tc.ignBlocks != ignBlocks {
  237. continue
  238. }
  239. if res := tc.a.isEquivalent(tc.b, FileInfoComparison{IgnorePerms: ignPerms, IgnoreBlocks: ignBlocks, IgnoreFlags: tc.ignFlags}); res != tc.eq {
  240. 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)
  241. }
  242. if res := tc.b.isEquivalent(tc.a, FileInfoComparison{IgnorePerms: ignPerms, IgnoreBlocks: ignBlocks, IgnoreFlags: tc.ignFlags}); res != tc.eq {
  243. 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)
  244. }
  245. }
  246. }
  247. }
  248. }
  249. func TestSha256OfEmptyBlock(t *testing.T) {
  250. // every block size should have a correct entry in sha256OfEmptyBlock
  251. for blockSize := MinBlockSize; blockSize <= MaxBlockSize; blockSize *= 2 {
  252. expected := sha256.Sum256(make([]byte, blockSize))
  253. if sha256OfEmptyBlock[blockSize] != expected {
  254. t.Error("missing or wrong hash for block of size", blockSize)
  255. }
  256. }
  257. }
  258. func TestBlocksEqual(t *testing.T) {
  259. blocksOne := []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}
  260. blocksTwo := []BlockInfo{{Hash: []byte{5, 6, 7, 8}}}
  261. hashOne := []byte{42, 42, 42, 42}
  262. hashTwo := []byte{29, 29, 29, 29}
  263. cases := []struct {
  264. b1 []BlockInfo
  265. h1 []byte
  266. b2 []BlockInfo
  267. h2 []byte
  268. eq bool
  269. }{
  270. {blocksOne, hashOne, blocksOne, hashOne, true}, // everything equal
  271. {blocksOne, hashOne, blocksTwo, hashTwo, false}, // nothing equal
  272. {blocksOne, hashOne, blocksOne, nil, true}, // blocks compared
  273. {blocksOne, nil, blocksOne, nil, true}, // blocks compared
  274. {blocksOne, nil, blocksTwo, nil, false}, // blocks compared
  275. {blocksOne, hashOne, blocksTwo, hashOne, true}, // hashes equal, blocks not looked at
  276. {blocksOne, hashOne, blocksOne, hashTwo, true}, // hashes different, blocks compared
  277. {blocksOne, hashOne, blocksTwo, hashTwo, false}, // hashes different, blocks compared
  278. {blocksOne, hashOne, nil, nil, false}, // blocks is different from no blocks
  279. {blocksOne, nil, nil, nil, false}, // blocks is different from no blocks
  280. {nil, hashOne, nil, nil, true}, // nil blocks are equal, even of one side has a hash
  281. }
  282. for _, tc := range cases {
  283. f1 := FileInfo{Blocks: tc.b1, BlocksHash: tc.h1}
  284. f2 := FileInfo{Blocks: tc.b2, BlocksHash: tc.h2}
  285. if !f1.BlocksEqual(f1) {
  286. t.Error("f1 is always equal to itself", f1)
  287. }
  288. if !f2.BlocksEqual(f2) {
  289. t.Error("f2 is always equal to itself", f2)
  290. }
  291. if res := f1.BlocksEqual(f2); res != tc.eq {
  292. t.Log("f1", f1.BlocksHash, f1.Blocks)
  293. t.Log("f2", f2.BlocksHash, f2.Blocks)
  294. t.Errorf("f1.BlocksEqual(f2) == %v but should be %v", res, tc.eq)
  295. }
  296. if res := f2.BlocksEqual(f1); res != tc.eq {
  297. t.Log("f1", f1.BlocksHash, f1.Blocks)
  298. t.Log("f2", f2.BlocksHash, f2.Blocks)
  299. t.Errorf("f2.BlocksEqual(f1) == %v but should be %v", res, tc.eq)
  300. }
  301. }
  302. }