symlink_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. "io/ioutil"
  10. "log"
  11. "os"
  12. "path/filepath"
  13. "testing"
  14. "time"
  15. "github.com/syncthing/protocol"
  16. "github.com/syncthing/syncthing/internal/config"
  17. "github.com/syncthing/syncthing/internal/symlinks"
  18. )
  19. func symlinksSupported() bool {
  20. tmp, err := ioutil.TempDir("", "symlink-test")
  21. if err != nil {
  22. return false
  23. }
  24. defer os.RemoveAll(tmp)
  25. err = os.Symlink("tmp", filepath.Join(tmp, "link"))
  26. return err == nil
  27. }
  28. func TestSymlinks(t *testing.T) {
  29. if !symlinksSupported() {
  30. t.Skip("symlinks unsupported")
  31. }
  32. // Use no versioning
  33. id, _ := protocol.DeviceIDFromString(id2)
  34. cfg, _ := config.Load("h2/config.xml", id)
  35. fld := cfg.Folders()["default"]
  36. fld.Versioning = config.VersioningConfiguration{}
  37. cfg.SetFolder(fld)
  38. cfg.Save()
  39. testSymlinks(t)
  40. }
  41. func TestSymlinksSimpleVersioning(t *testing.T) {
  42. if !symlinksSupported() {
  43. t.Skip("symlinks unsupported")
  44. }
  45. // Use simple versioning
  46. id, _ := protocol.DeviceIDFromString(id2)
  47. cfg, _ := config.Load("h2/config.xml", id)
  48. fld := cfg.Folders()["default"]
  49. fld.Versioning = config.VersioningConfiguration{
  50. Type: "simple",
  51. Params: map[string]string{"keep": "5"},
  52. }
  53. cfg.SetFolder(fld)
  54. cfg.Save()
  55. testSymlinks(t)
  56. }
  57. func TestSymlinksStaggeredVersioning(t *testing.T) {
  58. if !symlinksSupported() {
  59. t.Skip("symlinks unsupported")
  60. }
  61. // Use staggered versioning
  62. id, _ := protocol.DeviceIDFromString(id2)
  63. cfg, _ := config.Load("h2/config.xml", id)
  64. fld := cfg.Folders()["default"]
  65. fld.Versioning = config.VersioningConfiguration{
  66. Type: "staggered",
  67. }
  68. cfg.SetFolder(fld)
  69. cfg.Save()
  70. testSymlinks(t)
  71. }
  72. func testSymlinks(t *testing.T) {
  73. log.Println("Cleaning...")
  74. err := removeAll("s1", "s2", "h1/index*", "h2/index*")
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. log.Println("Generating files...")
  79. err = generateFiles("s1", 100, 20, "../LICENSE")
  80. if err != nil {
  81. t.Fatal(err)
  82. }
  83. // A file that we will replace with a symlink later
  84. fd, err := os.Create("s1/fileToReplace")
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. fd.Close()
  89. // A directory that we will replace with a symlink later
  90. err = os.Mkdir("s1/dirToReplace", 0755)
  91. if err != nil {
  92. t.Fatal(err)
  93. }
  94. // A file and a symlink to that file
  95. fd, err = os.Create("s1/file")
  96. if err != nil {
  97. t.Fatal(err)
  98. }
  99. fd.Close()
  100. err = symlinks.Create("s1/fileLink", "file", 0)
  101. if err != nil {
  102. log.Fatal(err)
  103. }
  104. // A directory and a symlink to that directory
  105. err = os.Mkdir("s1/dir", 0755)
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. err = symlinks.Create("s1/dirLink", "dir", 0)
  110. if err != nil {
  111. log.Fatal(err)
  112. }
  113. // A link to something in the repo that does not exist
  114. err = symlinks.Create("s1/noneLink", "does/not/exist", 0)
  115. if err != nil {
  116. log.Fatal(err)
  117. }
  118. // A link we will replace with a file later
  119. err = symlinks.Create("s1/repFileLink", "does/not/exist", 0)
  120. if err != nil {
  121. log.Fatal(err)
  122. }
  123. // A link we will replace with a directory later
  124. err = symlinks.Create("s1/repDirLink", "does/not/exist", 0)
  125. if err != nil {
  126. log.Fatal(err)
  127. }
  128. // A link we will remove later
  129. err = symlinks.Create("s1/removeLink", "does/not/exist", 0)
  130. if err != nil {
  131. log.Fatal(err)
  132. }
  133. // Verify that the files and symlinks sync to the other side
  134. log.Println("Syncing...")
  135. sender := syncthingProcess{ // id1
  136. instance: "1",
  137. argv: []string{"-home", "h1"},
  138. port: 8081,
  139. apiKey: apiKey,
  140. }
  141. err = sender.start()
  142. if err != nil {
  143. t.Fatal(err)
  144. }
  145. receiver := syncthingProcess{ // id2
  146. instance: "2",
  147. argv: []string{"-home", "h2"},
  148. port: 8082,
  149. apiKey: apiKey,
  150. }
  151. err = receiver.start()
  152. if err != nil {
  153. _ = sender.stop()
  154. t.Fatal(err)
  155. }
  156. for {
  157. comp, err := sender.peerCompletion()
  158. if err != nil {
  159. if isTimeout(err) {
  160. time.Sleep(time.Second)
  161. continue
  162. }
  163. _ = sender.stop()
  164. _ = receiver.stop()
  165. t.Fatal(err)
  166. }
  167. curComp := comp[id2]
  168. if curComp == 100 {
  169. break
  170. }
  171. time.Sleep(time.Second)
  172. }
  173. err = sender.stop()
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. err = receiver.stop()
  178. if err != nil {
  179. t.Fatal(err)
  180. }
  181. log.Println("Comparing directories...")
  182. err = compareDirectories("s1", "s2")
  183. if err != nil {
  184. t.Fatal(err)
  185. }
  186. log.Println("Making some changes...")
  187. // Remove one symlink
  188. err = os.Remove("s1/fileLink")
  189. if err != nil {
  190. log.Fatal(err)
  191. }
  192. // Change the target of another
  193. err = os.Remove("s1/dirLink")
  194. if err != nil {
  195. log.Fatal(err)
  196. }
  197. err = symlinks.Create("s1/dirLink", "file", 0)
  198. if err != nil {
  199. log.Fatal(err)
  200. }
  201. // Replace one with a file
  202. err = os.Remove("s1/repFileLink")
  203. if err != nil {
  204. log.Fatal(err)
  205. }
  206. fd, err = os.Create("s1/repFileLink")
  207. if err != nil {
  208. log.Fatal(err)
  209. }
  210. fd.Close()
  211. // Replace one with a directory
  212. err = os.Remove("s1/repDirLink")
  213. if err != nil {
  214. log.Fatal(err)
  215. }
  216. err = os.Mkdir("s1/repDirLink", 0755)
  217. if err != nil {
  218. log.Fatal(err)
  219. }
  220. // Replace a file with a symlink
  221. err = os.Remove("s1/fileToReplace")
  222. if err != nil {
  223. log.Fatal(err)
  224. }
  225. err = symlinks.Create("s1/fileToReplace", "somewhere/non/existent", 0)
  226. if err != nil {
  227. log.Fatal(err)
  228. }
  229. // Replace a directory with a symlink
  230. err = os.RemoveAll("s1/dirToReplace")
  231. if err != nil {
  232. log.Fatal(err)
  233. }
  234. err = symlinks.Create("s1/dirToReplace", "somewhere/non/existent", 0)
  235. if err != nil {
  236. log.Fatal(err)
  237. }
  238. // Remove a broken symlink
  239. err = os.Remove("s1/removeLink")
  240. if err != nil {
  241. log.Fatal(err)
  242. }
  243. // Sync these changes and recheck
  244. log.Println("Syncing...")
  245. err = sender.start()
  246. if err != nil {
  247. t.Fatal(err)
  248. }
  249. err = receiver.start()
  250. if err != nil {
  251. _ = sender.stop()
  252. t.Fatal(err)
  253. }
  254. for {
  255. comp, err := sender.peerCompletion()
  256. if err != nil {
  257. if isTimeout(err) {
  258. time.Sleep(time.Second)
  259. continue
  260. }
  261. _ = sender.stop()
  262. _ = receiver.stop()
  263. t.Fatal(err)
  264. }
  265. curComp := comp[id2]
  266. if curComp == 100 {
  267. break
  268. }
  269. time.Sleep(time.Second)
  270. }
  271. err = sender.stop()
  272. if err != nil {
  273. t.Fatal(err)
  274. }
  275. err = receiver.stop()
  276. if err != nil {
  277. t.Fatal(err)
  278. }
  279. log.Println("Comparing directories...")
  280. err = compareDirectories("s1", "s2")
  281. if err != nil {
  282. t.Fatal(err)
  283. }
  284. }