1
0

model_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This program is free software: you can redistribute it and/or modify it
  4. // under the terms of the GNU General Public License as published by the Free
  5. // Software Foundation, either version 3 of the License, or (at your option)
  6. // any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful, but WITHOUT
  9. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. // more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program. If not, see <http://www.gnu.org/licenses/>.
  15. package model
  16. import (
  17. "bytes"
  18. "fmt"
  19. "os"
  20. "testing"
  21. "time"
  22. "github.com/syncthing/protocol"
  23. "github.com/syncthing/syncthing/internal/config"
  24. "github.com/syndtr/goleveldb/leveldb"
  25. "github.com/syndtr/goleveldb/leveldb/storage"
  26. )
  27. var device1, device2 protocol.DeviceID
  28. func init() {
  29. device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  30. device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
  31. }
  32. var testDataExpected = map[string]protocol.FileInfo{
  33. "foo": {
  34. Name: "foo",
  35. Flags: 0,
  36. Modified: 0,
  37. 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}}},
  38. },
  39. "empty": {
  40. Name: "empty",
  41. Flags: 0,
  42. Modified: 0,
  43. 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}}},
  44. },
  45. "bar": {
  46. Name: "bar",
  47. Flags: 0,
  48. Modified: 0,
  49. 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}}},
  50. },
  51. }
  52. func init() {
  53. // Fix expected test data to match reality
  54. for n, f := range testDataExpected {
  55. fi, _ := os.Stat("testdata/" + n)
  56. f.Flags = uint32(fi.Mode())
  57. f.Modified = fi.ModTime().Unix()
  58. testDataExpected[n] = f
  59. }
  60. }
  61. func TestRequest(t *testing.T) {
  62. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  63. m := NewModel(config.Wrap("/tmp/test", config.Configuration{}), "device", "syncthing", "dev", db)
  64. // device1 shares default, but device2 doesn't
  65. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata", Devices: []config.FolderDeviceConfiguration{{DeviceID: device1}}})
  66. m.ScanFolder("default")
  67. // Existing, shared file
  68. bs, err := m.Request(device1, "default", "foo", 0, 6)
  69. if err != nil {
  70. t.Error(err)
  71. }
  72. if bytes.Compare(bs, []byte("foobar")) != 0 {
  73. t.Errorf("Incorrect data from request: %q", string(bs))
  74. }
  75. // Existing, nonshared file
  76. bs, err = m.Request(device2, "default", "foo", 0, 6)
  77. if err == nil {
  78. t.Error("Unexpected nil error on insecure file read")
  79. }
  80. if bs != nil {
  81. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  82. }
  83. // Nonexistent file
  84. bs, err = m.Request(device1, "default", "nonexistent", 0, 6)
  85. if err == nil {
  86. t.Error("Unexpected nil error on insecure file read")
  87. }
  88. if bs != nil {
  89. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  90. }
  91. // Shared folder, but disallowed file name
  92. bs, err = m.Request(device1, "default", "../walk.go", 0, 6)
  93. if err == nil {
  94. t.Error("Unexpected nil error on insecure file read")
  95. }
  96. if bs != nil {
  97. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  98. }
  99. // Larger block than available
  100. bs, err = m.Request(device1, "default", "foo", 0, 42)
  101. if err == nil {
  102. t.Error("Unexpected nil error on insecure file read")
  103. }
  104. if bs != nil {
  105. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  106. }
  107. // Negative offset
  108. bs, err = m.Request(device1, "default", "foo", -4, 6)
  109. if err == nil {
  110. t.Error("Unexpected nil error on insecure file read")
  111. }
  112. if bs != nil {
  113. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  114. }
  115. // Negative size
  116. bs, err = m.Request(device1, "default", "foo", 4, -4)
  117. if err == nil {
  118. t.Error("Unexpected nil error on insecure file read")
  119. }
  120. if bs != nil {
  121. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  122. }
  123. }
  124. func genFiles(n int) []protocol.FileInfo {
  125. files := make([]protocol.FileInfo, n)
  126. t := time.Now().Unix()
  127. for i := 0; i < n; i++ {
  128. files[i] = protocol.FileInfo{
  129. Name: fmt.Sprintf("file%d", i),
  130. Modified: t,
  131. Blocks: []protocol.BlockInfo{{0, 100, []byte("some hash bytes")}},
  132. }
  133. }
  134. return files
  135. }
  136. func BenchmarkIndex10000(b *testing.B) {
  137. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  138. m := NewModel(nil, "device", "syncthing", "dev", db)
  139. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  140. m.ScanFolder("default")
  141. files := genFiles(10000)
  142. b.ResetTimer()
  143. for i := 0; i < b.N; i++ {
  144. m.Index(device1, "default", files)
  145. }
  146. }
  147. func BenchmarkIndex00100(b *testing.B) {
  148. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  149. m := NewModel(nil, "device", "syncthing", "dev", db)
  150. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  151. m.ScanFolder("default")
  152. files := genFiles(100)
  153. b.ResetTimer()
  154. for i := 0; i < b.N; i++ {
  155. m.Index(device1, "default", files)
  156. }
  157. }
  158. func BenchmarkIndexUpdate10000f10000(b *testing.B) {
  159. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  160. m := NewModel(nil, "device", "syncthing", "dev", db)
  161. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  162. m.ScanFolder("default")
  163. files := genFiles(10000)
  164. m.Index(device1, "default", files)
  165. b.ResetTimer()
  166. for i := 0; i < b.N; i++ {
  167. m.IndexUpdate(device1, "default", files)
  168. }
  169. }
  170. func BenchmarkIndexUpdate10000f00100(b *testing.B) {
  171. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  172. m := NewModel(nil, "device", "syncthing", "dev", db)
  173. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  174. m.ScanFolder("default")
  175. files := genFiles(10000)
  176. m.Index(device1, "default", files)
  177. ufiles := genFiles(100)
  178. b.ResetTimer()
  179. for i := 0; i < b.N; i++ {
  180. m.IndexUpdate(device1, "default", ufiles)
  181. }
  182. }
  183. func BenchmarkIndexUpdate10000f00001(b *testing.B) {
  184. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  185. m := NewModel(nil, "device", "syncthing", "dev", db)
  186. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  187. m.ScanFolder("default")
  188. files := genFiles(10000)
  189. m.Index(device1, "default", files)
  190. ufiles := genFiles(1)
  191. b.ResetTimer()
  192. for i := 0; i < b.N; i++ {
  193. m.IndexUpdate(device1, "default", ufiles)
  194. }
  195. }
  196. type FakeConnection struct {
  197. id protocol.DeviceID
  198. requestData []byte
  199. }
  200. func (FakeConnection) Close() error {
  201. return nil
  202. }
  203. func (f FakeConnection) ID() protocol.DeviceID {
  204. return f.id
  205. }
  206. func (f FakeConnection) Name() string {
  207. return ""
  208. }
  209. func (f FakeConnection) Option(string) string {
  210. return ""
  211. }
  212. func (FakeConnection) Index(string, []protocol.FileInfo) error {
  213. return nil
  214. }
  215. func (FakeConnection) IndexUpdate(string, []protocol.FileInfo) error {
  216. return nil
  217. }
  218. func (f FakeConnection) Request(folder, name string, offset int64, size int) ([]byte, error) {
  219. return f.requestData, nil
  220. }
  221. func (FakeConnection) ClusterConfig(protocol.ClusterConfigMessage) {}
  222. func (FakeConnection) Ping() bool {
  223. return true
  224. }
  225. func (FakeConnection) Statistics() protocol.Statistics {
  226. return protocol.Statistics{}
  227. }
  228. func BenchmarkRequest(b *testing.B) {
  229. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  230. m := NewModel(nil, "device", "syncthing", "dev", db)
  231. m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
  232. m.ScanFolder("default")
  233. const n = 1000
  234. files := make([]protocol.FileInfo, n)
  235. t := time.Now().Unix()
  236. for i := 0; i < n; i++ {
  237. files[i] = protocol.FileInfo{
  238. Name: fmt.Sprintf("file%d", i),
  239. Modified: t,
  240. Blocks: []protocol.BlockInfo{{0, 100, []byte("some hash bytes")}},
  241. }
  242. }
  243. fc := FakeConnection{
  244. id: device1,
  245. requestData: []byte("some data to return"),
  246. }
  247. m.AddConnection(fc, fc)
  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)
  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. ccm := protocol.ClusterConfigMessage{
  262. ClientName: "syncthing",
  263. ClientVersion: "v0.9.4",
  264. }
  265. defer os.Remove("tmpconfig.xml")
  266. cfg := config.New(device1)
  267. cfg.Devices = []config.DeviceConfiguration{
  268. {
  269. DeviceID: device1,
  270. },
  271. }
  272. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  273. m := NewModel(config.Wrap("tmpconfig.xml", cfg), "device", "syncthing", "dev", db)
  274. if cfg.Devices[0].Name != "" {
  275. t.Errorf("Device already has a name")
  276. }
  277. m.ClusterConfig(device1, ccm)
  278. if cfg.Devices[0].Name != "" {
  279. t.Errorf("Device already has a name")
  280. }
  281. ccm.Options = []protocol.Option{
  282. {
  283. Key: "name",
  284. Value: "tester",
  285. },
  286. }
  287. m.ClusterConfig(device1, ccm)
  288. if cfg.Devices[0].Name != "tester" {
  289. t.Errorf("Device did not get a name")
  290. }
  291. ccm.Options[0].Value = "tester2"
  292. m.ClusterConfig(device1, ccm)
  293. if cfg.Devices[0].Name != "tester" {
  294. t.Errorf("Device name got overwritten")
  295. }
  296. cfgw, err := config.Load("tmpconfig.xml", protocol.LocalDeviceID)
  297. if err != nil {
  298. t.Error(err)
  299. return
  300. }
  301. if cfgw.Devices()[device1].Name != "tester" {
  302. t.Errorf("Device name not saved in config")
  303. }
  304. }
  305. func TestClusterConfig(t *testing.T) {
  306. cfg := config.New(device1)
  307. cfg.Devices = []config.DeviceConfiguration{
  308. {
  309. DeviceID: device1,
  310. Introducer: true,
  311. },
  312. {
  313. DeviceID: device2,
  314. },
  315. }
  316. cfg.Folders = []config.FolderConfiguration{
  317. {
  318. ID: "folder1",
  319. Devices: []config.FolderDeviceConfiguration{
  320. {DeviceID: device1},
  321. {DeviceID: device2},
  322. },
  323. },
  324. {
  325. ID: "folder2",
  326. Devices: []config.FolderDeviceConfiguration{
  327. {DeviceID: device1},
  328. {DeviceID: device2},
  329. },
  330. },
  331. }
  332. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  333. m := NewModel(config.Wrap("/tmp/test", cfg), "device", "syncthing", "dev", db)
  334. m.AddFolder(cfg.Folders[0])
  335. m.AddFolder(cfg.Folders[1])
  336. cm := m.clusterConfig(device2)
  337. if l := len(cm.Folders); l != 2 {
  338. t.Fatalf("Incorrect number of folders %d != 2", l)
  339. }
  340. r := cm.Folders[0]
  341. if r.ID != "folder1" {
  342. t.Errorf("Incorrect folder %q != folder1", r.ID)
  343. }
  344. if l := len(r.Devices); l != 2 {
  345. t.Errorf("Incorrect number of devices %d != 2", l)
  346. }
  347. if id := r.Devices[0].ID; bytes.Compare(id, device1[:]) != 0 {
  348. t.Errorf("Incorrect device ID %x != %x", id, device1)
  349. }
  350. if r.Devices[0].Flags&protocol.FlagIntroducer == 0 {
  351. t.Error("Device1 should be flagged as Introducer")
  352. }
  353. if id := r.Devices[1].ID; bytes.Compare(id, device2[:]) != 0 {
  354. t.Errorf("Incorrect device ID %x != %x", id, device2)
  355. }
  356. if r.Devices[1].Flags&protocol.FlagIntroducer != 0 {
  357. t.Error("Device2 should not be flagged as Introducer")
  358. }
  359. r = cm.Folders[1]
  360. if r.ID != "folder2" {
  361. t.Errorf("Incorrect folder %q != folder2", r.ID)
  362. }
  363. if l := len(r.Devices); l != 2 {
  364. t.Errorf("Incorrect number of devices %d != 2", l)
  365. }
  366. if id := r.Devices[0].ID; bytes.Compare(id, device1[:]) != 0 {
  367. t.Errorf("Incorrect device ID %x != %x", id, device1)
  368. }
  369. if r.Devices[0].Flags&protocol.FlagIntroducer == 0 {
  370. t.Error("Device1 should be flagged as Introducer")
  371. }
  372. if id := r.Devices[1].ID; bytes.Compare(id, device2[:]) != 0 {
  373. t.Errorf("Incorrect device ID %x != %x", id, device2)
  374. }
  375. if r.Devices[1].Flags&protocol.FlagIntroducer != 0 {
  376. t.Error("Device2 should not be flagged as Introducer")
  377. }
  378. }
  379. func TestIgnores(t *testing.T) {
  380. arrEqual := func(a, b []string) bool {
  381. if len(a) != len(b) {
  382. return false
  383. }
  384. for i := range a {
  385. if a[i] != b[i] {
  386. return false
  387. }
  388. }
  389. return true
  390. }
  391. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  392. fcfg := config.FolderConfiguration{ID: "default", Path: "testdata"}
  393. cfg := config.Wrap("/tmp", config.Configuration{
  394. Folders: []config.FolderConfiguration{fcfg},
  395. })
  396. m := NewModel(cfg, "device", "syncthing", "dev", db)
  397. m.AddFolder(fcfg)
  398. expected := []string{
  399. ".*",
  400. "quux",
  401. }
  402. ignores, _, err := m.GetIgnores("default")
  403. if err != nil {
  404. t.Error(err)
  405. }
  406. if !arrEqual(ignores, expected) {
  407. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  408. }
  409. ignores = append(ignores, "pox")
  410. err = m.SetIgnores("default", ignores)
  411. if err != nil {
  412. t.Error(err)
  413. }
  414. ignores2, _, err := m.GetIgnores("default")
  415. if err != nil {
  416. t.Error(err)
  417. }
  418. if arrEqual(expected, ignores2) {
  419. t.Errorf("Incorrect ignores: %v == %v", ignores2, expected)
  420. }
  421. if !arrEqual(ignores, ignores2) {
  422. t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
  423. }
  424. err = m.SetIgnores("default", expected)
  425. if err != nil {
  426. t.Error(err)
  427. }
  428. ignores, _, err = m.GetIgnores("default")
  429. if err != nil {
  430. t.Error(err)
  431. }
  432. if !arrEqual(ignores, expected) {
  433. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  434. }
  435. ignores, _, err = m.GetIgnores("doesnotexist")
  436. if err == nil {
  437. t.Error("No error")
  438. }
  439. err = m.SetIgnores("doesnotexist", expected)
  440. if err == nil {
  441. t.Error("No error")
  442. }
  443. m.AddFolder(config.FolderConfiguration{ID: "fresh", Path: "XXX"})
  444. ignores, _, err = m.GetIgnores("fresh")
  445. if err != nil {
  446. t.Error(err)
  447. }
  448. if len(ignores) > 0 {
  449. t.Errorf("Expected no ignores, got: %v", ignores)
  450. }
  451. }