sync_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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. // +build integration
  7. package integration
  8. import (
  9. "fmt"
  10. "log"
  11. "math/rand"
  12. "os"
  13. "testing"
  14. "time"
  15. "github.com/syncthing/protocol"
  16. "github.com/syncthing/syncthing/internal/config"
  17. "github.com/syncthing/syncthing/internal/rc"
  18. )
  19. const (
  20. longTimeLimit = 5 * time.Minute
  21. shortTimeLimit = 45 * time.Second
  22. )
  23. func TestSyncClusterWithoutVersioning(t *testing.T) {
  24. // Use no versioning
  25. id, _ := protocol.DeviceIDFromString(id2)
  26. cfg, _ := config.Load("h2/config.xml", id)
  27. fld := cfg.Folders()["default"]
  28. fld.Versioning = config.VersioningConfiguration{}
  29. cfg.SetFolder(fld)
  30. cfg.Save()
  31. testSyncCluster(t)
  32. }
  33. func TestSyncClusterSimpleVersioning(t *testing.T) {
  34. // Use simple versioning
  35. id, _ := protocol.DeviceIDFromString(id2)
  36. cfg, _ := config.Load("h2/config.xml", id)
  37. fld := cfg.Folders()["default"]
  38. fld.Versioning = config.VersioningConfiguration{
  39. Type: "simple",
  40. Params: map[string]string{"keep": "5"},
  41. }
  42. cfg.SetFolder(fld)
  43. cfg.Save()
  44. testSyncCluster(t)
  45. }
  46. func TestSyncClusterTrashcanVersioning(t *testing.T) {
  47. // Use simple versioning
  48. id, _ := protocol.DeviceIDFromString(id2)
  49. cfg, _ := config.Load("h2/config.xml", id)
  50. fld := cfg.Folders()["default"]
  51. fld.Versioning = config.VersioningConfiguration{
  52. Type: "trashcan",
  53. Params: map[string]string{"cleanoutDays": "1"},
  54. }
  55. cfg.SetFolder(fld)
  56. cfg.Save()
  57. testSyncCluster(t)
  58. }
  59. func TestSyncClusterStaggeredVersioning(t *testing.T) {
  60. // Use staggered versioning
  61. id, _ := protocol.DeviceIDFromString(id2)
  62. cfg, _ := config.Load("h2/config.xml", id)
  63. fld := cfg.Folders()["default"]
  64. fld.Versioning = config.VersioningConfiguration{
  65. Type: "staggered",
  66. }
  67. cfg.SetFolder(fld)
  68. cfg.Save()
  69. testSyncCluster(t)
  70. }
  71. func testSyncCluster(t *testing.T) {
  72. // This tests syncing files back and forth between three cluster members.
  73. // Their configs are in h1, h2 and h3. The folder "default" is shared
  74. // between all and stored in s1, s2 and s3 respectively.
  75. //
  76. // Another folder is shared between 1 and 2 only, in s12-1 and s12-2. A
  77. // third folders is shared between 2 and 3, in s23-2 and s23-3.
  78. // When -short is passed, keep it more reasonable.
  79. timeLimit := longTimeLimit
  80. if testing.Short() {
  81. timeLimit = shortTimeLimit
  82. }
  83. const (
  84. numFiles = 100
  85. fileSizeExp = 20
  86. )
  87. rand.Seed(42)
  88. log.Printf("Testing with numFiles=%d, fileSizeExp=%d, timeLimit=%v", numFiles, fileSizeExp, timeLimit)
  89. log.Println("Cleaning...")
  90. err := removeAll("s1", "s12-1",
  91. "s2", "s12-2", "s23-2",
  92. "s3", "s23-3",
  93. "h1/index*", "h2/index*", "h3/index*")
  94. if err != nil {
  95. t.Fatal(err)
  96. }
  97. // Create initial folder contents. All three devices have stuff in
  98. // "default", which should be merged. The other two folders are initially
  99. // empty on one side.
  100. log.Println("Generating files...")
  101. err = generateFiles("s1", numFiles, fileSizeExp, "../LICENSE")
  102. if err != nil {
  103. t.Fatal(err)
  104. }
  105. err = generateFiles("s12-1", numFiles, fileSizeExp, "../LICENSE")
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. // We'll use this file for appending data without modifying the time stamp.
  110. fd, err := os.Create("s1/test-appendfile")
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. _, err = fd.WriteString("hello\n")
  115. if err != nil {
  116. t.Fatal(err)
  117. }
  118. err = fd.Close()
  119. if err != nil {
  120. t.Fatal(err)
  121. }
  122. err = generateFiles("s2", numFiles, fileSizeExp, "../LICENSE")
  123. if err != nil {
  124. t.Fatal(err)
  125. }
  126. err = generateFiles("s23-2", numFiles, fileSizeExp, "../LICENSE")
  127. if err != nil {
  128. t.Fatal(err)
  129. }
  130. err = generateFiles("s3", numFiles, fileSizeExp, "../LICENSE")
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. // Prepare the expected state of folders after the sync
  135. c1, err := directoryContents("s1")
  136. if err != nil {
  137. t.Fatal(err)
  138. }
  139. c2, err := directoryContents("s2")
  140. if err != nil {
  141. t.Fatal(err)
  142. }
  143. c3, err := directoryContents("s3")
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. e1 := mergeDirectoryContents(c1, c2, c3)
  148. e2, err := directoryContents("s12-1")
  149. if err != nil {
  150. t.Fatal(err)
  151. }
  152. e3, err := directoryContents("s23-2")
  153. if err != nil {
  154. t.Fatal(err)
  155. }
  156. expected := [][]fileInfo{e1, e2, e3}
  157. // Start the syncers
  158. log.Println("Starting Syncthing...")
  159. p0 := startInstance(t, 1)
  160. defer checkedStop(t, p0)
  161. p1 := startInstance(t, 2)
  162. defer checkedStop(t, p1)
  163. p2 := startInstance(t, 3)
  164. defer checkedStop(t, p2)
  165. p := []*rc.Process{p0, p1, p2}
  166. start := time.Now()
  167. iteration := 0
  168. for time.Since(start) < timeLimit {
  169. iteration++
  170. log.Println("Iteration", iteration)
  171. log.Println("Forcing rescan...")
  172. // Force rescan of folders
  173. for i, device := range p {
  174. if err := device.RescanDelay("default", 86400); err != nil {
  175. t.Fatal(err)
  176. }
  177. if i == 0 || i == 1 {
  178. if err := device.RescanDelay("s12", 86400); err != nil {
  179. t.Fatal(err)
  180. }
  181. }
  182. if i == 1 || i == 2 {
  183. if err := device.RescanDelay("s23", 86400); err != nil {
  184. t.Fatal(err)
  185. }
  186. }
  187. }
  188. // Sync stuff and verify it looks right
  189. err = scSyncAndCompare(p, expected)
  190. if err != nil {
  191. t.Error(err)
  192. break
  193. }
  194. log.Println("Altering...")
  195. // Alter the source files for another round
  196. err = alterFiles("s1")
  197. if err != nil {
  198. t.Error(err)
  199. break
  200. }
  201. err = alterFiles("s12-1")
  202. if err != nil {
  203. t.Error(err)
  204. break
  205. }
  206. err = alterFiles("s23-2")
  207. if err != nil {
  208. t.Error(err)
  209. break
  210. }
  211. // Alter the "test-appendfile" without changing it's modification time. Sneaky!
  212. fi, err := os.Stat("s1/test-appendfile")
  213. if err != nil {
  214. t.Fatal(err)
  215. }
  216. fd, err := os.OpenFile("s1/test-appendfile", os.O_APPEND|os.O_WRONLY, 0644)
  217. if err != nil {
  218. t.Fatal(err)
  219. }
  220. _, err = fd.Seek(0, os.SEEK_END)
  221. if err != nil {
  222. t.Fatal(err)
  223. }
  224. _, err = fd.WriteString("more data\n")
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. err = fd.Close()
  229. if err != nil {
  230. t.Fatal(err)
  231. }
  232. err = os.Chtimes("s1/test-appendfile", fi.ModTime(), fi.ModTime())
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. // Prepare the expected state of folders after the sync
  237. e1, err = directoryContents("s1")
  238. if err != nil {
  239. t.Fatal(err)
  240. }
  241. e2, err = directoryContents("s12-1")
  242. if err != nil {
  243. t.Fatal(err)
  244. }
  245. e3, err = directoryContents("s23-2")
  246. if err != nil {
  247. t.Fatal(err)
  248. }
  249. expected = [][]fileInfo{e1, e2, e3}
  250. }
  251. }
  252. func scSyncAndCompare(p []*rc.Process, expected [][]fileInfo) error {
  253. log.Println("Syncing...")
  254. for {
  255. time.Sleep(250 * time.Millisecond)
  256. if !rc.InSync("default", p...) {
  257. continue
  258. }
  259. if !rc.InSync("s12", p[0], p[1]) {
  260. continue
  261. }
  262. if !rc.InSync("s23", p[1], p[2]) {
  263. continue
  264. }
  265. break
  266. }
  267. log.Println("Checking...")
  268. for _, dir := range []string{"s1", "s2", "s3"} {
  269. actual, err := directoryContents(dir)
  270. if err != nil {
  271. return err
  272. }
  273. if err := compareDirectoryContents(actual, expected[0]); err != nil {
  274. return fmt.Errorf("%s: %v", dir, err)
  275. }
  276. }
  277. if len(expected) > 1 {
  278. for _, dir := range []string{"s12-1", "s12-2"} {
  279. actual, err := directoryContents(dir)
  280. if err != nil {
  281. return err
  282. }
  283. if err := compareDirectoryContents(actual, expected[1]); err != nil {
  284. return fmt.Errorf("%s: %v", dir, err)
  285. }
  286. }
  287. }
  288. if len(expected) > 2 {
  289. for _, dir := range []string{"s23-2", "s23-3"} {
  290. actual, err := directoryContents(dir)
  291. if err != nil {
  292. return err
  293. }
  294. if err := compareDirectoryContents(actual, expected[2]); err != nil {
  295. return fmt.Errorf("%s: %v", dir, err)
  296. }
  297. }
  298. }
  299. return nil
  300. }