testutils_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
  6. package model
  7. import (
  8. "io/ioutil"
  9. "os"
  10. "testing"
  11. "time"
  12. "github.com/syncthing/syncthing/lib/config"
  13. "github.com/syncthing/syncthing/lib/db"
  14. "github.com/syncthing/syncthing/lib/db/backend"
  15. "github.com/syncthing/syncthing/lib/events"
  16. "github.com/syncthing/syncthing/lib/fs"
  17. "github.com/syncthing/syncthing/lib/ignore"
  18. "github.com/syncthing/syncthing/lib/protocol"
  19. "github.com/syncthing/syncthing/lib/rand"
  20. )
  21. var (
  22. myID, device1, device2 protocol.DeviceID
  23. defaultCfgWrapper config.Wrapper
  24. defaultFolderConfig config.FolderConfiguration
  25. defaultFs fs.Filesystem
  26. defaultCfg config.Configuration
  27. defaultAutoAcceptCfg config.Configuration
  28. )
  29. func init() {
  30. myID, _ = protocol.DeviceIDFromString("ZNWFSWE-RWRV2BD-45BLMCV-LTDE2UR-4LJDW6J-R5BPWEB-TXD27XJ-IZF5RA4")
  31. device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  32. device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
  33. defaultFolderConfig = testFolderConfig("testdata")
  34. defaultFs = defaultFolderConfig.Filesystem()
  35. defaultCfgWrapper = createTmpWrapper(config.New(myID))
  36. _, _ = defaultCfgWrapper.SetDevice(config.NewDeviceConfiguration(device1, "device1"))
  37. _, _ = defaultCfgWrapper.SetFolder(defaultFolderConfig)
  38. opts := defaultCfgWrapper.Options()
  39. opts.KeepTemporariesH = 1
  40. _, _ = defaultCfgWrapper.SetOptions(opts)
  41. defaultCfg = defaultCfgWrapper.RawCopy()
  42. defaultAutoAcceptCfg = config.Configuration{
  43. Version: config.CurrentVersion,
  44. Devices: []config.DeviceConfiguration{
  45. {
  46. DeviceID: myID, // self
  47. },
  48. {
  49. DeviceID: device1,
  50. AutoAcceptFolders: true,
  51. },
  52. {
  53. DeviceID: device2,
  54. AutoAcceptFolders: true,
  55. },
  56. },
  57. Options: config.OptionsConfiguration{
  58. DefaultFolderPath: ".",
  59. },
  60. }
  61. }
  62. func tmpDefaultWrapper() (config.Wrapper, config.FolderConfiguration) {
  63. w := createTmpWrapper(defaultCfgWrapper.RawCopy())
  64. fcfg := testFolderConfigTmp()
  65. _, _ = w.SetFolder(fcfg)
  66. return w, fcfg
  67. }
  68. func testFolderConfigTmp() config.FolderConfiguration {
  69. tmpDir := createTmpDir()
  70. return testFolderConfig(tmpDir)
  71. }
  72. func testFolderConfig(path string) config.FolderConfiguration {
  73. cfg := config.NewFolderConfiguration(myID, "default", "default", fs.FilesystemTypeBasic, path)
  74. cfg.FSWatcherEnabled = false
  75. cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1})
  76. return cfg
  77. }
  78. func testFolderConfigFake() config.FolderConfiguration {
  79. cfg := config.NewFolderConfiguration(myID, "default", "default", fs.FilesystemTypeFake, rand.String(32)+"?content=true")
  80. cfg.FSWatcherEnabled = false
  81. cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1})
  82. return cfg
  83. }
  84. func setupModelWithConnection() (*model, *fakeConnection, config.FolderConfiguration) {
  85. w, fcfg := tmpDefaultWrapper()
  86. m, fc := setupModelWithConnectionFromWrapper(w)
  87. return m, fc, fcfg
  88. }
  89. func setupModelWithConnectionFromWrapper(w config.Wrapper) (*model, *fakeConnection) {
  90. m := setupModel(w)
  91. fc := addFakeConn(m, device1)
  92. fc.folder = "default"
  93. _ = m.ScanFolder("default")
  94. return m, fc
  95. }
  96. func setupModel(w config.Wrapper) *model {
  97. db := db.NewLowlevel(backend.OpenMemory())
  98. m := newModel(w, myID, "syncthing", "dev", db, nil)
  99. m.ServeBackground()
  100. m.ScanFolders()
  101. return m
  102. }
  103. func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model {
  104. evLogger := events.NewLogger()
  105. m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger).(*model)
  106. go evLogger.Serve()
  107. return m
  108. }
  109. func cleanupModel(m *model) {
  110. m.Stop()
  111. m.db.Close()
  112. m.evLogger.Stop()
  113. os.Remove(m.cfg.ConfigPath())
  114. }
  115. func cleanupModelAndRemoveDir(m *model, dir string) {
  116. cleanupModel(m)
  117. os.RemoveAll(dir)
  118. }
  119. func createTmpDir() string {
  120. tmpDir, err := ioutil.TempDir("", "syncthing_testFolder-")
  121. if err != nil {
  122. panic("Failed to create temporary testing dir")
  123. }
  124. return tmpDir
  125. }
  126. type alwaysChangedKey struct {
  127. fs fs.Filesystem
  128. name string
  129. }
  130. // alwaysChanges is an ignore.ChangeDetector that always returns true on Changed()
  131. type alwaysChanged struct {
  132. seen map[alwaysChangedKey]struct{}
  133. }
  134. func newAlwaysChanged() *alwaysChanged {
  135. return &alwaysChanged{
  136. seen: make(map[alwaysChangedKey]struct{}),
  137. }
  138. }
  139. func (c *alwaysChanged) Remember(fs fs.Filesystem, name string, _ time.Time) {
  140. c.seen[alwaysChangedKey{fs, name}] = struct{}{}
  141. }
  142. func (c *alwaysChanged) Reset() {
  143. c.seen = make(map[alwaysChangedKey]struct{})
  144. }
  145. func (c *alwaysChanged) Seen(fs fs.Filesystem, name string) bool {
  146. _, ok := c.seen[alwaysChangedKey{fs, name}]
  147. return ok
  148. }
  149. func (c *alwaysChanged) Changed() bool {
  150. return true
  151. }
  152. func localSize(t *testing.T, m Model, folder string) db.Counts {
  153. t.Helper()
  154. snap := dbSnapshot(t, m, folder)
  155. defer snap.Release()
  156. return snap.LocalSize()
  157. }
  158. func globalSize(t *testing.T, m Model, folder string) db.Counts {
  159. t.Helper()
  160. snap := dbSnapshot(t, m, folder)
  161. defer snap.Release()
  162. return snap.GlobalSize()
  163. }
  164. func receiveOnlyChangedSize(t *testing.T, m Model, folder string) db.Counts {
  165. t.Helper()
  166. snap := dbSnapshot(t, m, folder)
  167. defer snap.Release()
  168. return snap.ReceiveOnlyChangedSize()
  169. }
  170. func needSize(t *testing.T, m Model, folder string) db.Counts {
  171. t.Helper()
  172. snap := dbSnapshot(t, m, folder)
  173. defer snap.Release()
  174. return snap.NeedSize(protocol.LocalDeviceID)
  175. }
  176. func dbSnapshot(t *testing.T, m Model, folder string) *db.Snapshot {
  177. t.Helper()
  178. snap, err := m.DBSnapshot(folder)
  179. if err != nil {
  180. t.Fatal(err)
  181. }
  182. return snap
  183. }
  184. // Reach in and update the ignore matcher to one that always does
  185. // reloads when asked to, instead of checking file mtimes. This is
  186. // because we will be changing the files on disk often enough that the
  187. // mtimes will be unreliable to determine change status.
  188. func folderIgnoresAlwaysReload(m *model, fcfg config.FolderConfiguration) {
  189. m.removeFolder(fcfg)
  190. fset := db.NewFileSet(fcfg.ID, fcfg.Filesystem(), m.db)
  191. ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged()))
  192. m.fmut.Lock()
  193. m.addAndStartFolderLockedWithIgnores(fcfg, fset, ignores)
  194. m.fmut.Unlock()
  195. }
  196. func basicClusterConfig(local, remote protocol.DeviceID, folders ...string) protocol.ClusterConfig {
  197. var cc protocol.ClusterConfig
  198. for _, folder := range folders {
  199. cc.Folders = append(cc.Folders, protocol.Folder{
  200. ID: folder,
  201. Devices: []protocol.Device{
  202. {
  203. ID: local,
  204. },
  205. {
  206. ID: remote,
  207. },
  208. },
  209. })
  210. }
  211. return cc
  212. }
  213. func localIndexUpdate(m *model, folder string, fs []protocol.FileInfo) {
  214. m.fmut.RLock()
  215. fset := m.folderFiles[folder]
  216. m.fmut.RUnlock()
  217. fset.Update(protocol.LocalDeviceID, fs)
  218. seq := fset.Sequence(protocol.LocalDeviceID)
  219. filenames := make([]string, len(fs))
  220. for i, file := range fs {
  221. filenames[i] = file.Name
  222. }
  223. m.evLogger.Log(events.LocalIndexUpdated, map[string]interface{}{
  224. "folder": folder,
  225. "items": len(fs),
  226. "filenames": filenames,
  227. "sequence": seq,
  228. "version": seq, // legacy for sequence
  229. })
  230. }