sync_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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. "io/ioutil"
  11. "log"
  12. "math/rand"
  13. "os"
  14. "testing"
  15. "time"
  16. "github.com/syncthing/syncthing/lib/config"
  17. "github.com/syncthing/syncthing/lib/protocol"
  18. "github.com/syncthing/syncthing/lib/rc"
  19. )
  20. const (
  21. longTimeLimit = 5 * time.Minute
  22. shortTimeLimit = 45 * time.Second
  23. s12Folder = `¯\_(ツ)_/¯ Räksmörgås 动作 Адрес` // This was renamed to ensure arbitrary folder IDs are fine.
  24. )
  25. func TestSyncClusterWithoutVersioning(t *testing.T) {
  26. // Use no versioning
  27. id, _ := protocol.DeviceIDFromString(id2)
  28. cfg, _ := config.Load("h2/config.xml", id)
  29. fld := cfg.Folders()["default"]
  30. fld.Versioning = config.VersioningConfiguration{}
  31. cfg.SetFolder(fld)
  32. cfg.Save()
  33. testSyncCluster(t)
  34. }
  35. func TestSyncClusterSimpleVersioning(t *testing.T) {
  36. // Use simple versioning
  37. id, _ := protocol.DeviceIDFromString(id2)
  38. cfg, _ := config.Load("h2/config.xml", id)
  39. fld := cfg.Folders()["default"]
  40. fld.Versioning = config.VersioningConfiguration{
  41. Type: "simple",
  42. Params: map[string]string{"keep": "5"},
  43. }
  44. cfg.SetFolder(fld)
  45. cfg.Save()
  46. testSyncCluster(t)
  47. }
  48. func TestSyncClusterTrashcanVersioning(t *testing.T) {
  49. // Use simple versioning
  50. id, _ := protocol.DeviceIDFromString(id2)
  51. cfg, _ := config.Load("h2/config.xml", id)
  52. fld := cfg.Folders()["default"]
  53. fld.Versioning = config.VersioningConfiguration{
  54. Type: "trashcan",
  55. Params: map[string]string{"cleanoutDays": "1"},
  56. }
  57. cfg.SetFolder(fld)
  58. cfg.Save()
  59. testSyncCluster(t)
  60. }
  61. func TestSyncClusterStaggeredVersioning(t *testing.T) {
  62. // Use staggered versioning
  63. id, _ := protocol.DeviceIDFromString(id2)
  64. cfg, _ := config.Load("h2/config.xml", id)
  65. fld := cfg.Folders()["default"]
  66. fld.Versioning = config.VersioningConfiguration{
  67. Type: "staggered",
  68. }
  69. cfg.SetFolder(fld)
  70. cfg.Save()
  71. testSyncCluster(t)
  72. }
  73. func TestSyncClusterForcedRescan(t *testing.T) {
  74. // Use no versioning
  75. id, _ := protocol.DeviceIDFromString(id2)
  76. cfg, _ := config.Load("h2/config.xml", id)
  77. fld := cfg.Folders()["default"]
  78. fld.Versioning = config.VersioningConfiguration{}
  79. cfg.SetFolder(fld)
  80. cfg.Save()
  81. testSyncClusterForcedRescan(t)
  82. }
  83. func testSyncCluster(t *testing.T) {
  84. // This tests syncing files back and forth between three cluster members.
  85. // Their configs are in h1, h2 and h3. The folder "default" is shared
  86. // between all and stored in s1, s2 and s3 respectively.
  87. //
  88. // Another folder is shared between 1 and 2 only, in s12-1 and s12-2. A
  89. // third folders is shared between 2 and 3, in s23-2 and s23-3.
  90. // When -short is passed, keep it more reasonable.
  91. timeLimit := longTimeLimit
  92. if testing.Short() {
  93. timeLimit = shortTimeLimit
  94. }
  95. const (
  96. numFiles = 100
  97. fileSizeExp = 20
  98. )
  99. rand.Seed(42)
  100. log.Printf("Testing with numFiles=%d, fileSizeExp=%d, timeLimit=%v", numFiles, fileSizeExp, timeLimit)
  101. log.Println("Cleaning...")
  102. err := removeAll("s1", "s12-1",
  103. "s2", "s12-2", "s23-2",
  104. "s3", "s23-3",
  105. "h1/index*", "h2/index*", "h3/index*")
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. // Create initial folder contents. All three devices have stuff in
  110. // "default", which should be merged. The other two folders are initially
  111. // empty on one side.
  112. log.Println("Generating files...")
  113. err = generateFiles("s1", numFiles, fileSizeExp, "../LICENSE")
  114. if err != nil {
  115. t.Fatal(err)
  116. }
  117. err = generateFiles("s12-1", numFiles, fileSizeExp, "../LICENSE")
  118. if err != nil {
  119. t.Fatal(err)
  120. }
  121. // We'll use this file for appending data without modifying the time stamp.
  122. fd, err := os.Create("s1/test-appendfile")
  123. if err != nil {
  124. t.Fatal(err)
  125. }
  126. _, err = fd.WriteString("hello\n")
  127. if err != nil {
  128. t.Fatal(err)
  129. }
  130. err = fd.Close()
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. err = generateFiles("s2", numFiles, fileSizeExp, "../LICENSE")
  135. if err != nil {
  136. t.Fatal(err)
  137. }
  138. err = generateFiles("s23-2", numFiles, fileSizeExp, "../LICENSE")
  139. if err != nil {
  140. t.Fatal(err)
  141. }
  142. err = generateFiles("s3", numFiles, fileSizeExp, "../LICENSE")
  143. if err != nil {
  144. t.Fatal(err)
  145. }
  146. // Prepare the expected state of folders after the sync
  147. c1, err := directoryContents("s1")
  148. if err != nil {
  149. t.Fatal(err)
  150. }
  151. c2, err := directoryContents("s2")
  152. if err != nil {
  153. t.Fatal(err)
  154. }
  155. c3, err := directoryContents("s3")
  156. if err != nil {
  157. t.Fatal(err)
  158. }
  159. e1 := mergeDirectoryContents(c1, c2, c3)
  160. e2, err := directoryContents("s12-1")
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. e3, err := directoryContents("s23-2")
  165. if err != nil {
  166. t.Fatal(err)
  167. }
  168. expected := [][]fileInfo{e1, e2, e3}
  169. // Start the syncers
  170. log.Println("Starting Syncthing...")
  171. p0 := startInstance(t, 1)
  172. defer checkedStop(t, p0)
  173. p1 := startInstance(t, 2)
  174. defer checkedStop(t, p1)
  175. p2 := startInstance(t, 3)
  176. defer checkedStop(t, p2)
  177. p := []*rc.Process{p0, p1, p2}
  178. start := time.Now()
  179. iteration := 0
  180. for time.Since(start) < timeLimit {
  181. iteration++
  182. log.Println("Iteration", iteration)
  183. log.Println("Forcing rescan...")
  184. // Force rescan of folders
  185. for i, device := range p {
  186. if err := device.RescanDelay("default", 86400); err != nil {
  187. t.Fatal(err)
  188. }
  189. if i == 0 || i == 1 {
  190. if err := device.RescanDelay(s12Folder, 86400); err != nil {
  191. t.Fatal(err)
  192. }
  193. }
  194. if i == 1 || i == 2 {
  195. if err := device.RescanDelay("s23", 86400); err != nil {
  196. t.Fatal(err)
  197. }
  198. }
  199. }
  200. // Sync stuff and verify it looks right
  201. err = scSyncAndCompare(p, expected)
  202. if err != nil {
  203. t.Error(err)
  204. break
  205. }
  206. log.Println("Altering...")
  207. // Alter the source files for another round
  208. err = alterFiles("s1")
  209. if err != nil {
  210. t.Error(err)
  211. break
  212. }
  213. err = alterFiles("s12-1")
  214. if err != nil {
  215. t.Error(err)
  216. break
  217. }
  218. err = alterFiles("s23-2")
  219. if err != nil {
  220. t.Error(err)
  221. break
  222. }
  223. // Alter the "test-appendfile" without changing it's modification time. Sneaky!
  224. fi, err := os.Stat("s1/test-appendfile")
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. fd, err := os.OpenFile("s1/test-appendfile", os.O_APPEND|os.O_WRONLY, 0644)
  229. if err != nil {
  230. t.Fatal(err)
  231. }
  232. _, err = fd.Seek(0, os.SEEK_END)
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. _, err = fd.WriteString("more data\n")
  237. if err != nil {
  238. t.Fatal(err)
  239. }
  240. err = fd.Close()
  241. if err != nil {
  242. t.Fatal(err)
  243. }
  244. err = os.Chtimes("s1/test-appendfile", fi.ModTime(), fi.ModTime())
  245. if err != nil {
  246. t.Fatal(err)
  247. }
  248. // Prepare the expected state of folders after the sync
  249. e1, err = directoryContents("s1")
  250. if err != nil {
  251. t.Fatal(err)
  252. }
  253. e2, err = directoryContents("s12-1")
  254. if err != nil {
  255. t.Fatal(err)
  256. }
  257. e3, err = directoryContents("s23-2")
  258. if err != nil {
  259. t.Fatal(err)
  260. }
  261. expected = [][]fileInfo{e1, e2, e3}
  262. }
  263. }
  264. func testSyncClusterForcedRescan(t *testing.T) {
  265. // During this test, we create 1K files, remove and then create them
  266. // again. However, during these operations we will perform scan operations
  267. // such that other nodes will retrieve these options while data is
  268. // changing.
  269. // When -short is passed, keep it more reasonable.
  270. timeLimit := longTimeLimit
  271. if testing.Short() {
  272. timeLimit = shortTimeLimit
  273. }
  274. log.Println("Cleaning...")
  275. err := removeAll("s1", "s12-1",
  276. "s2", "s12-2", "s23-2",
  277. "s3", "s23-3",
  278. "h1/index*", "h2/index*", "h3/index*")
  279. if err != nil {
  280. t.Fatal(err)
  281. }
  282. // Create initial folder contents. All three devices have stuff in
  283. // "default", which should be merged. The other two folders are initially
  284. // empty on one side.
  285. log.Println("Generating files...")
  286. if err := os.MkdirAll("s1/test-stable-files", 0755); err != nil {
  287. t.Fatal(err)
  288. }
  289. for i := 0; i < 1000; i++ {
  290. name := fmt.Sprintf("s1/test-stable-files/%d", i)
  291. if err := ioutil.WriteFile(name, []byte(time.Now().Format(time.RFC3339Nano)), 0644); err != nil {
  292. t.Fatal(err)
  293. }
  294. }
  295. // Prepare the expected state of folders after the sync
  296. expected, err := directoryContents("s1")
  297. if err != nil {
  298. t.Fatal(err)
  299. }
  300. // Start the syncers
  301. p0 := startInstance(t, 1)
  302. defer checkedStop(t, p0)
  303. p1 := startInstance(t, 2)
  304. defer checkedStop(t, p1)
  305. p2 := startInstance(t, 3)
  306. defer checkedStop(t, p2)
  307. p := []*rc.Process{p0, p1, p2}
  308. start := time.Now()
  309. for time.Since(start) < timeLimit {
  310. rescan := func() {
  311. for i := range p {
  312. if err := p[i].Rescan("default"); err != nil {
  313. t.Fatal(err)
  314. }
  315. }
  316. }
  317. log.Println("Forcing rescan...")
  318. rescan()
  319. // Sync stuff and verify it looks right
  320. err = scSyncAndCompare(p, [][]fileInfo{expected})
  321. if err != nil {
  322. t.Fatal(err)
  323. }
  324. log.Println("Altering...")
  325. // Delete and recreate stable files while scanners and pullers are active
  326. for i := 0; i < 1000; i++ {
  327. name := fmt.Sprintf("s1/test-stable-files/%d", i)
  328. if err := os.Remove(name); err != nil {
  329. t.Fatal(err)
  330. }
  331. if rand.Intn(10) == 0 {
  332. rescan()
  333. }
  334. }
  335. rescan()
  336. time.Sleep(50 * time.Millisecond)
  337. for i := 0; i < 1000; i++ {
  338. name := fmt.Sprintf("s1/test-stable-files/%d", i)
  339. if err := ioutil.WriteFile(name, []byte(time.Now().Format(time.RFC3339Nano)), 0644); err != nil {
  340. t.Fatal(err)
  341. }
  342. if rand.Intn(10) == 0 {
  343. rescan()
  344. }
  345. }
  346. rescan()
  347. // Prepare the expected state of folders after the sync
  348. expected, err = directoryContents("s1")
  349. if err != nil {
  350. t.Fatal(err)
  351. }
  352. if len(expected) != 1001 {
  353. t.Fatal("s1 does not have 1001 files;", len(expected))
  354. }
  355. }
  356. }
  357. func scSyncAndCompare(p []*rc.Process, expected [][]fileInfo) error {
  358. log.Println("Syncing...")
  359. for {
  360. time.Sleep(250 * time.Millisecond)
  361. if !rc.InSync("default", p...) {
  362. continue
  363. }
  364. if !rc.InSync(s12Folder, p[0], p[1]) {
  365. continue
  366. }
  367. if !rc.InSync("s23", p[1], p[2]) {
  368. continue
  369. }
  370. break
  371. }
  372. log.Println("Checking...")
  373. for _, dir := range []string{"s1", "s2", "s3"} {
  374. actual, err := directoryContents(dir)
  375. if err != nil {
  376. return err
  377. }
  378. if err := compareDirectoryContents(actual, expected[0]); err != nil {
  379. return fmt.Errorf("%s: %v", dir, err)
  380. }
  381. }
  382. if len(expected) > 1 {
  383. for _, dir := range []string{"s12-1", "s12-2"} {
  384. actual, err := directoryContents(dir)
  385. if err != nil {
  386. return err
  387. }
  388. if err := compareDirectoryContents(actual, expected[1]); err != nil {
  389. return fmt.Errorf("%s: %v", dir, err)
  390. }
  391. }
  392. }
  393. if len(expected) > 2 {
  394. for _, dir := range []string{"s23-2", "s23-3"} {
  395. actual, err := directoryContents(dir)
  396. if err != nil {
  397. return err
  398. }
  399. if err := compareDirectoryContents(actual, expected[2]); err != nil {
  400. return fmt.Errorf("%s: %v", dir, err)
  401. }
  402. }
  403. }
  404. return nil
  405. }
  406. func TestSyncSparseFile(t *testing.T) {
  407. // This test verifies that when syncing a file that consists mostly of
  408. // zeroes, those blocks are not transferred. It doesn't verify whether
  409. // the resulting file is actually *sparse* or not.alterFiles
  410. log.Println("Cleaning...")
  411. err := removeAll("s1", "s12-1",
  412. "s2", "s12-2", "s23-2",
  413. "s3", "s23-3",
  414. "h1/index*", "h2/index*", "h3/index*")
  415. if err != nil {
  416. t.Fatal(err)
  417. }
  418. log.Println("Generating files...")
  419. if err := os.Mkdir("s1", 0755); err != nil {
  420. t.Fatal(err)
  421. }
  422. fd, err := os.Create("s1/testfile")
  423. if err != nil {
  424. t.Fatal(err)
  425. }
  426. if _, err := fd.Write([]byte("Start")); err != nil {
  427. t.Fatal(err)
  428. }
  429. kib := make([]byte, 1024)
  430. for i := 0; i < 8192; i++ {
  431. if _, err := fd.Write(kib); err != nil {
  432. t.Fatal(err)
  433. }
  434. }
  435. if _, err := fd.Write([]byte("End")); err != nil {
  436. t.Fatal(err)
  437. }
  438. fd.Close()
  439. // Start the syncers
  440. log.Println("Syncing...")
  441. p0 := startInstance(t, 1)
  442. defer checkedStop(t, p0)
  443. p1 := startInstance(t, 2)
  444. defer checkedStop(t, p1)
  445. rc.AwaitSync("default", p0, p1)
  446. log.Println("Comparing...")
  447. if err := compareDirectories("s1", "s2"); err != nil {
  448. t.Fatal(err)
  449. }
  450. conns, err := p0.Connections()
  451. if err != nil {
  452. t.Fatal(err)
  453. }
  454. tot := conns["total"]
  455. if tot.OutBytesTotal > 256<<10 {
  456. t.Fatal("Sending side has sent", tot.OutBytesTotal, "bytes, which is too much")
  457. }
  458. }