sync_test.go 7.1 KB

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