sync_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 TestSyncCluster(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. p, err := scStartProcesses()
  115. if err != nil {
  116. t.Fatal(err)
  117. }
  118. // Prepare the expected state of folders after the sync
  119. e1 := mergeDirectoryContents(directoryContents("s1"),
  120. directoryContents("s2"),
  121. directoryContents("s3"))
  122. e2 := directoryContents("s12-1")
  123. e3 := directoryContents("s23-2")
  124. expected := [][]fileInfo{e1, e2, e3}
  125. for count := 0; count < 5; count++ {
  126. log.Println("Forcing rescan...")
  127. // Force rescan of folders
  128. for i := range p {
  129. p[i].post("/rest/scan?folder=default", nil)
  130. if i < 3 {
  131. p[i].post("/rest/scan?folder=s12", nil)
  132. }
  133. if i > 1 {
  134. p[i].post("/rest/scan?folder=s23", nil)
  135. }
  136. }
  137. // Sync stuff and verify it looks right
  138. err = scSyncAndCompare(p, expected)
  139. if err != nil {
  140. t.Error(err)
  141. break
  142. }
  143. log.Println("Altering...")
  144. // Alter the source files for another round
  145. err = alterFiles("s1")
  146. if err != nil {
  147. t.Error(err)
  148. break
  149. }
  150. err = alterFiles("s12-1")
  151. if err != nil {
  152. t.Error(err)
  153. break
  154. }
  155. err = alterFiles("s23-2")
  156. if err != nil {
  157. t.Error(err)
  158. break
  159. }
  160. // Alter the "appendfile" without changing it's modification time. Sneaky!
  161. fi, err := os.Stat("s1/appendfile")
  162. if err != nil {
  163. t.Fatal(err)
  164. }
  165. fd, err := os.OpenFile("s1/appendfile", os.O_APPEND|os.O_WRONLY, 0644)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. _, err = fd.Seek(0, os.SEEK_END)
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. _, err = fd.WriteString("more data\n")
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. err = fd.Close()
  178. if err != nil {
  179. t.Fatal(err)
  180. }
  181. err = os.Chtimes("s1/appendfile", fi.ModTime(), fi.ModTime())
  182. if err != nil {
  183. t.Fatal(err)
  184. }
  185. // Prepare the expected state of folders after the sync
  186. e1 = directoryContents("s1")
  187. e2 = directoryContents("s12-1")
  188. e3 = directoryContents("s23-2")
  189. expected = [][]fileInfo{e1, e2, e3}
  190. }
  191. for i := range p {
  192. p[i].stop()
  193. }
  194. }
  195. func scStartProcesses() ([]syncthingProcess, error) {
  196. p := make([]syncthingProcess, 3)
  197. p[0] = syncthingProcess{ // id1
  198. log: "1.out",
  199. argv: []string{"-home", "h1"},
  200. port: 8081,
  201. apiKey: apiKey,
  202. }
  203. err := p[0].start()
  204. if err != nil {
  205. return nil, err
  206. }
  207. p[1] = syncthingProcess{ // id2
  208. log: "2.out",
  209. argv: []string{"-home", "h2"},
  210. port: 8082,
  211. apiKey: apiKey,
  212. }
  213. err = p[1].start()
  214. if err != nil {
  215. _ = p[0].stop()
  216. return nil, err
  217. }
  218. p[2] = syncthingProcess{ // id3
  219. log: "3.out",
  220. argv: []string{"-home", "h3"},
  221. port: 8083,
  222. apiKey: apiKey,
  223. }
  224. err = p[2].start()
  225. if err != nil {
  226. _ = p[0].stop()
  227. _ = p[1].stop()
  228. return nil, err
  229. }
  230. return p, nil
  231. }
  232. func scSyncAndCompare(p []syncthingProcess, expected [][]fileInfo) error {
  233. ids := []string{id1, id2, id3}
  234. log.Println("Syncing...")
  235. mainLoop:
  236. for {
  237. time.Sleep(2500 * time.Millisecond)
  238. for i := range p {
  239. comp, err := p[i].peerCompletion()
  240. if err != nil {
  241. if isTimeout(err) {
  242. continue mainLoop
  243. }
  244. return err
  245. }
  246. for id, pct := range comp {
  247. if id == ids[i] {
  248. // Don't check for self, which will be 0%
  249. continue
  250. }
  251. if pct != 100 {
  252. log.Printf("%s not done yet: %d%%", id, pct)
  253. continue mainLoop
  254. }
  255. }
  256. }
  257. break
  258. }
  259. log.Println("Checking...")
  260. for _, dir := range []string{"s1", "s2", "s3"} {
  261. actual := directoryContents(dir)
  262. if err := compareDirectoryContents(actual, expected[0]); err != nil {
  263. return fmt.Errorf("%s: %v", dir, err)
  264. }
  265. }
  266. for _, dir := range []string{"s12-1", "s12-2"} {
  267. actual := directoryContents(dir)
  268. if err := compareDirectoryContents(actual, expected[1]); err != nil {
  269. return fmt.Errorf("%s: %v", dir, err)
  270. }
  271. }
  272. for _, dir := range []string{"s23-2", "s23-3"} {
  273. actual := directoryContents(dir)
  274. if err := compareDirectoryContents(actual, expected[2]); err != nil {
  275. return fmt.Errorf("%s: %v", dir, err)
  276. }
  277. }
  278. return nil
  279. }