folder_recvonly_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Copyright (C) 2018 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 model
  7. import (
  8. "bytes"
  9. "context"
  10. "io/ioutil"
  11. "os"
  12. "testing"
  13. "time"
  14. "github.com/syncthing/syncthing/lib/config"
  15. "github.com/syncthing/syncthing/lib/db"
  16. "github.com/syncthing/syncthing/lib/fs"
  17. "github.com/syncthing/syncthing/lib/protocol"
  18. "github.com/syncthing/syncthing/lib/scanner"
  19. )
  20. func TestRecvOnlyRevertDeletes(t *testing.T) {
  21. // Make sure that we delete extraneous files and directories when we hit
  22. // Revert.
  23. if err := os.RemoveAll("_recvonly"); err != nil && !os.IsNotExist(err) {
  24. t.Fatal(err)
  25. }
  26. defer func() {
  27. if err := os.RemoveAll("_recvonly"); err != nil && !os.IsNotExist(err) {
  28. t.Fatal(err)
  29. }
  30. }()
  31. // Create some test data
  32. if err := os.MkdirAll("_recvonly/.stfolder", 0755); err != nil {
  33. t.Fatal(err)
  34. }
  35. if err := os.MkdirAll("_recvonly/ignDir", 0755); err != nil {
  36. t.Fatal(err)
  37. }
  38. if err := os.MkdirAll("_recvonly/unknownDir", 0755); err != nil {
  39. t.Fatal(err)
  40. }
  41. if err := ioutil.WriteFile("_recvonly/ignDir/ignFile", []byte("hello\n"), 0644); err != nil {
  42. t.Fatal(err)
  43. }
  44. if err := ioutil.WriteFile("_recvonly/unknownDir/unknownFile", []byte("hello\n"), 0644); err != nil {
  45. t.Fatal(err)
  46. }
  47. if err := ioutil.WriteFile("_recvonly/.stignore", []byte("ignDir\n"), 0644); err != nil {
  48. t.Fatal(err)
  49. }
  50. knownFiles := setupKnownFiles(t, []byte("hello\n"))
  51. // Get us a model up and running
  52. m := setupROFolder()
  53. defer m.Stop()
  54. // Send and index update for the known stuff
  55. m.Index(device1, "ro", knownFiles)
  56. m.updateLocalsFromScanning("ro", knownFiles)
  57. size := m.GlobalSize("ro")
  58. if size.Files != 1 || size.Directories != 1 {
  59. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  60. }
  61. // Start the folder. This will cause a scan, should discover the other stuff in the folder
  62. m.StartFolder("ro")
  63. m.ScanFolder("ro")
  64. // We should now have two files and two directories.
  65. size = m.GlobalSize("ro")
  66. if size.Files != 2 || size.Directories != 2 {
  67. t.Fatalf("Global: expected 2 files and 2 directories: %+v", size)
  68. }
  69. size = m.LocalSize("ro")
  70. if size.Files != 2 || size.Directories != 2 {
  71. t.Fatalf("Local: expected 2 files and 2 directories: %+v", size)
  72. }
  73. size = m.ReceiveOnlyChangedSize("ro")
  74. if size.Files+size.Directories == 0 {
  75. t.Fatalf("ROChanged: expected something: %+v", size)
  76. }
  77. // Revert should delete the unknown stuff
  78. m.Revert("ro")
  79. // These should still exist
  80. for _, p := range []string{"_recvonly/knownDir/knownFile", "_recvonly/ignDir/ignFile"} {
  81. _, err := os.Stat(p)
  82. if err != nil {
  83. t.Error("Unexpected error:", err)
  84. }
  85. }
  86. // These should have been removed
  87. for _, p := range []string{"_recvonly/unknownDir", "_recvonly/unknownDir/unknownFile"} {
  88. _, err := os.Stat(p)
  89. if !os.IsNotExist(err) {
  90. t.Error("Unexpected existing thing:", p)
  91. }
  92. }
  93. // We should now have one file and directory again.
  94. size = m.GlobalSize("ro")
  95. if size.Files != 1 || size.Directories != 1 {
  96. t.Fatalf("Global: expected 1 files and 1 directories: %+v", size)
  97. }
  98. size = m.LocalSize("ro")
  99. if size.Files != 1 || size.Directories != 1 {
  100. t.Fatalf("Local: expected 1 files and 1 directories: %+v", size)
  101. }
  102. }
  103. func TestRecvOnlyRevertNeeds(t *testing.T) {
  104. // Make sure that a new file gets picked up and considered latest, then
  105. // gets considered old when we hit Revert.
  106. if err := os.RemoveAll("_recvonly"); err != nil && !os.IsNotExist(err) {
  107. t.Fatal(err)
  108. }
  109. defer func() {
  110. if err := os.RemoveAll("_recvonly"); err != nil && !os.IsNotExist(err) {
  111. t.Fatal(err)
  112. }
  113. }()
  114. // Create some test data
  115. if err := os.MkdirAll("_recvonly/.stfolder", 0755); err != nil {
  116. t.Fatal(err)
  117. }
  118. oldData := []byte("hello\n")
  119. knownFiles := setupKnownFiles(t, oldData)
  120. // Get us a model up and running
  121. m := setupROFolder()
  122. defer m.Stop()
  123. // Send and index update for the known stuff
  124. m.Index(device1, "ro", knownFiles)
  125. m.updateLocalsFromScanning("ro", knownFiles)
  126. // Start the folder. This will cause a scan.
  127. m.StartFolder("ro")
  128. m.ScanFolder("ro")
  129. // Everything should be in sync.
  130. size := m.GlobalSize("ro")
  131. if size.Files != 1 || size.Directories != 1 {
  132. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  133. }
  134. size = m.LocalSize("ro")
  135. if size.Files != 1 || size.Directories != 1 {
  136. t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
  137. }
  138. size = m.NeedSize("ro")
  139. if size.Files+size.Directories > 0 {
  140. t.Fatalf("Need: expected nothing: %+v", size)
  141. }
  142. size = m.ReceiveOnlyChangedSize("ro")
  143. if size.Files+size.Directories > 0 {
  144. t.Fatalf("ROChanged: expected nothing: %+v", size)
  145. }
  146. // Update the file.
  147. newData := []byte("totally different data\n")
  148. if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", newData, 0644); err != nil {
  149. t.Fatal(err)
  150. }
  151. // Rescan.
  152. if err := m.ScanFolder("ro"); err != nil {
  153. t.Fatal(err)
  154. }
  155. // We now have a newer file than the rest of the cluster. Global state should reflect this.
  156. size = m.GlobalSize("ro")
  157. const sizeOfDir = 128
  158. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
  159. t.Fatalf("Global: expected the new file to be reflected: %+v", size)
  160. }
  161. size = m.LocalSize("ro")
  162. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
  163. t.Fatalf("Local: expected the new file to be reflected: %+v", size)
  164. }
  165. size = m.NeedSize("ro")
  166. if size.Files+size.Directories > 0 {
  167. t.Fatalf("Need: expected nothing: %+v", size)
  168. }
  169. size = m.ReceiveOnlyChangedSize("ro")
  170. if size.Files+size.Directories == 0 {
  171. t.Fatalf("ROChanged: expected something: %+v", size)
  172. }
  173. // We hit the Revert button. The file that was new should become old.
  174. m.Revert("ro")
  175. size = m.GlobalSize("ro")
  176. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
  177. t.Fatalf("Global: expected the global size to revert: %+v", size)
  178. }
  179. size = m.LocalSize("ro")
  180. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
  181. t.Fatalf("Local: expected the local size to remain: %+v", size)
  182. }
  183. size = m.NeedSize("ro")
  184. if size.Files != 1 || size.Bytes != int64(len(oldData)) {
  185. t.Fatalf("Local: expected to need the old file data: %+v", size)
  186. }
  187. }
  188. func setupKnownFiles(t *testing.T, data []byte) []protocol.FileInfo {
  189. if err := os.MkdirAll("_recvonly/knownDir", 0755); err != nil {
  190. t.Fatal(err)
  191. }
  192. if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", data, 0644); err != nil {
  193. t.Fatal(err)
  194. }
  195. t0 := time.Now().Add(-1 * time.Minute)
  196. if err := os.Chtimes("_recvonly/knownDir/knownFile", t0, t0); err != nil {
  197. t.Fatal(err)
  198. }
  199. fi, err := os.Stat("_recvonly/knownDir/knownFile")
  200. if err != nil {
  201. t.Fatal(err)
  202. }
  203. blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), protocol.BlockSize(int64(len(data))), int64(len(data)), nil, true)
  204. knownFiles := []protocol.FileInfo{
  205. {
  206. Name: "knownDir",
  207. Type: protocol.FileInfoTypeDirectory,
  208. Permissions: 0755,
  209. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
  210. Sequence: 42,
  211. },
  212. {
  213. Name: "knownDir/knownFile",
  214. Type: protocol.FileInfoTypeFile,
  215. Permissions: 0644,
  216. Size: fi.Size(),
  217. ModifiedS: fi.ModTime().Unix(),
  218. ModifiedNs: int32(fi.ModTime().UnixNano() % 1e9),
  219. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
  220. Sequence: 42,
  221. Blocks: blocks,
  222. },
  223. }
  224. return knownFiles
  225. }
  226. func setupROFolder() *Model {
  227. fcfg := config.NewFolderConfiguration(protocol.LocalDeviceID, "ro", "receive only test", fs.FilesystemTypeBasic, "_recvonly")
  228. fcfg.Type = config.FolderTypeReceiveOnly
  229. fcfg.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}}
  230. fcfg.FSWatcherEnabled = false
  231. fcfg.RescanIntervalS = 86400
  232. cfg := defaultCfg.Copy()
  233. cfg.Folders = append(cfg.Folders, fcfg)
  234. wrp := config.Wrap("/dev/null", cfg)
  235. db := db.OpenMemory()
  236. m := NewModel(wrp, protocol.LocalDeviceID, "syncthing", "dev", db, nil)
  237. m.ServeBackground()
  238. m.AddFolder(fcfg)
  239. return m
  240. }