model_test.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694
  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. package model
  7. import (
  8. "bytes"
  9. "crypto/tls"
  10. "encoding/json"
  11. "fmt"
  12. "io/ioutil"
  13. "math/rand"
  14. "net"
  15. "os"
  16. "path/filepath"
  17. "runtime"
  18. "strconv"
  19. "testing"
  20. "time"
  21. "github.com/d4l3k/messagediff"
  22. "github.com/syncthing/syncthing/lib/config"
  23. "github.com/syncthing/syncthing/lib/connections"
  24. "github.com/syncthing/syncthing/lib/db"
  25. "github.com/syncthing/syncthing/lib/ignore"
  26. "github.com/syncthing/syncthing/lib/osutil"
  27. "github.com/syncthing/syncthing/lib/protocol"
  28. srand "github.com/syncthing/syncthing/lib/rand"
  29. )
  30. var device1, device2 protocol.DeviceID
  31. var defaultConfig *config.Wrapper
  32. var defaultFolderConfig config.FolderConfiguration
  33. func init() {
  34. device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  35. device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
  36. defaultFolderConfig = config.NewFolderConfiguration("default", "testdata")
  37. defaultFolderConfig.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}}
  38. _defaultConfig := config.Configuration{
  39. Folders: []config.FolderConfiguration{defaultFolderConfig},
  40. Devices: []config.DeviceConfiguration{config.NewDeviceConfiguration(device1, "device1")},
  41. Options: config.OptionsConfiguration{
  42. // Don't remove temporaries directly on startup
  43. KeepTemporariesH: 1,
  44. },
  45. }
  46. defaultConfig = config.Wrap("/tmp/test", _defaultConfig)
  47. }
  48. var testDataExpected = map[string]protocol.FileInfo{
  49. "foo": {
  50. Name: "foo",
  51. Type: protocol.FileInfoTypeFile,
  52. ModifiedS: 0,
  53. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0x7, Hash: []uint8{0xae, 0xc0, 0x70, 0x64, 0x5f, 0xe5, 0x3e, 0xe3, 0xb3, 0x76, 0x30, 0x59, 0x37, 0x61, 0x34, 0xf0, 0x58, 0xcc, 0x33, 0x72, 0x47, 0xc9, 0x78, 0xad, 0xd1, 0x78, 0xb6, 0xcc, 0xdf, 0xb0, 0x1, 0x9f}}},
  54. },
  55. "empty": {
  56. Name: "empty",
  57. Type: protocol.FileInfoTypeFile,
  58. ModifiedS: 0,
  59. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0x0, Hash: []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}},
  60. },
  61. "bar": {
  62. Name: "bar",
  63. Type: protocol.FileInfoTypeFile,
  64. ModifiedS: 0,
  65. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}},
  66. },
  67. }
  68. func init() {
  69. // Fix expected test data to match reality
  70. for n, f := range testDataExpected {
  71. fi, _ := os.Stat("testdata/" + n)
  72. f.Permissions = uint32(fi.Mode())
  73. f.ModifiedS = fi.ModTime().Unix()
  74. f.Size = fi.Size()
  75. testDataExpected[n] = f
  76. }
  77. }
  78. func TestRequest(t *testing.T) {
  79. db := db.OpenMemory()
  80. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  81. // device1 shares default, but device2 doesn't
  82. m.AddFolder(defaultFolderConfig)
  83. m.StartFolder("default")
  84. m.ServeBackground()
  85. m.ScanFolder("default")
  86. bs := make([]byte, protocol.BlockSize)
  87. // Existing, shared file
  88. bs = bs[:6]
  89. err := m.Request(device1, "default", "foo", 0, nil, false, bs)
  90. if err != nil {
  91. t.Error(err)
  92. }
  93. if !bytes.Equal(bs, []byte("foobar")) {
  94. t.Errorf("Incorrect data from request: %q", string(bs))
  95. }
  96. // Existing, nonshared file
  97. err = m.Request(device2, "default", "foo", 0, nil, false, bs)
  98. if err == nil {
  99. t.Error("Unexpected nil error on insecure file read")
  100. }
  101. // Nonexistent file
  102. err = m.Request(device1, "default", "nonexistent", 0, nil, false, bs)
  103. if err == nil {
  104. t.Error("Unexpected nil error on insecure file read")
  105. }
  106. // Shared folder, but disallowed file name
  107. err = m.Request(device1, "default", "../walk.go", 0, nil, false, bs)
  108. if err == nil {
  109. t.Error("Unexpected nil error on insecure file read")
  110. }
  111. // Negative offset
  112. err = m.Request(device1, "default", "foo", -4, nil, false, bs[:0])
  113. if err == nil {
  114. t.Error("Unexpected nil error on insecure file read")
  115. }
  116. // Larger block than available
  117. bs = bs[:42]
  118. err = m.Request(device1, "default", "foo", 0, nil, false, bs)
  119. if err == nil {
  120. t.Error("Unexpected nil error on insecure file read")
  121. }
  122. }
  123. func genFiles(n int) []protocol.FileInfo {
  124. files := make([]protocol.FileInfo, n)
  125. t := time.Now().Unix()
  126. for i := 0; i < n; i++ {
  127. files[i] = protocol.FileInfo{
  128. Name: fmt.Sprintf("file%d", i),
  129. ModifiedS: t,
  130. Sequence: int64(i + 1),
  131. Blocks: []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}},
  132. }
  133. }
  134. return files
  135. }
  136. func BenchmarkIndex_10000(b *testing.B) {
  137. benchmarkIndex(b, 10000)
  138. }
  139. func BenchmarkIndex_100(b *testing.B) {
  140. benchmarkIndex(b, 100)
  141. }
  142. func benchmarkIndex(b *testing.B, nfiles int) {
  143. db := db.OpenMemory()
  144. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  145. m.AddFolder(defaultFolderConfig)
  146. m.StartFolder("default")
  147. m.ServeBackground()
  148. files := genFiles(nfiles)
  149. m.Index(device1, "default", files)
  150. b.ResetTimer()
  151. for i := 0; i < b.N; i++ {
  152. m.Index(device1, "default", files)
  153. }
  154. b.ReportAllocs()
  155. }
  156. func BenchmarkIndexUpdate_10000_10000(b *testing.B) {
  157. benchmarkIndexUpdate(b, 10000, 10000)
  158. }
  159. func BenchmarkIndexUpdate_10000_100(b *testing.B) {
  160. benchmarkIndexUpdate(b, 10000, 100)
  161. }
  162. func BenchmarkIndexUpdate_10000_1(b *testing.B) {
  163. benchmarkIndexUpdate(b, 10000, 1)
  164. }
  165. func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
  166. db := db.OpenMemory()
  167. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  168. m.AddFolder(defaultFolderConfig)
  169. m.StartFolder("default")
  170. m.ServeBackground()
  171. files := genFiles(nfiles)
  172. ufiles := genFiles(nufiles)
  173. m.Index(device1, "default", files)
  174. b.ResetTimer()
  175. for i := 0; i < b.N; i++ {
  176. m.IndexUpdate(device1, "default", ufiles)
  177. }
  178. b.ReportAllocs()
  179. }
  180. type downloadProgressMessage struct {
  181. folder string
  182. updates []protocol.FileDownloadProgressUpdate
  183. }
  184. type FakeConnection struct {
  185. id protocol.DeviceID
  186. requestData []byte
  187. downloadProgressMessages []downloadProgressMessage
  188. }
  189. func (FakeConnection) Close() error {
  190. return nil
  191. }
  192. func (f FakeConnection) Start() {
  193. }
  194. func (f FakeConnection) ID() protocol.DeviceID {
  195. return f.id
  196. }
  197. func (f FakeConnection) Name() string {
  198. return ""
  199. }
  200. func (f FakeConnection) Option(string) string {
  201. return ""
  202. }
  203. func (FakeConnection) Index(string, []protocol.FileInfo) error {
  204. return nil
  205. }
  206. func (FakeConnection) IndexUpdate(string, []protocol.FileInfo) error {
  207. return nil
  208. }
  209. func (f FakeConnection) Request(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {
  210. return f.requestData, nil
  211. }
  212. func (FakeConnection) ClusterConfig(protocol.ClusterConfig) {}
  213. func (FakeConnection) Ping() bool {
  214. return true
  215. }
  216. func (FakeConnection) Closed() bool {
  217. return false
  218. }
  219. func (FakeConnection) Statistics() protocol.Statistics {
  220. return protocol.Statistics{}
  221. }
  222. func (f *FakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate) {
  223. f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{
  224. folder: folder,
  225. updates: updates,
  226. })
  227. }
  228. func BenchmarkRequest(b *testing.B) {
  229. db := db.OpenMemory()
  230. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  231. m.AddFolder(defaultFolderConfig)
  232. m.ServeBackground()
  233. m.ScanFolder("default")
  234. const n = 1000
  235. files := genFiles(n)
  236. fc := &FakeConnection{
  237. id: device1,
  238. requestData: []byte("some data to return"),
  239. }
  240. m.AddConnection(connections.Connection{
  241. IntermediateConnection: connections.IntermediateConnection{
  242. Conn: tls.Client(&fakeConn{}, nil),
  243. Type: "foo",
  244. Priority: 10,
  245. },
  246. Connection: fc,
  247. }, protocol.HelloResult{})
  248. m.Index(device1, "default", files)
  249. b.ResetTimer()
  250. for i := 0; i < b.N; i++ {
  251. data, err := m.requestGlobal(device1, "default", files[i%n].Name, 0, 32, nil, false)
  252. if err != nil {
  253. b.Error(err)
  254. }
  255. if data == nil {
  256. b.Error("nil data")
  257. }
  258. }
  259. }
  260. func TestDeviceRename(t *testing.T) {
  261. hello := protocol.HelloResult{
  262. ClientName: "syncthing",
  263. ClientVersion: "v0.9.4",
  264. }
  265. defer os.Remove("tmpconfig.xml")
  266. rawCfg := config.New(device1)
  267. rawCfg.Devices = []config.DeviceConfiguration{
  268. {
  269. DeviceID: device1,
  270. },
  271. }
  272. cfg := config.Wrap("tmpconfig.xml", rawCfg)
  273. db := db.OpenMemory()
  274. m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  275. if cfg.Devices()[device1].Name != "" {
  276. t.Errorf("Device already has a name")
  277. }
  278. conn := connections.Connection{
  279. IntermediateConnection: connections.IntermediateConnection{
  280. Conn: tls.Client(&fakeConn{}, nil),
  281. Type: "foo",
  282. Priority: 10,
  283. },
  284. Connection: &FakeConnection{
  285. id: device1,
  286. requestData: []byte("some data to return"),
  287. },
  288. }
  289. m.AddConnection(conn, hello)
  290. m.ServeBackground()
  291. if cfg.Devices()[device1].Name != "" {
  292. t.Errorf("Device already has a name")
  293. }
  294. m.Closed(conn, protocol.ErrTimeout)
  295. hello.DeviceName = "tester"
  296. m.AddConnection(conn, hello)
  297. if cfg.Devices()[device1].Name != "tester" {
  298. t.Errorf("Device did not get a name")
  299. }
  300. m.Closed(conn, protocol.ErrTimeout)
  301. hello.DeviceName = "tester2"
  302. m.AddConnection(conn, hello)
  303. if cfg.Devices()[device1].Name != "tester" {
  304. t.Errorf("Device name got overwritten")
  305. }
  306. cfgw, err := config.Load("tmpconfig.xml", protocol.LocalDeviceID)
  307. if err != nil {
  308. t.Error(err)
  309. return
  310. }
  311. if cfgw.Devices()[device1].Name != "tester" {
  312. t.Errorf("Device name not saved in config")
  313. }
  314. m.Closed(conn, protocol.ErrTimeout)
  315. opts := cfg.Options()
  316. opts.OverwriteRemoteDevNames = true
  317. cfg.SetOptions(opts)
  318. hello.DeviceName = "tester2"
  319. m.AddConnection(conn, hello)
  320. if cfg.Devices()[device1].Name != "tester2" {
  321. t.Errorf("Device name not overwritten")
  322. }
  323. }
  324. func TestClusterConfig(t *testing.T) {
  325. cfg := config.New(device1)
  326. cfg.Devices = []config.DeviceConfiguration{
  327. {
  328. DeviceID: device1,
  329. Introducer: true,
  330. },
  331. {
  332. DeviceID: device2,
  333. },
  334. }
  335. cfg.Folders = []config.FolderConfiguration{
  336. {
  337. ID: "folder1",
  338. Devices: []config.FolderDeviceConfiguration{
  339. {DeviceID: device1},
  340. {DeviceID: device2},
  341. },
  342. },
  343. {
  344. ID: "folder2",
  345. Devices: []config.FolderDeviceConfiguration{
  346. {DeviceID: device1},
  347. {DeviceID: device2},
  348. },
  349. },
  350. }
  351. db := db.OpenMemory()
  352. m := NewModel(config.Wrap("/tmp/test", cfg), protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  353. m.AddFolder(cfg.Folders[0])
  354. m.AddFolder(cfg.Folders[1])
  355. m.ServeBackground()
  356. cm := m.generateClusterConfig(device2)
  357. if l := len(cm.Folders); l != 2 {
  358. t.Fatalf("Incorrect number of folders %d != 2", l)
  359. }
  360. r := cm.Folders[0]
  361. if r.ID != "folder1" {
  362. t.Errorf("Incorrect folder %q != folder1", r.ID)
  363. }
  364. if l := len(r.Devices); l != 2 {
  365. t.Errorf("Incorrect number of devices %d != 2", l)
  366. }
  367. if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
  368. t.Errorf("Incorrect device ID %x != %x", id, device1)
  369. }
  370. if !r.Devices[0].Introducer {
  371. t.Error("Device1 should be flagged as Introducer")
  372. }
  373. if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
  374. t.Errorf("Incorrect device ID %x != %x", id, device2)
  375. }
  376. if r.Devices[1].Introducer {
  377. t.Error("Device2 should not be flagged as Introducer")
  378. }
  379. r = cm.Folders[1]
  380. if r.ID != "folder2" {
  381. t.Errorf("Incorrect folder %q != folder2", r.ID)
  382. }
  383. if l := len(r.Devices); l != 2 {
  384. t.Errorf("Incorrect number of devices %d != 2", l)
  385. }
  386. if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
  387. t.Errorf("Incorrect device ID %x != %x", id, device1)
  388. }
  389. if !r.Devices[0].Introducer {
  390. t.Error("Device1 should be flagged as Introducer")
  391. }
  392. if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
  393. t.Errorf("Incorrect device ID %x != %x", id, device2)
  394. }
  395. if r.Devices[1].Introducer {
  396. t.Error("Device2 should not be flagged as Introducer")
  397. }
  398. }
  399. func TestIgnores(t *testing.T) {
  400. arrEqual := func(a, b []string) bool {
  401. if len(a) != len(b) {
  402. return false
  403. }
  404. for i := range a {
  405. if a[i] != b[i] {
  406. return false
  407. }
  408. }
  409. return true
  410. }
  411. // Assure a clean start state
  412. ioutil.WriteFile("testdata/.stfolder", nil, 0644)
  413. ioutil.WriteFile("testdata/.stignore", []byte(".*\nquux\n"), 0644)
  414. db := db.OpenMemory()
  415. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  416. m.AddFolder(defaultFolderConfig)
  417. m.StartFolder("default")
  418. m.ServeBackground()
  419. expected := []string{
  420. ".*",
  421. "quux",
  422. }
  423. ignores, _, err := m.GetIgnores("default")
  424. if err != nil {
  425. t.Error(err)
  426. }
  427. if !arrEqual(ignores, expected) {
  428. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  429. }
  430. ignores = append(ignores, "pox")
  431. err = m.SetIgnores("default", ignores)
  432. if err != nil {
  433. t.Error(err)
  434. }
  435. ignores2, _, err := m.GetIgnores("default")
  436. if err != nil {
  437. t.Error(err)
  438. }
  439. if arrEqual(expected, ignores2) {
  440. t.Errorf("Incorrect ignores: %v == %v", ignores2, expected)
  441. }
  442. if !arrEqual(ignores, ignores2) {
  443. t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
  444. }
  445. err = m.SetIgnores("default", expected)
  446. if err != nil {
  447. t.Error(err)
  448. }
  449. ignores, _, err = m.GetIgnores("default")
  450. if err != nil {
  451. t.Error(err)
  452. }
  453. if !arrEqual(ignores, expected) {
  454. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  455. }
  456. ignores, _, err = m.GetIgnores("doesnotexist")
  457. if err == nil {
  458. t.Error("No error")
  459. }
  460. err = m.SetIgnores("doesnotexist", expected)
  461. if err == nil {
  462. t.Error("No error")
  463. }
  464. // Invalid path, marker should be missing, hence returns an error.
  465. m.AddFolder(config.FolderConfiguration{ID: "fresh", RawPath: "XXX"})
  466. _, _, err = m.GetIgnores("fresh")
  467. if err == nil {
  468. t.Error("No error")
  469. }
  470. }
  471. func TestROScanRecovery(t *testing.T) {
  472. ldb := db.OpenMemory()
  473. set := db.NewFileSet("default", ldb)
  474. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  475. {Name: "dummyfile"},
  476. })
  477. fcfg := config.FolderConfiguration{
  478. ID: "default",
  479. RawPath: "testdata/rotestfolder",
  480. Type: config.FolderTypeReadOnly,
  481. RescanIntervalS: 1,
  482. }
  483. cfg := config.Wrap("/tmp/test", config.Configuration{
  484. Folders: []config.FolderConfiguration{fcfg},
  485. Devices: []config.DeviceConfiguration{
  486. {
  487. DeviceID: device1,
  488. },
  489. },
  490. })
  491. os.RemoveAll(fcfg.RawPath)
  492. m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil)
  493. m.AddFolder(fcfg)
  494. m.StartFolder("default")
  495. m.ServeBackground()
  496. waitFor := func(status string) error {
  497. timeout := time.Now().Add(2 * time.Second)
  498. for {
  499. _, _, err := m.State("default")
  500. if err == nil && status == "" {
  501. return nil
  502. }
  503. if err != nil && err.Error() == status {
  504. return nil
  505. }
  506. if time.Now().After(timeout) {
  507. return fmt.Errorf("Timed out waiting for status: %s, current status: %v", status, err)
  508. }
  509. time.Sleep(10 * time.Millisecond)
  510. }
  511. }
  512. if err := waitFor("folder path missing"); err != nil {
  513. t.Error(err)
  514. return
  515. }
  516. os.Mkdir(fcfg.RawPath, 0700)
  517. if err := waitFor("folder marker missing"); err != nil {
  518. t.Error(err)
  519. return
  520. }
  521. fd, err := os.Create(filepath.Join(fcfg.RawPath, ".stfolder"))
  522. if err != nil {
  523. t.Error(err)
  524. return
  525. }
  526. fd.Close()
  527. if err := waitFor(""); err != nil {
  528. t.Error(err)
  529. return
  530. }
  531. os.Remove(filepath.Join(fcfg.RawPath, ".stfolder"))
  532. if err := waitFor("folder marker missing"); err != nil {
  533. t.Error(err)
  534. return
  535. }
  536. os.Remove(fcfg.RawPath)
  537. if err := waitFor("folder path missing"); err != nil {
  538. t.Error(err)
  539. return
  540. }
  541. }
  542. func TestRWScanRecovery(t *testing.T) {
  543. ldb := db.OpenMemory()
  544. set := db.NewFileSet("default", ldb)
  545. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  546. {Name: "dummyfile"},
  547. })
  548. fcfg := config.FolderConfiguration{
  549. ID: "default",
  550. RawPath: "testdata/rwtestfolder",
  551. Type: config.FolderTypeReadWrite,
  552. RescanIntervalS: 1,
  553. }
  554. cfg := config.Wrap("/tmp/test", config.Configuration{
  555. Folders: []config.FolderConfiguration{fcfg},
  556. Devices: []config.DeviceConfiguration{
  557. {
  558. DeviceID: device1,
  559. },
  560. },
  561. })
  562. os.RemoveAll(fcfg.RawPath)
  563. m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil)
  564. m.AddFolder(fcfg)
  565. m.StartFolder("default")
  566. m.ServeBackground()
  567. waitFor := func(status string) error {
  568. timeout := time.Now().Add(2 * time.Second)
  569. for {
  570. _, _, err := m.State("default")
  571. if err == nil && status == "" {
  572. return nil
  573. }
  574. if err != nil && err.Error() == status {
  575. return nil
  576. }
  577. if time.Now().After(timeout) {
  578. return fmt.Errorf("Timed out waiting for status: %s, current status: %v", status, err)
  579. }
  580. time.Sleep(10 * time.Millisecond)
  581. }
  582. }
  583. if err := waitFor("folder path missing"); err != nil {
  584. t.Error(err)
  585. return
  586. }
  587. os.Mkdir(fcfg.RawPath, 0700)
  588. if err := waitFor("folder marker missing"); err != nil {
  589. t.Error(err)
  590. return
  591. }
  592. fd, err := os.Create(filepath.Join(fcfg.RawPath, ".stfolder"))
  593. if err != nil {
  594. t.Error(err)
  595. return
  596. }
  597. fd.Close()
  598. if err := waitFor(""); err != nil {
  599. t.Error(err)
  600. return
  601. }
  602. os.Remove(filepath.Join(fcfg.RawPath, ".stfolder"))
  603. if err := waitFor("folder marker missing"); err != nil {
  604. t.Error(err)
  605. return
  606. }
  607. os.Remove(fcfg.RawPath)
  608. if err := waitFor("folder path missing"); err != nil {
  609. t.Error(err)
  610. return
  611. }
  612. }
  613. func TestGlobalDirectoryTree(t *testing.T) {
  614. db := db.OpenMemory()
  615. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  616. m.AddFolder(defaultFolderConfig)
  617. m.ServeBackground()
  618. b := func(isfile bool, path ...string) protocol.FileInfo {
  619. typ := protocol.FileInfoTypeDirectory
  620. blocks := []protocol.BlockInfo{}
  621. if isfile {
  622. typ = protocol.FileInfoTypeFile
  623. blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
  624. }
  625. return protocol.FileInfo{
  626. Name: filepath.Join(path...),
  627. Type: typ,
  628. ModifiedS: 0x666,
  629. Blocks: blocks,
  630. Size: 0xa,
  631. }
  632. }
  633. filedata := []interface{}{time.Unix(0x666, 0), 0xa}
  634. testdata := []protocol.FileInfo{
  635. b(false, "another"),
  636. b(false, "another", "directory"),
  637. b(true, "another", "directory", "afile"),
  638. b(false, "another", "directory", "with"),
  639. b(false, "another", "directory", "with", "a"),
  640. b(true, "another", "directory", "with", "a", "file"),
  641. b(true, "another", "directory", "with", "file"),
  642. b(true, "another", "file"),
  643. b(false, "other"),
  644. b(false, "other", "rand"),
  645. b(false, "other", "random"),
  646. b(false, "other", "random", "dir"),
  647. b(false, "other", "random", "dirx"),
  648. b(false, "other", "randomx"),
  649. b(false, "some"),
  650. b(false, "some", "directory"),
  651. b(false, "some", "directory", "with"),
  652. b(false, "some", "directory", "with", "a"),
  653. b(true, "some", "directory", "with", "a", "file"),
  654. b(true, "rootfile"),
  655. }
  656. expectedResult := map[string]interface{}{
  657. "another": map[string]interface{}{
  658. "directory": map[string]interface{}{
  659. "afile": filedata,
  660. "with": map[string]interface{}{
  661. "a": map[string]interface{}{
  662. "file": filedata,
  663. },
  664. "file": filedata,
  665. },
  666. },
  667. "file": filedata,
  668. },
  669. "other": map[string]interface{}{
  670. "rand": map[string]interface{}{},
  671. "random": map[string]interface{}{
  672. "dir": map[string]interface{}{},
  673. "dirx": map[string]interface{}{},
  674. },
  675. "randomx": map[string]interface{}{},
  676. },
  677. "some": map[string]interface{}{
  678. "directory": map[string]interface{}{
  679. "with": map[string]interface{}{
  680. "a": map[string]interface{}{
  681. "file": filedata,
  682. },
  683. },
  684. },
  685. },
  686. "rootfile": filedata,
  687. }
  688. mm := func(data interface{}) string {
  689. bytes, err := json.Marshal(data)
  690. if err != nil {
  691. panic(err)
  692. }
  693. return string(bytes)
  694. }
  695. m.Index(device1, "default", testdata)
  696. result := m.GlobalDirectoryTree("default", "", -1, false)
  697. if mm(result) != mm(expectedResult) {
  698. t.Errorf("Does not match:\n%#v\n%#v", result, expectedResult)
  699. }
  700. result = m.GlobalDirectoryTree("default", "another", -1, false)
  701. if mm(result) != mm(expectedResult["another"]) {
  702. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult["another"]))
  703. }
  704. result = m.GlobalDirectoryTree("default", "", 0, false)
  705. currentResult := map[string]interface{}{
  706. "another": map[string]interface{}{},
  707. "other": map[string]interface{}{},
  708. "some": map[string]interface{}{},
  709. "rootfile": filedata,
  710. }
  711. if mm(result) != mm(currentResult) {
  712. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  713. }
  714. result = m.GlobalDirectoryTree("default", "", 1, false)
  715. currentResult = map[string]interface{}{
  716. "another": map[string]interface{}{
  717. "directory": map[string]interface{}{},
  718. "file": filedata,
  719. },
  720. "other": map[string]interface{}{
  721. "rand": map[string]interface{}{},
  722. "random": map[string]interface{}{},
  723. "randomx": map[string]interface{}{},
  724. },
  725. "some": map[string]interface{}{
  726. "directory": map[string]interface{}{},
  727. },
  728. "rootfile": filedata,
  729. }
  730. if mm(result) != mm(currentResult) {
  731. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  732. }
  733. result = m.GlobalDirectoryTree("default", "", -1, true)
  734. currentResult = map[string]interface{}{
  735. "another": map[string]interface{}{
  736. "directory": map[string]interface{}{
  737. "with": map[string]interface{}{
  738. "a": map[string]interface{}{},
  739. },
  740. },
  741. },
  742. "other": map[string]interface{}{
  743. "rand": map[string]interface{}{},
  744. "random": map[string]interface{}{
  745. "dir": map[string]interface{}{},
  746. "dirx": map[string]interface{}{},
  747. },
  748. "randomx": map[string]interface{}{},
  749. },
  750. "some": map[string]interface{}{
  751. "directory": map[string]interface{}{
  752. "with": map[string]interface{}{
  753. "a": map[string]interface{}{},
  754. },
  755. },
  756. },
  757. }
  758. if mm(result) != mm(currentResult) {
  759. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  760. }
  761. result = m.GlobalDirectoryTree("default", "", 1, true)
  762. currentResult = map[string]interface{}{
  763. "another": map[string]interface{}{
  764. "directory": map[string]interface{}{},
  765. },
  766. "other": map[string]interface{}{
  767. "rand": map[string]interface{}{},
  768. "random": map[string]interface{}{},
  769. "randomx": map[string]interface{}{},
  770. },
  771. "some": map[string]interface{}{
  772. "directory": map[string]interface{}{},
  773. },
  774. }
  775. if mm(result) != mm(currentResult) {
  776. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  777. }
  778. result = m.GlobalDirectoryTree("default", "another", 0, false)
  779. currentResult = map[string]interface{}{
  780. "directory": map[string]interface{}{},
  781. "file": filedata,
  782. }
  783. if mm(result) != mm(currentResult) {
  784. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  785. }
  786. result = m.GlobalDirectoryTree("default", "some/directory", 0, false)
  787. currentResult = map[string]interface{}{
  788. "with": map[string]interface{}{},
  789. }
  790. if mm(result) != mm(currentResult) {
  791. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  792. }
  793. result = m.GlobalDirectoryTree("default", "some/directory", 1, false)
  794. currentResult = map[string]interface{}{
  795. "with": map[string]interface{}{
  796. "a": map[string]interface{}{},
  797. },
  798. }
  799. if mm(result) != mm(currentResult) {
  800. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  801. }
  802. result = m.GlobalDirectoryTree("default", "some/directory", 2, false)
  803. currentResult = map[string]interface{}{
  804. "with": map[string]interface{}{
  805. "a": map[string]interface{}{
  806. "file": filedata,
  807. },
  808. },
  809. }
  810. if mm(result) != mm(currentResult) {
  811. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  812. }
  813. result = m.GlobalDirectoryTree("default", "another", -1, true)
  814. currentResult = map[string]interface{}{
  815. "directory": map[string]interface{}{
  816. "with": map[string]interface{}{
  817. "a": map[string]interface{}{},
  818. },
  819. },
  820. }
  821. if mm(result) != mm(currentResult) {
  822. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  823. }
  824. // No prefix matching!
  825. result = m.GlobalDirectoryTree("default", "som", -1, false)
  826. currentResult = map[string]interface{}{}
  827. if mm(result) != mm(currentResult) {
  828. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  829. }
  830. }
  831. func TestGlobalDirectorySelfFixing(t *testing.T) {
  832. db := db.OpenMemory()
  833. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  834. m.AddFolder(defaultFolderConfig)
  835. m.ServeBackground()
  836. b := func(isfile bool, path ...string) protocol.FileInfo {
  837. typ := protocol.FileInfoTypeDirectory
  838. blocks := []protocol.BlockInfo{}
  839. if isfile {
  840. typ = protocol.FileInfoTypeFile
  841. blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
  842. }
  843. return protocol.FileInfo{
  844. Name: filepath.Join(path...),
  845. Type: typ,
  846. ModifiedS: 0x666,
  847. Blocks: blocks,
  848. Size: 0xa,
  849. }
  850. }
  851. filedata := []interface{}{time.Unix(0x666, 0).Format(time.RFC3339), 0xa}
  852. testdata := []protocol.FileInfo{
  853. b(true, "another", "directory", "afile"),
  854. b(true, "another", "directory", "with", "a", "file"),
  855. b(true, "another", "directory", "with", "file"),
  856. b(false, "other", "random", "dirx"),
  857. b(false, "other", "randomx"),
  858. b(false, "some", "directory", "with", "x"),
  859. b(true, "some", "directory", "with", "a", "file"),
  860. b(false, "this", "is", "a", "deep", "invalid", "directory"),
  861. b(true, "xthis", "is", "a", "deep", "invalid", "file"),
  862. }
  863. expectedResult := map[string]interface{}{
  864. "another": map[string]interface{}{
  865. "directory": map[string]interface{}{
  866. "afile": filedata,
  867. "with": map[string]interface{}{
  868. "a": map[string]interface{}{
  869. "file": filedata,
  870. },
  871. "file": filedata,
  872. },
  873. },
  874. },
  875. "other": map[string]interface{}{
  876. "random": map[string]interface{}{
  877. "dirx": map[string]interface{}{},
  878. },
  879. "randomx": map[string]interface{}{},
  880. },
  881. "some": map[string]interface{}{
  882. "directory": map[string]interface{}{
  883. "with": map[string]interface{}{
  884. "a": map[string]interface{}{
  885. "file": filedata,
  886. },
  887. "x": map[string]interface{}{},
  888. },
  889. },
  890. },
  891. "this": map[string]interface{}{
  892. "is": map[string]interface{}{
  893. "a": map[string]interface{}{
  894. "deep": map[string]interface{}{
  895. "invalid": map[string]interface{}{
  896. "directory": map[string]interface{}{},
  897. },
  898. },
  899. },
  900. },
  901. },
  902. "xthis": map[string]interface{}{
  903. "is": map[string]interface{}{
  904. "a": map[string]interface{}{
  905. "deep": map[string]interface{}{
  906. "invalid": map[string]interface{}{
  907. "file": filedata,
  908. },
  909. },
  910. },
  911. },
  912. },
  913. }
  914. mm := func(data interface{}) string {
  915. bytes, err := json.Marshal(data)
  916. if err != nil {
  917. panic(err)
  918. }
  919. return string(bytes)
  920. }
  921. m.Index(device1, "default", testdata)
  922. result := m.GlobalDirectoryTree("default", "", -1, false)
  923. if mm(result) != mm(expectedResult) {
  924. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult))
  925. }
  926. result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, false)
  927. currentResult := map[string]interface{}{
  928. "invalid": map[string]interface{}{
  929. "file": filedata,
  930. },
  931. }
  932. if mm(result) != mm(currentResult) {
  933. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  934. }
  935. result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, true)
  936. currentResult = map[string]interface{}{
  937. "invalid": map[string]interface{}{},
  938. }
  939. if mm(result) != mm(currentResult) {
  940. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  941. }
  942. // !!! This is actually BAD, because we don't have enough level allowance
  943. // to accept this file, hence the tree is left unbuilt !!!
  944. result = m.GlobalDirectoryTree("default", "xthis", 1, false)
  945. currentResult = map[string]interface{}{}
  946. if mm(result) != mm(currentResult) {
  947. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  948. }
  949. }
  950. func genDeepFiles(n, d int) []protocol.FileInfo {
  951. rand.Seed(int64(n))
  952. files := make([]protocol.FileInfo, n)
  953. t := time.Now().Unix()
  954. for i := 0; i < n; i++ {
  955. path := ""
  956. for i := 0; i <= d; i++ {
  957. path = filepath.Join(path, strconv.Itoa(rand.Int()))
  958. }
  959. sofar := ""
  960. for _, path := range filepath.SplitList(path) {
  961. sofar = filepath.Join(sofar, path)
  962. files[i] = protocol.FileInfo{
  963. Name: sofar,
  964. }
  965. i++
  966. }
  967. files[i].ModifiedS = t
  968. files[i].Blocks = []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}}
  969. }
  970. return files
  971. }
  972. func BenchmarkTree_10000_50(b *testing.B) {
  973. benchmarkTree(b, 10000, 50)
  974. }
  975. func BenchmarkTree_100_50(b *testing.B) {
  976. benchmarkTree(b, 100, 50)
  977. }
  978. func BenchmarkTree_100_10(b *testing.B) {
  979. benchmarkTree(b, 100, 10)
  980. }
  981. func benchmarkTree(b *testing.B, n1, n2 int) {
  982. db := db.OpenMemory()
  983. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  984. m.AddFolder(defaultFolderConfig)
  985. m.ServeBackground()
  986. m.ScanFolder("default")
  987. files := genDeepFiles(n1, n2)
  988. m.Index(device1, "default", files)
  989. b.ResetTimer()
  990. for i := 0; i < b.N; i++ {
  991. m.GlobalDirectoryTree("default", "", -1, false)
  992. }
  993. b.ReportAllocs()
  994. }
  995. func TestUnifySubs(t *testing.T) {
  996. cases := []struct {
  997. in []string // input to unifySubs
  998. exists []string // paths that exist in the database
  999. out []string // expected output
  1000. }{
  1001. {
  1002. // 0. trailing slashes are cleaned, known paths are just passed on
  1003. []string{"foo/", "bar//"},
  1004. []string{"foo", "bar"},
  1005. []string{"bar", "foo"}, // the output is sorted
  1006. },
  1007. {
  1008. // 1. "foo/bar" gets trimmed as it's covered by foo
  1009. []string{"foo", "bar/", "foo/bar/"},
  1010. []string{"foo", "bar"},
  1011. []string{"bar", "foo"},
  1012. },
  1013. {
  1014. // 2. "" gets simplified to the empty list; ie scan all
  1015. []string{"foo", ""},
  1016. []string{"foo"},
  1017. nil,
  1018. },
  1019. {
  1020. // 3. "foo/bar" is unknown, but it's kept
  1021. // because its parent is known
  1022. []string{"foo/bar"},
  1023. []string{"foo"},
  1024. []string{"foo/bar"},
  1025. },
  1026. {
  1027. // 4. two independent known paths, both are kept
  1028. // "usr/lib" is not a prefix of "usr/libexec"
  1029. []string{"usr/lib", "usr/libexec"},
  1030. []string{"usr", "usr/lib", "usr/libexec"},
  1031. []string{"usr/lib", "usr/libexec"},
  1032. },
  1033. {
  1034. // 5. "usr/lib" is a prefix of "usr/lib/exec"
  1035. []string{"usr/lib", "usr/lib/exec"},
  1036. []string{"usr", "usr/lib", "usr/libexec"},
  1037. []string{"usr/lib"},
  1038. },
  1039. {
  1040. // 6. .stignore and .stfolder are special and are passed on
  1041. // verbatim even though they are unknown
  1042. []string{".stfolder", ".stignore"},
  1043. []string{},
  1044. []string{".stfolder", ".stignore"},
  1045. },
  1046. {
  1047. // 7. but the presence of something else unknown forces an actual
  1048. // scan
  1049. []string{".stfolder", ".stignore", "foo/bar"},
  1050. []string{},
  1051. []string{".stfolder", ".stignore", "foo"},
  1052. },
  1053. {
  1054. // 8. explicit request to scan all
  1055. nil,
  1056. []string{"foo"},
  1057. nil,
  1058. },
  1059. {
  1060. // 9. empty list of subs
  1061. []string{},
  1062. []string{"foo"},
  1063. nil,
  1064. },
  1065. }
  1066. if runtime.GOOS == "windows" {
  1067. // Fixup path separators
  1068. for i := range cases {
  1069. for j, p := range cases[i].in {
  1070. cases[i].in[j] = filepath.FromSlash(p)
  1071. }
  1072. for j, p := range cases[i].exists {
  1073. cases[i].exists[j] = filepath.FromSlash(p)
  1074. }
  1075. for j, p := range cases[i].out {
  1076. cases[i].out[j] = filepath.FromSlash(p)
  1077. }
  1078. }
  1079. }
  1080. for i, tc := range cases {
  1081. exists := func(f string) bool {
  1082. for _, e := range tc.exists {
  1083. if f == e {
  1084. return true
  1085. }
  1086. }
  1087. return false
  1088. }
  1089. out := unifySubs(tc.in, exists)
  1090. if diff, equal := messagediff.PrettyDiff(tc.out, out); !equal {
  1091. t.Errorf("Case %d failed; got %v, expected %v, diff:\n%s", i, out, tc.out, diff)
  1092. }
  1093. }
  1094. }
  1095. func TestIssue3028(t *testing.T) {
  1096. // Create two files that we'll delete, one with a name that is a prefix of the other.
  1097. if err := ioutil.WriteFile("testdata/testrm", []byte("Hello"), 0644); err != nil {
  1098. t.Fatal(err)
  1099. }
  1100. defer os.Remove("testdata/testrm")
  1101. if err := ioutil.WriteFile("testdata/testrm2", []byte("Hello"), 0644); err != nil {
  1102. t.Fatal(err)
  1103. }
  1104. defer os.Remove("testdata/testrm2")
  1105. // Create a model and default folder
  1106. db := db.OpenMemory()
  1107. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  1108. defCfg := defaultFolderConfig.Copy()
  1109. defCfg.RescanIntervalS = 86400
  1110. m.AddFolder(defCfg)
  1111. m.StartFolder("default")
  1112. m.ServeBackground()
  1113. // Ugly hack for testing: reach into the model for the rwfolder and wait
  1114. // for it to complete the initial scan. The risk is that it otherwise
  1115. // runs during our modifications and screws up the test.
  1116. m.fmut.RLock()
  1117. folder := m.folderRunners["default"].(*rwFolder)
  1118. m.fmut.RUnlock()
  1119. <-folder.initialScanCompleted
  1120. // Get a count of how many files are there now
  1121. locorigfiles, _, _ := m.LocalSize("default")
  1122. globorigfiles, _, _ := m.GlobalSize("default")
  1123. // Delete and rescan specifically these two
  1124. os.Remove("testdata/testrm")
  1125. os.Remove("testdata/testrm2")
  1126. m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"})
  1127. // Verify that the number of files decreased by two and the number of
  1128. // deleted files increases by two
  1129. locnowfiles, locdelfiles, _ := m.LocalSize("default")
  1130. globnowfiles, globdelfiles, _ := m.GlobalSize("default")
  1131. if locnowfiles != locorigfiles-2 {
  1132. t.Errorf("Incorrect local accounting; got %d current files, expected %d", locnowfiles, locorigfiles-2)
  1133. }
  1134. if globnowfiles != globorigfiles-2 {
  1135. t.Errorf("Incorrect global accounting; got %d current files, expected %d", globnowfiles, globorigfiles-2)
  1136. }
  1137. if locdelfiles != 2 {
  1138. t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", locdelfiles)
  1139. }
  1140. if globdelfiles != 2 {
  1141. t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", globdelfiles)
  1142. }
  1143. }
  1144. func TestIssue3164(t *testing.T) {
  1145. osutil.RemoveAll("testdata/issue3164")
  1146. defer osutil.RemoveAll("testdata/issue3164")
  1147. if err := os.MkdirAll("testdata/issue3164/oktodelete/foobar", 0777); err != nil {
  1148. t.Fatal(err)
  1149. }
  1150. if err := ioutil.WriteFile("testdata/issue3164/oktodelete/foobar/file", []byte("Hello"), 0644); err != nil {
  1151. t.Fatal(err)
  1152. }
  1153. if err := ioutil.WriteFile("testdata/issue3164/oktodelete/file", []byte("Hello"), 0644); err != nil {
  1154. t.Fatal(err)
  1155. }
  1156. f := protocol.FileInfo{
  1157. Name: "issue3164",
  1158. }
  1159. m := ignore.New(false)
  1160. if err := m.Parse(bytes.NewBufferString("(?d)oktodelete"), ""); err != nil {
  1161. t.Fatal(err)
  1162. }
  1163. fl := rwFolder{
  1164. dbUpdates: make(chan dbUpdateJob, 1),
  1165. dir: "testdata",
  1166. }
  1167. fl.deleteDir(f, m)
  1168. if _, err := os.Stat("testdata/issue3164"); !os.IsNotExist(err) {
  1169. t.Fatal(err)
  1170. }
  1171. }
  1172. func TestScanNoDatabaseWrite(t *testing.T) {
  1173. // When scanning, nothing should be committed to database unless
  1174. // something actually changed.
  1175. db := db.OpenMemory()
  1176. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  1177. m.AddFolder(defaultFolderConfig)
  1178. m.StartFolder("default")
  1179. m.ServeBackground()
  1180. // Start with no ignores, and restore the previous state when the test completes
  1181. curIgn, _, err := m.GetIgnores("default")
  1182. if err != nil {
  1183. t.Fatal(err)
  1184. }
  1185. defer m.SetIgnores("default", curIgn)
  1186. m.SetIgnores("default", nil)
  1187. // Scan the folder twice. The second scan should be a no-op database wise
  1188. m.ScanFolder("default")
  1189. c0 := db.Committed()
  1190. m.ScanFolder("default")
  1191. c1 := db.Committed()
  1192. if c1 != c0 {
  1193. t.Errorf("scan should not commit data when nothing changed but %d != %d", c1, c0)
  1194. }
  1195. // Ignore a file we know exists. It'll be updated in the database.
  1196. m.SetIgnores("default", []string{"foo"})
  1197. m.ScanFolder("default")
  1198. c2 := db.Committed()
  1199. if c2 <= c1 {
  1200. t.Errorf("scan should commit data when something got ignored but %d <= %d", c2, c1)
  1201. }
  1202. // Scan again. Nothing should happen.
  1203. m.ScanFolder("default")
  1204. c3 := db.Committed()
  1205. if c3 != c2 {
  1206. t.Errorf("scan should not commit data when nothing changed (with ignores) but %d != %d", c3, c2)
  1207. }
  1208. }
  1209. func TestIssue2782(t *testing.T) {
  1210. // CheckFolderHealth should accept a symlinked folder, when using tilde-expanded path.
  1211. if runtime.GOOS == "windows" {
  1212. t.Skip("not reliable on Windows")
  1213. return
  1214. }
  1215. home := os.Getenv("HOME")
  1216. if home == "" {
  1217. t.Skip("no home")
  1218. }
  1219. // Create the test env. Needs to be based on $HOME as tilde expansion is
  1220. // part of the issue. Skip the test if any of this fails, as we are a
  1221. // bit outside of our stated domain here...
  1222. testName := ".syncthing-test." + srand.String(16)
  1223. testDir := filepath.Join(home, testName)
  1224. if err := osutil.RemoveAll(testDir); err != nil {
  1225. t.Skip(err)
  1226. }
  1227. if err := osutil.MkdirAll(testDir+"/syncdir", 0755); err != nil {
  1228. t.Skip(err)
  1229. }
  1230. if err := ioutil.WriteFile(testDir+"/syncdir/file", []byte("hello, world\n"), 0644); err != nil {
  1231. t.Skip(err)
  1232. }
  1233. if err := os.Symlink("syncdir", testDir+"/synclink"); err != nil {
  1234. t.Skip(err)
  1235. }
  1236. defer osutil.RemoveAll(testDir)
  1237. db := db.OpenMemory()
  1238. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
  1239. m.AddFolder(config.NewFolderConfiguration("default", "~/"+testName+"/synclink/"))
  1240. m.StartFolder("default")
  1241. m.ServeBackground()
  1242. defer m.Stop()
  1243. if err := m.ScanFolder("default"); err != nil {
  1244. t.Error("scan error:", err)
  1245. }
  1246. if err := m.CheckFolderHealth("default"); err != nil {
  1247. t.Error("health check error:", err)
  1248. }
  1249. }
  1250. func TestIndexesForUnknownDevicesDropped(t *testing.T) {
  1251. dbi := db.OpenMemory()
  1252. files := db.NewFileSet("default", dbi)
  1253. files.Replace(device1, genFiles(1))
  1254. files.Replace(device2, genFiles(1))
  1255. if len(files.ListDevices()) != 2 {
  1256. t.Error("expected two devices")
  1257. }
  1258. m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", dbi, nil)
  1259. m.AddFolder(defaultFolderConfig)
  1260. m.StartFolder("default")
  1261. // Remote sequence is cached, hence need to recreated.
  1262. files = db.NewFileSet("default", dbi)
  1263. if len(files.ListDevices()) != 1 {
  1264. t.Error("Expected one device")
  1265. }
  1266. }
  1267. func TestSharedWithClearedOnDisconnect(t *testing.T) {
  1268. dbi := db.OpenMemory()
  1269. fcfg := config.NewFolderConfiguration("default", "testdata")
  1270. fcfg.Devices = []config.FolderDeviceConfiguration{
  1271. {DeviceID: device1},
  1272. {DeviceID: device2},
  1273. }
  1274. cfg := config.Configuration{
  1275. Folders: []config.FolderConfiguration{fcfg},
  1276. Devices: []config.DeviceConfiguration{
  1277. config.NewDeviceConfiguration(device1, "device1"),
  1278. config.NewDeviceConfiguration(device2, "device2"),
  1279. },
  1280. Options: config.OptionsConfiguration{
  1281. // Don't remove temporaries directly on startup
  1282. KeepTemporariesH: 1,
  1283. },
  1284. }
  1285. wcfg := config.Wrap("/tmp/test", cfg)
  1286. d2c := &fakeConn{}
  1287. m := NewModel(wcfg, protocol.LocalDeviceID, "device", "syncthing", "dev", dbi, nil)
  1288. m.AddFolder(fcfg)
  1289. m.StartFolder(fcfg.ID)
  1290. m.ServeBackground()
  1291. conn1 := connections.Connection{
  1292. IntermediateConnection: connections.IntermediateConnection{
  1293. Conn: tls.Client(&fakeConn{}, nil),
  1294. Type: "foo",
  1295. Priority: 10,
  1296. },
  1297. Connection: &FakeConnection{
  1298. id: device1,
  1299. },
  1300. }
  1301. m.AddConnection(conn1, protocol.HelloResult{})
  1302. conn2 := connections.Connection{
  1303. IntermediateConnection: connections.IntermediateConnection{
  1304. Conn: tls.Client(d2c, nil),
  1305. Type: "foo",
  1306. Priority: 10,
  1307. },
  1308. Connection: &FakeConnection{
  1309. id: device2,
  1310. },
  1311. }
  1312. m.AddConnection(conn2, protocol.HelloResult{})
  1313. m.ClusterConfig(device1, protocol.ClusterConfig{
  1314. Folders: []protocol.Folder{
  1315. {
  1316. ID: "default",
  1317. Devices: []protocol.Device{
  1318. {ID: device1[:]},
  1319. {ID: device2[:]},
  1320. },
  1321. },
  1322. },
  1323. })
  1324. m.ClusterConfig(device2, protocol.ClusterConfig{
  1325. Folders: []protocol.Folder{
  1326. {
  1327. ID: "default",
  1328. Devices: []protocol.Device{
  1329. {ID: device1[:]},
  1330. {ID: device2[:]},
  1331. },
  1332. },
  1333. },
  1334. })
  1335. if !m.folderSharedWith("default", device1) {
  1336. t.Error("not shared with device1")
  1337. }
  1338. if !m.folderSharedWith("default", device2) {
  1339. t.Error("not shared with device2")
  1340. }
  1341. if d2c.closed {
  1342. t.Error("conn already closed")
  1343. }
  1344. cfg = cfg.Copy()
  1345. cfg.Devices = cfg.Devices[:1]
  1346. if err := wcfg.Replace(cfg); err != nil {
  1347. t.Error(err)
  1348. }
  1349. time.Sleep(100 * time.Millisecond) // Committer notification happens in a separate routine
  1350. if !m.folderSharedWith("default", device1) {
  1351. t.Error("not shared with device1")
  1352. }
  1353. if m.folderSharedWith("default", device2) { // checks m.deviceFolders
  1354. t.Error("shared with device2")
  1355. }
  1356. if !d2c.closed {
  1357. t.Error("connection not closed")
  1358. }
  1359. if _, ok := wcfg.Devices()[device2]; ok {
  1360. t.Error("device still in config")
  1361. }
  1362. fdevs, ok := m.folderDevices["default"]
  1363. if !ok {
  1364. t.Error("folder missing?")
  1365. }
  1366. for _, id := range fdevs {
  1367. if id == device2 {
  1368. t.Error("still there")
  1369. }
  1370. }
  1371. if _, ok := m.conn[device2]; !ok {
  1372. t.Error("conn missing early")
  1373. }
  1374. if _, ok := m.helloMessages[device2]; !ok {
  1375. t.Error("hello missing early")
  1376. }
  1377. if _, ok := m.deviceDownloads[device2]; !ok {
  1378. t.Error("downloads missing early")
  1379. }
  1380. m.Closed(conn2, fmt.Errorf("foo"))
  1381. if _, ok := m.conn[device2]; ok {
  1382. t.Error("conn not missing")
  1383. }
  1384. if _, ok := m.helloMessages[device2]; ok {
  1385. t.Error("hello not missing")
  1386. }
  1387. if _, ok := m.deviceDownloads[device2]; ok {
  1388. t.Error("downloads not missing")
  1389. }
  1390. }
  1391. type fakeAddr struct{}
  1392. func (fakeAddr) Network() string {
  1393. return "network"
  1394. }
  1395. func (fakeAddr) String() string {
  1396. return "address"
  1397. }
  1398. type fakeConn struct {
  1399. closed bool
  1400. }
  1401. func (c *fakeConn) Close() error {
  1402. c.closed = true
  1403. return nil
  1404. }
  1405. func (fakeConn) LocalAddr() net.Addr {
  1406. return &fakeAddr{}
  1407. }
  1408. func (fakeConn) RemoteAddr() net.Addr {
  1409. return &fakeAddr{}
  1410. }
  1411. func (fakeConn) Read([]byte) (int, error) {
  1412. return 0, nil
  1413. }
  1414. func (fakeConn) Write([]byte) (int, error) {
  1415. return 0, nil
  1416. }
  1417. func (fakeConn) SetDeadline(time.Time) error {
  1418. return nil
  1419. }
  1420. func (fakeConn) SetReadDeadline(time.Time) error {
  1421. return nil
  1422. }
  1423. func (fakeConn) SetWriteDeadline(time.Time) error {
  1424. return nil
  1425. }