conflict_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright (C) 2015 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. "log"
  10. "os"
  11. "path/filepath"
  12. "testing"
  13. "time"
  14. )
  15. func TestConflict(t *testing.T) {
  16. log.Println("Cleaning...")
  17. err := removeAll("s1", "s2", "h1/index*", "h2/index*")
  18. if err != nil {
  19. t.Fatal(err)
  20. }
  21. log.Println("Generating files...")
  22. err = generateFiles("s1", 100, 20, "../LICENSE")
  23. if err != nil {
  24. t.Fatal(err)
  25. }
  26. fd, err := os.Create("s1/testfile.txt")
  27. if err != nil {
  28. t.Fatal(err)
  29. }
  30. _, err = fd.WriteString("hello\n")
  31. if err != nil {
  32. t.Fatal(err)
  33. }
  34. err = fd.Close()
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. expected, err := directoryContents("s1")
  39. if err != nil {
  40. t.Fatal(err)
  41. }
  42. log.Println("Starting sender...")
  43. sender := syncthingProcess{ // id1
  44. instance: "1",
  45. argv: []string{"-home", "h1"},
  46. port: 8081,
  47. apiKey: apiKey,
  48. }
  49. err = sender.start()
  50. if err != nil {
  51. t.Fatal(err)
  52. }
  53. defer sender.stop()
  54. // Wait for one scan to succeed, or up to 20 seconds... This is to let
  55. // startup, UPnP etc complete and make sure the sender has the full index
  56. // before they connect.
  57. for i := 0; i < 20; i++ {
  58. err := sender.rescan("default")
  59. if err != nil {
  60. time.Sleep(time.Second)
  61. continue
  62. }
  63. break
  64. }
  65. log.Println("Starting receiver...")
  66. receiver := syncthingProcess{ // id2
  67. instance: "2",
  68. argv: []string{"-home", "h2"},
  69. port: 8082,
  70. apiKey: apiKey,
  71. }
  72. err = receiver.start()
  73. if err != nil {
  74. sender.stop()
  75. t.Fatal(err)
  76. }
  77. defer receiver.stop()
  78. if err = coCompletion(sender, receiver); err != nil {
  79. t.Fatal(err)
  80. }
  81. sender.stop()
  82. receiver.stop()
  83. log.Println("Verifying...")
  84. actual, err := directoryContents("s2")
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. err = compareDirectoryContents(actual, expected)
  89. if err != nil {
  90. t.Fatal(err)
  91. }
  92. log.Println("Introducing a conflict (simultaneous edit)...")
  93. fd, err = os.OpenFile("s1/testfile.txt", os.O_WRONLY|os.O_APPEND, 0644)
  94. if err != nil {
  95. t.Fatal(err)
  96. }
  97. _, err = fd.WriteString("text added to s1\n")
  98. if err != nil {
  99. t.Fatal(err)
  100. }
  101. err = fd.Close()
  102. if err != nil {
  103. t.Fatal(err)
  104. }
  105. fd, err = os.OpenFile("s2/testfile.txt", os.O_WRONLY|os.O_APPEND, 0644)
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. _, err = fd.WriteString("text added to s2\n")
  110. if err != nil {
  111. t.Fatal(err)
  112. }
  113. err = fd.Close()
  114. if err != nil {
  115. t.Fatal(err)
  116. }
  117. log.Println("Syncing...")
  118. err = receiver.start()
  119. err = sender.start()
  120. if err != nil {
  121. t.Fatal(err)
  122. }
  123. if err != nil {
  124. sender.stop()
  125. t.Fatal(err)
  126. }
  127. if err = coCompletion(sender, receiver); err != nil {
  128. t.Fatal(err)
  129. }
  130. sender.stop()
  131. receiver.stop()
  132. // The conflict is expected on the s2 side due to how we calculate which
  133. // file is the winner (based on device ID)
  134. files, err := filepath.Glob("s2/*sync-conflict*")
  135. if err != nil {
  136. t.Fatal(err)
  137. }
  138. if len(files) != 1 {
  139. t.Errorf("Expected 1 conflicted files instead of %d", len(files))
  140. }
  141. log.Println("Introducing a conflict (edit plus delete)...")
  142. err = os.Remove("s1/testfile.txt")
  143. if err != nil {
  144. t.Fatal(err)
  145. }
  146. fd, err = os.OpenFile("s2/testfile.txt", os.O_WRONLY|os.O_APPEND, 0644)
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. _, err = fd.WriteString("more text added to s2\n")
  151. if err != nil {
  152. t.Fatal(err)
  153. }
  154. err = fd.Close()
  155. if err != nil {
  156. t.Fatal(err)
  157. }
  158. log.Println("Syncing...")
  159. err = receiver.start()
  160. err = sender.start()
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. if err != nil {
  165. sender.stop()
  166. t.Fatal(err)
  167. }
  168. if err = coCompletion(sender, receiver); err != nil {
  169. t.Fatal(err)
  170. }
  171. sender.stop()
  172. receiver.stop()
  173. // The conflict should manifest on the s2 side again, where we should have
  174. // moved the file to a conflict copy instead of just deleting it.
  175. files, err = filepath.Glob("s2/*sync-conflict*")
  176. if err != nil {
  177. t.Fatal(err)
  178. }
  179. if len(files) != 2 {
  180. t.Errorf("Expected 2 conflicted files instead of %d", len(files))
  181. }
  182. }
  183. func coCompletion(p ...syncthingProcess) error {
  184. mainLoop:
  185. for {
  186. time.Sleep(2500 * time.Millisecond)
  187. tot := 0
  188. for i := range p {
  189. comp, err := p[i].peerCompletion()
  190. if err != nil {
  191. if isTimeout(err) {
  192. continue mainLoop
  193. }
  194. return err
  195. }
  196. for _, pct := range comp {
  197. tot += pct
  198. }
  199. }
  200. if tot == 100*(len(p)) {
  201. return nil
  202. }
  203. log.Printf("%d / %d...", tot, 100*(len(p)))
  204. }
  205. }