model_test.go 6.3 KB

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