sync_test.go 7.6 KB

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