model_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
  2. // Use of this source code is governed by an MIT-style license that can be
  3. // found in the LICENSE file.
  4. package model
  5. import (
  6. "bytes"
  7. "fmt"
  8. "os"
  9. "testing"
  10. "time"
  11. "github.com/calmh/syncthing/cid"
  12. "github.com/calmh/syncthing/config"
  13. "github.com/calmh/syncthing/protocol"
  14. "github.com/calmh/syncthing/scanner"
  15. )
  16. var testDataExpected = map[string]scanner.File{
  17. "foo": scanner.File{
  18. Name: "foo",
  19. Flags: 0,
  20. Modified: 0,
  21. Size: 7,
  22. Blocks: []scanner.Block{{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}}},
  23. },
  24. "empty": scanner.File{
  25. Name: "empty",
  26. Flags: 0,
  27. Modified: 0,
  28. Size: 0,
  29. Blocks: []scanner.Block{{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}}},
  30. },
  31. "bar": scanner.File{
  32. Name: "bar",
  33. Flags: 0,
  34. Modified: 0,
  35. Size: 10,
  36. Blocks: []scanner.Block{{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}}},
  37. },
  38. }
  39. func init() {
  40. // Fix expected test data to match reality
  41. for n, f := range testDataExpected {
  42. fi, _ := os.Stat("testdata/" + n)
  43. f.Flags = uint32(fi.Mode())
  44. f.Modified = fi.ModTime().Unix()
  45. testDataExpected[n] = f
  46. }
  47. }
  48. func TestRequest(t *testing.T) {
  49. m := NewModel("/tmp", &config.Configuration{}, "syncthing", "dev")
  50. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  51. m.ScanRepo("default")
  52. bs, err := m.Request("some node", "default", "foo", 0, 6)
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. if bytes.Compare(bs, []byte("foobar")) != 0 {
  57. t.Errorf("Incorrect data from request: %q", string(bs))
  58. }
  59. bs, err = m.Request("some node", "default", "../walk.go", 0, 6)
  60. if err == nil {
  61. t.Error("Unexpected nil error on insecure file read")
  62. }
  63. if bs != nil {
  64. t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
  65. }
  66. }
  67. func genFiles(n int) []protocol.FileInfo {
  68. files := make([]protocol.FileInfo, n)
  69. t := time.Now().Unix()
  70. for i := 0; i < n; i++ {
  71. files[i] = protocol.FileInfo{
  72. Name: fmt.Sprintf("file%d", i),
  73. Modified: t,
  74. Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
  75. }
  76. }
  77. return files
  78. }
  79. func BenchmarkIndex10000(b *testing.B) {
  80. m := NewModel("/tmp", nil, "syncthing", "dev")
  81. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  82. m.ScanRepo("default")
  83. files := genFiles(10000)
  84. b.ResetTimer()
  85. for i := 0; i < b.N; i++ {
  86. m.Index("42", "default", files)
  87. }
  88. }
  89. func BenchmarkIndex00100(b *testing.B) {
  90. m := NewModel("/tmp", nil, "syncthing", "dev")
  91. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  92. m.ScanRepo("default")
  93. files := genFiles(100)
  94. b.ResetTimer()
  95. for i := 0; i < b.N; i++ {
  96. m.Index("42", "default", files)
  97. }
  98. }
  99. func BenchmarkIndexUpdate10000f10000(b *testing.B) {
  100. m := NewModel("/tmp", nil, "syncthing", "dev")
  101. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  102. m.ScanRepo("default")
  103. files := genFiles(10000)
  104. m.Index("42", "default", files)
  105. b.ResetTimer()
  106. for i := 0; i < b.N; i++ {
  107. m.IndexUpdate("42", "default", files)
  108. }
  109. }
  110. func BenchmarkIndexUpdate10000f00100(b *testing.B) {
  111. m := NewModel("/tmp", nil, "syncthing", "dev")
  112. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  113. m.ScanRepo("default")
  114. files := genFiles(10000)
  115. m.Index("42", "default", files)
  116. ufiles := genFiles(100)
  117. b.ResetTimer()
  118. for i := 0; i < b.N; i++ {
  119. m.IndexUpdate("42", "default", ufiles)
  120. }
  121. }
  122. func BenchmarkIndexUpdate10000f00001(b *testing.B) {
  123. m := NewModel("/tmp", nil, "syncthing", "dev")
  124. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  125. m.ScanRepo("default")
  126. files := genFiles(10000)
  127. m.Index("42", "default", files)
  128. ufiles := genFiles(1)
  129. b.ResetTimer()
  130. for i := 0; i < b.N; i++ {
  131. m.IndexUpdate("42", "default", ufiles)
  132. }
  133. }
  134. type FakeConnection struct {
  135. id string
  136. requestData []byte
  137. }
  138. func (FakeConnection) Close() error {
  139. return nil
  140. }
  141. func (f FakeConnection) ID() string {
  142. return string(f.id)
  143. }
  144. func (f FakeConnection) Option(string) string {
  145. return ""
  146. }
  147. func (FakeConnection) Index(string, []protocol.FileInfo) {}
  148. func (f FakeConnection) Request(repo, name string, offset int64, size int) ([]byte, error) {
  149. return f.requestData, nil
  150. }
  151. func (FakeConnection) ClusterConfig(protocol.ClusterConfigMessage) {}
  152. func (FakeConnection) Ping() bool {
  153. return true
  154. }
  155. func (FakeConnection) Statistics() protocol.Statistics {
  156. return protocol.Statistics{}
  157. }
  158. func BenchmarkRequest(b *testing.B) {
  159. m := NewModel("/tmp", nil, "syncthing", "dev")
  160. m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
  161. m.ScanRepo("default")
  162. const n = 1000
  163. files := make([]protocol.FileInfo, n)
  164. t := time.Now().Unix()
  165. for i := 0; i < n; i++ {
  166. files[i] = protocol.FileInfo{
  167. Name: fmt.Sprintf("file%d", i),
  168. Modified: t,
  169. Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
  170. }
  171. }
  172. fc := FakeConnection{
  173. id: "42",
  174. requestData: []byte("some data to return"),
  175. }
  176. m.AddConnection(fc, fc)
  177. m.Index("42", "default", files)
  178. b.ResetTimer()
  179. for i := 0; i < b.N; i++ {
  180. data, err := m.requestGlobal("42", "default", files[i%n].Name, 0, 32, nil)
  181. if err != nil {
  182. b.Error(err)
  183. }
  184. if data == nil {
  185. b.Error("nil data")
  186. }
  187. }
  188. }
  189. func TestActivityMap(t *testing.T) {
  190. cm := cid.NewMap()
  191. fooID := cm.Get("foo")
  192. if fooID == 0 {
  193. t.Fatal("ID cannot be zero")
  194. }
  195. barID := cm.Get("bar")
  196. if barID == 0 {
  197. t.Fatal("ID cannot be zero")
  198. }
  199. m := make(activityMap)
  200. if node := m.leastBusyNode(1<<fooID, cm); node != "foo" {
  201. t.Errorf("Incorrect least busy node %q", node)
  202. }
  203. if node := m.leastBusyNode(1<<barID, cm); node != "bar" {
  204. t.Errorf("Incorrect least busy node %q", node)
  205. }
  206. if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "foo" {
  207. t.Errorf("Incorrect least busy node %q", node)
  208. }
  209. if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "bar" {
  210. t.Errorf("Incorrect least busy node %q", node)
  211. }
  212. }