model_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. package model
  5. import (
  6. "bytes"
  7. "fmt"
  8. "os"
  9. "testing"
  10. "time"
  11. "github.com/syncthing/syncthing/internal/config"
  12. "github.com/syncthing/syncthing/internal/protocol"
  13. "github.com/syndtr/goleveldb/leveldb"
  14. "github.com/syndtr/goleveldb/leveldb/storage"
  15. )
  16. var node1, node2 protocol.NodeID
  17. func init() {
  18. node1, _ = protocol.NodeIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  19. node2, _ = protocol.NodeIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
  20. }
  21. var testDataExpected = map[string]protocol.FileInfo{
  22. "foo": protocol.FileInfo{
  23. Name: "foo",
  24. Flags: 0,
  25. Modified: 0,
  26. 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}}},
  27. },
  28. "empty": protocol.FileInfo{
  29. Name: "empty",
  30. Flags: 0,
  31. Modified: 0,
  32. 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}}},
  33. },
  34. "bar": protocol.FileInfo{
  35. Name: "bar",
  36. Flags: 0,
  37. Modified: 0,
  38. 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}}},
  39. },
  40. }
  41. func init() {
  42. // Fix expected test data to match reality
  43. for n, f := range testDataExpected {
  44. fi, _ := os.Stat("testdata/" + n)
  45. f.Flags = uint32(fi.Mode())
  46. f.Modified = fi.ModTime().Unix()
  47. testDataExpected[n] = f
  48. }
  49. }
  50. func TestRequest(t *testing.T) {
  51. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  52. m := NewModel("/tmp", &config.Configuration{}, "node", "syncthing", "dev", db)
  53. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  54. m.ScanRepo("default")
  55. bs, err := m.Request(node1, "default", "foo", 0, 6)
  56. if err != nil {
  57. t.Fatal(err)
  58. }
  59. if bytes.Compare(bs, []byte("foobar")) != 0 {
  60. t.Errorf("Incorrect data from request: %q", string(bs))
  61. }
  62. bs, err = m.Request(node1, "default", "../walk.go", 0, 6)
  63. if err == nil {
  64. t.Error("Unexpected nil error on insecure file read")
  65. }
  66. if bs != nil {
  67. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  68. }
  69. }
  70. func genFiles(n int) []protocol.FileInfo {
  71. files := make([]protocol.FileInfo, n)
  72. t := time.Now().Unix()
  73. for i := 0; i < n; i++ {
  74. files[i] = protocol.FileInfo{
  75. Name: fmt.Sprintf("file%d", i),
  76. Modified: t,
  77. Blocks: []protocol.BlockInfo{{0, 100, []byte("some hash bytes")}},
  78. }
  79. }
  80. return files
  81. }
  82. func BenchmarkIndex10000(b *testing.B) {
  83. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  84. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  85. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  86. m.ScanRepo("default")
  87. files := genFiles(10000)
  88. b.ResetTimer()
  89. for i := 0; i < b.N; i++ {
  90. m.Index(node1, "default", files)
  91. }
  92. }
  93. func BenchmarkIndex00100(b *testing.B) {
  94. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  95. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  96. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  97. m.ScanRepo("default")
  98. files := genFiles(100)
  99. b.ResetTimer()
  100. for i := 0; i < b.N; i++ {
  101. m.Index(node1, "default", files)
  102. }
  103. }
  104. func BenchmarkIndexUpdate10000f10000(b *testing.B) {
  105. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  106. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  107. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  108. m.ScanRepo("default")
  109. files := genFiles(10000)
  110. m.Index(node1, "default", files)
  111. b.ResetTimer()
  112. for i := 0; i < b.N; i++ {
  113. m.IndexUpdate(node1, "default", files)
  114. }
  115. }
  116. func BenchmarkIndexUpdate10000f00100(b *testing.B) {
  117. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  118. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  119. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  120. m.ScanRepo("default")
  121. files := genFiles(10000)
  122. m.Index(node1, "default", files)
  123. ufiles := genFiles(100)
  124. b.ResetTimer()
  125. for i := 0; i < b.N; i++ {
  126. m.IndexUpdate(node1, "default", ufiles)
  127. }
  128. }
  129. func BenchmarkIndexUpdate10000f00001(b *testing.B) {
  130. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  131. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  132. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  133. m.ScanRepo("default")
  134. files := genFiles(10000)
  135. m.Index(node1, "default", files)
  136. ufiles := genFiles(1)
  137. b.ResetTimer()
  138. for i := 0; i < b.N; i++ {
  139. m.IndexUpdate(node1, "default", ufiles)
  140. }
  141. }
  142. type FakeConnection struct {
  143. id protocol.NodeID
  144. requestData []byte
  145. }
  146. func (FakeConnection) Close() error {
  147. return nil
  148. }
  149. func (f FakeConnection) ID() protocol.NodeID {
  150. return f.id
  151. }
  152. func (f FakeConnection) Name() string {
  153. return ""
  154. }
  155. func (f FakeConnection) Option(string) string {
  156. return ""
  157. }
  158. func (FakeConnection) Index(string, []protocol.FileInfo) error {
  159. return nil
  160. }
  161. func (FakeConnection) IndexUpdate(string, []protocol.FileInfo) error {
  162. return nil
  163. }
  164. func (f FakeConnection) Request(repo, name string, offset int64, size int) ([]byte, error) {
  165. return f.requestData, nil
  166. }
  167. func (FakeConnection) ClusterConfig(protocol.ClusterConfigMessage) {}
  168. func (FakeConnection) Ping() bool {
  169. return true
  170. }
  171. func (FakeConnection) Statistics() protocol.Statistics {
  172. return protocol.Statistics{}
  173. }
  174. func BenchmarkRequest(b *testing.B) {
  175. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  176. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  177. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  178. m.ScanRepo("default")
  179. const n = 1000
  180. files := make([]protocol.FileInfo, n)
  181. t := time.Now().Unix()
  182. for i := 0; i < n; i++ {
  183. files[i] = protocol.FileInfo{
  184. Name: fmt.Sprintf("file%d", i),
  185. Modified: t,
  186. Blocks: []protocol.BlockInfo{{0, 100, []byte("some hash bytes")}},
  187. }
  188. }
  189. fc := FakeConnection{
  190. id: node1,
  191. requestData: []byte("some data to return"),
  192. }
  193. m.AddConnection(fc, fc)
  194. m.Index(node1, "default", files)
  195. b.ResetTimer()
  196. for i := 0; i < b.N; i++ {
  197. data, err := m.requestGlobal(node1, "default", files[i%n].Name, 0, 32, nil)
  198. if err != nil {
  199. b.Error(err)
  200. }
  201. if data == nil {
  202. b.Error("nil data")
  203. }
  204. }
  205. }
  206. func TestActivityMap(t *testing.T) {
  207. isValid := func(protocol.NodeID) bool {
  208. return true
  209. }
  210. m := make(activityMap)
  211. if node := m.leastBusyNode([]protocol.NodeID{node1}, isValid); node != node1 {
  212. t.Errorf("Incorrect least busy node %q", node)
  213. }
  214. if node := m.leastBusyNode([]protocol.NodeID{node2}, isValid); node != node2 {
  215. t.Errorf("Incorrect least busy node %q", node)
  216. }
  217. if node := m.leastBusyNode([]protocol.NodeID{node1, node2}, isValid); node != node1 {
  218. t.Errorf("Incorrect least busy node %q", node)
  219. }
  220. if node := m.leastBusyNode([]protocol.NodeID{node1, node2}, isValid); node != node2 {
  221. t.Errorf("Incorrect least busy node %q", node)
  222. }
  223. }
  224. func TestNodeRename(t *testing.T) {
  225. ccm := protocol.ClusterConfigMessage{
  226. ClientName: "syncthing",
  227. ClientVersion: "v0.9.4",
  228. }
  229. cfg := config.New("/tmp/test", node1)
  230. cfg.Nodes = []config.NodeConfiguration{
  231. {
  232. NodeID: node1,
  233. },
  234. }
  235. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  236. m := NewModel("/tmp", &cfg, "node", "syncthing", "dev", db)
  237. if cfg.Nodes[0].Name != "" {
  238. t.Errorf("Node already has a name")
  239. }
  240. m.ClusterConfig(node1, ccm)
  241. if cfg.Nodes[0].Name != "" {
  242. t.Errorf("Node already has a name")
  243. }
  244. ccm.Options = []protocol.Option{
  245. {
  246. Key: "name",
  247. Value: "tester",
  248. },
  249. }
  250. m.ClusterConfig(node1, ccm)
  251. if cfg.Nodes[0].Name != "tester" {
  252. t.Errorf("Node did not get a name")
  253. }
  254. ccm.Options[0].Value = "tester2"
  255. m.ClusterConfig(node1, ccm)
  256. if cfg.Nodes[0].Name != "tester" {
  257. t.Errorf("Node name got overwritten")
  258. }
  259. }
  260. func TestClusterConfig(t *testing.T) {
  261. cfg := config.New("/tmp/test", node1)
  262. cfg.Nodes = []config.NodeConfiguration{
  263. {
  264. NodeID: node1,
  265. Introducer: true,
  266. },
  267. {
  268. NodeID: node2,
  269. },
  270. }
  271. cfg.Repositories = []config.RepositoryConfiguration{
  272. {
  273. ID: "repo1",
  274. Nodes: []config.RepositoryNodeConfiguration{
  275. {NodeID: node1},
  276. {NodeID: node2},
  277. },
  278. },
  279. {
  280. ID: "repo2",
  281. Nodes: []config.RepositoryNodeConfiguration{
  282. {NodeID: node1},
  283. {NodeID: node2},
  284. },
  285. },
  286. }
  287. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  288. m := NewModel("/tmp", &cfg, "node", "syncthing", "dev", db)
  289. m.AddRepo(cfg.Repositories[0])
  290. m.AddRepo(cfg.Repositories[1])
  291. cm := m.clusterConfig(node2)
  292. if l := len(cm.Repositories); l != 2 {
  293. t.Fatalf("Incorrect number of repos %d != 2", l)
  294. }
  295. r := cm.Repositories[0]
  296. if r.ID != "repo1" {
  297. t.Errorf("Incorrect repo %q != repo1", r.ID)
  298. }
  299. if l := len(r.Nodes); l != 2 {
  300. t.Errorf("Incorrect number of nodes %d != 2", l)
  301. }
  302. if id := r.Nodes[0].ID; bytes.Compare(id, node1[:]) != 0 {
  303. t.Errorf("Incorrect node ID %x != %x", id, node1)
  304. }
  305. if r.Nodes[0].Flags&protocol.FlagIntroducer == 0 {
  306. t.Error("Node1 should be flagged as Introducer")
  307. }
  308. if id := r.Nodes[1].ID; bytes.Compare(id, node2[:]) != 0 {
  309. t.Errorf("Incorrect node ID %x != %x", id, node2)
  310. }
  311. if r.Nodes[1].Flags&protocol.FlagIntroducer != 0 {
  312. t.Error("Node2 should not be flagged as Introducer")
  313. }
  314. r = cm.Repositories[1]
  315. if r.ID != "repo2" {
  316. t.Errorf("Incorrect repo %q != repo2", r.ID)
  317. }
  318. if l := len(r.Nodes); l != 2 {
  319. t.Errorf("Incorrect number of nodes %d != 2", l)
  320. }
  321. if id := r.Nodes[0].ID; bytes.Compare(id, node1[:]) != 0 {
  322. t.Errorf("Incorrect node ID %x != %x", id, node1)
  323. }
  324. if r.Nodes[0].Flags&protocol.FlagIntroducer == 0 {
  325. t.Error("Node1 should be flagged as Introducer")
  326. }
  327. if id := r.Nodes[1].ID; bytes.Compare(id, node2[:]) != 0 {
  328. t.Errorf("Incorrect node ID %x != %x", id, node2)
  329. }
  330. if r.Nodes[1].Flags&protocol.FlagIntroducer != 0 {
  331. t.Error("Node2 should not be flagged as Introducer")
  332. }
  333. }
  334. func TestIgnores(t *testing.T) {
  335. arrEqual := func(a, b []string) bool {
  336. if len(a) != len(b) {
  337. return false
  338. }
  339. for i := range a {
  340. if a[i] != b[i] {
  341. return false
  342. }
  343. }
  344. return true
  345. }
  346. db, _ := leveldb.Open(storage.NewMemStorage(), nil)
  347. m := NewModel("/tmp", nil, "node", "syncthing", "dev", db)
  348. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  349. expected := []string{
  350. ".*",
  351. "quux",
  352. }
  353. ignores, err := m.GetIgnores("default")
  354. if err != nil {
  355. t.Error(err)
  356. }
  357. if !arrEqual(ignores, expected) {
  358. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  359. }
  360. ignores = append(ignores, "pox")
  361. err = m.SetIgnores("default", ignores)
  362. if err != nil {
  363. t.Error(err)
  364. }
  365. ignores2, err := m.GetIgnores("default")
  366. if err != nil {
  367. t.Error(err)
  368. }
  369. if arrEqual(expected, ignores2) {
  370. t.Errorf("Incorrect ignores: %v == %v", ignores2, expected)
  371. }
  372. if !arrEqual(ignores, ignores2) {
  373. t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
  374. }
  375. err = m.SetIgnores("default", expected)
  376. if err != nil {
  377. t.Error(err)
  378. }
  379. ignores, err = m.GetIgnores("default")
  380. if err != nil {
  381. t.Error(err)
  382. }
  383. if !arrEqual(ignores, expected) {
  384. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  385. }
  386. ignores, err = m.GetIgnores("doesnotexist")
  387. if err == nil {
  388. t.Error("No error")
  389. }
  390. err = m.SetIgnores("doesnotexist", expected)
  391. if err == nil {
  392. t.Error("No error")
  393. }
  394. m.AddRepo(config.RepositoryConfiguration{ID: "fresh", Directory: "XXX"})
  395. ignores, err = m.GetIgnores("fresh")
  396. if err != nil {
  397. t.Error(err)
  398. }
  399. if len(ignores) > 0 {
  400. t.Errorf("Expected no ignores, got: %v", ignores)
  401. }
  402. }